#!/usr/bin/env python3 """ Test horizontal splitting accuracy on different panel types """ import cv2 import numpy as np from pathlib import Path import os def test_splitting_accuracy(image_path: str, expected_panels: int, layout_type: str): """Test horizontal splitting accuracy for a specific layout""" print(f"\n{'='*60}") print(f"Testing {layout_type}: {Path(image_path).name}") print(f"Expected panels: {expected_panels}") print(f"{'='*60}") # Load image img = cv2.imread(image_path) height, width = img.shape[:2] print(f"Image dimensions: {width}x{height}") # Test current algorithm crops = test_current_algorithm(img, width, height, expected_panels) # Save crop previews save_crop_previews(img, crops, image_path, expected_panels) # Analyze accuracy analyze_accuracy(crops, expected_panels, layout_type) return crops def test_current_algorithm(img, width: int, height: int, expected_panels: int): """Test the current horizontal splitting algorithm""" crops = [] # Current algorithm logic if width > height * 1.2: # Wide image, horizontal panels print(f"Using horizontal splitting for {width}x{height} image") # Convert to grayscale for analysis gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY) # Calculate horizontal histogram horizontal_hist = np.sum(gray, axis=0) inverted_hist = np.max(horizontal_hist) - horizontal_hist # Smooth the inverted histogram from scipy.ndimage import gaussian_filter1d smoothed_hist = gaussian_filter1d(inverted_hist, sigma=10) # Current parameters expected_panels_est = min(15, max(6, width // 800)) min_distance = width // (expected_panels_est * 1.5) print(f"Algorithm estimates {expected_panels_est} panels, min_distance={min_distance}") # Find peaks from scipy.signal import find_peaks peaks, properties = find_peaks(smoothed_hist, distance=min_distance, height=np.max(smoothed_hist) * 0.15, prominence=np.max(smoothed_hist) * 0.1) print(f"Found {len(peaks)} separator peaks") # Create panels x_boundaries = [0] + list(peaks) + [width] x_boundaries = sorted(list(set(x_boundaries))) for i in range(len(x_boundaries) - 1): x1, x2 = x_boundaries[i], x_boundaries[i + 1] if x2 - x1 >= 200: # min_crop_size crops.append({ 'bbox': (x1, 0, x2, height), 'width': x2 - x1, 'height': height, 'crop_id': f"horizontal_{i}" }) print(f"Generated {len(crops)} crops") else: print("Image not wide enough for horizontal splitting") crops.append({ 'bbox': (0, 0, width, height), 'width': width, 'height': height, 'crop_id': "single" }) return crops def save_crop_previews(img, crops, image_path: str, expected_panels: int): """Save individual crop images for visual verification""" base_name = Path(image_path).stem crops_dir = Path("panel_test_crops") crops_dir.mkdir(exist_ok=True) print(f"\nSaving {len(crops)} crop previews to {crops_dir}/") for i, crop in enumerate(crops): x1, y1, x2, y2 = crop['bbox'] cropped = img[y1:y2, x1:x2] crop_filename = f"{base_name}_expected{expected_panels}_crop{i+1:02d}.jpg" crop_path = crops_dir / crop_filename cv2.imwrite(str(crop_path), cropped) print(f" Crop {i+1}: {crop['width']}px wide -> {crop_filename}") def analyze_accuracy(crops, expected_panels: int, layout_type: str): """Analyze how well the splitting matches expectations""" detected_panels = len(crops) print(f"\n--- ACCURACY ANALYSIS ---") print(f"Layout type: {layout_type}") print(f"Expected panels: {expected_panels}") print(f"Detected panels: {detected_panels}") if detected_panels == expected_panels: print("✅ PERFECT MATCH!") elif abs(detected_panels - expected_panels) <= 1: print("✅ CLOSE MATCH (within 1)") elif detected_panels < expected_panels: print("❌ UNDER-SEGMENTATION (missing splits)") else: print("❌ OVER-SEGMENTATION (too many splits)") # Analyze crop sizes widths = [crop['width'] for crop in crops] avg_width = np.mean(widths) std_width = np.std(widths) print(f"Crop widths: {widths}") print(f"Average width: {avg_width:.0f}px (±{std_width:.0f}px)") # Check for suspiciously small or large crops min_reasonable = 300 # Minimum reasonable panel width max_reasonable = 2000 # Maximum reasonable panel width small_crops = [w for w in widths if w < min_reasonable] large_crops = [w for w in widths if w > max_reasonable] if small_crops: print(f"⚠️ Warning: {len(small_crops)} suspiciously small crops: {small_crops}") if large_crops: print(f"⚠️ Warning: {len(large_crops)} suspiciously large crops: {large_crops}") def main(): """Test horizontal splitting on various layout types""" test_cases = [ # Single panels (should not be split) { "path": "/Users/michael.clervi/Documents/projects/master_adapt_detect/layouts/6785934.jpg", "expected": 1, "type": "Single Panel" }, { "path": "/Users/michael.clervi/Documents/projects/master_adapt_detect/layouts/6813573.jpg", "expected": 1, "type": "Single Panel" }, # Double panels { "path": "/Users/michael.clervi/Documents/projects/master_adapt_detect/layouts/6785852.jpg", "expected": 2, "type": "Double Panel" }, # 4-panel layouts { "path": "/Users/michael.clervi/Documents/projects/master_adapt_detect/layouts/6799150.jpg", "expected": 4, "type": "4-Panel Layout" }, { "path": "/Users/michael.clervi/Documents/projects/master_adapt_detect/layouts/6813643.jpg", "expected": 4, "type": "4-Panel Layout" }, # Multi-panel layouts { "path": "/Users/michael.clervi/Documents/projects/master_adapt_detect/layouts/6791144.jpg", "expected": 8, "type": "Multi-Panel Layout" }, { "path": "/Users/michael.clervi/Documents/projects/master_adapt_detect/layouts/6786505.jpg", "expected": 10, "type": "Multi-Panel Layout" } ] print("HORIZONTAL SPLITTING ACCURACY TEST") print("="*60) results = [] for test_case in test_cases: if os.path.exists(test_case["path"]): crops = test_splitting_accuracy( test_case["path"], test_case["expected"], test_case["type"] ) results.append({ "file": Path(test_case["path"]).name, "type": test_case["type"], "expected": test_case["expected"], "detected": len(crops), "accurate": abs(len(crops) - test_case["expected"]) <= 1 }) else: print(f"⚠️ File not found: {test_case['path']}") # Summary print(f"\n{'='*60}") print("SUMMARY") print(f"{'='*60}") accurate_count = sum(1 for r in results if r["accurate"]) total_count = len(results) print(f"Accurate results: {accurate_count}/{total_count} ({accurate_count/total_count*100:.1f}%)") print() for result in results: status = "✅" if result["accurate"] else "❌" print(f"{status} {result['file']}: {result['detected']}/{result['expected']} panels ({result['type']})") print(f"\nCrop previews saved to: panel_test_crops/") print("Review the crop images to verify splitting accuracy!") if __name__ == "__main__": main()