minAreaRect OpenCV [Python] 返回的裁剪矩形
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/37177811/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Crop Rectangle returned by minAreaRect OpenCV [Python]
提问by Abdul Fatir
minAreaRect
in OpenCV returns a rotated rectangle. How do I crop this part of the image which is inside the rectangle?
minAreaRect
在 OpenCV 中返回一个旋转的矩形。如何裁剪矩形内的这部分图像?
boxPoints
returns the co-ordinates of the corner points of the rotated rectangle so one can access the pixels by looping through the points inside the box, but is there a faster way to crop in Python?
boxPoints
返回旋转矩形角点的坐标,因此可以通过循环访问框内的点来访问像素,但是在 Python 中是否有更快的裁剪方法?
EDIT
编辑
See code
in my answer below.
请看code
下面我的回答。
采纳答案by tfv
You have not given sample code, so I am answering without code also. You could proceed as follows:
你没有给出示例代码,所以我也没有代码回答。您可以按照以下步骤进行:
- From corners of rectangle, determine angle alpha of rotation against horizontal axis.
- Rotate image by alpha so that cropped rectangle is parallel to image borders. Make sure that the temporary image is larger in size so that no information gets lost (cf: Rotate image without cropping OpenCV)
- Crop image using numpy slicing (cf: How to crop an image in OpenCV using Python)
- Rotate image back by -alpha.
- 从矩形的角,确定相对于水平轴的旋转角度 alpha。
- 按 alpha 旋转图像,使裁剪的矩形与图像边框平行。确保临时图像的尺寸更大,以便不会丢失任何信息(参见:旋转图像而不裁剪 OpenCV)
- 使用 numpy 切片裁剪图像(参见:如何使用 Python 在 OpenCV 中裁剪图像)
- 将图像旋转回 -alpha。
回答by Oliver Wilken
here a function that does this task:
这里有一个执行此任务的函数:
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
here an example usage
这是一个示例用法
# 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()
this is the output
这是输出
回答by Abdul Fatir
Here's the code to perform the above task. To speed up the process, instead of first rotating the entire image and cropping, part of the image which has the rotated rectangle is first cropped, then rotated, and cropped again to give the final result.
这是执行上述任务的代码。为了加快这个过程,不是首先旋转整个图像和裁剪,而是首先裁剪具有旋转矩形的部分图像,然后旋转,再次裁剪以给出最终结果。
# 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))
回答by mkrinblk
@AbdulFatir was on to a good solution but as the comments stated(@Randika @epinal) it wasn't quite working for me either so I modified it slightly and it seems to be working for my case. here is the image I am using.
@AbdulFatir 提出了一个很好的解决方案,但正如评论所述(@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()
This should produce a series of images like these: