File size: 9,292 Bytes
097f176
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
import cv2
import math
import numpy as np
import matplotlib.pyplot as plt
from utils.detections_utils import run_detection
import os


def get_box_center(box):
    """Calculate the center point of a bounding box"""
    x = (box[0] + box[2]) / 2
    y = (box[1] + box[3]) / 2
    return (x, y)

def calculate_angle(center, point, reference_point):
    """Calculate angle between two points relative to 12 o'clock position"""
    # Calculate vectors
    ref_vector = (reference_point[0] - center[0], reference_point[1] - center[1])
    point_vector = (point[0] - center[0], point[1] - center[1])
    
    # Calculate angles from vectors
    ref_angle = math.atan2(ref_vector[1], ref_vector[0])
    point_angle = math.atan2(point_vector[1], point_vector[0])
    
    # Calculate relative angle in degrees
    angle = math.degrees(point_angle - ref_angle)
    
    # Normalize angle to 0-360 range
    angle = (angle + 360) % 360
    
    return angle

def process_clock_time(detections_data, image_path):
    """Process clock time from detections"""
    # Organize detections by class_name and select the one with highest confidence for each class
    detections_by_class = {}
    for detection in detections_data[0]:
        class_name = detection['class_name']
        if class_name not in detections_by_class or detection['confidence'] > detections_by_class[class_name]['confidence']:
            detections_by_class[class_name] = detection

    # Validate required keys
    required_keys = ['hours', 'minutes', '12', 'circle']
    for key in required_keys:
        if key not in detections_by_class:
            print(f"Error: Missing required key '{key}' in detection data.")
            return None

    # Calculate circle center
    circle_box_point = get_box_center(detections_by_class['circle']['box'])
    
    # Determine center point: use 'center' if exists, otherwise use circle center
    if 'center' in detections_by_class:
        center_point = get_box_center(detections_by_class['center']['box'])
    else:
        center_point = circle_box_point

    hours_point = get_box_center(detections_by_class['hours']['box'])
    number_12_point = get_box_center(detections_by_class['12']['box'])

    # Try to get seconds point with highest confidence
    seconds_point = None
    seconds_angle = None
    calculated_seconds = 0
    
    if 'minutes' in detections_by_class:
        minutes_point = get_box_center(detections_by_class['minutes']['box'])

    if 'seconds' in detections_by_class:
        seconds_point = get_box_center(detections_by_class['seconds']['box'])

    # Calculate raw angles relative to 12 o'clock position
    hour_angle = calculate_angle(center_point, hours_point, number_12_point)
    
    # Calculate minute angle
    if minutes_point:
        minute_angle = calculate_angle(center_point, minutes_point, number_12_point)

    # Calculate seconds angle if seconds point exists
    if seconds_point:
        seconds_angle = calculate_angle(center_point, seconds_point, number_12_point)

    # Convert angles to time
    hours = (hour_angle / 30)  # Each hour is 30 degrees

    # Round to nearest hour and minute
    hours = math.floor(hours) % 12
    if hours == 0:
        hours = 12
    
    if minute_angle is not None:
        minutes = (minute_angle / 6)
        minutes = round(minutes) % 60
        calculated_minutes = minutes
    
    # Calculate seconds if angle exists
    if seconds_angle is not None:
        seconds = (seconds_angle / 6)  # Each second is 6 degrees
        seconds = round(seconds) % 60
        calculated_seconds = seconds

    return {
        'hours': hours,
        'minutes': calculated_minutes if minute_angle is not None else None,
        'seconds': calculated_seconds if seconds_angle is not None else None
    }

def draw_clock(image_path, center_point, hours_point, minutes_point, seconds_point, number_12_point, hour_angle, minute_angle, seconds_angle, calculated_hours, calculated_minutes, calculated_seconds, image_name):
    """Draw clock and reference points on the image"""

    img = cv2.imread(image_path)
    
    # To int
    center = (int(center_point[0]), int(center_point[1]))
    hours = (int(hours_point[0]), int(hours_point[1]))
    minutes = (int(minutes_point[0]), int(minutes_point[1])) if minutes_point else None
    seconds = (int(seconds_point[0]), int(seconds_point[1])) if seconds_point else None
    twelve = (int(number_12_point[0]), int(number_12_point[1]))
    
    # Draw the reference points
    cv2.circle(img, center, 3, (0, 0, 255), -1)  # Centro em vermelho
    cv2.circle(img, twelve, 3, (255, 0, 0), -1)  # Ponto 12 em azul
    
    # Draw the lines with thicker strokes
    cv2.line(img, center, hours, (0, 0, 255), 5)     # Ponteiro das horas em vermelho 
    if minutes:
        cv2.line(img, center, minutes, (255, 0, 0), 4) # Ponteiro dos minutos em azul 
    if seconds:
        cv2.line(img, center, seconds, (255, 165, 0), 2)   # Ponteiro dos segundos em laranja 
    
    cv2.line(img, center, twelve, (0, 255, 0), 1)    # Linha de referência (12h) em verde
    
    # Draw the text
    cv2.putText(img, f"Hour angle: {hour_angle:.1f}", 
                (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
    if minute_angle is not None:
        cv2.putText(img, f"Minute angle: {minute_angle:.1f}", 
                    (10, 60), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
        time_text = f"Time: {int(calculated_hours):02d}:{int(calculated_minutes):02d}"
    else:
        time_text = f"Time: {int(calculated_hours):02d}"
    
    if seconds_angle is not None:
        cv2.putText(img, f"Seconds angle: {seconds_angle:.1f}", 
                    (10, 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
        time_text = f"Time: {int(calculated_hours):02d}:{int(calculated_minutes):02d}:{int(calculated_seconds):02d}"
    else:
        time_text = f"Time: {int(calculated_hours):02d}:{int(calculated_minutes):02d}"
    
    cv2.putText(img, time_text, 
                (10, 120 if seconds else 90), cv2.FONT_HERSHEY_SIMPLEX, 1, (0, 0, 0), 2)
    

    output_path = f'results/images/{image_name}'
    cv2.imwrite(output_path, img)
    print(f"Annotated image saved to {output_path}")
    
def zoom_into_clock_circle(image_path, confidence=0.01):
    """

    Attempt to find the clock circle and zoom into it for more precise detection.

    

    Args:

        image_path (str): Path to the input image

        confidence (float): Confidence threshold for detection

    

    Returns:

        str: Path to the zoomed-in image, or None if no suitable circle found

    """
    # Read the image
    image = cv2.imread(image_path)
    
    # Run detection to find clock circle
    detections = run_detection(image_path, confidence=confidence)
    
    # Find the circle detection with highest confidence
    circle_detection = None
    for detection in detections[0]:
        if detection['class_name'] == 'circle' and detection['confidence'] >= confidence:
            if not circle_detection or detection['confidence'] > circle_detection['confidence']:
                circle_detection = detection
    
    if not circle_detection:
        return None
    
    # Extract bounding box
    x1, y1, x2, y2 = circle_detection['box']
    
    # Add some padding (20% on each side)
    height, width = image.shape[:2]
    pad_x = int((x2 - x1) * 0.2)
    pad_y = int((y2 - y1) * 0.2)
    
    # Calculate padded coordinates with boundary checks
    x1_pad = max(0, x1 - pad_x)
    y1_pad = max(0, y1 - pad_y)
    x2_pad = min(width, x2 + pad_x)
    y2_pad = min(height, y2 + pad_y)
    
    # Crop the image
    zoomed_image = image[int(y1_pad):int(y2_pad), int(x1_pad):int(x2_pad)]
    
    # Save the zoomed image
    zoomed_image_path = f'results/zoomed_images/{os.path.splitext(os.path.basename(image_path))[0]}_zoomed.jpg'
    os.makedirs('results/zoomed_images', exist_ok=True)
    cv2.imwrite(zoomed_image_path, zoomed_image)
    
    return zoomed_image_path

def process_clock_with_fallback(image_path, confidence=0.01):
    """

    Attempt to process clock time with fallback to zoomed detection.

    

    Args:

        image_path (str): Path to the input image

        confidence (float): Confidence threshold for detection

    

    Returns:

        dict or None: Processed clock time result

    """
    # First attempt with original image
    #original_result = process_clock_time(run_detection(image_path, confidence=confidence), image_path)
    
    # If original detection succeeds, return the result
    #if original_result:
    #    return original_result
    
    # Try zooming into clock circle
    zoomed_image_path = zoom_into_clock_circle(image_path, confidence)
    
    # If no zoom possible, return None
    if not zoomed_image_path:
        return None
    
    detections = run_detection(zoomed_image_path, confidence=confidence, zoom = True)
    # Attempt detection on zoomed image
    zoomed_result = process_clock_time(detections, zoomed_image_path)
    
    return detections, zoomed_result