我有一个Pi相机指向白色背景上的卡片。然而,局部阴影似乎阻止了我用于卡片检测的轮廓的关闭,这意味着整体检测失败。这是我的意思截图:
你可以看到它特别是在底角附近褴褛。这是我用来实现这个目标的代码:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
gray = cv2.blur(gray, (5,5))
gray = cv2.bilateralFilter(gray, 11, 17, 17) #blur. very CPU intensive.
cv2.imshow("Gray map", gray)
edges = cv2.Canny(gray, 30, 120)
cv2.imshow("Edge map", edges)
#find contours in the edged image, keep only the largest
# ones, and initialize our screen contour
# use RETR_EXTERNAL since we know the largest (external) contour will be the card edge.
_, cnts, _ = cv2.findContours(edges.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
cnts = sorted(cnts, key = cv2.contourArea, reverse = True)[:1]
screenCnt = None
# loop over our contours
for c in cnts:
# approximate the contour
peri = cv2.arcLength(c, True)
approx = cv2.approxPolyDP(c, 0.3 * peri, True)
cv2.drawContours(image, [cnts[0]], -1, (0, 255, 0), 2)
# if our approximated contour has four points, then
# we can assume that we have found our card
if len(approx) == 4:
screenCnt = approx;
break
有没有办法强迫它关闭特定的轮廓?如果我模糊图像更多以平滑不起作用的阴影,因为它只是忽略那些没有边缘的角落。令人讨厌的是它距离关闭轮廓只有几个像素,但它从来没有......
编辑:我现在有一个更逼真的设置,背景是米色,并且有更多的阴影干扰。米色是必要的,因为有一些白色边框卡,所以白色不起作用。边缘检测主要在阴影所在的左侧失败。
答案 0 :(得分:8)
正如我在评论中提到的那样,在边框中“连接”线条的最简单方法之一是使用形态运算符。在以下代码中,使用椭圆体形状扩大图像的边缘。这种技术允许我们合并靠近的线并填充一些空的空间。您可以在OpenCV Documentation。
中获得有关此主题的更多信息kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE,(9,9))
dilated = cv2.dilate(image, kernel)
_, cnts, _ = cv2.findContours(dilated.copy(), cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
在这里,您可以看到原始边缘图像,扩张图像和使用扩张边缘获得的轮廓(使用原始屏幕截图的裁剪区域获得的图像):
但是,正如你所看到的那样,也可以想象,为更一般的情况解决这个问题更复杂,需要使用其他方法,并且可能比SO问题更广泛(或者至少在制定它的方式现在)。
通过查看更困难的情况,我建议您使用其他图像表示来替换灰度输入图像(例如HSV色彩空间中的H通道),以减少或减弱阴影效果。您还可以探索问题中的一些约束:卡总是将直线作为边界并使用能够处理参数形式的方法,例如Hough Lines检测器。看看这个问题,它可以为您提供有关如何改善结果的一些见解:How to identify square or rectangle with variable lengths and width by using javacv?
备注:双边过滤的计算成本非常高,尤其是在使用RPi运行应用程序时。我建议投资其他一些替代方案,例如高斯滤波,以减少图片中的噪声量(假设你真的需要这样做)。