crop center portion of a numpy image
Question:
Let’s say I have a numpy image of some width x and height y.
I have to crop the center portion of the image to width cropx and height cropy. Let’s assume that cropx and cropy are positive non zero integers and less than the respective image size. What’s the best way to apply the slicing for the output image?
Answers:
Something along these lines –
def crop_center(img,cropx,cropy):
y,x = img.shape
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[starty:starty+cropy,startx:startx+cropx]
Sample run –
In [45]: img
Out[45]:
array([[88, 93, 42, 25, 36, 14, 59, 46, 77, 13, 52, 58],
[43, 47, 40, 48, 23, 74, 12, 33, 58, 93, 87, 87],
[54, 75, 79, 21, 15, 44, 51, 68, 28, 94, 78, 48],
[57, 46, 14, 98, 43, 76, 86, 56, 86, 88, 96, 49],
[52, 83, 13, 18, 40, 33, 11, 87, 38, 74, 23, 88],
[81, 28, 86, 89, 16, 28, 66, 67, 80, 23, 95, 98],
[46, 30, 18, 31, 73, 15, 90, 77, 71, 57, 61, 78],
[33, 58, 20, 11, 80, 25, 96, 80, 27, 40, 66, 92],
[13, 59, 77, 53, 91, 16, 47, 79, 33, 78, 25, 66],
[22, 80, 40, 24, 17, 85, 20, 70, 81, 68, 50, 80]])
In [46]: crop_center(img,4,6)
Out[46]:
array([[15, 44, 51, 68],
[43, 76, 86, 56],
[40, 33, 11, 87],
[16, 28, 66, 67],
[73, 15, 90, 77],
[80, 25, 96, 80]])
Thanks, Divakar.
Your answer got me going the right direction. I came up with this using negative slice offsets to count ‘from the end’:
def cropimread(crop, xcrop, ycrop, fn):
"Function to crop center of an image file"
img_pre= msc.imread(fn)
if crop:
ysize, xsize, chan = img_pre.shape
xoff = (xsize - xcrop) // 2
yoff = (ysize - ycrop) // 2
img= img_pre[yoff:-yoff,xoff:-xoff]
else:
img= img_pre
return img
A more general solution based on @Divakar ‘s answer:
def cropND(img, bounding):
start = tuple(map(lambda a, da: a//2-da//2, img.shape, bounding))
end = tuple(map(operator.add, start, bounding))
slices = tuple(map(slice, start, end))
return img[slices]
and if we have an array a
>>> a = np.arange(100).reshape((10,10))
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
We can clip it with cropND(a, (5,5))
, you will get:
>>> cropND(a, (5,5))
array([[33, 34, 35, 36, 37],
[43, 44, 45, 46, 47],
[53, 54, 55, 56, 57],
[63, 64, 65, 66, 67],
[73, 74, 75, 76, 77]])
It not only works with 2D image but also 3D image.
Have a nice day.
A simple modification from @Divakar ‘s answer that preserves the image channel:
def crop_center(self, img, cropx, cropy):
_, y, x = img.shape
startx = x // 2 - (cropx // 2)
starty = y // 2 - (cropy // 2)
return img[:, starty:starty + cropy, startx:startx + cropx]
Another simple modification from @Divakar’s answer to preserve color channels:
def crop_center(img,cropx,cropy):
y,x,_ = img.shape
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[starty:starty+cropy,startx:startx+cropx,:]
I had a problem in which I needed to crop the center from both 2D and 3D arrays. That meant unpacking a varying number of items from img.shape
.
Dropping this here in case someone runs into the same problem. This modification of Divkar’s solution allows cropping 2D OR 3D arrays
def crop_center(img, cropx, cropy):
y, x, *_ = img.shape
startx = x // 2 - (cropx // 2)
starty = y // 2 - (cropy // 2)
return img[starty:starty + cropy, startx:startx + cropx, ...]
Let’s say I have a numpy image of some width x and height y.
I have to crop the center portion of the image to width cropx and height cropy. Let’s assume that cropx and cropy are positive non zero integers and less than the respective image size. What’s the best way to apply the slicing for the output image?
Something along these lines –
def crop_center(img,cropx,cropy):
y,x = img.shape
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[starty:starty+cropy,startx:startx+cropx]
Sample run –
In [45]: img
Out[45]:
array([[88, 93, 42, 25, 36, 14, 59, 46, 77, 13, 52, 58],
[43, 47, 40, 48, 23, 74, 12, 33, 58, 93, 87, 87],
[54, 75, 79, 21, 15, 44, 51, 68, 28, 94, 78, 48],
[57, 46, 14, 98, 43, 76, 86, 56, 86, 88, 96, 49],
[52, 83, 13, 18, 40, 33, 11, 87, 38, 74, 23, 88],
[81, 28, 86, 89, 16, 28, 66, 67, 80, 23, 95, 98],
[46, 30, 18, 31, 73, 15, 90, 77, 71, 57, 61, 78],
[33, 58, 20, 11, 80, 25, 96, 80, 27, 40, 66, 92],
[13, 59, 77, 53, 91, 16, 47, 79, 33, 78, 25, 66],
[22, 80, 40, 24, 17, 85, 20, 70, 81, 68, 50, 80]])
In [46]: crop_center(img,4,6)
Out[46]:
array([[15, 44, 51, 68],
[43, 76, 86, 56],
[40, 33, 11, 87],
[16, 28, 66, 67],
[73, 15, 90, 77],
[80, 25, 96, 80]])
Thanks, Divakar.
Your answer got me going the right direction. I came up with this using negative slice offsets to count ‘from the end’:
def cropimread(crop, xcrop, ycrop, fn):
"Function to crop center of an image file"
img_pre= msc.imread(fn)
if crop:
ysize, xsize, chan = img_pre.shape
xoff = (xsize - xcrop) // 2
yoff = (ysize - ycrop) // 2
img= img_pre[yoff:-yoff,xoff:-xoff]
else:
img= img_pre
return img
A more general solution based on @Divakar ‘s answer:
def cropND(img, bounding):
start = tuple(map(lambda a, da: a//2-da//2, img.shape, bounding))
end = tuple(map(operator.add, start, bounding))
slices = tuple(map(slice, start, end))
return img[slices]
and if we have an array a
>>> a = np.arange(100).reshape((10,10))
array([[ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9],
[10, 11, 12, 13, 14, 15, 16, 17, 18, 19],
[20, 21, 22, 23, 24, 25, 26, 27, 28, 29],
[30, 31, 32, 33, 34, 35, 36, 37, 38, 39],
[40, 41, 42, 43, 44, 45, 46, 47, 48, 49],
[50, 51, 52, 53, 54, 55, 56, 57, 58, 59],
[60, 61, 62, 63, 64, 65, 66, 67, 68, 69],
[70, 71, 72, 73, 74, 75, 76, 77, 78, 79],
[80, 81, 82, 83, 84, 85, 86, 87, 88, 89],
[90, 91, 92, 93, 94, 95, 96, 97, 98, 99]])
We can clip it with cropND(a, (5,5))
, you will get:
>>> cropND(a, (5,5))
array([[33, 34, 35, 36, 37],
[43, 44, 45, 46, 47],
[53, 54, 55, 56, 57],
[63, 64, 65, 66, 67],
[73, 74, 75, 76, 77]])
It not only works with 2D image but also 3D image.
Have a nice day.
A simple modification from @Divakar ‘s answer that preserves the image channel:
def crop_center(self, img, cropx, cropy):
_, y, x = img.shape
startx = x // 2 - (cropx // 2)
starty = y // 2 - (cropy // 2)
return img[:, starty:starty + cropy, startx:startx + cropx]
Another simple modification from @Divakar’s answer to preserve color channels:
def crop_center(img,cropx,cropy):
y,x,_ = img.shape
startx = x//2-(cropx//2)
starty = y//2-(cropy//2)
return img[starty:starty+cropy,startx:startx+cropx,:]
I had a problem in which I needed to crop the center from both 2D and 3D arrays. That meant unpacking a varying number of items from img.shape
.
Dropping this here in case someone runs into the same problem. This modification of Divkar’s solution allows cropping 2D OR 3D arrays
def crop_center(img, cropx, cropy):
y, x, *_ = img.shape
startx = x // 2 - (cropx // 2)
starty = y // 2 - (cropy // 2)
return img[starty:starty + cropy, startx:startx + cropx, ...]