Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file removed images/clock.jpg
Binary file not shown.
Binary file removed images/clock12.png
Binary file not shown.
221 changes: 143 additions & 78 deletions libraries/image_processor.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import cv2
import numpy as np
import tkinter as tk
from time import strftime

def resize_image(src, width = 600):
# Calculate the aspect ratio and resize the image
Expand All @@ -24,103 +26,166 @@ def process_circles(gray_image):
param1=100, param2=30, minRadius=200, maxRadius=500)


def detect_line(circle, source_image, gray_image):
def detect_line_and_draw_circle(circle, source_image, gray_image, filtered_lines):
center = (circle[0], circle[1])
radius = circle[2]

# Draw circle center
cv2.circle(source_image, center, 1, (0, 100, 100), 3)
cv2.circle(source_image, center, 1, (255, 0, 0), 3)

# Draw circle outline
cv2.circle(source_image, center, radius, (255, 0, 255), 3)

# Detect lines using Hough Line Transform
edges = cv2.Canny(gray_image, 50, 150)
lines = cv2.HoughLinesP(edges, 1, np.pi / 180, threshold=100, minLineLength=60, maxLineGap=5)

filtered_lines = [] # Initialize an empty list to store filtered lines

if lines is not None:
for line in lines:
x1, y1, x2, y2 = line[0]
angle = (np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi - 90) % 360 # Adjusted angle calculation
if abs(y1 - center[1]) < 20 or abs(y2 - center[1]) < 20:
cv2.line(source_image, (x1, y1), (x2, y2), (0, 255, 0), 2)

angle = np.arctan2(y2 - y1, x2 - x1) * 180 / np.pi # Calculate the angle of the line
line_with_angle = np.append(line[0], angle) # Add the angle to the line
# Existing line filtering logic
found = False

# Check if the current line is similar to any existing lines
for existing_line in filtered_lines:
if abs(angle - existing_line['angle']) < 10:
# If the angle is similar, compare and keep the longer line
length = np.sqrt((x2 - x1) ** 2 + (y2 - y1) ** 2)
existing_length = np.sqrt(
(existing_line['x2'] - existing_line['x1']) ** 2 +
(existing_line['y2'] - existing_line['y1']) ** 2
)
if length > existing_length:
# Update the existing line
existing_line['x1'], existing_line['y1'] = x1, y1
existing_line['x2'], existing_line['y2'] = x2, y2
existing_line['angle'] = angle
found = True
break

# If no similar line was found, add the current line as a new filtered line
if len(filtered_lines) > 0:
for existing_line in filtered_lines:
if abs(angle - existing_line[-1]) < 10:
length = np.sqrt((x2 - x1) * 2 + (y2 - y1) * 2)
length_ = np.sqrt(
(existing_line[2] - existing_line[0]) ** 2 +
(existing_line[3] - existing_line[1]) ** 2
)
if length_ > length:
existing_line[0], existing_line[1] = x1, y1
existing_line[2], existing_line[3] = x2, y2
existing_line[-1] = angle
found = True
break
if not found:
filtered_lines.append({
'x1': x1,
'y1': y1,
'x2': x2,
'y2': y2,
'angle': angle
})
filtered_lines.append([x1, y1, x2, y2, angle])

return source_image
for line in filtered_lines:
x1, y1, x2, y2, angle = line
cv2.line(source_image, (x1, y1), (x2, y2), (0, 255, 0), 2)

def detect_clock_hands(filtered_lines):
# Sort the lines by length from longest to shortest
sorted_lines = sorted(filtered_lines, key=lambda x: np.sqrt((x['x2'] - x['x1'])**2 + (x['y2'] - x['y1'])**2), reverse=True)
return source_image

clock_hands = {
'hour': None,
'minutes': None,
'seconds': None
}

# Take the three longest lines (seconds, minutes, and hour)
for i, line in enumerate(sorted_lines[:3]):
if i == 0:
clock_hands['seconds'] = line
elif i == 1:
clock_hands['minutes'] = line
elif i == 2:
clock_hands['hour'] = line
def determine_closest_point_to_center(circle, filtered_lines, lines_and_center):

center_x, center_y = circle[0], circle[1]

for line in filtered_lines:
x1, y1, x2, y2 = line[:4]
dist_to_point1 = np.sqrt((x1 - center_x)**2 + (y1 - center_y)**2)
dist_to_point2 = np.sqrt((x2 - center_x)**2 + (y2 - center_y)**2)

if dist_to_point1 < dist_to_point2:
closest_point = (x1, y1)
else:
closest_point = (x2, y2)

# Append the closest point to the line as new columns
line_with_closest_point = np.append(line, closest_point)
lines_and_center.append(line_with_closest_point)

lines_and_center = np.array(lines_and_center)

return lines_and_center


def hands_angle(lines_and_center):

for line in lines_and_center:
x1, y1, x2, y2, angle, center_x, center_y = line[:7]

if (x1 == center_x and y1 == center_y):
pointer_x, pointer_y = x2, y2
else:
pointer_x, pointer_y = x1, y1

radius = np.sqrt((x1 - x2)**2 + (y1 - y2)**2)
start_x, start_y = center_x, center_y + radius
vector_pointer = np.array([pointer_x - center_x, pointer_y - center_y])
vector_start = np.array([start_x - center_x, start_y - center_y])

magnitude_pointer = np.linalg.norm(vector_pointer)
magnitude_start = np.linalg.norm(vector_start)

#to find cosine
dot_product = np.dot(vector_pointer, vector_start)
cos_angle = (dot_product/(magnitude_pointer*magnitude_start))

#to find sine
matrix = np.array([vector_pointer, vector_start])
determinant = np.linalg.det(matrix)
sin_angle = (determinant/(magnitude_pointer*magnitude_start))

angle_pointer = np.degrees(np.arctan2(cos_angle, sin_angle))

if 0 <= angle_pointer <= 180:
orientation_angle = 90 + angle_pointer
elif -90 <= angle_pointer < 0:
orientation_angle = 90 + angle_pointer
else:
orientation_angle = angle_pointer + 450

line[4] = orientation_angle

length = []

for line in lines_and_center:
x1, y1, x2, y2, orientation_angle, center_x, center_y = line[:7]
l = np.sqrt((x2 - x1)**2 + (y2 - y1)**2)
length.append(l)

result_array = np.column_stack((lines_and_center, length))

# Sort lines based on their lengths
sorted_hands = sorted(result_array, key=lambda x: x[7], reverse=True)

hands = {
'hour': sorted_hands[2],
'minute': sorted_hands[1],
'second': sorted_hands[0]
}

# Find the expected minute angle based on the hours hand
expected_minute_angle = (hands['hour'][4] * 12) % 360
if abs(expected_minute_angle - hands['minute'][4]) > 15:
hands['minute'], hands['second'] = hands['second'], hands['minute']


return hands

return clock_hands

def detect_exact_time(clock_hands):
# Check if all three clock hands are available
if all(clock_hands.values()):
hour_hand = clock_hands['hour']
minutes_hand = clock_hands['minutes']
seconds_hand = clock_hands['seconds']

# Calculate the angle between the hour and minute hands
angle_hour_minutes = abs(hour_hand['angle'] - minutes_hand['angle'])

# Calculate the angle for each hand
angle_seconds = seconds_hand['angle']
angle_minutes = minutes_hand['angle']
angle_hour = hour_hand['angle']

# Calculate the exact time
hours = int(angle_hour / 30) # Assuming 360 degrees for 12 hours
minutes = int((angle_minutes / 6) % 60) # Assuming 360 degrees for 60 minutes
seconds = int((angle_seconds / 6) % 60) # Assuming 360 degrees for 60 seconds

# Format the time as "hh:mm:ss"
formatted_time = f"{hours:02d}:{minutes:02d}:{seconds:02d}"

return formatted_time

else:
return "Clock hands not detected"

hour_hand = clock_hands['hour']
minutes_hand = clock_hands['minute']
seconds_hand = clock_hands['second']

angle_seconds = seconds_hand[4]
angle_minutes = minutes_hand[4]
angle_hour = hour_hand[4]

hour_read = (angle_hour / 360) * 12
minutes_read = (angle_minutes / 360) * 60
seconds_read = (angle_seconds / 360) * 60

formatted_time = f"{int(hour_read):02d}:{int(minutes_read):02d}:{int(seconds_read):02d}"

return formatted_time


def display_time(exact_time):
# Create a tkinter window for displaying the time
time_window = tk.Tk()
time_window.title('Exact Time')

# Create a label widget to display the exact time
label = tk.Label(time_window, font=('Arial', 60), background='white', foreground='black', text=exact_time)
label.pack()
time_window.mainloop()
48 changes: 21 additions & 27 deletions main.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,15 @@
from libraries.image_processor import * # Functions for process the image
from libraries.image_processor import * # Functions for process the image
from os import environ
from sys import argv
import pdb

DEFAULT_IMAGE_PATH = "./images/clock.jpeg"
DEFAULT_IMAGE_PATH = 'images\clock.jpeg'

def image_path():
if len(argv) > 1:
return argv[1]
else:
return DEFAULT_IMAGE_PATH


def main():
filename = image_path()
# Loads an image
Expand All @@ -28,40 +26,36 @@ def main():
gray = to_gray(src)
circles = process_circles(gray)

# Processing Image
if circles is not None:
circles = np.uint16(np.around(circles))
for circle in circles[0, :]:
src = detect_line(circle, src, gray)

# Detect clock hands
filtered_lines = [] # Initialize an empty list to store filtered lines
filtered_lines = [] # Initialize an empty list

# Processing Image
if circles is not None:
circles = np.uint16(np.around(circles))
for circle in circles[0, :]:
src = detect_line(circle, src, gray)

# After detecting clock hands
clock_hands = detect_clock_hands(filtered_lines)
src = detect_line_and_draw_circle(circle, src, gray, filtered_lines) # Pass filtered_lines as an argument
else:
print("Failed to detect circles in the image.")
return

lines_and_center = []

# Calculate the angles for the clock hands
hour_hand_angle = clock_hands['hour']['angle']
minutes_hand_angle = clock_hands['minutes']['angle']
seconds_hand_angle = clock_hands['seconds']['angle']

# Calculate the exact time
exact_time = detect_exact_time(clock_hands)
print("Exact Time:", exact_time)
lines_and_center = determine_closest_point_to_center(circle, filtered_lines, lines_and_center)

hands = hands_angle(lines_and_center)

exact_time = detect_exact_time(hands)



# Showing results
print('salio todo bien!')
if environ.get('DOCKER_ENV'):
return
print("Exact Time:", exact_time)
cv2.imshow("detected circles and lines", src)
display_time(exact_time)
cv2.waitKey(0)
cv2.destroyAllWindows()


if __name__ == "__main__":
main()
main()