diff options
Diffstat (limited to 'silx/image/test/test_bilinear.py')
-rw-r--r-- | silx/image/test/test_bilinear.py | 142 |
1 files changed, 142 insertions, 0 deletions
diff --git a/silx/image/test/test_bilinear.py b/silx/image/test/test_bilinear.py new file mode 100644 index 0000000..1789aca --- /dev/null +++ b/silx/image/test/test_bilinear.py @@ -0,0 +1,142 @@ +# -*- coding: utf-8 -*- +# +# Project: silx (originally pyFAI) +# https://github.com/silx-kit/silx +# +# Copyright (C) 2012-2016 European Synchrotron Radiation Facility, Grenoble, France +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to deal +# in the Software without restriction, including without limitation the rights +# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +# copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +# THE SOFTWARE. + +__authors__ = ["J. Kieffer"] +__license__ = "MIT" +__date__ = "02/08/2016" + +import unittest +import numpy +import logging +logger = logging.getLogger("test_bilinear") +from ..bilinear import BilinearImage + + +class TestBilinear(unittest.TestCase): + """basic maximum search test""" + N = 1000 + + def test_max_search_round(self): + """test maximum search using random points: maximum is at the pixel center""" + a = numpy.arange(100) - 40. + b = numpy.arange(100) - 60. + ga = numpy.exp(-a * a / 4000) + gb = numpy.exp(-b * b / 6000) + gg = numpy.outer(ga, gb) + b = BilinearImage(gg) + ok = 0 + for s in range(self.N): + i, j = numpy.random.randint(100), numpy.random.randint(100) + k, l = b.local_maxi((i, j)) + if abs(k - 40) > 1e-4 or abs(l - 60) > 1e-4: + logger.warning("Wrong guess maximum (%i,%i) -> (%.1f,%.1f)", i, j, k, l) + else: + logger.debug("Good guess maximum (%i,%i) -> (%.1f,%.1f)", i, j, k, l) + ok += 1 + logger.debug("Success rate: %.1f", 100. * ok / self.N) + self.assertEqual(ok, self.N, "Maximum is always found") + + def test_max_search_half(self): + """test maximum search using random points: maximum is at a pixel edge""" + a = numpy.arange(100) - 40.5 + b = numpy.arange(100) - 60.5 + ga = numpy.exp(-a * a / 4000) + gb = numpy.exp(-b * b / 6000) + gg = numpy.outer(ga, gb) + b = BilinearImage(gg) + ok = 0 + for s in range(self.N): + i, j = numpy.random.randint(100), numpy.random.randint(100) + k, l = b.local_maxi((i, j)) + if abs(k - 40.5) > 0.5 or abs(l - 60.5) > 0.5: + logger.warning("Wrong guess maximum (%i,%i) -> (%.1f,%.1f)", i, j, k, l) + else: + logger.debug("Good guess maximum (%i,%i) -> (%.1f,%.1f)", i, j, k, l) + ok += 1 + logger.debug("Success rate: %.1f", 100. * ok / self.N) + self.assertEqual(ok, self.N, "Maximum is always found") + + def test_map(self): + N = 100 + y, x = numpy.ogrid[:N, :N + 10] + img = x + y + b = BilinearImage(img) + x2d = numpy.zeros_like(y) + x + y2d = numpy.zeros_like(x) + y + res1 = b.map_coordinates((y2d, x2d)) + self.assertEquals(abs(res1 - img).max(), 0, "images are the same (corners)") + + x2d = numpy.zeros_like(y) + (x[:, :-1] + 0.5) + y2d = numpy.zeros_like(x[:, :-1]) + y + res1 = b.map_coordinates((y2d, x2d)) + self.assertEquals(abs(res1 - img[:, :-1] - 0.5).max(), 0, "images are the same (middle)") + + x2d = numpy.zeros_like(y[:-1, :]) + (x[:, :-1] + 0.5) + y2d = numpy.zeros_like(x[:, :-1]) + (y[:-1, :] + 0.5) + res1 = b.map_coordinates((y2d, x2d)) + self.assertEquals(abs(res1 - img[:-1, 1:]).max(), 0, "images are the same (center)") + + def test_profile_grad(self): + N = 100 + img = numpy.arange(N * N).reshape(N, N) + b = BilinearImage(img) + res1 = b.profile_line((0, 0), (N - 1, N - 1)) + l = numpy.ceil(numpy.sqrt(2) * N) + self.assertEquals(len(res1), l, "Profile has correct length") + self.assertLess((res1[:-2] - res1[1:-1]).std(), 1e-3, "profile is linear (excluding last point)") + + def test_profile_gaus(self): + N = 100 + x = numpy.arange(N) - N // 2.0 + g = numpy.exp(-x * x / (N * N)) + img = numpy.outer(g, g) + b = BilinearImage(img) + res_hor = b.profile_line((N // 2, 0), (N // 2, N - 1)) + res_ver = b.profile_line((0, N // 2), (N - 1, N // 2)) + self.assertEquals(len(res_hor), N, "Profile has correct length") + self.assertEquals(len(res_ver), N, "Profile has correct length") + self.assertLess(abs(res_hor - g).max(), 1e-5, "correct horizontal profile") + self.assertLess(abs(res_ver - g).max(), 1e-5, "correct vertical profile") + + # Profile with linewidth=3 + expected_profile = img[:, N // 2 - 1:N // 2 + 2].mean(axis=1) + res_hor = b.profile_line((N // 2, 0), (N // 2, N - 1), linewidth=3) + res_ver = b.profile_line((0, N // 2), (N - 1, N // 2), linewidth=3) + + self.assertEquals(len(res_hor), N, "Profile has correct length") + self.assertEquals(len(res_ver), N, "Profile has correct length") + self.assertLess(abs(res_hor - expected_profile).max(), 1e-5, + "correct horizontal profile") + self.assertLess(abs(res_ver - expected_profile).max(), 1e-5, + "correct vertical profile") + + +def suite(): + testsuite = unittest.TestSuite() + testsuite.addTest(TestBilinear("test_max_search_round")) + testsuite.addTest(TestBilinear("test_max_search_half")) + testsuite.addTest(TestBilinear("test_map")) + testsuite.addTest(TestBilinear("test_profile_grad")) + testsuite.addTest(TestBilinear("test_profile_gaus")) + return testsuite |