PIL之Image.crop的box参数超界的问题

PIL之Image.crop的box参数超界的问题

结论

Image.crop(box=None)
Returns a rectangular region from this image. The box is a 4-tuple defining the left, upper, right, and lower pixel coordinate.

This is a lazy operation. Changes to the source image may or may not be reflected in the cropped image. To break the connection, call the load() method on the cropped copy.

Parameters:	box – The crop rectangle, as a (left, upper, right, lower)-tuple.
Return type:	Image
Returns:	An Image object.

这里的crop对于box参数超出图片的时候,不会报错,对于超出的部分会用0代替

分析

先看这样一段代码:

class RandomCrop(object):
    def __init__(self, size, padding=0):
        if isinstance(size, numbers.Number):
            self.size = (int(size), int(size))
        else:
            self.size = size
        self.padding = padding

    def __call__(self, img, mask):
        if self.padding > 0:
            img = ImageOps.expand(img, border=self.padding, fill=0)
            mask = ImageOps.expand(mask, border=self.padding, fill=0)

        assert img.size == mask.size
        w, h = img.size
        th, tw = self.size
        if w == tw and h == th:
            return img, mask
        if w < tw or h < th:
            return img.resize((tw, th), Image.BILINEAR), mask.resize((tw, th), Image.NEAREST)

        x1 = random.randint(0, w - tw)
        y1 = random.randint(0, h - th)
        return img.crop((x1, y1, x1 + tw, y1 + th)), mask.crop((x1, y1, x1 + tw, y1 + th))

这段代码主要是用来在pytorch的代码中做一个数据的预处理的。逻辑很简单,但是这里因为代码“乱改”的时候,遇到了一个出乎意料的地方,这里是我改后的代码,我已经忘了当时为什么这样改动了,但是代码就清清楚楚那样写着,我也不好推脱。

class RandomCrop(object):
    def __init__(self, size, size1, padding=0):
        if isinstance(size, numbers.Number):
            self.size = (int(size), int(size1))
        else:
            self.size = size
        self.padding = padding

    def __call__(self, img, mask):
        if self.padding > 0:
            img = ImageOps.expand(img, border=self.padding, fill=0)
            mask = ImageOps.expand(mask, border=self.padding, fill=0)

        assert img.size == mask.size
        w, h = img.size
        if w >= h:
            w = 640
            h = int(640 * (h / w))
        else:
            w = int(640 * (w / h))
            h = 640

        th, tw = self.size
        if w == tw and h == th:
            return img, mask
        if w < tw or h < th:
            return img.resize((tw, th), Image.BILINEAR), \
                   mask.resize((tw, th), Image.NEAREST)

        x1 = random.randint(0, w - tw)
        y1 = random.randint(0, h - th)
        print("大于", (x1, y1, x1 + tw, y1 + th), img.size, mask.size)
        return img.crop((x1, y1, x1 + tw, y1 + th)), \
               mask.crop((x1, y1, x1 + tw, y1 + th))

上面的代码中,需要关注的一个改动是那个if:

if w >= h:
    w = 640
    h = int(640 * (h / w))
else:
    w = int(640 * (w / h))
    h = 640

因为这个,连带引起了后面一个想不到的错误。加了它之后,对于我的小尺寸(tw,th)而言,基本上都会直接使用跳过中间的两个if判断。

先来放一下测试代码:

class RandomCrop(object):
    def __init__(self, size, size1, padding=0):
        if isinstance(size, numbers.Number):
            self.size = (int(size), int(size1))
        else:
            self.size = size
        self.padding = padding

    @pysnooper.snoop()
    def __call__(self, img, mask):
        if self.padding > 0:
            img = ImageOps.expand(img, border=self.padding, fill=0)
            mask = ImageOps.expand(mask, border=self.padding, fill=0)

        assert img.size == mask.size
        w, h = img.size
        if w >= h:
            w = 640
            h = int(640 * (h / w))
        else:
            w = int(640 * (w / h))
            h = 640

        th, tw = self.size
        if w == tw and h == th:
            return img, mask
        if w < tw or h < th:
            return img.resize((tw, th), Image.BILINEAR), \
                   mask.resize((tw, th), Image.NEAREST)

        x1 = random.randint(0, w - tw)
        y1 = random.randint(0, h - th)
        print("大于", (x1, y1, x1 + tw, y1 + th), img.size, mask.size)
        return img.crop((x1, y1, x1 + tw, y1 + th)), \
               mask.crop((x1, y1, x1 + tw, y1 + th))


if __name__ == '__main__':
    a = torch.randn((3, 320, 320))
    b = torch.randn((1, 320, 320))
    to_pil = transforms.ToPILImage()
    crop_rand = RandomCrop(320, 320)

    a = to_pil(a)
    b = to_pil(b)
    a, b = crop_rand(a, b)
    print(np.asarray(a), np.asarray(b))

输出是这样的:

Starting var:.. mask = <PIL.Image.Image image mode=L size=320x320 at 0x7F387886FC18>
Starting var:.. img = <PIL.Image.Image image mode=RGB size=320x320 at 0x7F38B7902EB8>
Starting var:.. self = <__main__.RandomCrop object at 0x7f387886f940>
16:31:46.754249 call        30     @pysnooper.snoop()
16:31:46.754382 line        32         if self.padding > 0:
16:31:46.754407 line        36         assert img.size == mask.size
16:31:46.754430 line        37         w, h = img.size
New var:....... h = 320
New var:....... w = 320
16:31:46.754462 line        38         if w >= h:
16:31:46.754482 line        39             w = 640
Modified var:.. w = 640
16:31:46.754508 line        40             h = int(640 * (h / w))
16:31:46.754529 line        45         th, tw = self.size
New var:....... tw = 320
New var:....... th = 320
16:31:46.754560 line        46         if w == tw and h == th:
16:31:46.754579 line        48         if w < tw or h < th:
16:31:46.754599 line        52         x1 = random.randint(0, w - tw)
New var:....... x1 = 224
16:31:46.754659 line        53         y1 = random.randint(0, h - th)
New var:....... y1 = 0
16:31:46.754708 line        54         print("大于", (x1, y1, x1 + tw, y1 + th), img.size, mask.size)
16:31:46.754748 line        55         return img.crop((x1, y1, x1 + tw, y1 + th)), \
16:31:46.754848 line        56                mask.crop((x1, y1, x1 + tw, y1 + th))
16:31:46.754915 return      56                mask.crop((x1, y1, x1 + tw, y1 + th))
大于 (224, 0, 544, 320) (320, 320) (320, 320)
[[[102 254  26]
  [145 171 119]
  [165   4 131]
  ...
  [  0   0   0]
  [  0   0   0]
  [  0   0   0]]

 [[ 72  83  73]
  [ 19 216 110]
  [ 49  84 237]
  ...
  [  0   0   0]
  [  0   0   0]
  [  0   0   0]]

 [[ 91 212 189]
  [176  94 233]
  [107  89 209]
  ...
  [  0   0   0]
  [  0   0   0]
  [  0   0   0]]

 ...

 [[184  39 186]
  [186  79 116]
  [ 15 250 253]
  ...
  [  0   0   0]
  [  0   0   0]
  [  0   0   0]]

 [[205  86  26]
  [ 78  48 214]
  [231  66  66]
  ...
  [  0   0   0]
  [  0   0   0]
  [  0   0   0]]

 [[195 158  16]
  [120  92 177]
  [244 176  85]
  ...
  [  0   0   0]
  [  0   0   0]
  [  0   0   0]]] [[ 84  15 222 ...   0   0   0]
 [175 166 167 ...   0   0   0]
 [ 73 155  19 ...   0   0   0]
 ...
 [246  37  34 ...   0   0   0]
 [ 62 204  81 ...   0   0   0]
 [113 228 149 ...   0   0   0]]

Process finished with exit code 0

从上面的输出可以看出,这里的crop方法对于坐标超界,是没有提示的,而且会直接补零。

进一步有一个更简单的验证:

if __name__ == '__main__':
    a = torch.randn((3, 320, 320))
    b = torch.randn((1, 320, 320))
    to_pil = transforms.ToPILImage()
    crop_rand = RandomCrop(320, 320)

    a = to_pil(a)
    b = to_pil(b)
    lu_x, lu_y, rb_x, rb_y = (320, 320, 544, 544)
    b.crop((lu_x, lu_y, rb_x, rb_y))
    print(np.asarray(b.crop((lu_x, lu_y, rb_x, rb_y))))

输出

[[0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 ...
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]
 [0 0 0 ... 0 0 0]]
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值