tesalonikahtp commited on
Commit
d1f4d62
·
1 Parent(s): 4785d8e

fix zoom shoulder

Browse files
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, head_percent=0.75):
25
  """
26
- REPLACES 'rotate_and_expand_face' AND 'crop_to_ratio'.
27
 
28
- This function guarantees the face height is exactly 'head_percent' (75%)
29
- of the final image height, performing rotation and padding in one step.
30
  """
31
  h0, w0 = img_bgr.shape[:2]
32
  fx, fy, fw, fh = raw_face_box
33
 
34
- # 1. Estimate Real Head Height (Chin to Top of Hair)
35
- # Haar usually detects eyebrows to chin. We multiply by 1.35 to get full head.
36
- estimated_head_h = fh * 1.35
 
 
 
 
 
 
37
 
38
- # 2. Calculate the Required Canvas Height based on the 75% rule
39
- # If head needs to be 75%, then Canvas = Head / 0.75
40
- required_crop_h = int(estimated_head_h / head_percent)
41
 
42
- # 3. Calculate Width based on the Aspect Ratio of the output (e.g. 3.5cm / 4.5cm)
 
 
43
  required_crop_w = int(required_crop_h * self.target_aspect)
44
 
45
- # 4. Find the center of the face
46
  face_center_x = fx + fw // 2
47
  face_center_y = fy + fh // 2
48
 
49
- # 5. Determine the center of the Crop
50
- # Eyes are usually at 45-50% from the top, not dead center.
51
- # We shift the crop box UP slightly so the face sits nicely.
52
- shift_y = int(required_crop_h * 0.05)
53
- center_x = face_center_x
54
- center_y = face_center_y - shift_y
 
 
55
 
56
- # 6. Create the Rotation Matrix
57
- # We rotate around the calculated center
58
- M = cv2.getRotationMatrix2D((float(center_x), float(center_y)), -angle_deg, 1.0)
59
 
60
- # 7. Modify the Matrix for Translation
61
- # This is the math magic: We tell OpenCV to move the (center_x, center_y)
62
- # to the center of our new (required_crop_w, required_crop_h) image.
63
- M[0, 2] += (required_crop_w / 2) - center_x
64
- M[1, 2] += (required_crop_h / 2) - center_y
65
 
66
- # 8. Generate the Final Image
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
- # 9. Resize to the final output pixels (e.g. 413x531)
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
- head_percent=0.75
 
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)