可以将文章内容翻译成中文,广告屏蔽插件可能会导致该功能失效(如失效,请关闭广告屏蔽插件后再试):
问题:
minAreaRect
in OpenCV returns a rotated rectangle. How do I crop this part of the image which is inside the rectangle?
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?
EDIT
See code
in my answer below.
回答1:
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.
回答2:
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 W else W croppedH = H if H
回答3:
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(rect) pts = np.int0(cv2.transform(np.array([box]), M))[0] pts[pts
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

回答4:
@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.
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
This should produce a series of images like these: 


And it will also give a result image like this: 