Spaces:
Build error
Build error
tesalonikahtp commited on
Commit ·
d1f4d62
1
Parent(s): 4785d8e
fix zoom shoulder
Browse files- app/util/passport_photo_engine/passport_cropper.py +36 -30
- server.py +2 -1
app/util/passport_photo_engine/passport_cropper.py
CHANGED
|
@@ -20,51 +20,57 @@ class PassportCropper:
|
|
| 20 |
|
| 21 |
# Blend
|
| 22 |
return (bgr.astype(float) * alpha + bg.astype(float) * (1.0 - alpha)).astype(np.uint8)
|
| 23 |
-
|
| 24 |
-
def crop_with_dynamic_zoom(self, img_bgr, angle_deg, raw_face_box,
|
| 25 |
"""
|
| 26 |
-
|
| 27 |
|
| 28 |
-
|
| 29 |
-
|
| 30 |
"""
|
| 31 |
h0, w0 = img_bgr.shape[:2]
|
| 32 |
fx, fy, fw, fh = raw_face_box
|
| 33 |
|
| 34 |
-
# 1.
|
| 35 |
-
#
|
| 36 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 37 |
|
| 38 |
-
#
|
| 39 |
-
|
| 40 |
-
required_crop_h = int(estimated_head_h / head_percent)
|
| 41 |
|
| 42 |
-
#
|
|
|
|
|
|
|
| 43 |
required_crop_w = int(required_crop_h * self.target_aspect)
|
| 44 |
|
| 45 |
-
#
|
| 46 |
face_center_x = fx + fw // 2
|
| 47 |
face_center_y = fy + fh // 2
|
| 48 |
|
| 49 |
-
#
|
| 50 |
-
#
|
| 51 |
-
#
|
| 52 |
-
|
| 53 |
-
|
| 54 |
-
|
|
|
|
|
|
|
| 55 |
|
| 56 |
-
#
|
| 57 |
-
|
| 58 |
-
M = cv2.getRotationMatrix2D(
|
| 59 |
|
| 60 |
-
#
|
| 61 |
-
|
| 62 |
-
|
| 63 |
-
M[0, 2] += (required_crop_w / 2) - center_x
|
| 64 |
-
M[1, 2] += (required_crop_h / 2) - center_y
|
| 65 |
|
| 66 |
-
#
|
| 67 |
-
# cv2.warpAffine does the rotation, cropping, and background padding all at once.
|
| 68 |
result = cv2.warpAffine(
|
| 69 |
img_bgr,
|
| 70 |
M,
|
|
@@ -73,5 +79,5 @@ class PassportCropper:
|
|
| 73 |
flags=cv2.INTER_LINEAR
|
| 74 |
)
|
| 75 |
|
| 76 |
-
#
|
| 77 |
return cv2.resize(result, (self.out_w, self.out_h), interpolation=cv2.INTER_AREA)
|
|
|
|
| 20 |
|
| 21 |
# Blend
|
| 22 |
return (bgr.astype(float) * alpha + bg.astype(float) * (1.0 - alpha)).astype(np.uint8)
|
| 23 |
+
|
| 24 |
+
def crop_with_dynamic_zoom(self, img_bgr, angle_deg, raw_face_box, top_margin=0.6, bottom_margin=1.3):
|
| 25 |
"""
|
| 26 |
+
Calculates the crop based on padding the face box.
|
| 27 |
|
| 28 |
+
top_margin=0.6: Space for hair and top gap.
|
| 29 |
+
bottom_margin=1.3: Space for neck and chest/shoulders.
|
| 30 |
"""
|
| 31 |
h0, w0 = img_bgr.shape[:2]
|
| 32 |
fx, fy, fw, fh = raw_face_box
|
| 33 |
|
| 34 |
+
# 1. Determine the Height of the Crop First
|
| 35 |
+
# We use the face height (fh) as the unit of measurement.
|
| 36 |
+
# Top padding = 0.6 * face height
|
| 37 |
+
# Bottom padding = 1.3 * face height (Includes chest)
|
| 38 |
+
# Face itself = 1.0
|
| 39 |
+
# Total Height = 0.6 + 1.0 + 1.3 = 2.9 units
|
| 40 |
+
|
| 41 |
+
needed_top_pad = int(fh * top_margin)
|
| 42 |
+
needed_bottom_pad = int(fh * bottom_margin)
|
| 43 |
|
| 44 |
+
# This is the total height of our final image in the original scale
|
| 45 |
+
required_crop_h = needed_top_pad + fh + needed_bottom_pad
|
|
|
|
| 46 |
|
| 47 |
+
# 2. Calculate Width based on Aspect Ratio
|
| 48 |
+
# We force the width to match the target aspect (e.g., 3.5/4.5)
|
| 49 |
+
# to ensure no distortion.
|
| 50 |
required_crop_w = int(required_crop_h * self.target_aspect)
|
| 51 |
|
| 52 |
+
# 3. Find Center Points
|
| 53 |
face_center_x = fx + fw // 2
|
| 54 |
face_center_y = fy + fh // 2
|
| 55 |
|
| 56 |
+
# 4. Determine Crop Center
|
| 57 |
+
# The crop shouldn't be centered exactly on the face center.
|
| 58 |
+
# It needs to be lower, because we added more padding to the bottom (chest) than the top.
|
| 59 |
+
# Center_Y = Face_Top - Top_Pad + (Total_Height / 2)
|
| 60 |
+
|
| 61 |
+
crop_top_y = fy - needed_top_pad
|
| 62 |
+
center_y = crop_top_y + (required_crop_h / 2.0)
|
| 63 |
+
center_x = float(face_center_x) # Center horizontally on face
|
| 64 |
|
| 65 |
+
# 5. Create Rotation Matrix
|
| 66 |
+
center_tuple = (float(center_x), float(center_y))
|
| 67 |
+
M = cv2.getRotationMatrix2D(center_tuple, -angle_deg, 1.0)
|
| 68 |
|
| 69 |
+
# 6. Translation (Move crop to origin)
|
| 70 |
+
M[0, 2] += (required_crop_w / 2.0) - center_x
|
| 71 |
+
M[1, 2] += (required_crop_h / 2.0) - center_y
|
|
|
|
|
|
|
| 72 |
|
| 73 |
+
# 7. Warp (Rotate + Crop + Pad)
|
|
|
|
| 74 |
result = cv2.warpAffine(
|
| 75 |
img_bgr,
|
| 76 |
M,
|
|
|
|
| 79 |
flags=cv2.INTER_LINEAR
|
| 80 |
)
|
| 81 |
|
| 82 |
+
# 8. Final Resize
|
| 83 |
return cv2.resize(result, (self.out_w, self.out_h), interpolation=cv2.INTER_AREA)
|
server.py
CHANGED
|
@@ -272,7 +272,8 @@ def create_app() -> Flask:
|
|
| 272 |
img_clean,
|
| 273 |
angle,
|
| 274 |
(x, y, w, h),
|
| 275 |
-
|
|
|
|
| 276 |
)
|
| 277 |
# Return result
|
| 278 |
is_success, buffer = cv2.imencode(".jpg", final_passport)
|
|
|
|
| 272 |
img_clean,
|
| 273 |
angle,
|
| 274 |
(x, y, w, h),
|
| 275 |
+
top_margin=0.6,
|
| 276 |
+
bottom_margin=1.3
|
| 277 |
)
|
| 278 |
# Return result
|
| 279 |
is_success, buffer = cv2.imencode(".jpg", final_passport)
|