Spaces:
Sleeping
Sleeping
File size: 7,240 Bytes
d18d4d2 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 | """Tests for histogram analysis module."""
import numpy as np
import pytest
from PIL import Image
from ptpd_calibration.imaging import (
HistogramAnalyzer,
HistogramResult,
HistogramStats,
HistogramScale,
)
@pytest.fixture
def analyzer():
"""Create histogram analyzer."""
return HistogramAnalyzer()
@pytest.fixture
def gray_gradient_image():
"""Create a grayscale gradient image."""
arr = np.linspace(0, 255, 256).reshape(1, 256).repeat(100, axis=0).astype(np.uint8)
return Image.fromarray(arr, mode="L")
@pytest.fixture
def rgb_test_image():
"""Create a test RGB image."""
arr = np.zeros((100, 100, 3), dtype=np.uint8)
arr[:50, :, 0] = 200 # Red top half
arr[50:, :, 2] = 200 # Blue bottom half
arr[:, :, 1] = 100 # Green everywhere
return Image.fromarray(arr, mode="RGB")
@pytest.fixture
def dark_image():
"""Create a dark image."""
arr = np.full((100, 100), 30, dtype=np.uint8)
return Image.fromarray(arr, mode="L")
@pytest.fixture
def bright_image():
"""Create a bright image."""
arr = np.full((100, 100), 220, dtype=np.uint8)
return Image.fromarray(arr, mode="L")
class TestHistogramAnalyzer:
"""Test histogram analyzer functionality."""
def test_analyze_grayscale_image(self, analyzer, gray_gradient_image):
"""Test analyzing a grayscale gradient."""
result = analyzer.analyze(gray_gradient_image)
assert isinstance(result, HistogramResult)
assert result.histogram is not None
assert len(result.histogram) == 256
assert result.image_mode == "L"
assert result.total_pixels == 256 * 100
def test_analyze_rgb_image(self, analyzer, rgb_test_image):
"""Test analyzing an RGB image."""
result = analyzer.analyze(rgb_test_image, include_rgb=True)
assert result.red_histogram is not None
assert result.green_histogram is not None
assert result.blue_histogram is not None
assert len(result.red_histogram) == 256
def test_analyze_numpy_array(self, analyzer):
"""Test analyzing from numpy array."""
arr = np.random.randint(0, 256, (100, 100), dtype=np.uint8)
result = analyzer.analyze(arr)
assert result.image_size == (100, 100)
assert result.total_pixels == 10000
def test_histogram_stats_basic(self, analyzer, gray_gradient_image):
"""Test basic statistics computation."""
result = analyzer.analyze(gray_gradient_image)
stats = result.stats
assert isinstance(stats, HistogramStats)
# Gradient should have mean around 127
assert 100 < stats.mean < 160
assert stats.min_value == 0
assert stats.max_value == 255
def test_histogram_stats_dark_image(self, analyzer, dark_image):
"""Test statistics for dark image."""
result = analyzer.analyze(dark_image)
stats = result.stats
assert stats.brightness < 0.2
assert stats.mean < 50
assert "dark" in " ".join(stats.notes).lower() or len(stats.notes) >= 0
def test_histogram_stats_bright_image(self, analyzer, bright_image):
"""Test statistics for bright image."""
result = analyzer.analyze(bright_image)
stats = result.stats
assert stats.brightness > 0.8
assert stats.mean > 200
def test_zone_distribution(self, analyzer, gray_gradient_image):
"""Test zone distribution calculation."""
result = analyzer.analyze(gray_gradient_image)
stats = result.stats
assert len(stats.zone_distribution) == 11
# Gradient should have some pixels in each zone
total_pct = sum(stats.zone_distribution.values())
assert 0.99 <= total_pct <= 1.01 # Should sum to ~100%
def test_clipping_detection_no_clipping(self, analyzer):
"""Test clipping detection with no clipping."""
arr = np.full((100, 100), 128, dtype=np.uint8)
result = analyzer.analyze(arr)
assert result.stats.shadow_clipping_percent < 1
assert result.stats.highlight_clipping_percent < 1
def test_clipping_detection_with_clipping(self, analyzer):
"""Test clipping detection with clipping."""
# Create image with heavy shadows
arr = np.zeros((100, 100), dtype=np.uint8)
arr[:50] = 0 # Half pure black
arr[50:] = 128
result = analyzer.analyze(arr)
assert result.stats.shadow_clipping_percent > 40
def test_dynamic_range(self, analyzer, gray_gradient_image):
"""Test dynamic range calculation."""
result = analyzer.analyze(gray_gradient_image)
# Gradient should have high dynamic range
assert result.stats.dynamic_range > 5
def test_compare_histograms(self, analyzer, gray_gradient_image, dark_image):
"""Test histogram comparison."""
comparison = analyzer.compare_histograms(gray_gradient_image, dark_image)
assert "similarity" in comparison
assert "changes" in comparison
assert "histogram_intersection" in comparison["similarity"]
assert "mean_shift" in comparison["changes"]
def test_create_histogram_plot(self, analyzer, gray_gradient_image):
"""Test histogram plot creation."""
result = analyzer.analyze(gray_gradient_image)
fig = analyzer.create_histogram_plot(result)
assert fig is not None
def test_create_histogram_plot_logarithmic(self, analyzer, gray_gradient_image):
"""Test logarithmic histogram plot."""
result = analyzer.analyze(gray_gradient_image)
fig = analyzer.create_histogram_plot(result, scale=HistogramScale.LOGARITHMIC)
assert fig is not None
def test_create_histogram_plot_with_rgb(self, analyzer, rgb_test_image):
"""Test histogram plot with RGB channels."""
result = analyzer.analyze(rgb_test_image, include_rgb=True)
fig = analyzer.create_histogram_plot(result, show_rgb=True)
assert fig is not None
def test_create_histogram_plot_with_zones(self, analyzer, gray_gradient_image):
"""Test histogram plot with zone boundaries."""
result = analyzer.analyze(gray_gradient_image)
fig = analyzer.create_histogram_plot(result, show_zones=True)
assert fig is not None
def test_to_dict(self, analyzer, gray_gradient_image):
"""Test result conversion to dictionary."""
result = analyzer.analyze(gray_gradient_image)
d = result.to_dict()
assert "image_size" in d
assert "statistics" in d
assert "total_pixels" in d
def test_stats_to_dict(self, analyzer, gray_gradient_image):
"""Test stats conversion to dictionary."""
result = analyzer.analyze(gray_gradient_image)
d = result.stats.to_dict()
assert "mean" in d
assert "median" in d
assert "zone_distribution" in d
def test_zone_descriptions(self):
"""Test zone descriptions retrieval."""
descs = HistogramAnalyzer.get_zone_descriptions()
assert len(descs) == 11
assert 0 in descs
assert 10 in descs
assert "black" in descs[0].lower()
assert "white" in descs[10].lower()
|