如何用霍夫线检测检测时钟指针

问题描述

我想从模拟时钟中获取时间。现在我有点卡住了,我设法得到了分割的图像(虽然我无法删除它的底部......),并进行了 Canny 检测。我遇到的问题是,我无法移除底部部分,以及时钟指针的检测。我的目标是以一种可以计算角度的方式检测手,然后从这些角度计算时间。我知道我需要霍夫线变换,但我不太明白它是如何工作的,如何设置参数。

原始图片、分割图片和 Canny 检测到的图片

enter image description here

enter image description here

enter image description here

这是我用来到达那里的代码

img = cv2.imread('clock.jpg')
cv2.imshow('img',img)
cv2.waitKey(0)

gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
blur = cv2.medianBlur(gray,5)
cv2.imshow('blur',blur)
cv2.waitKey(0)

circles = cv2.HoughCircles(blur,cv2.HOUGH_GRADIENT,1,20,param1=20,param2=100,minRadius=0,maxRadius=0)
detected_cricles = np.uint16(np.around(circles))
circle = detected_cricles[0][0]

x = circle[0]
y = circle[1]
r = circle[2]

rect = (x - r,y - r,x+r,y+(r-10))
mask = np.zeros(img.shape[:2],dtype = np.uint8)
bgdModel = np.zeros((1,65),np.float64)
fgdModel = np.zeros((1,np.float64)

cv2.grabCut(img,mask,rect,bgdModel,fgdModel,cv2.GC_INIT_WITH_RECT)
mask2 = np.where((mask == 1) + (mask == 3),255,0).astype('uint8')
segmented = cv2.bitwise_and(img,img,mask=mask2)
cv2.imshow('segmented',segmented)
cv2.waitKey(0)

blur = cv2.GaussianBlur(segmented,(11,11),0)
cv2.imshow('blur2',blur)
cv2.waitKey(0)

canny = cv2.Canny(blur,30,150,None,3)
cv2.imshow('canny',canny)
cv2.waitKey(0)

解决方法

这是在 Python/OpenCV 中使用 HoughLinesP 的一种方法。该方法在获得霍夫线之前使用阈值、轮廓和细化。我会让你计算线端点的角度。

输入:

enter image description here

import cv2
import numpy as np
from skimage.morphology import skeletonize

# Read image
img = cv2.imread('clock.jpg')
hh,ww = img.shape[:2]

# convert to gray
gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)

# threshold
thresh = cv2.threshold(gray,128,255,cv2.THRESH_BINARY)[1]

# invert so shapes are white on black background
thresh = 255 - thresh

# get contours and save area
cntrs_info = []
contours = cv2.findContours(thresh,cv2.RETR_LIST,cv2.CHAIN_APPROX_SIMPLE)
contours = contours[0] if len(contours) == 2 else contours[1]
index=0
for cntr in contours:
    area = cv2.contourArea(cntr)
    cntrs_info.append((index,area))
    index = index + 1

# sort contours by area
def takeSecond(elem):
    return elem[1]
cntrs_info.sort(key=takeSecond,reverse=True)

# get third largest contour
arms = np.zeros_like(thresh)
index_third = cntrs_info[2][0]
cv2.drawContours(arms,[contours[index_third]],(1),-1)

#arms=cv2.ximgproc.thinning(arms)
arms_thin = skeletonize(arms)
arms_thin = (255*arms_thin).clip(0,255).astype(np.uint8)

# get hough lines and draw on copy of input
result = img.copy()
lineThresh = 15
minLineLength = 20
maxLineGap = 100
max
lines = cv2.HoughLinesP(arms_thin,1,np.pi/180,lineThresh,None,minLineLength,maxLineGap)

for [line] in lines:
    x1 = line[0]
    y1 = line[1]
    x2 = line[2]
    y2 = line[3]
    cv2.line(result,(x1,y1),(x2,y2),(0,255),2)   

# save results
cv2.imwrite('clock_thresh.jpg',thresh)
cv2.imwrite('clock_arms.jpg',(255*arms).clip(0,255).astype(np.uint8))
cv2.imwrite('clock_arms_thin.jpg',arms_thin)
cv2.imwrite('clock_lines.jpg',result)

cv2.imshow('thresh',thresh)
cv2.imshow('arms',255).astype(np.uint8))
cv2.imshow('arms_thin',arms_thin)
cv2.imshow('result',result)
cv2.waitKey(0)
cv2.destroyAllWindows()

阈值图像:

enter image description here

手臂轮廓:

enter image description here

细化(骨架):

enter image description here

输入上的霍夫线段:

enter image description here

,

这是另一种可能的解决方案。我们将尝试分割时钟指针并通过 Hough 的线变换运行它们以检测线。现在,这种检测将产生所有可能穿过时钟指针像素的直线——产生多条线。您可以尝试使用线变换参数将结果缩小到目标线,但最终可能会得到一组线。我将尝试使用 K-Means 对这些线进行聚类,以只获得两条线,而不管 Hough 线变换的输出如何。这些是步骤:

  1. 获取图像的二进制蒙版以隔离时钟指针
  2. 应用一些形态来消除噪音
  3. 通过霍夫线检测
  4. 运行二进制掩码
  5. 在多条线上使用K-means以获得只有2(平均)线(每个时钟指针一条)

让我们看看代码:

# Imports
import cv2
import numpy as np

# Read image
imagePath = "D://opencvImages//"
inputImage = cv2.imread(imagePath+"orFGl.jpg")

# Store deep copy for results:
originalImg = inputImage.copy()

# Convert BGR back to grayscale:
grayInput = cv2.cvtColor(inputImage,cv2.COLOR_BGR2GRAY)

# Threshold via Otsu + bias adjustment:
threshValue,binaryImage = cv2.threshold(grayInput,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU)

第一位是微不足道的并产生这个二进制掩码:

enter image description here

我们可以通过一些形态学去除小元素。让我们应用 erosion 后跟 dilation 来过滤除较大组件(时钟指针)之外的所有内容:

# Set morph operation iterations:
opIterations = 1

# Get the structuring element:
structuringElement = cv2.getStructuringElement(cv2.MORPH_RECT,(3,3))

# Perform Erode:
erodeImg = cv2.morphologyEx(binaryImage,cv2.MORPH_ERODE,structuringElement,opIterations,cv2.BORDER_REFLECT101)
# Perform Dilate:
dilateImg = cv2.morphologyEx(erodeImg,cv2.MORPH_DILATE,cv2.BORDER_REFLECT101)

这会产生这个图像:

enter image description here

非常好,几乎所有的噪音都消失了。让我们直接通过线路检测运行它,看看我们得到什么样的结果。此外,我准备了一些列表来存储行的每个起始 (x1,y1) 和结束 (x2,y2) 点:

# Set HoughLinesP parameters:
lineThresh = 50
minLineLength = 20
maxLineGap = 100
# Run the line detection:
lines = cv2.HoughLinesP(dilateImg,maxLineGap)

# Prepare some lists to store every coordinate of the detected lines:
X1 = []
X2 = []
Y1 = []
Y2 = []

# Store and draw the lines:
for [currentLine] in lines:

    # First point:
    x1 = currentLine[0]
    y1 = currentLine[1]
    X1.append(x1)
    Y1.append(y1)

    # Second point:
    x2 = currentLine[2]
    y2 = currentLine[3]
    X2.append(x2)
    Y2.append(y2)

    # Draw the lines:
    cv2.line(originalImg,2)
    cv2.imshow("Lines",originalImg)
    cv2.waitKey(0)

结果如下:

enter image description here

如您所见,有多行。幸运的是,这些线条聚集在两个非常明显的组中:左手和右手。如果我们将四个坐标聚类分成两组,我们就可以得到每只手的平均起点和终点。这可以通过应用聚类算法来完成,在本例中为 K-MeansK-means 将需要四个保存数据的数组来生成两个聚类中心。在向它提供我们的数据之前,我们需要按照 K-means 期望的方式对其进行重塑:

# Reshape the arrays for K-means
X1 = np.array(X1)
Y1 = np.array(Y1)
X2 = np.array(X2)
Y2 = np.array(Y2)

X1dash = X1.reshape(-1,1)
Y1dash = Y1.reshape(-1,1)
X2dash = X2.reshape(-1,1)
Y2dash = Y2.reshape(-1,1)

# Stack the data
Z = np.hstack((X1dash,Y1dash,X2dash,Y2dash))

# K-means operates on 32-bit float data:
floatPoints = np.float32(Z)

# Set the convergence criteria and call K-means:
criteria = (cv2.TERM_CRITERIA_EPS + cv2.TERM_CRITERIA_MAX_ITER,10,1.0)
# Set the desired number of clusters
K = 2
ret,label,center = cv2.kmeans(floatPoints,K,criteria,cv2.KMEANS_RANDOM_CENTERS)

结果在 center 数组中。在这里,我们给出了最后一对线。让我们遍历它并在原始图像上绘制它们:

# Loop through the center points
# and draw the lines:
for p in range(len(center)):

    # Get line points:
    print(center[p])
    x1 = int(center[p][0])
    y1 = int(center[p][1])
    x2 = int(center[p][2])
    y2 = int(center[p][3])

    cv2.line(originalImg,0),1)
    cv2.imshow("Lines",originalImg)
    cv2.waitKey(0)

这是最后一对线(绿色):

enter image description here

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...