检测彩色图片的边缘 OpenCV

3
我是CV新手,刚学会如何检测纸张边缘。我想尝试更复杂的事情。因此,我从电影网站截图,并希望能够从网站中检测海报。如果背景颜色与海报不同,则可以正常工作。但是当它们的颜色相似时,我就无法通过cv2.findContours()找到图片的边缘。 原始图片链接如下: Poster 我的方法如下:
img = cv2.imread('pic5.jpg')
orig = img.copy()
image = orig
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
binary = cv2.medianBlur(gray,3)
# blur = cv2.GaussianBlur(binary, (5, 5), 0)
# ret, binary = cv2.threshold(blur,127,255,cv2.THRESH_TRUNC)
edged = cv2.Canny(binary, 3, 30)
show(edged)

# detect edge
contours, hierarchy = cv2.findContours(edged.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
cnts = sorted(contours, key=cv2.contourArea, reverse=True)[:5]

#
for c in cnts:
    # approx
    peri = cv2.arcLength(c, True)
    eps = 0.02
    approx = cv2.approxPolyDP(c, eps*peri, True)

    # detect square (4 points)
    if len(approx) == 4:
        screenCnt = approx
        break

res = cv2.drawContours(image, [screenCnt], -1, (0, 255, 0), 2)
show(orig)

结果如下:

经过预处理后的结果: 预处理后的图像 检测到的内容

我不知道这种方法是否可行。能否基于背景颜色(无论海报的颜色如何)来检测方形部分?


你好!在检测到边缘后,您可以尝试执行闭合操作,以便填充海报中的空隙并获得更平滑、更接近矩形的效果。https://docs.opencv.org/master/d9/d61/tutorial_py_morphological_ops.html - Filip Kubicz
但在这里检测边缘似乎不是一个好的方法,因为它非常依赖于海报的内容和颜色。您可以尝试定位网站的一些固定元素,然后从已知的x、y偏移处裁剪海报。 - Filip Kubicz
另一种方法是遍历图像,并找到边界框,在这其中有一些颜色与背景不同且与文本颜色不同。但是如果你有一个与网站背景相同颜色的海报,这种方法可能会失败。 - Filip Kubicz
如果这是一个真正的任务,那么您可能需要结合几种方法,检查每个算法找到的海报有多大,并在算法失败时回退到另一个。如果仅用于教育目的,可以继续进行一些明确定义的问题,或者按照这里的优秀教程进行操作: https://docs.opencv.org/master/d6/d00/tutorial_py_root.html - Filip Kubicz
感谢您的回复!我考虑了海报与网站背景颜色相同的情况。但我认为只有很小一部分的海报颜色相同。在这种情况下,即使边缘的某些部分没有被找到,我们也可以找到包含剩余部分的最小矩形。 - Zakkkk
显示剩余2条评论
1个回答

6

您可以继续使用 edged 的结果,并使用闭合形态学操作关闭小间隙。

建议您不要使用 approxPolyDP 搜索矩形,而是找到最大连通组件(或最大轮廓)的边界矩形。

在我的示例代码中,由于存在外部边界线,我用 connectedComponentsWithStats 替换了 findContours
您可以使用开放形态学操作来消除外部线条(并继续使用 findContours)。

您还可以使用 approxPolyDP 对结果进行优化。


以下是代码示例:

import numpy as np
import cv2

img = cv2.imread('pic5.png')
orig = img.copy()
image = orig
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
binary = cv2.medianBlur(gray, 3)
edged = cv2.Canny(binary, 3, 30)

edged = cv2.morphologyEx(edged, cv2.MORPH_CLOSE, np.ones((5,5)))  # Close small gaps

#contours, hierarchy = cv2.findContours(edged, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_NONE)
#c = max(contours, key=cv2.contourArea) # Get the largest contour
#x, y, w, h = cv2.boundingRect(c)  # Find bounding rectangle.

nb_components, output, stats, centroids = cv2.connectedComponentsWithStats(edged, 8)  # finding components

# https://dev59.com/tqbja4cB1Zd3GeqPf2qp#61662694
# Find the largest non background component.
# Note: range() starts from 1 since 0 is the background label.
max_label, max_size = max([(i, stats[i, cv2.CC_STAT_AREA]) for i in range(1, nb_components)], key=lambda x: x[1])

# Find bounding rectangle of largest connected component.
x = stats[max_label, cv2.CC_STAT_LEFT]
y = stats[max_label, cv2.CC_STAT_TOP]
w = stats[max_label, cv2.CC_STAT_WIDTH]
h = stats[max_label, cv2.CC_STAT_HEIGHT]

res = image.copy()
cv2.rectangle(res, (x, y), (x+w, y+h), (0, 255, 0), 2)  # Draw a rectangle

cv2.imshow('edged', edged)
cv2.imshow('res', res)
cv2.waitKey()
cv2.destroyAllWindows()

结果:

edged
这里输入图片描述

res
这里输入图片描述


找到最大的连通组件是一个很好的想法!我刚刚想到cv2.erode(),cv2.dilate(),cv2.morphologyEx()可能会有帮助,但我不确定如何使用它们。非常感谢您的回复! - Zakkkk
这正是我所需要的。非常酷。 - JJS

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接