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

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

我正在做以下但没有得到所需的结果:

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) 

以下是一些例子:

第一个例子 :我可以在这个图像中find矩形,但是,如果剩余的部分也可以被裁剪出来。 在这里输入图像说明

在这里输入图像说明

第二个例子 :在这个图像中找不到矩形的正确尺寸。 在这里输入图像说明

在这里输入图像说明

第三个示例 :无法在此图像中find正确的尺寸。 在这里输入图像说明 在这里输入图像说明

第四例 :与此相同。 在这里输入图像说明 在这里输入图像说明

正如我以前做过类似的事情,我经历了霍夫变换,但是对于我的情况来说,要比使用轮廓更难。 我有以下build议来帮助您开始使用:

  1. 一般来说,纸张(边缘至less)是白色的,所以你可能会有更好的运气,比如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. 然后find轮廓。 在我的情况下,我只使用极端的外轮廓。 您可以使用CHAIN_APPROX_SIMPLE标志来压缩轮廓

     contours,hierarchy = cv2.findContours(edges,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) 
  5. 现在你应该有一堆轮廓。 时间find正确的。 对于每个轮廓cnt ,首先find凸包,然后使用approaxPolyDP尽可能简化轮廓。

     hull = cv2.convexHull(cnt) simplified_cnt = cv2.approxPolyDP(hull,0.001*cv2.arcLength(hull,True),True) 
  6. 现在我们应该使用这个简化的轮廓来寻找包围的四边形。 你可以尝试你提出的很多规则。 最简单的方法是挑选轮廓中最长的四个最长的段,然后通过将这四条线相交来创build包围的四边形。 根据你的情况,你可以根据线条的对比度,angular度和类似的东西find这些线条。

  7. 现在你有一堆四边形。 你现在可以执行两步法find你需要的四边形。 首先你删除那些可能是错误的。 例如四边形的一个angular度大于175度。 那么你可以select最大的面积作为最终结果。 你可以看到橙色轮廓是我在这一点上得到的结果之一: 所有的轮廓

  8. find(希望)正确的四边形后的最后一步,正在变成一个矩形。 为此,您可以使用findHomography提出一个转换matrix。

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

    这个翘曲应该导致类似于以下内容(从我之前的结果): 整经

我希望这可以帮助你find适合你的情况。

这是一个相当复杂的任务,不能通过简单地search轮廓来解决。 例如“经济学家”杂志的封面只显示了将图像分成一半的杂志边缘。 你的计算机应该如何知道哪个是杂志,哪个是桌子? 所以你必须为你的程序添加更多的智能。

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

总而言之,这对于StackOverflow来说是一个太广泛的问题。 尝试把它分解成更小的子问题,尝试解决它们,如果你打墙,回到这里。