使用Python中的Pillow库从图像中裁剪区域

3

Rectangle with lines which are not parallel to the margins

我想使用Python中的Pillow从图像中裁剪一个矩形区域。问题在于,矩形不一定与图像边缘平行,因此我无法使用.crop((left, top, right, bottom))函数。
是否有办法使用Pillow实现这一点?(假设我们知道矩形的所有4个点的坐标) 如果不行,如何使用其他Python库完成?

The end result should look like this


2
请提供您认为的角落坐标。您希望结果是什么样子? - Mark Setchell
1
你期望得到什么?带有矩形外部黑色像素的图像?还是经过校正的图像? - api55
3个回答

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

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

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

作为结果,您有矩形的中心坐标(x,y)、宽度、高度和旋转角度。您可以使用此矩形的角度旋转整个图像。您的图像现在将会被旋转:

Rotated image

您可以计算出四个矩形顶点的新坐标(您已经得到了角度)。然后,只需为这些点计算正常的矩形(正常的矩形=不是最小的,没有任何旋转)。使用此矩形,您可以裁剪旋转后的图像。如果我理解正确,裁剪后的图像将是您想要的样子,如下所示:

result

因此,您只需要使用OpenCV即可。也许有一些库可以更轻松地完成此操作。


2
这里有一个基于scikit-image(而不是Pillow)的解决方案,您可能会发现它很有用。
您可以将要裁剪的区域的顶点传递给函数skimage.draw.polygon,然后使用检索到的像素坐标来遮罩原始图像(例如通过alpha通道)。最初的回答。
import numpy as np
from skimage import io, draw

img = io.imread('https://istack.dev59.com/x5Ym4.webp')

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)

Masked image


0

我将基于opencv的解决方案(sub_image改编为适用于PIL。它接受一个(center, size, theta)矩形,我从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))

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