239 lines
No EOL
8.2 KiB
Python
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() |