Make it passport size photo with white background
ls -la /tmp/workspace/ 2>/dev/null || mkdir -p /tmp/workspace && ls -la /tmp/workspace/
mkdir -p /tmp/workspace/passport-photo && curl -s -o /tmp/workspace/passport-photo/original.jpg "https://d361swluvexzqs.cloudfront.net/chats-attachments/4b6324e8-c9b4-4709-97a2-28122b00f492-1785212762017-h3urq0.jpg" && echo "Downloaded OK" && python3 -c "from PIL import Image; img = Image.open('/tmp/workspace/passport-photo/original.jpg'); print(img.size, img.mode)"
pip install --user rembg[gpu] 2>/dev/null || pip install --user rembg 2>&1 | tail -5
pip install --user rembg 2>&1 | tail -5
python3 -c "import rembg; print('rembg OK')"
python3 << 'EOF' import cv2 import numpy as np from PIL import Image # Load image img = cv2.imread('/tmp/workspace/passport-photo/original.jpg') h, w = img.shape[:2] print(f"Image size: {w}x{h}") # --- Step 1: GrabCut background removal --- mask = np.zeros((h, w), np.uint8) bgdModel = np.zeros((1, 65), np.float64) fgdModel = np.zeros((1, 65), np.float64) # Define rect around subject (leave some margin) margin_x = int(w * 0.02) margin_y = int(h * 0.02) rect = (margin_x, margin_y, w - 2*margin_x, h - 2*margin_y) cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 10, cv2.GC_INIT_WITH_RECT) # Additional hint: top corners are background # Mark top corners as definite background corner_size = int(min(w, h) * 0.15) mask[:corner_size, :corner_size] = cv2.GC_BGD mask[:corner_size, w-corner_size:] = cv2.GC_BGD # Run grabcut again with updated mask cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK) # Create binary mask: probable/definite foreground = 1 mask2 = np.where((mask == cv2.GC_FGD) | (mask == cv2.GC_PR_FGD), 255, 0).astype('uint8') # Smooth mask edges kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) mask2 = cv2.morphologyEx(mask2, cv2.MORPH_CLOSE, kernel, iterations=3) mask2 = cv2.GaussianBlur(mask2, (11, 11), 0) # --- Step 2: Compose on white background --- img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) white_bg = np.ones_like(img_rgb) * 255 alpha = mask2.astype(float) / 255.0 alpha3 = np.stack([alpha]*3, axis=-1) result = (img_rgb * alpha3 + white_bg * (1 - alpha3)).astype(np.uint8) # --- Step 3: Crop to passport size (35mm x 45mm @ 300dpi = 413x531 px) --- # Find bounding box of face/person in mask rows = np.any(mask2 > 127, axis=1) cols = np.any(mask2 > 127, axis=0) rmin, rmax = np.where(rows)[0][[0, -1]] cmin, cmax = np.where(cols)[0][[0, -1]] person_h = rmax - rmin person_w = cmax - cmin cx = (cmin + cmax) // 2 cy = (rmin + rmax) // 2 # Passport: 35x45mm -> aspect ratio 35/45 = 0.778 # Head should occupy ~70-80% of height # Add padding: top 10%, sides balanced target_w_px = 413 # 35mm @ 300dpi target_h_px = 531 # 45mm @ 300dpi # Make crop box centered on face with passport proportions # Use person height as base crop_h = int(person_h * 1.35) # add 35% padding crop_w = int(crop_h * 35 / 45) # Center horizontally on face center, vertically shift slightly up crop_x1 = cx - crop_w // 2 crop_y1 = rmin - int(person_h * 0.12) # small top margin crop_x2 = crop_x1 + crop_w crop_y2 = crop_y1 + crop_h # Clamp to image bounds with white padding def safe_crop(img_arr, x1, y1, x2, y2): ch, cw = img_arr.shape[:2] pad_top = max(0, -y1) pad_bot = max(0, y2 - ch) pad_left = max(0, -x1) pad_right = max(0, x2 - cw) img_padded = np.pad(img_arr, ((pad_top, pad_bot), (pad_left, pad_right), (0,0)), mode='constant', constant_values=255) y1p = y1 + pad_top y2p = y2 + pad_top x1p = x1 + pad_left x2p = x2 + pad_left return img_padded[y1p:y2p, x1p:x2p] cropped = safe_crop(result, crop_x1, crop_y1, crop_x2, crop_y2) # Resize to standard passport dimensions final = cv2.resize(cropped, (target_w_px, target_h_px), interpolation=cv2.INTER_LANCZOS4) # Save out_path = '/tmp/workspace/passport-photo/passport_photo.jpg' Image.fromarray(final).save(out_path, 'JPEG', quality=95, dpi=(300, 300)) print(f"Saved: {out_path}") print(f"Final size: {target_w_px}x{target_h_px} px (35x45mm @300dpi)") EOF
python3 -c " from PIL import Image img = Image.open('/tmp/workspace/passport-photo/passport_photo.jpg') print('Size:', img.size) print('Mode:', img.mode) # Check corner pixels (should be white) px = img.load() print('Top-left corner pixel:', px[0,0]) print('Top-right corner pixel:', px[img.width-1, 0]) print('File size:', __import__('os').path.getsize('/tmp/workspace/passport-photo/passport_photo.jpg'), 'bytes') "
passport photo
Image · JPG
Just remove background
python3 << 'EOF' import cv2 import numpy as np from PIL import Image # Load original img = cv2.imread('/tmp/workspace/passport-photo/original.jpg') h, w = img.shape[:2] # --- GrabCut background removal --- mask = np.zeros((h, w), np.uint8) bgdModel = np.zeros((1, 65), np.float64) fgdModel = np.zeros((1, 65), np.float64) margin_x = int(w * 0.02) margin_y = int(h * 0.02) rect = (margin_x, margin_y, w - 2*margin_x, h - 2*margin_y) cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 10, cv2.GC_INIT_WITH_RECT) # Mark top corners as definite background corner_size = int(min(w, h) * 0.15) mask[:corner_size, :corner_size] = cv2.GC_BGD mask[:corner_size, w-corner_size:] = cv2.GC_BGD cv2.grabCut(img, mask, rect, bgdModel, fgdModel, 5, cv2.GC_INIT_WITH_MASK) mask2 = np.where((mask == cv2.GC_FGD) | (mask == cv2.GC_PR_FGD), 255, 0).astype('uint8') # Refine mask edges kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (7, 7)) mask2 = cv2.morphologyEx(mask2, cv2.MORPH_CLOSE, kernel, iterations=3) mask2 = cv2.GaussianBlur(mask2, (9, 9), 0) # --- Compose on WHITE background (keep original size) --- img_rgb = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) white_bg = np.ones_like(img_rgb) * 255 alpha = mask2.astype(float) / 255.0 alpha3 = np.stack([alpha]*3, axis=-1) result = (img_rgb * alpha3 + white_bg * (1 - alpha3)).astype(np.uint8) out_path = '/tmp/workspace/passport-photo/bg_removed.jpg' Image.fromarray(result).save(out_path, 'JPEG', quality=95) print(f"Saved: {out_path}, size: {result.shape[1]}x{result.shape[0]}") EOF
bg removed
Image · JPG