Crop Rectangle returned by minAreaRect OpenCV [Python]

匿名 (未验证) 提交于 2019-12-03 01:25:01

问题:

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:

  1. From corners of rectangle, determine angle alpha of rotation against horizontal axis.
  2. 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)
  3. Crop image using numpy slicing (cf: How to crop an image in OpenCV using Python)
  4. 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:



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!