0

I tried to read the analog clock image and display the time using the digital image using the opencv python, I have done for reading the hours and minutes by separating the hours and minutes hand from the image using HoughLineP() using opencv but I am unable to separate the seconds hand from the image, Here is the code I am working with Please help me to separate the Seconds hand and to read the seconds values

import cv2
import math
import numpy as np
import matplotlib.pyplot as plt.
from math import sqrt, acos, degrees
import tkinter as tk

kernel = np.ones((5,5),np.uint8)
img = cv2.imread('input3.jpg')
ret, thresh = cv2.threshold(gray_img, 50, 255, cv2.THRESH_BINARY)

height, width = gray_img.shape
mask = np.zeros((height,width), np.uint8)

edges = cv2.Canny(thresh, 100, 200)

cimg=cv2.cvtColor(gray_img, cv2.COLOR_GRAY2BGR)

circles = cv2.HoughCircles(gray_img, cv2.HOUGH_GRADIENT, 1.2, 100)


for i in circles[0,:]:
  # print(i) # -> [429.00003 240.6     226.20001]
  i[2] = i[2] + 4
  # used to detect the circle in the image
  cv2.circle(mask, (int(i[0]), int(i[1])), int(i[2]), (255,255,255), thickness=-1)
    
masked_data = cv2.bitwise_and(img, img, mask=mask)

_,thresh = cv2.threshold(mask,1,255,cv2.THRESH_BINARY)

contours,hierarchy = cv2.findContours(thresh,cv2.RETR_EXTERNAL,cv2.CHAIN_APPROX_SIMPLE)

# Crop masked_data
crop = masked_data[y + 30 : y + h - 30, x + 30 : x + w - 30
i=crop

height, width, channels = i.shape

ret, mask = cv2.threshold(i, 10, 255, cv2.THRESH_BINARY)

edges = cv2.Canny(i,100,200)

kernel = np.ones((11,11),np.uint8)

kernel1 = np.ones((13,13),np.uint8)

edges = cv2.dilate(edges,kernel,iterations = 1)

edges = cv2.erode(edges,kernel1,iterations = 1)

minLineLength = 1000

maxLineGap = 10

lines = cv2.HoughLinesP(edges,
                        1,
                        np.pi/180,
                        15,
                        minLineLength,
                        maxLineGap)


l=[]

# l -> long, s -> short
xl1, xl2, yl1, yl2 = 0, 0, 0 , 0

xs1, xs2, ys1, ys2=0, 0, 0 , 0

for line in lines:

    # getting the values from the line
    x1, y1, x2, y2 = line[0]
    dx = x2 - x1
    if(dx<0):
        dx = dx * -1
    dy = y2 - y1
    if(dy < 0):
        dy = dy * -1
    hypo = sqrt(dx ** 2 + dy ** 2 )

    l.append(hypo)
    

a=len(l) # -> 295

l.sort(reverse=True)
m=0
h=0
    
for f in range(a):

    for line in lines:

        # getting the values from the line
        x1, y1, x2, y2 = line[0]

        
        dx = x2 - x1
        
        if(dx < 0):
            dx = dx * -1
        dy = y2 - y1
        if(dy < 0):
            dy = dy * -1
        hypo2 = sqrt(dx ** 2 + dy ** 2 )
        
        if(hypo2 == l[0]):
            m = hypo2
            xl1 = x1
            xl2 = x2
            yl1 = y1
            yl2 = y2

            # getting line region
            cv2.line(crop, (xl1, yl1), (xl2, yl2), (255, 0, 0), 3)
        
        if(m==l[0]):

            if(hypo2 ==l[f]):

                if((sqrt((xl2 - x2)**2 + (yl2 - y2)**2)) > 20):

                    if((sqrt((xl1 - x1)**2 + (yl1 - y1)**2))>20):

                        xs1 = x1
                        xs2 = x2
                        ys1 = y1
                        ys2 = y2

                        # getting line region
                        cv2.line(crop, (xs1, ys1), (xs2, ys2), (0, 255, 0), 3)
                        h=1
                        break

    if(h==1):
        break


xcenter = int(width / 2)
ycenter = int(height / 2)


hour1 = abs(xcenter - xs1)
hour2 = abs(xcenter - xs2)

if(hour1 > hour2):
    xhour = xs1
    yhour = ys1
else:
    xhour = xs2
    yhour = ys2


min1 = abs(xcenter - xl1)
min2 = abs(xcenter - xl2)

if(min1 > min2):
    xmin = xl1
    ymin = yl1
else:
    xmin = xl2
    ymin = yl2

l1 = sqrt( ((xcenter - xhour) ** 2) + ((ycenter - yhour) ** 2) )

l2 = ycenter

l3 = sqrt( ((xcenter - xhour) ** 2) + ((0 - yhour) ** 2) )

cos_theta_hour = ( ( (l1) ** 2 ) + ( (l2) ** 2 ) - ( (l3) ** 2) ) / ( 2 * (l1) * (l2) )
theta_hours_radian = acos(cos_theta_hour)
theta_hours = math.degrees(theta_hours_radian)

if(xhour > xcenter):
    right=1

else:
    right=0


if(right==1):
    hour = int(theta_hours / (6*5))

if(right==0):
    hour = 12 - (int(theta_hours / (6*5)))

if(hour==0):
    hour=12

l1 = sqrt( ((xcenter - xmin) ** 2) + ((ycenter - ymin) ** 2) )

l2 = ycenter

l3 = sqrt( ((xcenter - xmin) ** 2) + ((0 - ymin) ** 2) )

cos_theta_min = ( ( (l1) ** 2 ) + ( (l2) ** 2 ) - ( (l3) ** 2) ) / ( 2 * (l1) * (l2) )
theta_min_radian = acos(cos_theta_min)
theta_min = math.degrees(theta_min_radian)

if(xmin > xcenter):
    right=1

else:
    right=0


if(right==1):
    minute = int(theta_min / ((6*5)/5))

if(right==0):
    minute = 60 - (int(theta_min / ((6*5)/5)))
    if(xmin == xcenter):
        minutes=30

if (minute < 10):
    def display():
        value = "{}:0{}".format(hour,minute)
        digit.config(text = value)
else:
    def display():
        value = "{}:{}".format(hour,minute)
        digit.config(text = value)

canvas = tk.Tk()

canvas.title("Analog to Digital")

canvas.geometry("300x250")

digit = tk.Label(canvas, font=("ds-digital", 65, "bold"), bg="black", fg="blue", bd = 80)

digit.grid(row=0, column = 1)

display()

canvas.mainloop()

This is the sample clock image used

Ajay
  • 55
  • 10
  • 1
    Can you provide sample of your clock images? – ARK1375 Jun 25 '21 at 16:30
  • 2
    Wow, there is a lot of code, clean it up a bit by removing unnecessary comments. Meanwhile, I explain my solution. – ARK1375 Jun 25 '21 at 16:50
  • I have updated my code now, please go through with the code my main problem is I dont know how to separate the seconds hand from other hands, like in my code i have used 2 for loops, in 2nd nested for loop i have separated the x1, x2, y2, y2 coordinated of both minutes and hours but I don't know how to get thus x1, x2, y1, y2 for the seconds hand, Please I am new to this opencv kind of stuff, Please consider it and help me with it @Bilal – Ajay Jun 26 '21 at 01:42
  • related: https://forum.opencv.org/t/reading-analog-clock-using-opencv/4034 – Christoph Rackwitz Jun 26 '21 at 13:59

1 Answers1

1

Considering that your approach to retrieve the values of hours and minute is successful, here is a simple solution to find the value of seconds.
There are certain properties associated with the second-hand of your clock image which need a little preparation before estimating its value. The first one is the color. Because the hand is colored in red, you have a good leading point on separating it from the rest of the image. If you can mask the red color of the second-hand in the image using the cv2.inRange() function (read more about it here), you'll be left with a clean image of only the second-hand. After that, you can convert it to black and white using cv2.cvtColor() function. Now, the second problem might be the thin line of second-hand. You can solve that using Morphological Transformations. Using the Erosion_ function, you can grow the white area of the image and thicken the second-hand. After that, use whatever method you were using to find the value for minutes and hours hand and you are good to go.

ARK1375
  • 790
  • 3
  • 19
  • I am new to this opencv kind of stuff, can you please collaborate and help me with this thing? – Ajay Jun 25 '21 at 18:38
  • 1
    Well, I explained everything in detail and provided some links for you to check out. Take a look at them first. – ARK1375 Jun 25 '21 at 18:40
  • 1
    What is the problem, I don't understand... I told you what you have to do – ARK1375 Jun 25 '21 at 19:01
  • I have updated my code now, please go through with the code my main problem is I dont know how to separate the seconds hand from other hands, like in my code i have used 2 for loops, in 2nd nested for loop i have separated the x1, x2, y2, y2 coordinated of both minutes and hours but I don't know how to get thus x1, x2, y1, y2 for the seconds hand, Please I am new to this opencv kind of stuff, Please consider it and help me with it – Ajay Jun 26 '21 at 01:35