基于OpenCV Python的模拟时钟转数字时间 如何实现秒针识别?
模拟时钟秒针识别调整方案
原代码无法识别秒针的核心原因
- 预处理阶段的腐蚀、膨胀卷积核尺寸过大,秒针本身线条较细,经过大核形态学操作后边缘会被直接消除,无法被后续检测捕获
HoughLinesP的minLineLength参数设置为1000,远大于裁剪后时钟图内秒针的实际长度,直接把秒针直线过滤掉了- 原有逻辑只提取长度排名前2的直线对应时针、分针,没有预留第三根线的提取逻辑
- 固定阈值处理对颜色偏浅的秒针兼容性差,容易把秒针和背景合并
具体修改步骤
1. 调整预处理参数
将形态学操作的卷积核缩小,避免抹除细秒针边缘,同时可以把固定阈值换成自适应阈值,提升对不同亮度指针的兼容性
2. 修正霍夫直线检测参数
大幅降低minLineLength,根据裁剪后时钟的尺寸调整到合适范围,同时降低阈值参数,允许更短的直线被检测到
3. 新增秒针提取逻辑
在原有取前两根长直线的基础上,新增第三根长度排名第三、且和前两根指针端点距离都超过阈值的直线作为秒针
4. 增加颜色特征辅助(可选)
大部分时钟的秒针是红色,可以在BGR空间提取红色区域做掩码,辅助秒针定位,大幅提升准确率
修改后的可运行代码
import cv2 import math import numpy as np from math import sqrt kernel = np.ones((5,5),np.uint8) img1 = cv2.imread('input1.jpg') img = cv2.imread('input1.jpg',0) gray = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) # 替换固定阈值为自适应阈值,提升对细秒针的识别率 thresh = cv2.adaptiveThreshold(gray,255,cv2.ADAPTIVE_THRESH_GAUSSIAN_C,cv2.THRESH_BINARY,11,2) # Create mask height,width = img.shape mask = np.zeros((height,width), np.uint8) edges = cv2.Canny(thresh, 100, 200) cimg=cv2.cvtColor(img, cv2.COLOR_GRAY2BGR) circles = cv2.HoughCircles(gray, cv2.HOUGH_GRADIENT, 1.2, 100) for i in circles[0,:]: i[2]=i[2]+4 # Draw on mask cv2.circle(mask,(int(i[0]),int(i[1])),int(i[2]),(255,255,255),thickness=-1) # Copy that image using that mask masked_data = cv2.bitwise_and(img1, img1, mask=mask) # Apply Threshold _,thresh = cv2.threshold(mask,1,255,cv2.THRESH_BINARY) # Find Contour contours = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE) contours = contours[0] if len(contours) == 2 else contours[1] x,y,w,h = cv2.boundingRect(contours[0]) # Crop masked_data crop = masked_data[y+30:y+h-30,x+30:x+w-30] i=crop height, width, channels = i.shape print (width, height, channels) ######################################################################### # 缩小形态学核尺寸,避免抹除细秒针 kernel = np.ones((3,3),np.uint8) kernel2 = np.ones((3,3),np.uint8) edges = cv2.Canny(i,50,150) edges = cv2.dilate(edges,kernel,iterations = 1) edges = cv2.erode(edges,kernel2,iterations = 1) # 大幅降低minLineLength,适配秒针长度 minLineLength = 80 maxLineGap = 5 lines = cv2.HoughLinesP(edges,1,np.pi/180,10,minLineLength,maxLineGap) h=[] # 新增秒针存储变量 xsec1=0 xsec2=0 ysec1=0 ysec2=0 for line in lines: x1, y1, x2, y2 = line[0] dx=abs(x2-x1) dy=abs(y2-y1) hypo=sqrt(dx**2 + dy**2) h.append(hypo) print(f"检测到直线数量:{len(h)}") a=len(h) h_sorted = sorted(list(set(h)), reverse=True) # 去重后排序 m=0 k=0 s=0 # 提取分针(最长)、时针(第二长)、秒针(第三长) for f in range(a): for line in lines: x1, y1, x2, y2 = line[0] dx=abs(x2-x1) dy=abs(y2-y1) hypo2=sqrt(dx**2 + dy**2) if hypo2 == h_sorted[0] and m == 0: m=hypo2 xmax1=x1 xmax2=x2 ymax1=y1 ymax2=y2 cv2.line(crop, (xmax1, ymax1), (xmax2, ymax2), (255, 0, 0), 3) print(f"分针:x1={xmax1}, y1={ymax1}, x2={xmax2}, y2={ymax2}, 长度={m}") if m !=0 and hypo2 == h_sorted[f] and k == 0: # 确认和分针不是同一条 dist_end1 = sqrt((xmax2-x2)**2 + (ymax2-y2)**2) dist_end2 = sqrt((xmax1-x1)**2 + (ymax1-y1)**2) if dist_end1 > 20 and dist_end2 > 20: k = hypo2 xs1=x1 xs2=x2 ys1=y1 ys2=y2 cv2.line(crop, (xs1, ys1), (xs2, ys2), (0, 255, 0), 3) print(f"时针:x1={xs1}, y1={ys1}, x2={xs2}, y2={ys2}, 长度={k}") if m !=0 and k !=0 and hypo2 == h_sorted[f] and s ==0: # 确认和时针、分针都不是同一条 dist_hour1 = sqrt((xs2-x2)**2 + (ys2-y2)**2) dist_hour2 = sqrt((xs1-x1)**2 + (ys1-y1)**2) dist_min1 = sqrt((xmax2-x2)**2 + (ymax2-y2)**2) dist_min2 = sqrt((xmax1-x1)**2 + (ymax1-y1)**2) if dist_hour1>20 and dist_hour2>20 and dist_min1>20 and dist_min2>20: s = hypo2 xsec1 = x1 xsec2 = x2 ysec1 = y1 ysec2 = y2 cv2.line(crop, (xsec1, ysec1), (xsec2, ysec2), (0, 0, 255), 3) print(f"秒针:x1={xsec1}, y1={ysec1}, x2={xsec2}, y2={ysec2}, 长度={s}") break if s !=0: break # 显示结果 cv2.imshow('识别结果 蓝=分针 绿=时针 红=秒针', crop) cv2.waitKey(0) cv2.destroyAllWindows()
额外优化建议
如果你的时钟秒针是红色,可以在预处理阶段新增红色区域提取逻辑,进一步降低误识别率:
# 提取红色区域掩码 hsv = cv2.cvtColor(crop, cv2.COLOR_BGR2HSV) lower_red1 = np.array([0,50,50]) upper_red1 = np.array([10,255,255]) lower_red2 = np.array([170,50,50]) upper_red2 = np.array([180,255,255]) mask_red1 = cv2.inRange(hsv, lower_red1, upper_red1) mask_red2 = cv2.inRange(hsv, lower_red2, upper_red2) mask_red = mask_red1 + mask_red2 # 红色掩码和边缘检测结果做与运算,只保留红色边缘 edges = cv2.bitwise_and(edges, edges, mask=mask_red)
内容的提问来源于stack exchange,提问作者Ajay
相关产品推荐
相关产品推荐

