|
|
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 |
|
|
|
|
|
|
|
|
image_width = right - left |
|
|
image_height = bottom - top |
|
|
|
|
|
print(f"Image area: {image_width:.0f}x{image_height:.0f} at ({left:.0f}, {top:.0f})") |
|
|
|
|
|
|
|
|
safe_positions = _get_safe_image_positions(left, right, top, bottom, image_width, image_height) |
|
|
|
|
|
|
|
|
if lip_coords and lip_coords[0] != -1 and lip_coords[1] != -1: |
|
|
lip_x, lip_y = lip_coords |
|
|
|
|
|
print(f"Lip detected at coords: ({lip_x}, {lip_y})") |
|
|
|
|
|
|
|
|
face_exclusion_radius = 60 |
|
|
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") |
|
|
|
|
|
|
|
|
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 = [] |
|
|
|
|
|
|
|
|
margin_x = BUBBLE_WIDTH / 2 + 20 |
|
|
margin_y = BUBBLE_HEIGHT / 2 + 20 |
|
|
|
|
|
|
|
|
grid_cols = 4 |
|
|
grid_rows = 4 |
|
|
|
|
|
|
|
|
cell_width = image_width / grid_cols |
|
|
cell_height = image_height / grid_rows |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
if (left + margin_x <= x <= right - margin_x and |
|
|
top + margin_y <= y <= bottom - margin_y): |
|
|
positions.append((x, y)) |
|
|
|
|
|
|
|
|
upper_positions = [] |
|
|
upper_margin = 40 |
|
|
|
|
|
|
|
|
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_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) |
|
|
|
|
|
|
|
|
corner_margin = 30 |
|
|
corners = [ |
|
|
(left + corner_margin, top + corner_margin), |
|
|
(right - corner_margin, top + corner_margin), |
|
|
(left + corner_margin, top + (bottom - top) * 0.2), |
|
|
(right - corner_margin, top + (bottom - top) * 0.2), |
|
|
(left + corner_margin, bottom - corner_margin), |
|
|
(right - corner_margin, bottom - corner_margin) |
|
|
] |
|
|
|
|
|
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) |
|
|
|
|
|
|
|
|
edge_positions = [] |
|
|
edge_margin = 50 |
|
|
|
|
|
|
|
|
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)) |
|
|
|
|
|
|
|
|
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 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: |
|
|
|
|
|
return (left + (right - left) / 2, top + (bottom - top) * 0.2) |
|
|
|
|
|
|
|
|
scored_positions = [] |
|
|
for pos in positions: |
|
|
x, y = pos |
|
|
score = 0 |
|
|
|
|
|
|
|
|
upper_threshold = (bottom - top) * 0.4 |
|
|
if y < top + upper_threshold: |
|
|
score += 200 |
|
|
|
|
|
|
|
|
top_quarter = (bottom - top) * 0.25 |
|
|
if y < top + top_quarter: |
|
|
score += 150 |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
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 |
|
|
|
|
|
|
|
|
if x > left + (right - left) * 0.6: |
|
|
score += 30 |
|
|
|
|
|
|
|
|
lower_threshold = (bottom - top) * 0.7 |
|
|
if y > top + lower_threshold: |
|
|
score -= 50 |
|
|
|
|
|
scored_positions.append((pos, score)) |
|
|
|
|
|
|
|
|
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 |