Python-opencv的指针检测、表盘识别算法案例分析

该博客介绍了使用Python和OpenCV库进行钟表指针检测和表盘去除的方法。首先,通过对比两张图片的亮部去除表盘,然后利用霍夫变换检测圆心。接着,通过直线检测确定指针位置,并计算角度。最终,通过直线交点和圆心计算读数。文章提供了完整的代码示例和结果展示,指出在某些情况下可能需要调整参数以应对不同图像的挑战。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

主要通过opencv库内霍夫曼直线检测和圆检测实现指针识别,文章主要介绍思路。
源码下载地址https://download.csdn.net/download/qq_44781688/20009503.
(内涵报告和解释 分析)
python版本为3.7 但运行的库都很普通也没有什么特别复杂的计算,所以其他版本理论上应该也不会出现什么错误,其他库自行安装即可。
欢迎交流讨论

去除表盘,保留指针(效果好可省略)

首先创建了一个light.py文件,利用两个表盘一样指针不同的图片对应位置比较留下最亮的像素,得到没有指针的表盘。
需要表盘位置比较准确,两张图表盘位置相近,否则太大差异没有意义
不相同也可以通过模板匹配或者截取部分等方式对齐,自行学习研究

用Image库求最亮去除指针,保存方便下一步去除表盘
from PIL import Image,ImageChops
#两图像求最亮去除指针
im=ImageChops.lighter(Image.open('6.jpg'), Image.open('7.jpg'))
im.save("none1.jpg")

创建了一个pointer.py文件 保留指针

与没有指针的none.jpg作比较 尽量去除表盘留下指针
实际发现2.5量程的表盘没有完全对齐 效果不是特别好 还需要进一步处理
1.6量程对齐了 效果很好
def pointer(filepath,judge):
    im=Image.open(filepath)
    if judge:
        im_none= Image.open("none1.jpg")
    else:
        im_none = Image.open("none.jpg")   #选取不同的量程图片做处理
    im1= ImageChops.difference(im_none,im)
    im1.save("pointer.jpg")
    im = cv2.imread("pointer.jpg")
    return im

保留指针后图片实例
已经难以看到表盘,效果非常理想

表盘圆心检测

利用 cv2.HoughCircles函数检测圆,一般来说不需要太多预处理,直接彩色图读进去处理即可,简单调整参数后即可检测到想要大小范围的圆。也可以用导入形态学

from scipy.ndimage import measurements

其中measurements.center_of_mass()可以找到各个物体中心,结果类似。但是不会一个物体检测到多次,但对同一圆参数不同可能cv2.HoughCircles会检测到多个,以下代码借此求平均加强精度
中心一般都不是特别准,需要简单调整结果如加减等

图像预处理 求中心

import cv2
import numpy as np
def centre(filepath,judge):
    org = cv2.imread(filepath,1)
    img = org
    img_gray = cv2.cvtColor(org, cv2.COLOR_BGR2GRAY)
    # 低同滤波进行平滑图像
    img_gray = cv2.medianBlur(img_gray, 5)
    cimg = cv2.cvtColor(img_gray, cv2.COLOR_GRAY2BGR)
    # 提取圆形
    circles = cv2.HoughCircles(img_gray,cv2.HOUGH_GRADIENT,1,10,param1=150,param2=90,minRadius=465,maxRadius=485)

    circles = np.uint16(np.around(circles))
    x = 0
    y = 0
    j=0
    for i in circles[0,:]:
    # draw the outer circle
        cv2.circle(img,(i[0],i[1]),i[2],(0,0,255),10)
        x = (x*j+i[0])/(j+1)
        y = (y*j+i[1])/(j+1)
        j=j+1
    # draw the center of the circle
        if judge:
            y=y+8
            x=x+1
            cv2.circle(img, (int(x), int(y)), 2, (0, 0, 255), 5)#刻度不同图像不同偏差不同
        else:
            x=x-8
            y=y-4
            cv2.circle(img,(int(x), int(y)),2,(0,0,255),5)

    # 显示原图和处理后的图像
    # cv2.imshow("org",org)
    cv2.imshow("processed",img)
    print("中心为:",i[0]-8,i[1]-4)
    cv2.waitKey(0)
    img = cv2.cvtColor(img, cv2.COLOR_RGB2BGR)
    return img,int(x),int(y)
if __name__ == "__main__":
    img,x,y=centre("5.jpg",0)
    print(x,y)

圆心结果图示可见检测精度非常高。标记基本和圆心完全重合。

主程序段(指针识别到输出)

pointer.py文件完成调取上述函数,完成对应部分后输出结果和图像
主要程序流程图

预处理

主要为了边缘检测后方便检测直线

frame = pointer.pointer(filepath,judge)#进行指针处理的图像 大部分运算用这个
frame = cv2.cvtColor(frame,cv2.COLOR_RGB2BGR)
gray = frame.copy()
# cv2.imshow('origin', gray)
subplot(332)
imshow(gray)
title("去除表盘")

# 高斯除噪
kernel = np.ones((6, 6), np.float32) / 36
gray_cut_filter2D = cv2.filter2D(org[0:org.shape[0], 0:org.shape[1]], -1, kernel)
# 灰度图 二值化
gray_img = cv2.cvtColor(gray, cv2.COLOR_BGR2GRAY)
ret, thresh1 = cv2.threshold(gray_img, 80, 100, cv2.THRESH_BINARY)  # 小于70 黑 大于200白
#边缘检测
test_main = thresh1.copy()
edges = cv2.Canny(test_main, 50, 200, apertureSize=3)

直线检测

主要用了cv2.HoughLinesP直接输出确定直线的两个点坐标,选取最长的两条直线,输出的前两个line,即为表针两边直线。(由于表针较粗所以能检测到两条,但是由于不是从中心所以角度不能直接计算读数,要求交点再通过中心计算)通过sympy库内的Line函数表示直线用intersection函数求两条直线交集,交点即为直线段点
也可以用cv2.HoughLines函数,输出的是距离和直线角度

lines = cv2.HoughLinesP(edges, 1, np.pi / 180, 100, minLineLength=15, maxLineGap=60)  # 函数将通过步长为1的半径和步长为π/180的角来搜索所有可能的直线
for line in lines:
    # print(lines)  # 多维数组
    x1, y1, x2, y2 = line[0]
    cv2.line(result, (x1, y1), (x2, y2), (0, 0, 255),3)
    cv2.drawMarker(result,(x2,y2),(255, 0, 0),thickness=2,markerType=cv2.MARKER_STAR,line_type=cv2.LINE_8,markerSize=20)

x1, y1, x2, y2 = lines[0][0]
l = Line(Point(x1, y1),Point( x2, y2))
x1, y1, x2, y2 = lines[1][0]
l1 = Line(Point(x1, y1),Point( x2, y2))
x = np.float64(l1.intersection(l)) #sympy库求直线交点
print("指针端点",x)

主要问题/其他方案

1.表针细,只检测到一条直线且过圆心,就通过输出的直线角度直接计算读数,但是通常误差可能大
2.其他影响检测到一条直线不过圆心:这就要圆心检测之后以圆心和半径创建用sympy库内Circle,直线与圆求交点,交点即与圆心和指针端点在同一条直线上。可视为指针端点

C1 = Circle(Point(x0, y0), 400)
l1 = Line(Point(x1, y1),Point( x2, y2))
 print(l1.intersection(C1))

但是这样会输出两个点,需要区分指针方向来区分交点选哪一个
3.利用两条直线检测,可以不用区分交点位置,因为两条直线交点只有一个。但是还有问题就是个别图片检测不到两条直线或者检测到其他非表针的直线,就要多修改参数或者改变预处理方式尽量去除干扰
本文就是在发现指针旁文字多时出错进而多加了去除表盘的操作

角度计算

直接使用指针端点和圆心直线斜率计算角度容易出现返回值小于180度的情况,无法显示180-360度的角度。
下面代码自己编写了计算函数通过两直线向量积求角度沿顺时针0-360输出

def angle(v1,v2):
    x1,y1 = [v1[2]-v1[0],v1[3]-v1[1]]
    x2,y2 = [v2[2]-v2[0],v2[3]-v2[1]]
    dot = x1*x2+y1*y2
    det = x1*y2-y1*x2
    theta = np.arctan2(det, dot)
    theta = theta if theta>0 else 2*np.pi+theta
        distinguish = 2.5/(2.54/2.5)/265 #比例关系
    output = theta * 180 / np.pi * distinguish
    return output

绘制和显示

outpointer=org
if judge:
    org_x0, org_y0 = 340,719  #图像零点坐标 第一个刻度不是很均匀 实际选为零刻度下标点位置
else:
    org_x0, org_y0 = 418,738
cv2.line(outpointer, (int(x[0][0]), int(x[0][1])), (x0,y0), 255, 3)
cv2.line(outpointer, (org_x0,org_y0), (x0,y0), 255, 3)
out = angle.angle((x0, y0,org_x0,org_y0),(x0, y0,int(x[0][0]), int(x[0][1])),judge)
subplot(337)
imshow(outpointer)
title("指针")
number = Image.new('RGB', (1280, 1024), 'black')
drawer = ImageDraw.Draw(number)  #标注
font = ImageFont.truetype("simhei.ttf",200, encoding="utf-8")
drawer.text((0, 500), text="读数为", font=font, fill='red')
drawer.text((600, 500), text="%0.4f"%out, font=font, fill='red')
number = array(number)
subplot(338)
imshow(number)
title("读数")
show()

结果展示

表盘2.5量程结果示意图
表盘1.6量程结果示意图
图示可见运行结果精度非常高
由于许多段落对于思路展示没有意思,所有代码有一定删减
有意者下载测试使用
源码下载地址https://download.csdn.net/download/qq_44781688/20009503.(内含报告和使用说明)其中每个函数成文件写了main函数可以单独运行调试
欢迎大家学习交流,有问题可以私信或者评论,我见到会尽快回复。

<think>嗯,用户想知道如何使用霍夫变换来识别图像中的指针,特别是用OpenCV。首先,我需要回忆一下霍夫变换的基本原理和应用。霍夫变换常用于检测图像中的几何形状,比如直线、圆等。指针通常是直线,所以霍夫直线检测应该适用。 首先,用户的问题涉及指针检测,可能需要预处理步骤。比如,图像转灰度,因为很多OpenCV处理需要灰度图。根据引用[4],模板匹配需要灰度图,可能霍夫变换也需要。所以步骤一应该是将图像转换为灰度,使用cvtColor函数,如引用[1]和[4]提到的。 接下来,可能需要边缘检测。像Canny边缘检测,这样霍夫变换可以检测边缘中的直线。引用[3]提到了Scharr算子,但Canny可能更常用。不过用户的问题中没有提到边缘检测的具体方法,所以需要说明这一步的重要性。 然后,应用霍夫变换。OpenCV中的HoughLines或HoughLinesP函数。HoughLinesP是概率霍夫变换,检测线段,可能更适合指针检测,因为指针通常是线段而不是无限长的直线。 之后,需要从检测到的直线中筛选出指针。可能需要根据角度、长度或位置来判断。例如,指针可能位于图像中心,或者有特定的角度范围。用户可能需要结合具体应用场景调整参数。 另外,引用[5]提到卷积和滤波,比如高斯滤波可能在预处理中使用,去噪以便更好地检测边缘。所以预处理步骤可能包括高斯滤波,然后Canny边缘检测。 代码方面,应该按照步骤来写:读取图像,转灰度,高斯模糊,Canny边缘检测,霍夫变换检测直线,筛选直线,绘制结果。同时,需要注意参数调整,如霍夫变换的阈值、最小长度等。 可能的问题:如何调整参数?比如阈值设置高低的影响,如何处理噪声导致的误检,如何区分指针和其他直线结构。这些在回答中需要提及,或者作为相关问题。 用户提供的引用中有关于图像处理的基本步骤,比如灰度转换、边缘检测、滤波,这些都可以整合到回答中。例如,引用[4]中的灰度转换步骤,引用[5]中的高斯滤波,引用[3]中的Scharr边缘检测(但可能更常用Canny)。 总结步骤: 1. 转灰度:cvtColor 2. 高斯滤波去噪:GaussianBlur 3. Canny边缘检测 4. 霍夫直线检测:HoughLinesP 5. 筛选符合条件的直线(角度、长度) 6. 绘制结果 需要确保代码示例正确,参数合理,并解释关键步骤。同时,引用相关的引用来源,比如引用[4]中的灰度转换部分,引用[5]中的滤波部分。 可能的相关问题包括霍夫变换参数调整、优化检测的方法、处理噪声的策略,以及在不同光照条件下的应用。这些问题可以作为相关问题列出,帮助用户进一步学习。</think>### 使用霍夫变换检测图像中的指针 #### 核心步骤及实现原理 1. **图像预处理** - **灰度化**:霍夫变换通常在灰度图上操作。使用 `cv::cvtColor` 将图像转换为灰度格式[^4]: ```cpp cv::Mat grayImage; cv::cvtColor(srcImage, grayImage, cv::COLOR_BGR2GRAY); ``` - **去噪**:通过高斯滤波(`cv::GaussianBlur`)减少噪声干扰[^5]: ```cpp cv::GaussianBlur(grayImage, blurredImage, cv::Size(5,5), 1.5); ``` 2. **边缘检测** 使用 Canny 算法提取边缘轮廓,为霍夫变换提供候选像素: ```cpp cv::Canny(blurredImage, edgeImage, 50, 150); ``` 3. **霍夫直线检测** - **标准霍夫变换**:检测无限长的直线(`cv::HoughLines`)。 - **概率霍夫变换**(推荐):检测线段(`cv::HoughLinesP`),更适合指针检测: ```cpp std::vector<cv::Vec4i> lines; cv::HoughLinesP(edgeImage, lines, 1, CV_PI/180, 50, 30, 10); ``` 4. **筛选指针直线** - **角度约束**:指针通常接近垂直或水平方向(例如电表指针)。 - **长度约束**:过滤短线段,保留显著直线。 ```cpp for (const auto& line : lines) { double angle = atan2(line[3]-line[1], line[2]-line[0]) * 180 / CV_PI; if (abs(angle - targetAngle) < tolerance) { // 绘制或记录该直线 } } ``` 5. **可视化结果** 在原图上绘制检测到的直线: ```cpp cv::line(dstImage, cv::Point(line[0], line[1]), cv::Point(line[2], line[3]), cv::Scalar(0,0,255), 2); ``` #### 关键参数说明 - `rho`(距离分辨率):建议设为1像素。 - `theta`(角度分辨率):通常设为1度(`CV_PI/180`)。 - `threshold`:投票阈值,值越高检测到的直线越少。 - `minLineLength`:线段最小长度(根据图像尺寸调整)。 #### 优化方向 - 结合 **ROI(感兴趣区域)** 缩小检测范围[^2]。 - 使用 **多尺度霍夫变换** 适应不同尺寸的指针- 融合 **形态学操作** 增强边缘连续性[^5]。
评论 20
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ζั͡ޓއއ

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值