裁剪numpy图像的中心部分

47
假设我有一张numpy图像,宽度为x,高度为y。 我需要裁剪图像中心部分到宽度cropx和高度cropy。假设cropx和cropy是正的非零整数,并且小于相应的图像尺寸。如何最好地对输出图像应用切片?
6个回答

69

在这个方向上有类似的事情 -

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]

运行示例 -

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]])

23

基于@Divakar的答案,有一个更一般化的解决方案:

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]

如果我们有一个数组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]])

我们可以使用 cropND(a, (5,5)) 进行裁剪,得到:

>>> 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]])

它不仅适用于2D图像,也适用于3D图像。

祝您有美好的一天。


2
为什么这个没有更多的赞?图像通常可以有多个通道(3D)。 - simplename
你好,我有一个三维图像,形状为(WxHxD) (281, 389, 104),但是当我尝试运行cropND时,我收到了一个错误:Traceback (most recent call last): File "<pyshell#42>", line 1, in <module> cropND(img,(256,256)) File "<pyshell#27>", line 2, in cropND start = tuple(map(lambda a, da: a//2-da//2, img.shape, bounding)) File "<pyshell#27>", line 2, in <lambda> start = tuple(map(lambda a, da: a//2-da//2, img.shape, bounding)) TypeError: unsupported operand type(s) for //: 'NoneType' and 'int' - S.EB
@S.EB 试试这样的代码 cropND(img, (256, 256, 104)) - Losses Don

2
我曾遇到一个问题,需要从2D和3D数组中裁剪中心部分。这意味着需要从 img.shape 中解包不同数量的项。

如果有人遇到相同的问题,可以参考这个修改版的Divkar解决方案,可以裁剪2D或3D数组。

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, ...]

2
@Divakar的回答基础上进行简单修改,同时保留图像通道:

最初的回答

    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]

2

谢谢,Divakar。

你的回答让我朝着正确的方向前进。我使用负数切片偏移量来计算“从结尾开始”:

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

你可能希望使用 // 而不是 /,以避免将它们作为浮点数使用,因为在 Python 3.x 版本中,它们可能无法用于索引,至少我这样猜想。 - Divakar
我会把这个“答案”中的问题删除。 - simplename
1
这对于奇数不起作用。例如,将101宽的图像裁剪为50将导致51宽的图像! - Flamefire

1

这是对 @Divakar 答案的简单修改,以保留颜色通道:

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,:]

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接