【问题标题】:Crop area from image using Pillow in Python在 Python 中使用 Pillow 从图像中裁剪区域
【发布时间】:2023-03-23 07:30:02
【问题描述】:

我想在 python 中使用 Pillow 从图像中裁剪出一个矩形区域。问题是矩形不需要与图像边距平行,所以我不能使用 .crop((left, top, right, bottom)) 函数。

有没有办法通过 Pillow 实现这一点? (假设我们知道矩形所有 4 个点的坐标) 如果没有,如何使用不同的 Python 库来完成?

【问题讨论】:

  • 请提供您可能拥有的角坐标。您希望结果如何?
  • 您希望得到什么?矩形外有黑色像素的图像?还是修正后的图像?

标签: python opencv image-processing computer-vision python-imaging-library


【解决方案1】:

您可以在 OpenCV 中使用最小旋转矩形:

rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)

因此您有:中心坐标 (x,y)、宽度、高度、矩形的旋转角度。您可以从这个矩形角度旋转整个图像。您的图像现在将被旋转:

你可以计算四个矩形顶点的新坐标(你有角度)。然后只计算这些点的正常矩形(正常矩形=不是最小的,没有任何旋转)。使用此矩形,您可以裁剪旋转的图像。如果我正确理解您,在此裁剪图像中将是您想要的。类似的东西:

所以你只需要 Opencv。也许有一些库可以让您更轻松地完成它。

【讨论】:

    【解决方案2】:

    这是一个基于 scikit-image(不是 Pillow)的解决方案,您可能会觉得有用。

    您可以将要裁剪的区域的顶点传递给函数skimage.draw.polygon,然后使用检索到的像素坐标来遮盖原始图像(例如,通过 Alpha 通道)。

    import numpy as np
    from skimage import io, draw
    
    img = io.imread('https://i.stack.imgur.com/x5Ym4.png')
    
    vertices = np.asarray([[150, 140],
                           [300, 240],
                           [210, 420],
                           [90, 320],
                           [150, 150]])
    
    rows, cols = draw.polygon(vertices[:, 0], vertices[:, 1])
    
    crop = img.copy()
    crop[:, :, -1] = 0
    crop[rows, cols, -1] = 255
    
    io.imshow(crop)
    

    【讨论】:

      【解决方案3】:

      我将this opencv-based solution (sub_image) 改编为与PIL 一起使用。它需要一个(center, size, theta) rect,我从cv2.minAreaRect 获得,但可以从点等数学上构建。

      我见过其他一些解决方案,但它们留下了一些奇怪的伪影。

           
      def crop_tilted_rect(image, rect):
          """ crop rect out of image, handing rotation
          
          rect in this case is a tuple of ((center_x, center_y), (width, height), theta),
          which I get from opencv's cv2.minAreaRect(contour)
          """
          # Get center, size, and angle from rect
          center, size, theta = rect
          width, height = [int(d) for d in size]
      
          if 45 < theta <= 90:
              theta = theta - 90
              width, height = height, width
      
          theta *= math.pi / 180 # convert to rad
          v_x = (math.cos(theta), math.sin(theta))
          v_y = (-math.sin(theta), math.cos(theta))
          s_x = center[0] - v_x[0] * (width / 2) - v_y[0] * (height / 2)
          s_y = center[1] - v_x[1] * (width / 2) - v_y[1] * (height / 2)
          mapping = np.array([v_x[0],v_y[0], s_x, v_x[1],v_y[1], s_y])
          return image.transform((width, height), Image.AFFINE, data=mapping, resample=0, fill=1, fillcolor=(255,255,255))
      

      【讨论】:

        猜你喜欢
        • 2013-12-20
        • 2019-01-31
        • 2021-03-09
        • 2012-05-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多