使用OpenCV进行矩形检测/跟踪

22

我需要什么

我目前正在开发一款增强现实游戏。游戏使用的控制器(我指的是物理输入设备)是一个单色、矩形的纸片。我需要在相机的捕捉流中检测出该矩形的位置、旋转和大小。检测应该与比例尺度无关,以及在X和Y轴上的旋转无关。

比例尺度的不变性是必需的,以防用户将纸张向相机远离或靠近。我不需要知道矩形的距离,因此比例尺度转化为大小不变性。

旋转不变性是必需的,以防用户沿其本地X和/或Y轴倾斜矩形。这样的旋转会将纸张的形状从矩形变为梯形。在这种情况下,可以使用面向对象的边界框来测量纸张的大小。

我所做的事情

一开始是校准步骤。一个窗口显示相机的视频流,用户必须点击矩形。点击后,鼠标指向的像素颜色被作为参考颜色。帧被转换到HSV颜色空间以改善颜色区分。我有6个滑块,调整每个通道的上下阈值。这些阈值用于对图像进行二值化(使用opencv的inRange函数)。
之后我对二值图像进行腐蚀和膨胀,以消除噪声和汇集附近的块(使用opencv的erodedilate函数)。
下一步是在二值图像中找到轮廓(使用opencv的findContours函数)。这些轮廓用于检测最小定向矩形(使用opencv的minAreaRect函数)。作为最终结果,我使用面积最大的矩形。

该过程的简要总结:

  1. 获取一帧
  2. 将该帧转换为HSV格式
  3. 二值化(使用用户选择的颜色和滑块中的阈值)
  4. 应用形态学操作(腐蚀和膨胀)
  5. 查找轮廓
  6. 获取每个轮廓的最小定向边界框
  7. 选择其中最大的边界框作为结果
  8. 正如你所注意到的那样,我没有利用有关纸张实际形状的知识优势,仅仅是因为我不知道如何正确地使用这些信息。

    我也考虑过使用OpenCV的跟踪算法。但是有三个原因阻止了我使用它们:

    1. 尺度不变性:据我所知,一些算法不支持不同尺度的对象。
    2. 运动预测:一些算法使用运动预测来提高性能,但我正在跟踪的对象完全随机移动,因此是不可预测的。
    3. 简单性:我只是在图像中寻找一个单色矩形,没有像汽车或人物追踪那样花哨。

    这是一个 - 相对 - 不错的拍摄(经过腐蚀和膨胀后的二进制图像) ok

    这是一个不好的拍摄 bad

    问题

    如何改进一般的检测,尤其是更加抵抗光照变化?

    更新

    这里有一些用于测试的原始图像。

    你不能只使用更厚的材料吗?
    是的,我可以做到,并且已经在做了(不幸的是,我目前无法访问这些素材)。然而,问题仍然存在。即使我使用卡纸等材料,它也不像纸张那样容易弯曲,但人们仍然可以将其弯曲。

    如何获取矩形的大小、旋转和位置?
    OpenCV的minAreaRect函数返回一个RotatedRect对象。该对象包含我需要的所有数据。

    注意
    因为矩形是单色的,所以无法区分顶部和底部或左侧和右侧。这意味着旋转始终在范围[0, 180]内,这对我的目的来说完全没问题。矩形的两边比例总是w:h > 2:1。如果矩形是正方形,则旋转范围将更改为[0, 90],但这在这里可以被认为不相关。

    正如评论中建议的,我将尝试直方图均衡化以减少亮度问题,并查看ORB、SURF和SIFT。

    我会在进展方面进行更新。


1
也许你可以尝试像这样做:使用直方图均衡化来获得更加均匀的亮度,以消除视频中明暗变化剧烈的问题。参考链接 - api55
1
如果您总是有相同的矩形框,为什么不保存一个二维“模板图像”并运行ORB/SURF/SIFT来查找它呢?颜色也可以通过“模板图像”使其对光照变化具有鲁棒性。您可以将所有检测到的颜色映射到一种颜色。当然,这将高度取决于您的检测。 - Rick M.
1
目前来看,你采取的方法存在一些问题,而不是矩形检测本身。从图片来看,你使用了一张容易弯曲的薄纸片,在最后一张图片中,你的矩形已经不再是一个真正的矩形了。鉴于你正确地识别了其区域,你打算如何计算轴?考虑将使用硬纸板作为要求。这样做可能会发现,即使没有弯曲,光照也没有任何差异。 - KjMag
@Timo,进展如何?你能上传一张只有红色卡片的图片吗? - Rick M.
很抱歉,@Rick M. 我没能在这上面花太多时间,因为我现在有很多工作要做。你说的“只有红卡”是什么意思?我已经上传了一些测试图片(请看我的先前评论)。 - Timo
显示剩余4条评论
2个回答

11
在HSV空间中,H通道代表色调,不受光线变化的影响。红色范围大约在[150, 180]之间。
基于上述信息,我进行以下工作:
1. 转换为HSV空间,拆分H通道,进行阈值处理和归一化。 2. 应用形态学操作(开运算)。 3. 查找轮廓,按一些属性进行筛选(如宽度、高度、面积、比例等)。
PS:由于网络问题,我无法获取您在Dropbox上上传的图像,因此我只能使用裁剪后的第二张图像的右侧作为输入。

enter image description here

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)

现在,我们得到了这样的结果(h,normed,opened):

enter image description here

然后找到轮廓并过滤它们。

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)

结果如下:
rot_angle: -2.4540319442749023
rot_angle: -1.8476102352142334

enter image description here

由于原始图像中的蓝色矩形标记,该卡片被分成两个部分。但是干净的图像不会有问题。

5

我知道我之前问了这个问题已经有一段时间了。最近,我又继续探讨了这个话题,并解决了我的问题(虽然不是通过矩形检测)。

更改

  • 使用木材加强我的控制器(即“矩形”),如下所示。
  • 在每个控制器上放置2个ArUco标记。

Controller

工作原理

  • 将帧转换为灰度图像,
  • 对其进行降采样(以提高检测性能),
  • 使用cv::equalizeHist均衡化直方图,
  • 使用cv::aruco::detectMarkers找到标记,
  • 相关标记(如果有多个控制器),
  • 分析标记(位置和旋转),
  • 计算结果并应用一些纠错。

结果表明,标记检测非常稳健,可以忽略任何校准步骤。

我在每个控制器上放置了2个标记,以增加检测的稳健性。只需要检测到每个标记一次(以测量它们的相关性)。之后,仅需找到每个控制器上的一个标记即可,因为另一个标记可以从先前计算的相关性中外推出来。

这是在明亮环境下的检测结果:

Detection in a bright environment

在较暗的环境中:

Detection in a dark environment

当隐藏其中一个标记时(蓝色点表示外推标记的位置):

Detection of missing markers

失败

我最初实现的形状检测表现不佳。它对光照变化非常脆弱。此外,它需要进行初始校准步骤。

在形状检测方法之后,我尝试了结合暴力匹配和KNN匹配使用SIFT和ORB提取并定位帧中的特征。结果发现,单色物体提供的关键点很少(真是个惊喜)。无论如何,SIFT的性能都很糟糕(大约为10 fps @ 540p)。 我在控制器上画了一些线条和其他形状,这样可以获得更多的关键点。然而,这并没有带来巨大的改进。


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