如何从图像中裁剪出最大的矩形

时间:2016-05-02 12:41:52

标签: python opencv image-processing

我在桌子上有一些页面图像。我想从图像中裁剪页面。通常,页面将是图像中最大的矩形,但是,在某些情况下,矩形的所有四个边可能都不可见。

我正在做以下但没有得到理想的结果:

import cv2
import numpy as np

im = cv2.imread('images/img5.jpg')
gray=cv2.cvtColor(im,cv2.COLOR_BGR2GRAY)
ret,thresh = cv2.threshold(gray,127,255,0)
_,contours,_ = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
areas = [cv2.contourArea(c) for c in contours]
max_index = np.argmax(areas)
cnt=contours[max_index]
x,y,w,h = cv2.boundingRect(cnt)
cv2.rectangle(im,(x,y),(x+w,y+h),(0,255,0),2)
cv2.imshow("Show",im)
cv2.imwrite("images/img5_rect.jpg", im)
cv2.waitKey(0)

以下是一些例子:

第一个例子:我可以在这个图像中找到矩形,但是,如果木材的剩余部分也可以裁剪掉。 enter image description here

enter image description here

第二个示例:未在此图像中找到矩形的正确尺寸。 enter image description here

enter image description here

第3个示例:无法在此图片中找到正确的尺寸。 enter image description here enter image description here

第4个例子:同样如此。 enter image description here enter image description here

2 个答案:

答案 0 :(得分:29)

正如我以前做过类似的事情一样,我经历过霍夫变换,但是对于我的情况而言,使用轮廓比使用轮廓要困难得多。我有以下建议可以帮助您入门:

  1. 一般来说,纸张(至少是边缘)是白色的,所以你可以通过像YUV这样更好地区分光度的颜色空间来获得更好的运气:

    image_yuv = cv2.cvtColor(image,cv2.COLOR_BGR2YUV)
    image_y = np.zeros(image_yuv.shape[0:2],np.uint8)
    image_y[:,:] = image_yuv[:,:,0]
    
  2. 纸上的文字是个问题。使用模糊效果,(希望)消除这些高频噪音。您也可以使用扩张等形态学操作。

    image_blurred = cv2.GaussianBlur(image_y,(3,3),0)
    
  3. 您可以尝试应用canny边缘检测器,而不是简单的阈值。不一定,但可以帮助你:

     edges = cv2.Canny(image_blurred,100,300,apertureSize = 3)
    
  4. 然后找到轮廓。在我的情况下,我只使用极端外轮廓。您可以使用CHAIN_APPROX_SIMPLE标志来压缩轮廓

    contours,hierarchy = cv2.findContours(edges,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)
    
  5. 现在你应该有一堆轮廓。是时候找到合适的人了。对于每个轮廓cnt,首先找到凸包,然后使用approaxPolyDP尽可能简化轮廓。

    hull = cv2.convexHull(cnt)
    simplified_cnt = cv2.approxPolyDP(hull,0.001*cv2.arcLength(hull,True),True)
    
  6. 现在我们应该使用这个简化的轮廓来找到封闭的四边形。您可以尝试一些您提出的规则。最简单的方法是选取轮廓的四个最长的段,然后通过交叉这四条线来创建包围的四边形。根据您的情况,您可以根据线条的对比度,它们所构成的角度以及类似的东西找到这些线条。

  7. 现在你有一堆四边形。您现在可以执行两步法查找所需的四边形。首先,删除可能错误的那些。例如,四边形的一个角度超过175度。然后你可以选择面积最大的那个作为最终结果。你可以看到橙色轮廓是我在这一点上得到的结果之一: All Contours

  8. 找到(希望)正确的四边形之后的最后一步,正在转变为一个矩形。为此,您可以使用findHomography来创建转换矩阵。

    (H,mask) = cv2.findHomography(cnt.astype('single'),np.array([[[0., 0.]],[[2150., 0.]],[[2150., 2800.]],[[0.,2800.]]],dtype=np.single))
    

    这些数字假设投射到信纸上。您可能会想出更好,更聪明的数字。您还需要重新排序轮廓点以匹配信纸的坐标顺序。然后调用warpPerspective来创建最终图像:

    final_image = cv2.warpPerspective(image,H,(2150, 2800))
    

    这种扭曲应该导致类似下面的内容(来自我以前的结果): Warping

  9. 我希望这可以帮助您找到合适的方法。

答案 1 :(得分:10)

这是一项非常复杂的任务,只能通过搜索轮廓来解决。例如,“经济学人”封面仅显示了杂志的1个边缘,将图像分成两半。您的计算机应该如何知道哪个是杂志,哪个是表?因此,您必须为您的计划添加更多智能。

您可以在图片中查找线条。霍夫变换例如。然后找到一组或多或少的平行或正交线,一定长度的线...... 通过检查您通常无法在桌面上找到的典型打印颜色或颜色来查找打印件。搜索由打印文本创建的高对比度频率...... 想象一下,作为一个人,你如何认识一张印刷纸......

总而言之,对于StackOverflow来说,这是一个过于宽泛的问题。尝试将其分解为较小的子问题,尝试解决它们,如果你碰壁,请回到这里。