master_adapt_detect/test_panel_accuracy.py
2025-10-01 14:32:55 -05:00

239 lines
No EOL
8.2 KiB
Python

#!/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()