[英]Crop area from image using Pillow in Python
I want to crop a rectangle shape area from an image using Pillow in python.我想在 python 中使用 Pillow 从图像中裁剪一个矩形区域。 The problem is that the rectangle is not necessary parallel with the image margins so I cannot use the .crop((left, top, right, bottom)) function.问题是矩形不需要与图像边距平行,所以我不能使用 .crop((left, top, right, bottom)) 函数。
Is there a way to achieve this with Pillow?有没有办法用 Pillow 实现这一目标? (assuming we know the coordinates of all 4 points of rectangle) If not, how it can be done using a different Python library? (假设我们知道矩形的所有 4 个点的坐标)如果没有,如何使用不同的 Python 库来完成?
You can use min rotated rectangle in OpenCV:您可以在 OpenCV 中使用最小旋转矩形:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
As a result You have: center coordinates (x,y), width, height, angle of rotation of rectangle.结果你有:矩形的中心坐标(x,y),宽度,高度,旋转角度。 You can rotate whole image with angle from this rectangle.您可以从这个矩形角度旋转整个图像。 You image now will be rotated:您的图像现在将被旋转:
You can calculate new coordinates of four rectangle vertices (you got angle).您可以计算四个矩形顶点的新坐标(您有角度)。 Then just calculate normal rectangle for this points (normal rectangle = not minimal, without any rotation).然后只需计算这些点的正常矩形(正常矩形 = 不是最小的,没有任何旋转)。 With this rect You can crop Your rotated image.使用此矩形,您可以裁剪旋转后的图像。 In this crop image will be what You want if I understand You correctly.如果我对您的理解正确,则在此裁剪图像中将是您想要的。 Something like that:类似的东西:
So You only need Opencv.所以你只需要Opencv。 Maybe there is some library with which You can do it easier.也许有一些图书馆可以让您更轻松地做到这一点。
Here's a solution based on scikit-image (not Pillow) that you might find useful.这是一个基于 scikit-image(不是 Pillow)的解决方案,您可能会觉得它很有用。
You could pass the vertices of the region you wish to crop to the function skimage.draw.polygon
and then use the retrieved pixel coordinates to mask the original image (for example, through the alpha channel).您可以将要裁剪的区域的顶点传递给函数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)
I adapted this opencv
-based solution ( sub_image
) for use with PIL
.我改编了这个基于opencv
的解决方案( sub_image
)以与PIL
一起使用。 It takes a (center, size, theta)
rect which I'm getting from cv2.minAreaRect
, but could be constructed mathmatically from points, etc.它需要一个(center, size, theta)
rect,我从cv2.minAreaRect
得到,但可以从点等数学上构造。
I've seen a few other solutions but they left some weird artifacts.我看过其他一些解决方案,但它们留下了一些奇怪的工件。
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))
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.