| import cv2 |
| import numpy as np |
| import os |
| import argparse |
|
|
| def detect_and_crop_image(image_path, output_image_path=None): |
| if not os.path.exists(image_path): |
| print(f"Error: Image file not found at {image_path}") |
| return None |
|
|
| |
| img = cv2.imread(image_path) |
| if img is None: |
| print("Error: Could not open image.") |
| return None |
|
|
| |
| gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) |
| |
| |
| |
| mask = cv2.inRange(gray, 20, 235) |
| |
| |
| |
| |
| kernel_open = cv2.getStructuringElement(cv2.MORPH_RECT, (15, 15)) |
| mask = cv2.morphologyEx(mask, cv2.MORPH_OPEN, kernel_open) |
| |
| |
| |
| |
| kernel_close = cv2.getStructuringElement(cv2.MORPH_RECT, (51, 51)) |
| mask = cv2.morphologyEx(mask, cv2.MORPH_CLOSE, kernel_close) |
| |
| |
| contours, _ = cv2.findContours(mask, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE) |
| print(f"📊 Encontrados {len(contours)} contornos potenciais na imagem.") |
| |
| if not contours: |
| print("Error: No significant non-background regions detected.") |
| return None |
| |
| |
| max_area = 0 |
| best_bbox = None |
| |
| for c in contours: |
| x, y, w, h = cv2.boundingRect(c) |
| area = w * h |
| if area > max_area: |
| max_area = area |
| best_bbox = (x, y, w, h) |
| |
| if best_bbox is None or max_area < 500: |
| print(f"❌ Aviso: Nenhum conteúdo significativo detectado (max_area={max_area} < 500).") |
| return None |
| |
| x, y, w, h = best_bbox |
| print(f"✅ Melhor região de conteúdo: {w}x{h} @ ({x},{y}) | Área: {max_area}px") |
|
|
| x, y, w, h = best_bbox |
| |
| |
| |
| |
| img_h, img_w = img.shape[:2] |
| |
| def check_corners(cx, cy, cw, ch, m): |
| |
| |
| coords = [ |
| (cy, cx), |
| (cy, cx + cw - 1), |
| (cy + ch - 1, cx), |
| (cy + ch - 1, cx + cw - 1) |
| ] |
| for py, px in coords: |
| if m[py, px] == 0: |
| return False |
| return True |
|
|
| zoom_inset = 0 |
| max_zoom = min(w, h) // 4 |
| |
| while not check_corners(x, y, w, h, mask) and zoom_inset < max_zoom: |
| x += 1 |
| y += 1 |
| w -= 2 |
| h -= 2 |
| zoom_inset += 1 |
| if w <= 20 or h <= 20: |
| break |
| |
| if zoom_inset > 0: |
| print(f"Smart Zoom applied: {zoom_inset}px inset to clear rounded corners.") |
|
|
| |
| |
| prop_x_min = x |
| prop_y_min = y |
| prop_x_max = x + w |
| prop_y_max = y + h |
|
|
| def validate_crop(region, border_region, edge_thresh=0.80, region_thresh=0.60): |
| if region.size == 0 or border_region.size == 0: |
| return False |
| |
| dark_edge = np.count_nonzero(border_region < 20) / border_region.size |
| light_edge = np.count_nonzero(border_region > 235) / border_region.size |
| |
| dark_region = np.count_nonzero(region < 20) / region.size |
| light_region = np.count_nonzero(region > 235) / region.size |
| |
| is_dark_bg = (dark_edge >= edge_thresh) and (dark_region >= region_thresh) |
| is_light_bg = (light_edge >= edge_thresh) and (light_region >= region_thresh) |
| |
| return is_dark_bg or is_light_bg |
|
|
| |
| if prop_y_min > 0: |
| top_region = gray[0:prop_y_min, :] |
| top_border = gray[0:min(3, prop_y_min), :] |
| if not validate_crop(top_region, top_border): |
| prop_y_min = 0 |
|
|
| |
| if prop_y_max < img_h: |
| bottom_region = gray[prop_y_max:img_h, :] |
| bottom_border = gray[max(img_h-3, prop_y_max):img_h, :] |
| if not validate_crop(bottom_region, bottom_border): |
| prop_y_max = img_h |
|
|
| |
| if prop_x_min > 0: |
| left_region = gray[:, 0:prop_x_min] |
| left_border = gray[:, 0:min(3, prop_x_min)] |
| if not validate_crop(left_region, left_border): |
| prop_x_min = 0 |
|
|
| |
| if prop_x_max < img_w: |
| right_region = gray[:, prop_x_max:img_w] |
| right_border = gray[:, max(img_w-3, prop_x_max):img_w] |
| if not validate_crop(right_region, right_border): |
| prop_x_max = img_w |
|
|
| |
| inset = 2 |
| x_min = prop_x_min + inset if prop_x_min > 0 else 0 |
| y_min = prop_y_min + inset if prop_y_min > 0 else 0 |
| x_max = prop_x_max - inset if prop_x_max < img_w else img_w |
| y_max = prop_y_max - inset if prop_y_max < img_h else img_h |
| |
| final_w = x_max - x_min |
| final_h = y_max - y_min |
| |
| if final_w <= 0 or final_h <= 0: |
| print("Error: Invalid crop dimensions after zoom.") |
| return None |
| |
| |
| if final_w % 2 != 0: final_w -= 1 |
| if final_h % 2 != 0: final_h -= 1 |
| |
| x_max = x_min + final_w |
| y_max = y_min + final_h |
| |
| print(f"Proposed Crop: w={final_w}, h={final_h}, x={x_min}, y={y_min}") |
| |
| |
| cropped_img = img[y_min:y_max, x_min:x_max] |
| |
| if output_image_path is None: |
| filename, ext = os.path.splitext(image_path) |
| output_image_path = f"{filename}_cropped{ext}" |
| |
| cv2.imwrite(output_image_path, cropped_img) |
| print(f"Successfully created cropped image at {output_image_path}") |
| return output_image_path |
|
|
| if __name__ == "__main__": |
| import sys |
| |
| input_image = "image.png" |
| output_image = "image_cropped.png" |
| |
| if len(sys.argv) > 1: |
| input_image = sys.argv[1] |
| if len(sys.argv) > 2: |
| output_image = sys.argv[2] |
| |
| print(f"Processing: {input_image} -> {output_image}") |
| result = detect_and_crop_image(input_image, output_image) |
| |
| if result and os.path.exists(result): |
| print(f"\n✅ Done! Cropped image saved as: {result}") |
| else: |
| print(f"\n❌ Failed to create cropped image.") |