minAreaRect OpenCV [Python] 返回的裁剪矩形
Posted
技术标签:
【中文标题】minAreaRect OpenCV [Python] 返回的裁剪矩形【英文标题】:Crop Rectangle returned by minAreaRect OpenCV [Python] 【发布时间】:2016-09-07 18:00:44 【问题描述】:minAreaRect
在 OpenCV 中返回一个旋转的矩形。如何裁剪矩形内的这部分图像?
boxPoints
返回旋转矩形角点的坐标,因此可以通过循环遍历框内的点来访问像素,但是在 Python 中是否有更快的裁剪方法?
编辑
请参阅下面我的回答中的code
。
【问题讨论】:
您可以:1) 为旋转的矩形创建遮罩(使用fillConvexPoly
或drawContours(... CV_FILLED)
很简单)。 2)黑色初始化一个与原始大小相同的矩阵。 3) 只复制新图片中的蒙版内容(new_image.setTo(old_image, mask)
), 4) 在旋转矩形的边框上裁剪新图片
How to straighten a rotated rectangle area of an image using opencv in python?的可能重复
【参考方案1】:
这里有一个执行此任务的函数:
import cv2
import numpy as np
def crop_minAreaRect(img, rect):
# rotate img
angle = rect[2]
rows,cols = img.shape[0], img.shape[1]
M = cv2.getRotationMatrix2D((cols/2,rows/2),angle,1)
img_rot = cv2.warpAffine(img,M,(cols,rows))
# rotate bounding box
rect0 = (rect[0], rect[1], 0.0)
box = cv2.boxPoints(rect0)
pts = np.int0(cv2.transform(np.array([box]), M))[0]
pts[pts < 0] = 0
# crop
img_crop = img_rot[pts[1][1]:pts[0][1],
pts[1][0]:pts[2][0]]
return img_crop
这里是一个示例用法
# generate image
img = np.zeros((1000, 1000), dtype=np.uint8)
img = cv2.line(img,(400,400),(511,511),1,120)
img = cv2.line(img,(300,300),(700,500),1,120)
# find contours / rectangle
_,contours,_ = cv2.findContours(img, 1, 1)
rect = cv2.minAreaRect(contours[0])
# crop
img_croped = crop_minAreaRect(img, rect)
# show
import matplotlib.pylab as plt
plt.figure()
plt.subplot(1,2,1)
plt.imshow(img)
plt.subplot(1,2,2)
plt.imshow(img_croped)
plt.show()
这是输出
【讨论】:
这正是我想要的,功能完全清楚!但是我无法让它与这个图像一起工作。 i.imgur.com/4E8ILuI.jpg 它最终旋转略有错误,边缘被切断。你愿意看一看吗? 图像应该围绕旋转矩形的中心旋转,而不是图像中心。有正确答案here。 感谢@oliver-wilken 的决定,但如果 angle = 90 那么你会被零除。我的决定是在rect0 = (rect[0], rect[1], angle)
上更改rect0 = (rect[0], rect[1], 0.0)
。【参考方案2】:
@AbdulFatir 提出了一个很好的解决方案,但正如 cmets 所说(@Randika @epinal),它对我来说也不是很有效,所以我稍微修改了一下,它似乎对我的情况有效。这是我正在使用的图像。
im, contours, hierarchy = cv2.findContours(open_mask, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)
print("num of contours: ".format(len(contours)))
mult = 1.2 # I wanted to show an area slightly larger than my min rectangle set this to one if you don't
img_box = cv2.cvtColor(img.copy(), cv2.COLOR_GRAY2BGR)
for cnt in contours:
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
cv2.drawContours(img_box, [box], 0, (0,255,0), 2) # this was mostly for debugging you may omit
W = rect[1][0]
H = rect[1][1]
Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
rotated = False
angle = rect[2]
if angle < -45:
angle+=90
rotated = True
center = (int((x1+x2)/2), int((y1+y2)/2))
size = (int(mult*(x2-x1)),int(mult*(y2-y1)))
cv2.circle(img_box, center, 10, (0,255,0), -1) #again this was mostly for debugging purposes
M = cv2.getRotationMatrix2D((size[0]/2, size[1]/2), angle, 1.0)
cropped = cv2.getRectSubPix(img_box, size, center)
cropped = cv2.warpAffine(cropped, M, size)
croppedW = W if not rotated else H
croppedH = H if not rotated else W
croppedRotated = cv2.getRectSubPix(cropped, (int(croppedW*mult), int(croppedH*mult)), (size[0]/2, size[1]/2))
plt.imshow(croppedRotated)
plt.show()
plt.imshow(img_box)
plt.show()
这应该会产生一系列像这样的图像:
它还会给出这样的结果图像:
【讨论】:
【参考方案3】:这是执行上述任务的代码。为了加快这个过程,不是首先旋转整个图像并裁剪,而是首先裁剪具有旋转矩形的部分图像,然后旋转,然后再次裁剪以给出最终结果。
# Let cnt be the contour and img be the input
rect = cv2.minAreaRect(cnt)
box = cv2.boxPoints(rect)
box = np.int0(box)
W = rect[1][0]
H = rect[1][1]
Xs = [i[0] for i in box]
Ys = [i[1] for i in box]
x1 = min(Xs)
x2 = max(Xs)
y1 = min(Ys)
y2 = max(Ys)
angle = rect[2]
if angle < -45:
angle += 90
# Center of rectangle in source image
center = ((x1+x2)/2,(y1+y2)/2)
# Size of the upright rectangle bounding the rotated rectangle
size = (x2-x1, y2-y1)
M = cv2.getRotationMatrix2D((size[0]/2, size[1]/2), angle, 1.0)
# Cropped upright rectangle
cropped = cv2.getRectSubPix(img, size, center)
cropped = cv2.warpAffine(cropped, M, size)
croppedW = H if H > W else W
croppedH = H if H < W else W
# Final cropped & rotated rectangle
croppedRotated = cv2.getRectSubPix(cropped, (int(croppedW),int(croppedH)), (size[0]/2, size[1]/2))
【讨论】:
我试过这段代码,但它没有给我投资回报率。从那以后有什么改进吗?【参考方案4】:你没有给出示例代码,所以我也没有代码回答。 您可以按以下方式进行:
-
从矩形的角,确定相对于水平轴的旋转角度 alpha。
按 Alpha 旋转图像,使裁剪后的矩形与图像边框平行。确保临时图像的尺寸更大,以免信息丢失(参见:Rotate image without cropping OpenCV)
使用 numpy 切片裁剪图像(参见:How to crop an image in OpenCV using Python)
将图像旋转回 -alpha。
【讨论】:
大图,这不是很贵吗? 我的猜测是内置函数总是比在像素上进行嵌套循环更快。但找出这一点的唯一方法是测量它,它只是如上所述的几行代码。 如果我有很多矩形,那么它可能会显示。我会编码并在尝试后回复您。 可能有其他方法取决于您想对裁剪后的图像做什么:您是想以原始方向使用它,还是旋转以使裁剪区域的边界平行于图片边框? 不,我不想按原来的方向使用它。只想从裁剪部分中提取一些信息。以上是关于minAreaRect OpenCV [Python] 返回的裁剪矩形的主要内容,如果未能解决你的问题,请参考以下文章
OpenCV-最小包围旋转矩形边框cv::minAreaRect
OpenCV-最小包围旋转矩形边框cv::minAreaRect