Tim*_*imo 15 c++ opencv image-processing object-detection
我目前正致力于增强现实游戏.游戏使用的控制器(我在这里谈论的是物理输入设备)是单色的,长方形的纸片.我必须在摄像机的捕获流中检测该矩形的位置,旋转和大小.检测应在尺度上不变,并且在沿X和Y轴旋转时不变.
在用户将纸张移开或朝向相机移动的情况下,需要比例不变性.我不需要知道矩形的距离,因此尺度不变性转换为尺寸不变性.
如果用户沿其局部X和/或Y轴倾斜矩形,则需要旋转不变性.这种旋转将纸张的形状从矩形变为梯形.在这种情况下,面向对象的边界框可用于测量纸张的尺寸.
开始时有一个校准步骤.窗口显示摄像机源,用户必须单击矩形.单击时,鼠标指向的像素颜色将作为参考颜色.帧被转换为HSV颜色空间以改善颜色区分.我有6个滑块可以调整每个通道的上限和下限.这些阈值用于对图像进行二值化(使用opencv inRange函数).
在那之后,我正在侵蚀和扩展二进制图像以消除噪声并联合nerby块(使用opencv erode和dilate函数).
下一步是findContours在二进制图像中查找轮廓(使用opencv 函数).这些轮廓用于检测最小的方向矩形(使用opencv minAreaRect函数).作为最终结果,我正在使用面积最大的矩形.
该程序的简短结论:
您可能已经注意到,我没有利用有关纸张实际形状的知识,仅仅因为我不知道如何正确使用这些信息.
我也考虑过使用opencv的跟踪算法.但有三个原因使我无法使用它们:
这是一个 - 相对 - 好的捕获(侵蚀和扩张后的二进制图像)

如何提高检测效果,尤其是更能抵抗照明变化?
以下是一些用于测试的原始图像.
你不能只使用更厚的材料吗?
是的,我可以而且我已经做过了(不幸的是我现在无法访问这些内容).但问题仍然存在.即使我使用像cartboard这样的材料.它不像纸一样容易弯曲,但仍然可以弯曲它.
你如何获得矩形的大小,旋转和位置?opencv
的minAreaRect功能返回一个RotatedRect对象.该对象包含我需要的所有数据.
注意
由于矩形是单色的,因此无法区分顶部和底部或左右.这意味着旋转始终在[0, 180]我的目的范围内完全正常.矩形两边的比例总是如此w:h > 2:1.如果矩形是方形,则旋转范围将变为[0, 90],但这可以认为是无关紧要的.
正如评论中所建议的那样,我将尝试使用直方图均衡来减少亮度问题并查看ORB,SURF和SIFT.
我会更新进展情况.
HSV空间中的H通道为Hue,对光线变化不敏感。红色范围约为 [150,180]。
根据上述信息,我做了以下工作。
附注。由于网络原因,我无法获取您上传到保管箱的图像。所以,我只是使用裁剪第二张图像的右侧作为输入。
imgname = "src.png"
img = cv2.imread(imgname)
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
## Split the H channel in HSV, and get the red range
hsv = cv2.cvtColor(img, cv2.COLOR_BGR2HSV)
h,s,v = cv2.split(hsv)
h[h<150]=0
h[h>180]=0
## normalize, do the open-morp-op
normed = cv2.normalize(h, None, 0, 255, cv2.NORM_MINMAX, cv2.CV_8UC1)
kernel = cv2.getStructuringElement(shape=cv2.MORPH_ELLIPSE, ksize=(3,3))
opened = cv2.morphologyEx(normed, cv2.MORPH_OPEN, kernel)
res = np.hstack((h, normed, opened))
cv2.imwrite("tmp1.png", res)
Run Code Online (Sandbox Code Playgroud)
现在,我们得到这样的结果 (h, normed, open):
然后找到轮廓并过滤它们。
contours = cv2.findContours(opened, cv2.RETR_LIST, cv2.CHAIN_APPROX_SIMPLE)
print(len(contours))[-2]
bboxes = []
rboxes = []
cnts = []
dst = img.copy()
for cnt in contours:
## Get the stright bounding rect
bbox = cv2.boundingRect(cnt)
x,y,w,h = bbox
if w<30 or h < 30 or w*h < 2000 or w > 500:
continue
## Draw rect
cv2.rectangle(dst, (x,y), (x+w,y+h), (255,0,0), 1, 16)
## Get the rotated rect
rbox = cv2.minAreaRect(cnt)
(cx,cy), (w,h), rot_angle = rbox
print("rot_angle:", rot_angle)
## backup
bboxes.append(bbox)
rboxes.append(rbox)
cnts.append(cnt)
Run Code Online (Sandbox Code Playgroud)
结果是这样的:
rot_angle: -2.4540319442749023
rot_angle: -1.8476102352142334
Run Code Online (Sandbox Code Playgroud)
由于源图像中的蓝色矩形标记,卡片被分成了两侧。但是干净的图像不会有问题。
我知道我问这个问题已经有一段时间了。我最近继续讨论这个话题并解决了我的问题(虽然不是通过矩形检测)。
cv::equalizeHist,cv::aruco::detectMarkers,查找标记事实证明,标记检测对光照变化和不同的视角非常有效,这让我可以跳过任何校准步骤。
我在每个控制器上放置了 2 个标记,以进一步提高检测鲁棒性。两种标记都必须仅检测一次(以测量它们的相关性)。之后,每个控制器只找到一个标记就足够了,因为另一个标记可以从之前计算的相关性中推断出来。
下面是在明亮环境下的检测结果:
在较暗的环境中:
以及隐藏标记之一时(蓝点表示外推标记位置):
我实施的初始形状检测效果不佳。它对照明变化非常脆弱。此外,它需要一个初始校准步骤。
在形状检测方法之后,我尝试了 SIFT 和 ORB 结合蛮力和 knn 匹配器来提取和定位帧中的特征。事实证明,单色对象并没有提供太多的关键点(多么令人惊讶)。无论如何,SIFT 的性能都很糟糕(大约 10 fps @ 540p)。我在控制器上画了一些线条和其他形状,这导致了更多的关键点可用。然而,这并没有带来巨大的改进。