# Function to crop an image: def crop_image(orig_img, cropped_min_x, cropped_max_x, cropped_min_y, cropped_max_y): '''Crops an image object of type <class 'bpy.types.Image'>. For example, for a 10x10 image, if you put cropped_min_x = 2 and cropped_max_x = 6, you would get back a cropped image with width 4, and pixels ranging from the 2 to 5 in the x-coordinate Note: here y increasing as you down the image. So, if cropped_min_x and cropped_min_y are both zero, you'll get the top-left of the image (as in GIMP). Returns: An image of type <class 'bpy.types.Image'> ''' num_channels=orig_img.channels #calculate cropped image size cropped_size_x = cropped_max_x - cropped_min_x cropped_size_y = cropped_max_y - cropped_min_y #original image size orig_size_x = orig_img.size[0] orig_size_y = orig_img.size[1] cropped_img = bpy.data.images.new(name="cropped_img", width=cropped_size_x, height=cropped_size_y) print("Exctracting image fragment, this could take a while...") #loop through each row of the cropped image grabbing the appropriate pixels from original #the reason for the strange limits is because of the #order that Blender puts pixels into a 1-D array. current_cropped_row = 0 for yy in range(orig_size_y - cropped_max_y, orig_size_y - cropped_min_y): #the index we start at for copying this row of pixels from the original image orig_start_index = (cropped_min_x + yy*orig_size_x) * num_channels #and to know where to stop we add the amount of pixels we must copy orig_end_index = orig_start_index + (cropped_size_x * num_channels) #the index we start at for the cropped image cropped_start_index = (current_cropped_row * cropped_size_x) * num_channels cropped_end_index = cropped_start_index + (cropped_size_x * num_channels) #copy over pixels cropped_img.pixels[cropped_start_index : cropped_end_index] = orig_img.pixels[orig_start_index : orig_end_index] #move to the next row before restarting loop current_cropped_row += 1 return cropped_img # How this script works The image class stores the pixel information as a one-dimensional array, and save 4 channels for RGBA (even if you load a .jpg). For example, on this 2x2 image, I've labelled the order that the pixels appear: ![enter image description here][1] and so if you have an image object named `my_image`, then `my_image.pixels` will be a 1-D array: [R value for pixel 0, G value for 0, B value for 0, A value for 0, R value for pixel 1, etc.]. # Example usage #crop image to 100x100 square cropped_min_x = 300 cropped_max_x = 400 cropped_min_y = 300 cropped_max_y = 400 input_image_filepath='/home/garrett/Desktop/kjEh.jpg' orig_img = bpy.data.images.load(input_image_filepath) cropped_img = crop_image(orig_img, cropped_min_x, cropped_max_x, cropped_min_y, cropped_max_y) print("Saving new image...") cropped_img.filepath_raw = "/home/garrett/Desktop/myImage6.png" cropped_img.file_format = 'PNG' cropped_img.save() print("Finished saving") [1]: https://i.sstatic.net/dA4EM.png