如何用opencv读取电表指针?

Ada*_*ive 0 c++ opencv computer-vision

我不一定需要源代码,但寻找伪代码或如何解决这个问题的想法。我有一个摄像头对准我的水表,想要将使用情况记录到数据库中。过去几周我一直在使用 OpenCV,并且数字检测工作正常。(最后一个数字会带来问题,因为它会无休止地旋转,而不是像前 5 个那样与数字对齐,因此完整数字的显示时间不到一半。)

无论如何,为了获得最佳分辨率,我想跟踪实际的大红色针。现在我要绕一圈找到“最红”的像素,并用它来计算针的角度。只要光线充足并且相机不移动,它就可以工作。

我猜我可以用 Canny 边缘检测来实现一个技巧......最终我只是想找到最长线的角度?针的颜色独特,应该有助于消除很多误报,但我如何实际应用过滤器呢?

(请注意,仪表左下角有一个红色的小旋转器,与指针的颜色相同。我需要确保它不会误触发某些东西。)

谢谢你!-亚当水表示例

Kin*_*t 金 5

\xe5\x9b\xbd\xe5\xba\x86\xe8\x8a\x82\xe5\xbf\xab\xe4\xb9\x90\xef\xbc\x8c\xe4\xb8\xad\xe7\xa7\x8b\xe8 \x8a\x82\xe5\xbf\xab\xe4\xb9\x90\xef\xbc\x81 O(\xe2\x88\xa9_\xe2\x88\xa9)O \xe5\x93\x88\xe5\x93\x88 \xe5\x93\x88~

\n\n

新答案:

\n\n

你好,今天我对这个问题做了一些研究,经过多次尝试和错误,我现在得到了一个很好的解决方案。结果如下。

\n\n

在此输入图像描述

\n\n

基本步骤如下:

\n\n
    \n
  1. 转换为灰度,阈值,投影到轴,这样我们就可以找到观察区域。
  2. \n
  3. 转换为Lab颜色空间,因此可以在通道中轻松区分红针(暖色)a
  4. \n
  5. 对通道、阈值进行归一化a,并进行形态学运算,得到一个二值掩模。经过这些操作后,将有利于找到潜在的红针区域。
  6. \n
  7. 找到蒙版中的轮廓,通过一些属性进行过滤,然后你就会得到它。
  8. \n
\n\n

1.找到手表区域

\n\n
# https://i.stack.imgur.com/5oOGL.jpg\nimgname = "stkdata/5oOGL.jpg"\nimg = cv2.imread(imgname)\n## Threshold in grayscale\ngray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\nretval, threshed = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY )\n\n## Find wathc region by counting the projector\nh,w = img.shape[:2]\nx = np.sum(threshed, axis=0)\ny = np.sum(threshed, axis=1)\nyy = np.nonzero(y>(w/5*255))[0]\nxx = np.nonzero(x > (h/5*255))[0]\nregion = img[yy[0]:yy[-1], xx[0]:xx[-1]]\n#cv2.imwrite("region.png", region)\n
Run Code Online (Sandbox Code Playgroud)\n\n

在此输入图像描述

\n\n

2.转换为LAB

\n\n
## Change to LAB space\nlab = cv2.cvtColor(region, cv2.COLOR_BGR2LAB)\nl,a,b = cv2.split(lab)\nimglab = np.hstack((l,a,b))\ncv2.imwrite("region_lab.png", imglab)\n
Run Code Online (Sandbox Code Playgroud)\n\n

在实验室颜色空间中,可以很容易地区分红针。

\n\n

在此输入图像描述

\n\n

3.对a通道、阈值进行归一化,并进行形态学运算。

\n\n
## normalized the a channel to all dynamic range\nna = cv2.normalize(a, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1)\ncv2.imwrite("region_a_normalized.png", na)\n\n## Threshold to binary\nretval, threshed = cv2.threshold(na, thresh = 180,  maxval=255, type=cv2.THRESH_BINARY)\n\n## Do morphology\nkernel = cv2.getStructuringElement( cv2.MORPH_ELLIPSE , (3,3))\nopened = cv2.morphologyEx(threshed, cv2.MORPH_OPEN,kernel)\nres = np.hstack((threshed, opened))\ncv2.imwrite("region_a_binary.png", res)\n
Run Code Online (Sandbox Code Playgroud)\n\n

标准化通道:

\n\n

在此输入图像描述

\n\n

阈值并进行形态学运算,我们得到一个二值掩模作为右侧。

\n\n

在此输入图像描述

\n\n

4. 找到轮廓,并通过一些属性(例如面积等)过滤它们。

\n\n
## Find contours\ncontours = cv2.findContours(opened, mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE)[-2]\n\n## Draw Contours\nres = region.copy()\ncv2.drawContours(res, contours, -1, (255,0,0), 1)\ncv2.imwrite("region_contours.png", res)\n\n## Filter Contours\nfor idx, contour in enumerate(contours):\n    bbox = cv2.boundingRect(contour)\n    area = bbox[-1]*bbox[-2]\n    if area < 100:\n        continue\n    rot_rect = cv2.minAreaRect(contour)\n    (cx,cy), (w,h), rot_angle = rot_rect\n    rbox = np.int0(cv2.boxPoints(rot_rect))\n    cv2.drawContours(res, [rbox], 0, (0,255,0), 1)\n    text="#{}: {:2.3f}".format(idx, rot_angle)\n    org=(int(cx)-10,int(cy)-10)\n    cv2.putText(res, text=text, org = org, fontFace = 1, fontScale=0.8, color=(0,0,255), thickness = 1, lineType=16)\n\ncv2.imwrite("region_result.png", res)\n
Run Code Online (Sandbox Code Playgroud)\n\n

建立的轮廓:

\n\n

在此输入图像描述

\n\n

结果:

\n\n

在此输入图像描述

\n\n

我们可以发现,#1轮廓属于红针,它的角度是-85.601。

\n\n

( 右方向为 0\xc2\xb0\xef\xbc\x8c 逆时针为负,顺时针为正\xef\xbc\x89

\n\n

所有Python代码都在这里:

\n\n
#!/usr/bin/python3\n# 2017.10.01 22:59:02 CST\n# 2017.10.03 23:49:18 CST\n\nimport numpy as np\nimport cv2\n\n# https://i.stack.imgur.com/5oOGL.jpg\nimgname = "stkdata/5oOGL.jpg"\nimg = cv2.imread(imgname)\n## Threshold in grayscale\ngray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\nretval, threshed = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY )\n\n## Find wathc region by counting the projector\nh,w = img.shape[:2]\nx = np.sum(threshed, axis=0)\ny = np.sum(threshed, axis=1)\nyy = np.nonzero(y>(w/5*255))[0]\nxx = np.nonzero(x > (h/5*255))[0]\nregion = img[yy[0]:yy[-1], xx[0]:xx[-1]]\ncv2.imwrite("region.png", region)\n\n## Change to LAB space\nlab = cv2.cvtColor(region, cv2.COLOR_BGR2LAB)\nl,a,b = cv2.split(lab)\nimglab = np.hstack((l,a,b))\ncv2.imwrite("region_lab.png", imglab)\n\n## normalized the a channel to all dynamic range\nna = cv2.normalize(a, None, alpha=0, beta=255, norm_type=cv2.NORM_MINMAX, dtype=cv2.CV_8UC1)\ncv2.imwrite("region_a_normalized.png", na)\n\n## Threshold to binary\nretval, threshed = cv2.threshold(na, thresh = 180,  maxval=255, type=cv2.THRESH_BINARY)\n\n## Do morphology\nkernel = cv2.getStructuringElement( cv2.MORPH_ELLIPSE , (3,3))\nopened = cv2.morphologyEx(threshed, cv2.MORPH_OPEN,kernel)\nres = np.hstack((threshed, opened))\ncv2.imwrite("region_a_binary.png", res)\n\n## Find contours\ncontours = cv2.findContours(opened, mode=cv2.RETR_LIST, method=cv2.CHAIN_APPROX_SIMPLE)[-2]\n\n## Draw Contours\nres = region.copy()\ncv2.drawContours(res, contours, -1, (255,0,0), 1)\ncv2.imwrite("region_contours.png", res)\n\n## Filter Contours\nfor idx, contour in enumerate(contours):\n    bbox = cv2.boundingRect(contour)\n    area = bbox[-1]*bbox[-2]\n    if area < 100:\n        continue\n    rot_rect = cv2.minAreaRect(contour)\n    (cx,cy), (w,h), rot_angle = rot_rect\n    rbox = np.int0(cv2.boxPoints(rot_rect))\n    cv2.drawContours(res, [rbox], 0, (0,255,0), 1)\n    text="#{}: {:2.3f}".format(idx, rot_angle)\n    org=(int(cx)-10,int(cy)-10)\n    #cv2.putText(res, text=text, org = org, fontFace = cv2.FONT_HERSHEY_PLAIN, fontScale=0.7, color=(0,0,255), thickness = 1, lineType=cv2.LINE_AA)\n    cv2.putText(res, text=text, org = org, fontFace = 1, fontScale=0.8, color=(0,0,255), thickness = 1, lineType=16)\n\ncv2.imwrite("region_result.png", res)\ncv2.imshow("result", res); cv2.waitKey();cv2.destroyAllWindows()\n
Run Code Online (Sandbox Code Playgroud)\n\n

起源答案:

\n\n

我认为首先你可以提取手表区域,然后在该区域中搜索红针,你可能会改变颜色空间。这是我的 python 示例代码。

\n\n
imgname = "5oOGL.jpg"\n# https://i.stack.imgur.com/5oOGL.jpg\nimg = cv2.imread(imgname)\n\n## Threshold in grayscale\ngray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)\nth, imgbin = cv2.threshold(gray, 180, 255, cv2.THRESH_BINARY )\n\n## Find watch region by projecting and counting\nh,w = img.shape[:2]\nx = np.sum(imgbin, axis=0)\ny = np.sum(imgbin, axis=1)\nyy = np.nonzero(y>(w/5*255))[0]\nxx = np.nonzero(x > (h/5*255))[0]\nregion = img[yy[0]:yy[-1], xx[0]:xx[-1]]\ncv2.imwrite("region.png", region)\ncv2.imshow("region", region); cv2.waitKey(0);# cv2.destroyAllWindows()\n\n## Change to LAB space\nlab = cv2.cvtColor(region, cv2.COLOR_BGR2LAB)\nl,a,b = cv2.split(lab)\nimglab = np.hstack((l,a,b))\ncv2.imwrite("imglab.png", imglab)\n
Run Code Online (Sandbox Code Playgroud)\n\n

监视区域\n观看区域

\n\n

实验室空间中的监视区域。\nLAB 中的观看区域

\n\n
\n\n

一些链接:

\n\n
    \n
  1. 使用“cv::inRange”(OpenCV) 选择正确的 HSV 上下边界进行颜色检测

  2. \n
  3. 如何在不同的 OpenCV 版本中使用 `cv2.findContours`?

  4. \n
  5. 如何使用 OpenCV 找到红色区域?

  6. \n
\n