File size: 8,943 Bytes
83e35a7
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
from backend.utils import convert_to_css_pixel, get_panel_type, types
import math

BUBBLE_WIDTH = 200
BUBBLE_HEIGHT = 94

def add_bubble_padding(least_roi_x, least_roi_y, crop_coord):
    left,right,top,bottom = crop_coord
    panel = get_panel_type(left, right, top, bottom)
    
    image_width = types[panel]['width']
    image_height = types[panel]['height']

    if least_roi_x == 0:
        if panel == '1' or panel == '2':
            least_roi_x += 10
        elif panel == '3':
            least_roi_x += 30
        else:
            least_roi_x += 20

    elif least_roi_x == image_width:
        least_roi_x -= BUBBLE_WIDTH + 15

    elif least_roi_x >= image_width - BUBBLE_WIDTH:
        least_roi_x -= BUBBLE_WIDTH - (image_width - least_roi_x) + 15

    if least_roi_y == 0:
        if panel == '2':
            least_roi_y += 30
        else:
            least_roi_y += 15

    elif least_roi_y == image_height:
        least_roi_y -= BUUBLE_HEIGHT + 15

    elif least_roi_y >= image_height - BUUBLE_HEIGHT:
        least_roi_y -= BUUBLE_HEIGHT - (image_height - least_roi_y) + 15
    
    return least_roi_x, least_roi_y


def get_bubble_position(image_coords, CAM_data=None, lip_coords=None):
    """
    Redesigned bubble placement for smart resize - positions relative to actual image content
    """
    left, right, top, bottom = image_coords
    
    # Calculate image dimensions within panel
    image_width = right - left
    image_height = bottom - top
    
    print(f"Image area: {image_width:.0f}x{image_height:.0f} at ({left:.0f}, {top:.0f})")
    
    # Define safe bubble positions relative to the actual image content
    safe_positions = _get_safe_image_positions(left, right, top, bottom, image_width, image_height)
    
    # If we have lip coordinates, create face exclusion zones
    if lip_coords and lip_coords[0] != -1 and lip_coords[1] != -1:
        lip_x, lip_y = lip_coords
        # Lip coordinates are already in panel coordinate system
        print(f"Lip detected at coords: ({lip_x}, {lip_y})")
        
        # Filter out positions too close to the face
        face_exclusion_radius = 60  # Standard exclusion radius
        filtered_positions = []
        
        for pos in safe_positions:
            distance = math.sqrt((pos[0] - lip_x)**2 + (pos[1] - lip_y)**2)
            if distance > face_exclusion_radius:
                filtered_positions.append(pos)
        
        if filtered_positions:
            safe_positions = filtered_positions
            print(f"Filtered to {len(safe_positions)} face-safe positions")
        else:
            print("Warning: No face-safe positions found, using all safe positions")
    
    # Select the best position (prefer corners and edges of image)
    best_position = _select_best_image_position(safe_positions, left, right, top, bottom)
    
    print(f"Selected bubble position: {best_position}")
    return best_position

def _get_safe_image_positions(left, right, top, bottom, image_width, image_height):
    """
    Generate safe bubble positions relative to the actual image content
    """
    positions = []
    
    # Calculate margins to keep bubbles within image bounds
    margin_x = BUBBLE_WIDTH / 2 + 20
    margin_y = BUBBLE_HEIGHT / 2 + 20
    
    # Define grid within the image area - focus on upper areas
    grid_cols = 4
    grid_rows = 4  # More rows for better upper area coverage
    
    # Calculate grid cell size within image
    cell_width = image_width / grid_cols
    cell_height = image_height / grid_rows
    
    # Generate grid positions within image - prioritize upper areas
    for row in range(grid_rows):
        for col in range(grid_cols):
            x = left + col * cell_width + cell_width / 2
            y = top + row * cell_height + cell_height / 2
            
            # Ensure bubble fits within image bounds
            if (left + margin_x <= x <= right - margin_x and 
                top + margin_y <= y <= bottom - margin_y):
                positions.append((x, y))
    
    # Add extra positions in upper areas for better coverage
    upper_positions = []
    upper_margin = 40
    
    # Top edge positions
    for i in range(1, grid_cols):
        x = left + i * cell_width
        y = top + upper_margin
        if (left + margin_x <= x <= right - margin_x and 
            top + margin_y <= y <= bottom - margin_y):
            upper_positions.append((x, y))
    
    # Upper quarter positions
    upper_quarter_y = top + (bottom - top) * 0.25
    for i in range(1, grid_cols):
        x = left + i * cell_width
        y = upper_quarter_y
        if (left + margin_x <= x <= right - margin_x and 
            top + margin_y <= y <= bottom - margin_y):
            upper_positions.append((x, y))
    
    positions.extend(upper_positions)
    
    # Add corner positions relative to image - prioritize upper corners
    corner_margin = 30
    corners = [
        (left + corner_margin, top + corner_margin),  # Top-left of image (highest priority)
        (right - corner_margin, top + corner_margin),  # Top-right of image (highest priority)
        (left + corner_margin, top + (bottom - top) * 0.2),  # Upper-left area
        (right - corner_margin, top + (bottom - top) * 0.2),  # Upper-right area
        (left + corner_margin, bottom - corner_margin),  # Bottom-left of image (lower priority)
        (right - corner_margin, bottom - corner_margin)  # Bottom-right of image (lower priority)
    ]
    
    for corner in corners:
        if (left + margin_x <= corner[0] <= right - margin_x and 
            top + margin_y <= corner[1] <= bottom - margin_y):
            positions.append(corner)
    
    # Add edge positions along image boundaries
    edge_positions = []
    edge_margin = 50
    
    # Top edge of image
    for i in range(1, grid_cols):
        x = left + i * cell_width
        y = top + edge_margin
        if (left + margin_x <= x <= right - margin_x and 
            top + margin_y <= y <= bottom - margin_y):
            edge_positions.append((x, y))
    
    # Right edge of image
    for i in range(1, grid_rows):
        x = right - edge_margin
        y = top + i * cell_height
        if (left + margin_x <= x <= right - margin_x and 
            top + margin_y <= y <= bottom - margin_y):
            edge_positions.append((x, y))
    
    positions.extend(edge_positions)
    
    # If still no positions, use image center
    if len(positions) == 0:
        center_x = left + image_width / 2
        center_y = top + image_height / 2
        positions.append((center_x, center_y))
        print(f"Warning: Image too small, using center position only")
    
    print(f"Generated {len(positions)} safe positions for image area {image_width:.0f}x{image_height:.0f}")
    return positions

def _select_best_image_position(positions, left, right, top, bottom):
    """
    Select the best position relative to image content
    Priority: TOP areas > corners > edges > center
    """
    if not positions:
        # Fallback to upper area if no positions available
        return (left + (right - left) / 2, top + (bottom - top) * 0.2)  # 20% from top
    
    # Score positions based on preference
    scored_positions = []
    for pos in positions:
        x, y = pos
        score = 0
        
        # STRONGLY prefer upper areas (highest priority)
        upper_threshold = (bottom - top) * 0.4  # Top 40% of image
        if y < top + upper_threshold:
            score += 200  # Much higher score for upper areas
        
        # Prefer top quarter (highest score)
        top_quarter = (bottom - top) * 0.25
        if y < top + top_quarter:
            score += 150
        
        # Prefer corners of image (high score)
        corner_threshold = 50
        if (x < left + corner_threshold or x > right - corner_threshold) and \
           (y < top + corner_threshold or y > bottom - corner_threshold):
            score += 100
        
        # Prefer edges of image (medium score)
        edge_threshold = 80
        if (x < left + edge_threshold or x > right - edge_threshold) or \
           (y < top + edge_threshold or y > bottom - edge_threshold):
            score += 50
        
        # Prefer right side (common comic bubble placement)
        if x > left + (right - left) * 0.6:  # Right 40% of image
            score += 30
        
        # Penalize lower areas
        lower_threshold = (bottom - top) * 0.7  # Bottom 30% of image
        if y > top + lower_threshold:
            score -= 50  # Negative score for lower areas
        
        scored_positions.append((pos, score))
    
    # Sort by score (highest first) and return the best
    scored_positions.sort(key=lambda x: x[1], reverse=True)
    best_position = scored_positions[0][0]
    
    print(f"Selected position with score {scored_positions[0][1]} at y={best_position[1]:.0f} (top={top:.0f}, bottom={bottom:.0f})")
    return best_position