【问题标题】:How to crop biggest rectangle out of an image如何从图像中裁剪出最大的矩形
【发布时间】:2016-08-27 05:39:21
【问题描述】:

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

我正在执行以下操作,但没有得到想要的结果:

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)

以下是几个例子:

第一个示例:我可以在这张图片中找到矩形,但是,我希望木头的剩余部分也可以被裁剪掉。

第二个示例:在此图像中找不到正确的矩形尺寸。

第三个示例:也无法在此图像中找到正确的尺寸。

第 4 个示例:与此相同。

【问题讨论】:

  • 这个问题很难解决,因为木材颜色和纸张颜色都不是相同的......我的意思是,这个算法的问题是阈值......因为您正在此阈值之后找到轮廓...我建议您尝试在彩色图像中对类似木材的颜色进行阈值处理,或者尝试不同的方法。您可以尝试一些边缘检测器,如 canny(已经有一些阈值),然后对其进行 findcontours。

标签: python opencv image-processing


【解决方案1】:

正如我之前做过类似的事情一样,我体验过霍夫变换,但它们比使用轮廓更难适合我的情况。我有以下建议可以帮助您入门:

  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. 您可以尝试应用精巧的边缘检测器,而不是简单的阈值。不一定,但可能对您有所帮助:

     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度。然后你可以选择面积最大的作为最终结果。您可以将橙色轮廓视为我此时得到的结果之一:

  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))
    

    这种扭曲应该会导致如下结果(来自我之前的结果):

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

【讨论】:

  • 我在 image_y[:,:] = imy[:,:,0] imy not defined 处遇到错误
  • @usernan 打错了,应该是image_y[:,:] = image_yuv[:,:,0]。感谢您指出。它现在已在答案中修复。
  • 我有问题你能帮我解决这个问题吗,我想用 opencv 代替 imagemagick stackoverflow.com/questions/37803903/image-magic-auto-cropping
  • 我在contours, hierarchy = cv2.findContours(edges,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 收到“ValueError: too many values to unpack (expected 2)”。此外,我很困惑:hull = cv2.convexHull(cnt)cnt 来自哪里? for cnt in contours?
  • @ytu 此代码是为 OpenCV2 编写的。在 OpenCV3 中,函数 findContours 也返回图像。所以,像edges, contours, hierarchy 这样的返回值应该可以解决我认为的问题。对于下一部分,是的,cnt 是您在遍历 contours 时得到的轮廓之一。
【解决方案2】:

这是一项相当复杂的任务,无法通过简单地搜索轮廓来解决。例如,《经济学人》封面仅显示杂志的 1 个边缘,将图像一分为二。你的电脑应该怎么知道哪一本是杂志哪一本是桌子?所以你必须为你的程序添加更多的智能。

您可能会在图像中寻找线条。例如霍夫变换。然后找到一组或多或少平行或正交的线,一定长度的线...... 通过检查典型的印刷颜色或通常在桌子上找不到的颜色来查找印刷品。搜索由印刷文本创建的高对比度频率... 想象一下,作为人类,您是如何识别印刷纸的……

总而言之,这对于 StackOverflow 来说是一个过于宽泛的问题。试着把它分解成更小的子问题,试着解决它们,如果你碰壁了,回到这里。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2017-09-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-08-13
    • 2012-11-02
    相关资源
    最近更新 更多