How can I crop an image in the center? Because I know that the box is a 4-tuple defining the left, upper, right, and lower pixel coordinate but I don't know how to get these coordinates so it crops in the center.
-
1you do know the size of the image and the size of the smaller image you want to get, I assume? Have you anything to show where you tried? – Thomas Fenzl May 20 '13 at 10:21
8 Answers
Assuming you know the size you would like to crop to (new_width X new_height):
import Image
im = Image.open(<your image>)
width, height = im.size # Get dimensions
left = (width - new_width)/2
top = (height - new_height)/2
right = (width + new_width)/2
bottom = (height + new_height)/2
# Crop the center of the image
im = im.crop((left, top, right, bottom))
This will break if you attempt to crop a small image larger, but I'm going to assume you won't be trying that (Or that you can catch that case and not crop the image).

- 13
- 5

- 2,103
- 2
- 14
- 19
-
Small correction, crop takes an array. im.crop((left, top, right, bottom)) – freakTheMighty Aug 20 '13 at 21:37
-
1
-
4A catch is that `im.crop()` does not crop the image in-place but return the cropped image. Hence, `im=im.crop((left, top, right, bottom))` is required. – jeanggi90 Apr 30 '19 at 16:06
-
1This code also breaks if (width - new_width)/2 != int if int is forced by casting, the pixel-count of the output image may not be consistent, which is a problem in ML where a certain data format is expected. wait ... am I missing something? – maxalmond Apr 02 '20 at 09:47
-
@maxalmond rounding happens and will be the same for left,right and for top, bottom, so the diff after rounding will always be exactly new_width and new_height – andre_bauer Apr 22 '20 at 14:50
-
Unfortunately, the rounding is a problem indeed. e.g. assume you have `width, height = 357,450`, `new_width = new_height = 357` you end up with a 357×358 image. You have to make the explicit decision, where to crop that extra pixel, either top or bottom in this case. – tuky Mar 01 '21 at 13:25
One potential problem with the proposed solution is in the case there is an odd difference between the desired size, and old size. You can't have a half pixel on each side. One has to choose a side to put an extra pixel on.
If there is an odd difference for the horizontal the code below will put the extra pixel to the right, and if there is and odd difference on the vertical the extra pixel goes to the bottom.
import numpy as np
def center_crop(img, new_width=None, new_height=None):
width = img.shape[1]
height = img.shape[0]
if new_width is None:
new_width = min(width, height)
if new_height is None:
new_height = min(width, height)
left = int(np.ceil((width - new_width) / 2))
right = width - int(np.floor((width - new_width) / 2))
top = int(np.ceil((height - new_height) / 2))
bottom = height - int(np.floor((height - new_height) / 2))
if len(img.shape) == 2:
center_cropped_img = img[top:bottom, left:right]
else:
center_cropped_img = img[top:bottom, left:right, ...]
return center_cropped_img

- 4,956
- 7
- 42
- 64

- 159
- 1
- 3
-
5rounding is incorrect, the result image will not always have the required dimensions. Correct formula is `right = width - floor((width - new_width) / 2)` – panda-34 May 03 '16 at 15:33
I feel like the simplest solution that is most suitable for most applications is still missing. The accepted answer has an issue with uneven pixels and especially for ML algorithms, the pixel count of the cropped image is paramount.
In the following example, I would like to crop an image to 224/100, from the center. I do not care if the pixels are shifted to the left or right by 0.5, as long as the output picture will always be of the defined dimensions. It avoids the reliance on math.*.
from PIL import Image
import matplotlib.pyplot as plt
im = Image.open("test.jpg")
left = int(im.size[0]/2-224/2)
upper = int(im.size[1]/2-100/2)
right = left +224
lower = upper + 100
im_cropped = im.crop((left, upper,right,lower))
print(im_cropped.size)
plt.imshow(np.asarray(im_cropped))
The output is before cropping (not shown in code):
after:
The touples show the dimensions.

- 375
- 3
- 9
This is the function I was looking for:
from PIL import Image
im = Image.open("test.jpg")
crop_rectangle = (50, 50, 200, 200)
cropped_im = im.crop(crop_rectangle)
cropped_im.show()
Taken from another answer

- 4,743
- 35
- 37
I originally used the accepted answer:
import Image
im = Image.open(<your image>)
width, height = im.size # Get dimensions
left = (width - new_width)/2
top = (height - new_height)/2
right = (width + new_width)/2
bottom = (height + new_height)/2
# Crop the center of the image
im = im.crop((left, top, right, bottom))
But I came into the problem mentioned by Dean Pospisil
One potential problem with the proposed solution is in the case there is an odd difference between the desired size, and old size. You can't have a half pixel on each side. One has to choose a side to put an extra pixel on.
Dean Pospisil's solution works, I also came up with my own calculation to fix this:
import Image
im = Image.open(<your image>)
width, height = im.size # Get dimensions
left = round((width - new_width)/2)
top = round((height - new_height)/2)
x_right = round(width - new_width) - left
x_bottom = round(height - new_height) - top
right = width - x_right
bottom = height - x_bottom
# Crop the center of the image
im = im.crop((left, top, right, bottom))
With the accepted answer, an image of 180px x 180px
to be cropped to 180px x 101px
will result in a cropped image to 180px x 102px
.
With my calculation, it will be correctly cropped to 180px x 101px

- 103
- 7
Crop center and around:
def im_crop_around(img, xc, yc, w, h):
img_width, img_height = img.size # Get dimensions
left, right = xc - w / 2, xc + w / 2
top, bottom = yc - h / 2, yc + h / 2
left, top = round(max(0, left)), round(max(0, top))
right, bottom = round(min(img_width - 0, right)), round(min(img_height - 0, bottom))
return img.crop((left, top, right, bottom))
def im_crop_center(img, w, h):
img_width, img_height = img.size
left, right = (img_width - w) / 2, (img_width + w) / 2
top, bottom = (img_height - h) / 2, (img_height + h) / 2
left, top = round(max(0, left)), round(max(0, top))
right, bottom = round(min(img_width - 0, right)), round(min(img_height - 0, bottom))
return img.crop((left, top, right, bottom))

- 3,350
- 1
- 23
- 24
May be i am late to this party but at least i am here I want to center crop the image convert 9:16 image to 16:9 portrait to landscape
This is the algo i used :
- divide image in 4 equal parts
- discard part 1 and part four
- Set left to 0, right to width of image
code :
from PIL import Image
im = Image.open('main.jpg')
width, height = im.size
if height > width:
h2 = height/2
h4 = h2/2
border = (0, h4, width, h4*3)
cropped_img = im.crop(border)
cropped_img.save("test.jpg")
before :
after:

- 1,589
- 1
- 14
- 18
You could use Torchvision's CenterCrop transformation for this. Here's an example
from PIL import Image
from torchvision.transforms import functional as F
crop_size = 256 # can be either an integer or a tuple of ints for (height, width) separately
img = Image.open(<path_to_your_image>)
cropped_img = F.center_crop(img, crop_size)
F.center_crop
works with torch.Tensor
s or PIL.Image
s and retains the data type i.e. when input is a PIL.Image
then output is also a (cropped) PIL.Image
. An added bonus is that the above transformation would automatically apply padding in case the input image size is smaller than the requested crop size.

- 399
- 3
- 6