问题描述
我有一个Pi相机指向白色背景上的一张卡片.但是,局部阴影似乎阻止了我用于卡片检测的轮廓的闭合,这意味着检测总体上失败了.这是我的意思的屏幕截图:
I have a Pi camera pointed at a card on a white background. However, local shadows seem to be preventing the closing of the contours that I use for card detection, which means detection fails overall. Here's a screenshot of what I mean:
您可以看到它在底角处特别参差不齐.这是我用来实现这一目标的代码:
You can see it gets ragged around the bottom corners in particular. This is the code I'm using to get this far:
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
是否有一种方法可以强制其关闭特定轮廓?如果我进一步模糊图像以平滑不起作用的阴影,因为它只是将那些角视为没有边缘而忽略了它们.令人讨厌的是,它距离闭合轮廓线只有几像素之遥,但从未如此……
Is there a way to force it to close specific contours? If I blur the image more to smooth the shadows that doesn't work either since it simply ignores those corners as not having an edge. It's annoying that it's merely a few pixels away from closing the contours, yet it never does...
edit:我现在有一个更现实的设置,其中背景是米色,并且有更多的阴影干扰.必须使用米色,因为有些卡片带有白色边框,因此白色将无法使用.边缘检测主要在阴影所在的左侧失败.
edit: I now have a more realistic setup where the background is a beige colour and with a lot more shadows interfering. Beige is necessary because there are some cards with white borders, so white wouldn't work. The edge detection fails mostly in the left side where the shadows are.
推荐答案
正如我在对您的答复的评论中提到的那样,连接"边界线的最简单方法之一是使用形态运算符.在以下代码中,图像的边缘使用椭圆形进行了扩张.这种技术使我们能够合并闭合的线并填充一些空白区域.您可以在 OpenCV文档.
As I mentioned in my comment to you answer, one of the easiest ways to "connect" the lines in the border is using morphological operators. In the following code, the edges of the image are dilated using an ellipsoid shape. This technique allows us to merge the lines that are close and fill some of the empty spaces. You can have more information about this topic in the 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)
在这里您可以看到原始边缘图像,膨胀图像和使用膨胀边缘获得的轮廓(图像是使用原始屏幕快照的裁剪区域获得的):
Here you can see the original edge image, the dilated image and the contour obtained using the dilated edges (image obtained using a cropped region of your original screenshot):
但是,正如您所看到和想象的那样,在更一般的情况下解决此问题更为复杂,并且将需要使用其他方法,并且可能比SO问题(或至少以其表达方式更广泛)现在).
But, as you can see and also imagine, solving this issue for more general cases is more complex and will demand the usage of other approaches, and probably is broader than a SO question (or at least in the way it is formulated now).
通过查看更困难的情况,我建议您使用其他图像表示形式来替换灰度输入图像(例如HSV色彩空间中的H通道),以减少或减弱阴影效果.您还可以探究问题中的一些约束条件:卡总是以直线为边界,并使用能够处理参数形式的方法,例如霍夫线"检测器.看看这个问题,它可能会给您一些有关如何改善结果的见解:
By looking at your more difficult case, I could recommend you using other image representations to replace the grayscale input image (such as the H channel from HSV colorspace) in order reduce or attenuate the effects you are having with shadows. You could also explore some of the constraints in your problem: cards always have straight lines as borders and use a method capable of dealing with parametric forms, such as Hough Lines detector. Have a look at this question, it may give you some insights about how to improve your results: How to identify square or rectangle with variable lengths and width by using javacv?
备注:双边过滤在计算上非常昂贵,尤其是当您使用RPi运行应用程序时.我建议您投资其他选择,例如高斯滤波,以减少图片中的噪点(假设您确实需要这样做).
Remark: Bilateral filtering is very computationally expensive, especially if you are using an RPi to run your application. I would recommend investing in some other alternatives, such as a Gaussian filtering, to reduce the amount of noise in the picture (assuming that you really need to do that).
这篇关于如何在python opencv中完成/关闭轮廓?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!