Spaces:
Running
Running
File size: 6,029 Bytes
80b326d 1496c35 80b326d 1496c35 80b326d 1496c35 80b326d 1496c35 80b326d |
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 |
import cv2
import mediapipe as mp
import numpy as np
def crop_and_maintain_ar(frame, face_box, target_w, target_h, zoom_out_factor=2.2):
"""
Recorta uma região baseada no rosto mantendo o aspect ratio do target.
Previne deformação (esticar/espremer).
"""
img_h, img_w, _ = frame.shape
x, y, w, h = face_box
# Centro do rosto
cx = x + w // 2
cy = y + h // 2
# Dimensão base do rosto (maior lado para garantir cobertura)
face_size = max(w, h)
# Altura desejada do crop (altura do rosto * fator de zoom/afastamento)
# zoom_out_factor: quanto maior, mais afastado (mais cenário)
req_h = face_size * zoom_out_factor
# Aspect Ratio alvo (1080 / 960 = 1.125)
target_ar = target_w / target_h
# Calcular largura e altura do crop mantendo AR
crop_h = req_h
crop_w = crop_h * target_ar
# Verificar limitações da imagem original (não podemos cortar mais que existe)
# Se a largura necessária for maior que a imagem, limitamos pela largura
if crop_w > img_w:
crop_w = float(img_w)
crop_h = crop_w / target_ar
# Se a altura necessária for maior que a imagem, limitamos pela altura
if crop_h > img_h:
crop_h = float(img_h)
crop_w = crop_h * target_ar
# Converter para inteiros
crop_w = int(crop_w)
crop_h = int(crop_h)
# Calcular coordenadas top-left do crop centralizado no rosto
x1 = int(cx - crop_w // 2)
y1 = int(cy - crop_h // 2)
# Ajuste de bordas (Clamp) deslisando a janela se possível
# Se sair pela esquerda, encosta na esquerda
if x1 < 0:
x1 = 0
# Se sair pela direita, encosta na direita
elif x1 + crop_w > img_w:
x1 = img_w - crop_w
# Se sair por cima
if y1 < 0:
y1 = 0
# Se sair por baixo
elif y1 + crop_h > img_h:
y1 = img_h - crop_h
# Verificação de segurança final se a imagem for menor que o crop (embora lógica acima evite)
x2 = x1 + crop_w
y2 = y1 + crop_h
# Crop
cropped = frame[y1:y2, x1:x2]
# Se o crop falhar (tamanho 0), retorna preto
if cropped.size == 0 or cropped.shape[0] == 0 or cropped.shape[1] == 0:
return np.zeros((target_h, target_w, 3), dtype=np.uint8)
# Redimensionar para o tamanho alvo final (1080x960)
# Como garantimos o AR, o resize mantém a proporção correta
resized = cv2.resize(cropped, (target_w, target_h), interpolation=cv2.INTER_LINEAR)
return resized
def crop_and_resize_two_faces(frame, face_positions, zoom_out_factor=2.2):
"""
Recorta e redimensiona dois rostos detectados no frame, ajustando para uma composição vertical
1080x1920 onde cada rosto ocupa metade da tela (1080x960).
"""
# Target dimensoes para cada metade
target_w = 1080
target_h = 960
# Se não temos 2 faces, fallback (segurança)
if len(face_positions) < 2:
return np.zeros((1920, 1080, 3), dtype=np.uint8)
# Primeiro rosto (Topo)
face1_img = crop_and_maintain_ar(frame, face_positions[0], target_w, target_h, zoom_out_factor)
# Segundo rosto (Embaixo)
face2_img = crop_and_maintain_ar(frame, face_positions[1], target_w, target_h, zoom_out_factor)
# Compor imagem final (Stack Vertical)
result_frame = np.vstack((face1_img, face2_img))
return result_frame
def detect_face_or_body_two_faces(frame, face_detection, face_mesh, pose):
# Converter a imagem para RGB
frame_rgb = cv2.cvtColor(frame, cv2.COLOR_BGR2RGB)
# Processar a detecção de rosto
results_face_detection = face_detection.process(frame_rgb)
results_face_mesh = face_mesh.process(frame_rgb)
results_pose = pose.process(frame_rgb)
face_positions_detection = []
if results_face_detection.detections:
for detection in results_face_detection.detections[:2]:
bbox = detection.location_data.relative_bounding_box
x_min = int(bbox.xmin * frame.shape[1])
y_min = int(bbox.ymin * frame.shape[0])
width = int(bbox.width * frame.shape[1])
height = int(bbox.height * frame.shape[0])
face_positions_detection.append((x_min, y_min, width, height))
if len(face_positions_detection) == 2:
return face_positions_detection
face_positions_mesh = []
if results_face_mesh.multi_face_landmarks:
for landmarks in results_face_mesh.multi_face_landmarks[:2]:
x_coords = [int(landmark.x * frame.shape[1]) for landmark in landmarks.landmark]
y_coords = [int(landmark.y * frame.shape[0]) for landmark in landmarks.landmark]
x_min, x_max = min(x_coords), max(x_coords)
y_min, y_max = min(y_coords), max(y_coords)
width = x_max - x_min
height = y_max - y_min
face_positions_mesh.append((x_min, y_min, width, height))
if len(face_positions_mesh) == 2:
return face_positions_mesh
# If neither found 2, return what we found (prefer detection as it is bounding box optimized)
if face_positions_detection:
return face_positions_detection
if face_positions_mesh:
return face_positions_mesh
# Se nenhum rosto for detectado, usar a pose para estimar o corpo
if results_pose.pose_landmarks:
x_coords = [lmk.x for lmk in results_pose.pose_landmarks.landmark]
y_coords = [lmk.y for lmk in results_pose.pose_landmarks.landmark]
x_min = int(min(x_coords) * frame.shape[1])
x_max = int(max(x_coords) * frame.shape[1])
y_min = int(min(y_coords) * frame.shape[0])
y_max = int(max(y_coords) * frame.shape[0])
width = x_max - x_min
height = y_max - y_min
return [(x_min, y_min, width, height)]
return None
|