File size: 10,430 Bytes
347d1a8
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
"""
Shared visualization constants for debug output across all algorithms.

This module provides centralized configuration for fonts, colors, sizes, and
layout used in debug visualizations throughout the Ring Sizer system.

Used by:
- card_detection.py - Multi-strategy card detection debug output
- finger_segmentation.py - Hand/finger detection debug output
- geometry.py - Axis, zone, measurement debug output
- visualization.py - Final composite debug overlay
- confidence.py - Confidence visualization

Example usage:
    from viz_constants import Color, FontScale, FontThickness, FONT_FACE

    cv2.putText(img, "Title", (20, 100), FONT_FACE,
                FontScale.TITLE, Color.WHITE,
                FontThickness.TITLE_OUTLINE, cv2.LINE_AA)
"""

import cv2
from typing import Tuple

# ============================================================================
# FONT SETTINGS
# ============================================================================

# Font face used across all visualizations
FONT_FACE = cv2.FONT_HERSHEY_SIMPLEX


class FontScale:
    """
    Font scale constants for text hierarchy levels.

    Larger values = bigger text. These are base scales that may be
    adjusted based on image size in some visualizations.
    """
    TITLE = 3.5          # Main titles (e.g., "Card Detection", "Final Result")
    SUBTITLE = 2.5       # Section headers (e.g., "Score: 0.85")
    LABEL = 1.8          # Inline labels (e.g., "#1 Score:0.83")
    BODY = 1.5           # Body text (normal annotations)
    SMALL = 1.0          # Small text (fine details)


class FontThickness:
    """
    Font thickness (stroke width) for text rendering.

    Larger values = thicker/bolder text.
    Use OUTLINE variants for background layer to create outlined text effect.
    """
    # Main text thickness
    TITLE = 7
    SUBTITLE = 5
    LABEL = 4
    BODY = 2

    # Outline/shadow thickness (draw first for outline effect)
    TITLE_OUTLINE = 10
    SUBTITLE_OUTLINE = 8
    LABEL_OUTLINE = 6
    BODY_OUTLINE = 4


# ============================================================================
# COLORS (BGR format for OpenCV)
# ============================================================================

class Color:
    """
    Standard colors used across all visualizations.

    All colors in BGR format (Blue, Green, Red) as required by OpenCV.
    Example: (255, 255, 255) = White in BGR

    Usage:
        cv2.circle(img, center, radius, Color.GREEN, -1)
    """
    # ========================================================================
    # Basic Colors
    # ========================================================================
    WHITE = (255, 255, 255)
    BLACK = (0, 0, 0)
    RED = (0, 0, 255)      # BGR: (0, 0, 255)
    GREEN = (0, 255, 0)     # BGR: (0, 255, 0)
    BLUE = (255, 0, 0)      # BGR: (255, 0, 0)

    # ========================================================================
    # Extended Palette
    # ========================================================================
    CYAN = (255, 255, 0)    # BGR: (255, 255, 0)
    YELLOW = (0, 255, 255)   # BGR: (0, 255, 255)
    MAGENTA = (255, 0, 255)  # BGR: (255, 0, 255)
    ORANGE = (0, 128, 255)   # BGR: (0, 128, 255)
    PINK = (128, 128, 255)   # BGR: (128, 128, 255)

    # ========================================================================
    # Semantic Colors (what they represent in the system)
    # ========================================================================

    # Object colors
    CARD = GREEN            # Credit card outline
    FINGER = MAGENTA        # Finger contour

    # Axis/geometry colors
    AXIS_PALM = CYAN        # Palm-side axis endpoint
    AXIS_TIP = ORANGE       # Fingertip axis endpoint
    AXIS_LINE = YELLOW      # Finger principal axis line

    # Measurement colors
    RING_ZONE = CYAN        # Ring-wearing zone overlay
    CROSS_SECTION = ORANGE  # Cross-section lines
    POINT = BLUE            # Intersection/measurement points

    # Text colors
    TEXT_PRIMARY = WHITE    # Primary text (titles, main info)
    TEXT_SUCCESS = GREEN    # Success messages
    TEXT_ERROR = RED        # Error messages
    TEXT_WARNING = YELLOW   # Warning messages


class StrategyColor:
    """
    Colors for different card detection strategies.

    Used to visually distinguish candidates from different detection methods
    in debug visualizations.
    """
    CANNY = Color.CYAN           # Canny edge detection (cyan)
    ADAPTIVE = Color.ORANGE      # Adaptive thresholding (orange)
    OTSU = Color.MAGENTA         # Otsu's thresholding (magenta)
    COLOR_BASED = Color.GREEN    # Color-based detection (green)
    ALL_CANDIDATES = Color.PINK  # Combined candidates (pink/purple)


# ============================================================================
# DRAWING SIZES
# ============================================================================

class Size:
    """
    Size constants for drawing geometric elements (circles, lines, etc.).

    All sizes in pixels.
    """
    # Circle radii
    CORNER_RADIUS = 8           # Card corners, small points
    ENDPOINT_RADIUS = 15        # Axis endpoints (palm/tip)
    INTERSECTION_RADIUS = 8     # Cross-section intersection points
    POINT_RADIUS = 5            # Generic points

    # Line thicknesses
    CONTOUR_THICK = 5           # Thick contours (finger, card)
    CONTOUR_NORMAL = 3          # Normal contours (candidates)
    LINE_THICK = 4              # Thick lines (axis)
    LINE_NORMAL = 2             # Normal lines (cross-sections)
    LINE_THIN = 1               # Thin lines (grid, reference)


# ============================================================================
# LAYOUT CONSTANTS
# ============================================================================

class Layout:
    """
    Layout positioning constants for text and elements.

    All positions in pixels from top-left corner.
    """
    # Title positioning (top-left text block)
    TITLE_Y = 100               # Y position for main title
    SUBTITLE_Y = 200            # Y position for subtitle/secondary text
    LINE_SPACING = 100          # Vertical spacing between text lines

    # Text offsets
    TEXT_OFFSET_X = 20          # Horizontal margin from left edge
    TEXT_OFFSET_Y = 25          # Vertical offset for inline text
    LABEL_OFFSET = 20           # Offset for labels near objects

    # Result text area (final visualization)
    RESULT_TEXT_Y_START = 60           # Starting Y for result text block
    RESULT_TEXT_LINE_HEIGHT = 55       # Height between result text lines
    RESULT_TEXT_X_OFFSET = 40          # X offset for result text


# ============================================================================
# HELPER FUNCTIONS
# ============================================================================

def get_scaled_font_size(base_scale: float, image_height: int,
                         reference_height: int = 1200,
                         min_scale: float = 1.5) -> float:
    """
    Scale font size based on image dimensions for consistent appearance.

    Args:
        base_scale: Base font scale (e.g., FontScale.TITLE)
        image_height: Height of the image in pixels
        reference_height: Reference height for scaling (default: 1200px)
        min_scale: Minimum scale to prevent text from being too small

    Returns:
        Scaled font size adjusted for image dimensions

    Example:
        # For a 2400px tall image, double the font size
        scale = get_scaled_font_size(FontScale.TITLE, 2400)
        # scale = 3.5 * 2 = 7.0
    """
    scale_factor = image_height / reference_height
    scaled = base_scale * scale_factor
    return max(scaled, min_scale)


def create_outlined_text(image, text, position, font_scale,
                        color, outline_color=None,
                        thickness=None, outline_thickness=None):
    """
    Draw text with outline for better visibility.

    Args:
        image: Image to draw on
        text: Text string to draw
        position: (x, y) position tuple
        font_scale: Font scale (from FontScale)
        color: Main text color (from Color)
        outline_color: Outline color (default: Color.WHITE)
        thickness: Main text thickness (auto-selected if None)
        outline_thickness: Outline thickness (auto-selected if None)

    Example:
        create_outlined_text(img, "Title", (20, 100),
                           FontScale.TITLE, Color.GREEN)
    """
    if outline_color is None:
        outline_color = Color.WHITE

    # Auto-select thickness based on font scale
    if thickness is None:
        if font_scale >= FontScale.TITLE:
            thickness = FontThickness.TITLE
        elif font_scale >= FontScale.SUBTITLE:
            thickness = FontThickness.SUBTITLE
        elif font_scale >= FontScale.LABEL:
            thickness = FontThickness.LABEL
        else:
            thickness = FontThickness.BODY

    if outline_thickness is None:
        outline_thickness = thickness + 3

    # Draw outline first (background layer)
    cv2.putText(image, text, position, FONT_FACE,
                font_scale, outline_color, outline_thickness, cv2.LINE_AA)

    # Draw main text on top
    cv2.putText(image, text, position, FONT_FACE,
                font_scale, color, thickness, cv2.LINE_AA)


# ============================================================================
# VALIDATION (Optional: for type checking and debugging)
# ============================================================================

def validate_color(color: Tuple[int, int, int]) -> bool:
    """
    Validate that a color tuple is in correct BGR format.

    Args:
        color: Tuple of (B, G, R) values

    Returns:
        True if valid, False otherwise
    """
    if not isinstance(color, tuple) or len(color) != 3:
        return False
    return all(0 <= val <= 255 for val in color)


# ============================================================================
# EXPORTS
# ============================================================================

__all__ = [
    # Font settings
    'FONT_FACE',
    'FontScale',
    'FontThickness',

    # Colors
    'Color',
    'StrategyColor',

    # Sizes
    'Size',

    # Layout
    'Layout',

    # Helper functions
    'get_scaled_font_size',
    'create_outlined_text',
    'validate_color',
]