|
23 | 23 |
|
24 | 24 | import numpy |
25 | 25 |
|
26 | | -from odemis.util.synthetic import ParabolicMirrorRayTracer |
| 26 | +from odemis.util.synthetic import ParabolicMirrorRayTracer, simulate_peak |
27 | 27 |
|
28 | 28 |
|
29 | 29 | class TestParabolicMirrorRayTracer(unittest.TestCase): |
@@ -125,6 +125,84 @@ def test_constructor_raises_value_error_on_missing_keys(self): |
125 | 125 | with self.assertRaises(ValueError): |
126 | 126 | ParabolicMirrorRayTracer(good_pos=bad_pos_missing_all) |
127 | 127 |
|
| 128 | +class TestPeakSimulation(unittest.TestCase): |
| 129 | + |
| 130 | + def test_simulate_peak_1d(self): |
| 131 | + """ |
| 132 | + Verify simulate_peak generates a correct 1D Gaussian peak. |
| 133 | + """ |
| 134 | + |
| 135 | + amplitude = 1000 |
| 136 | + x0 = 50 |
| 137 | + width = 5 |
| 138 | + shape = 100 |
| 139 | + |
| 140 | + peak = simulate_peak(amplitude, x0, width, shape) |
| 141 | + |
| 142 | + self.assertEqual(peak.shape, (shape,)) |
| 143 | + self.assertEqual(peak.dtype, numpy.uint16) |
| 144 | + |
| 145 | + # peak maximum should occur near x0 |
| 146 | + peak_idx = numpy.argmax(peak) |
| 147 | + self.assertAlmostEqual(peak_idx, x0, delta=1) |
| 148 | + |
| 149 | + # peak value should be close to amplitude (clipped if necessary) |
| 150 | + self.assertAlmostEqual(peak[peak_idx], amplitude, delta=5) |
| 151 | + |
| 152 | + def test_simulate_peak_2d(self): |
| 153 | + """ |
| 154 | + Verify simulate_peak correctly expands the 1D peak to 2D. |
| 155 | + """ |
| 156 | + |
| 157 | + amplitude = 500 |
| 158 | + x0 = 40 |
| 159 | + width = 4 |
| 160 | + shape = (20, 100) |
| 161 | + |
| 162 | + peak = simulate_peak(amplitude, x0, width, shape) |
| 163 | + |
| 164 | + self.assertEqual(peak.shape, shape) |
| 165 | + |
| 166 | + # every row should be identical |
| 167 | + for row in peak: |
| 168 | + self.assertTrue(numpy.array_equal(row, peak[0])) |
| 169 | + |
| 170 | + # verify peak location |
| 171 | + peak_idx = numpy.argmax(peak[0]) |
| 172 | + self.assertAlmostEqual(peak_idx, x0, delta=1) |
| 173 | + |
| 174 | + def test_simulate_peak_dtype_clipping(self): |
| 175 | + """ |
| 176 | + Verify values are clipped to dtype limits. |
| 177 | + """ |
| 178 | + |
| 179 | + amplitude = 100000 # larger than uint16 max |
| 180 | + x0 = 30 |
| 181 | + width = 3 |
| 182 | + shape = 80 |
| 183 | + |
| 184 | + peak = simulate_peak(amplitude, x0, width, shape) |
| 185 | + |
| 186 | + dtype_max = numpy.iinfo(numpy.uint16).max |
| 187 | + |
| 188 | + self.assertEqual(peak.max(), dtype_max) |
| 189 | + |
| 190 | + def test_simulate_peak_dtype(self): |
| 191 | + """ |
| 192 | + Verify simulate_peak respects the dtype argument. |
| 193 | + """ |
| 194 | + |
| 195 | + amplitude = 200 |
| 196 | + x0 = 25 |
| 197 | + width = 3 |
| 198 | + shape = 60 |
| 199 | + |
| 200 | + peak = simulate_peak(amplitude, x0, width, shape, dtype=numpy.uint8) |
| 201 | + |
| 202 | + self.assertEqual(peak.dtype, numpy.uint8) |
| 203 | + |
| 204 | + dtype_max = numpy.iinfo(numpy.uint8).max |
| 205 | + self.assertLessEqual(peak.max(), dtype_max) |
128 | 206 |
|
129 | 207 | if __name__ == "__main__": |
130 | 208 | unittest.main() |
0 commit comments