id / core /crop.py
Esmaill1
Initialize Hugging Face Space with project files
e64ee47
import cv2
import os
from PIL import Image
import sys
import cv2.data
import numpy as np
# Locate the standard frontal face XML classifier provided by OpenCV
cascade_path = os.path.join(
cv2.data.haarcascades, "haarcascade_frontalface_default.xml"
)
face_cascade = cv2.CascadeClassifier(cascade_path)
def _load_image_exif_safe(image_path):
"""Loads an image using PIL, handles EXIF orientation, and converts to OpenCV BGR."""
try:
from PIL import ImageOps
pil_img = Image.open(image_path)
pil_img = ImageOps.exif_transpose(pil_img)
# Convert to BGR for OpenCV
return cv2.cvtColor(np.array(pil_img.convert("RGB")), cv2.COLOR_RGB2BGR)
except Exception as e:
print(f"Error loading image safe: {e}")
return None
def get_auto_crop_rect(image_path):
"""
Detects a face and calculates the 5:7 crop rectangle.
Returns (x1, y1, x2, y2) in original image coordinates or None.
"""
image = _load_image_exif_safe(image_path)
if image is None:
return None
h, w, _ = image.shape
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
faces = face_cascade.detectMultiScale(gray, scaleFactor=1.1, minNeighbors=5)
if len(faces) == 0:
# Fallback: Center crop if no face found
aspect_ratio = 5 / 7
crop_h = int(h * 0.8)
crop_w = int(crop_h * aspect_ratio)
x1 = (w - crop_w) // 2
y1 = (h - crop_h) // 2
return (x1, y1, x1 + crop_w, y1 + crop_h)
faces = sorted(faces, key=lambda x: x[2] * x[3], reverse=True)
(x, y, fw, fh) = faces[0]
cx, cy = x + fw // 2, y + fh // 2
aspect_ratio = 5 / 7
crop_height = int(min(h, w / aspect_ratio) * 0.7)
crop_width = int(crop_height * aspect_ratio)
head_top = y - int(fh * 0.35)
HEAD_SPACE_RATIO = 0.10
y1 = max(0, head_top - int(crop_height * HEAD_SPACE_RATIO))
x1 = max(0, cx - crop_width // 2)
x2 = min(w, x1 + crop_width)
y2 = min(h, y1 + crop_height)
# Adjust to maintain size
if x2 - x1 < crop_width:
x1 = max(0, x2 - crop_width)
if y2 - y1 < crop_height:
y1 = max(0, y2 - crop_height)
return (int(x1), int(y1), int(x1 + crop_width), int(y1 + crop_height))
def apply_custom_crop(image_path, output_path, rect):
"""
Applies a specific (x1, y1, x2, y2) crop and resizes to 10x14cm @ 300DPI.
"""
x1, y1, x2, y2 = rect
try:
image = _load_image_exif_safe(image_path)
if image is None: return False
cropped = image[y1:y2, x1:x2]
final = cv2.resize(cropped, (1181, 1654))
final_rgb = cv2.cvtColor(final, cv2.COLOR_BGR2RGB)
pil_img = Image.fromarray(final_rgb)
pil_img.save(output_path, dpi=(300, 300), quality=95)
return True
except Exception as e:
print(f"Error applying custom crop: {e}")
return False
def crop_to_4x6_opencv(image_path, output_path):
"""Standard AI auto-crop."""
rect = get_auto_crop_rect(image_path)
if rect:
return apply_custom_crop(image_path, output_path, rect)
return False
def batch_process(input_folder, output_folder):
if not os.path.exists(output_folder):
os.makedirs(output_folder)
files = [
f
for f in os.listdir(input_folder)
if f.lower().endswith((".jpg", ".jpeg", ".png"))
]
for filename in files:
crop_to_4x6_opencv(
os.path.join(input_folder, filename), os.path.join(output_folder, filename)
)