You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

基于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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.10.15 04:12:01