summaryrefslogtreecommitdiff
path: root/silx/image/test/test_shapes.py
diff options
context:
space:
mode:
Diffstat (limited to 'silx/image/test/test_shapes.py')
-rw-r--r--silx/image/test/test_shapes.py366
1 files changed, 0 insertions, 366 deletions
diff --git a/silx/image/test/test_shapes.py b/silx/image/test/test_shapes.py
deleted file mode 100644
index 6539bba..0000000
--- a/silx/image/test/test_shapes.py
+++ /dev/null
@@ -1,366 +0,0 @@
-# coding: utf-8
-# /*##########################################################################
-#
-# Copyright (c) 2016 European Synchrotron Radiation Facility
-#
-# 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.
-#
-# ############################################################################*/
-"""Tests for polygon functions
-"""
-
-__authors__ = ["T. Vincent"]
-__license__ = "MIT"
-__date__ = "15/02/2019"
-
-
-import logging
-import unittest
-import numpy
-
-from silx.utils.testutils import ParametricTestCase
-from silx.image import shapes
-
-_logger = logging.getLogger(__name__)
-
-
-class TestPolygonFill(ParametricTestCase):
- """basic poylgon test"""
-
- def test_squares(self):
- """Test polygon fill for a square polygons"""
- mask_shape = 4, 4
- tests = {
- # test name: [(row min, row max), (col min, col max)]
- 'square in': [(1, 3), (1, 3)],
- 'square out': [(1, 3), (1, 10)],
- 'square around': [(-1, 5), (-1, 5)],
- }
-
- for test_name, (rows, cols) in tests.items():
- with self.subTest(msg=test_name, rows=rows, cols=cols,
- mask_shape=mask_shape):
- ref_mask = numpy.zeros(mask_shape, dtype=numpy.uint8)
- ref_mask[max(0, rows[0]):rows[1],
- max(0, cols[0]):cols[1]] = True
-
- vertices = [(rows[0], cols[0]), (rows[1], cols[0]),
- (rows[1], cols[1]), (rows[0], cols[1])]
- mask = shapes.polygon_fill_mask(vertices, ref_mask.shape)
- is_equal = numpy.all(numpy.equal(ref_mask, mask))
- if not is_equal:
- _logger.debug('%s failed with mask != ref_mask:',
- test_name)
- _logger.debug('result:\n%s', str(mask))
- _logger.debug('ref:\n%s', str(ref_mask))
- self.assertTrue(is_equal)
-
- def test_eight(self):
- """Tests with eight shape with different rotation and direction"""
- ref_mask = numpy.array((
- (1, 1, 1, 1, 1, 0),
- (0, 1, 1, 1, 0, 0),
- (0, 0, 1, 0, 0, 0),
- (0, 0, 1, 0, 0, 0),
- (0, 1, 1, 1, 0, 0),
- (0, 0, 0, 0, 0, 0)), dtype=numpy.uint8)
- ref_mask_rot = numpy.asarray(numpy.logical_not(ref_mask),
- dtype=numpy.uint8)
- ref_mask_rot[:, -1] = 0
- ref_mask_rot[-1, :] = 0
-
- tests = {
- 'dir 1': ([(0, 0), (5, 5), (5, 0), (0, 5)], ref_mask),
- 'dir 1, rot 90': ([(5, 0), (0, 5), (5, 5), (0, 0)], ref_mask_rot),
- 'dir 1, rot 180': ([(5, 5), (0, 0), (0, 5), (5, 0)], ref_mask),
- 'dir 1, rot -90': ([(0, 5), (5, 0), (0, 0), (5, 5)], ref_mask_rot),
- 'dir 2': ([(0, 0), (0, 5), (5, 0), (5, 5)], ref_mask),
- 'dir 2, rot 90': ([(5, 0), (0, 0), (5, 5), (0, 5)], ref_mask_rot),
- 'dir 2, rot 180': ([(5, 5), (5, 0), (0, 5), (0, 0)], ref_mask),
- 'dir 2, rot -90': ([(0, 5), (5, 5), (0, 0), (5, 0)], ref_mask_rot),
- }
-
- for test_name, (vertices, ref_mask) in tests.items():
- with self.subTest(msg=test_name):
- mask = shapes.polygon_fill_mask(vertices, ref_mask.shape)
- is_equal = numpy.all(numpy.equal(ref_mask, mask))
- if not is_equal:
- _logger.debug('%s failed with mask != ref_mask:',
- test_name)
- _logger.debug('result:\n%s', str(mask))
- _logger.debug('ref:\n%s', str(ref_mask))
- self.assertTrue(is_equal)
-
- def test_shapes(self):
- """Tests with shapes and reference mask"""
- tests = {
- # name: (
- # polygon corners as a list of (row, col),
- # ref_mask)
- 'concave polygon': (
- [(1, 1), (4, 3), (1, 5), (2, 3)],
- numpy.array((
- (0, 0, 0, 0, 0, 0, 0, 0),
- (0, 0, 0, 0, 0, 0, 0, 0),
- (0, 0, 1, 1, 1, 0, 0, 0),
- (0, 0, 0, 1, 0, 0, 0, 0),
- (0, 0, 0, 0, 0, 0, 0, 0),
- (0, 0, 0, 0, 0, 0, 0, 0)), dtype=numpy.uint8)),
- 'concave polygon partly outside mask': (
- [(-1, -1), (4, 3), (1, 5), (2, 3)],
- numpy.array((
- (1, 0, 0, 0, 0, 0),
- (0, 1, 0, 0, 0, 0),
- (0, 0, 1, 1, 1, 0),
- (0, 0, 0, 1, 0, 0),
- (0, 0, 0, 0, 0, 0),
- (0, 0, 0, 0, 0, 0),
- (0, 0, 0, 0, 0, 0),
- (0, 0, 0, 0, 0, 0)), dtype=numpy.uint8)),
- 'polygon surrounding mask': (
- [(-1, -1), (-1, 7), (7, 7), (7, -1), (0, -1),
- (8, -2), (8, 8), (-2, 8)],
- numpy.zeros((6, 6), dtype=numpy.uint8))
- }
-
- for test_name, (vertices, ref_mask) in tests.items():
- with self.subTest(msg=test_name):
- mask = shapes.polygon_fill_mask(vertices, ref_mask.shape)
- is_equal = numpy.all(numpy.equal(ref_mask, mask))
- if not is_equal:
- _logger.debug('%s failed with mask != ref_mask:',
- test_name)
- _logger.debug('result:\n%s', str(mask))
- _logger.debug('ref:\n%s', str(ref_mask))
- self.assertTrue(is_equal)
-
-
-class TestDrawLine(ParametricTestCase):
- """basic draw line test"""
-
- def test_aligned_lines(self):
- """Test drawing horizontal, vertical and diagonal lines"""
-
- lines = { # test_name: (drow, dcol)
- 'Horizontal line, col0 < col1': (0, 10),
- 'Horizontal line, col0 > col1': (0, -10),
- 'Vertical line, row0 < row1': (10, 0),
- 'Vertical line, row0 > row1': (-10, 0),
- 'Diagonal col0 < col1 and row0 < row1': (10, 10),
- 'Diagonal col0 < col1 and row0 > row1': (-10, 10),
- 'Diagonal col0 > col1 and row0 < row1': (10, -10),
- 'Diagonal col0 > col1 and row0 > row1': (-10, -10),
- }
- row0, col0 = 1, 2 # Start point
-
- for test_name, (drow, dcol) in lines.items():
- row1 = row0 + drow
- col1 = col0 + dcol
- with self.subTest(msg=test_name, drow=drow, dcol=dcol):
- # Build reference coordinates from drow and dcol
- if drow == 0:
- rows = row0 * numpy.ones(abs(dcol) + 1)
- else:
- step = 1 if drow > 0 else -1
- rows = row0 + numpy.arange(0, drow + step, step)
-
- if dcol == 0:
- cols = col0 * numpy.ones(abs(drow) + 1)
- else:
- step = 1 if dcol > 0 else -1
- cols = col0 + numpy.arange(0, dcol + step, step)
- ref_coords = rows, cols
-
- result = shapes.draw_line(row0, col0, row1, col1)
- self.assertTrue(self.isEqual(test_name, result, ref_coords))
-
- def test_noline(self):
- """Test pt0 == pt1"""
- for width in range(4):
- with self.subTest(width=width):
- result = shapes.draw_line(1, 2, 1, 2, width)
- self.assertTrue(numpy.all(numpy.equal(result, [(1,), (2,)])))
-
- def test_lines(self):
- """Test lines not aligned with axes for 8 slopes and directions"""
- row0, col0 = 1, 1
-
- dy, dx = 3, 5
- ref_coords = numpy.array(
- [(0, 0), (1, 1), (1, 2), (2, 3), (2, 4), (3, 5)])
-
- # Build lines for the 8 octants from this coordinantes
- lines = { # name: (drow, dcol, ref_coords)
- '1st octant': (dy, dx, ref_coords),
- '2nd octant': (dx, dy, ref_coords[:, (1, 0)]), # invert x and y
- '3rd octant': (dx, -dy, ref_coords[:, (1, 0)] * (1, -1)),
- '4th octant': (dy, -dx, ref_coords * (1, -1)),
- '5th octant': (-dy, -dx, ref_coords * (-1, -1)),
- '6th octant': (-dx, -dy, ref_coords[:, (1, 0)] * (-1, -1)),
- '7th octant': (-dx, dy, ref_coords[:, (1, 0)] * (-1, 1)),
- '8th octant': (-dy, dx, ref_coords * (-1, 1))
- }
-
- # Test with different starting points with positive and negative coords
- for row0, col0 in ((0, 0), (2, 3), (-4, 1), (-5, -6), (8, -7)):
- for name, (drow, dcol, ref_coords) in lines.items():
- row1 = row0 + drow
- col1 = col0 + dcol
- # Transpose from ((row0, col0), ...) to (rows, cols)
- ref_coords = numpy.transpose(ref_coords + (row0, col0))
-
- with self.subTest(msg=name,
- pt0=(row0, col0), pt1=(row1, col1)):
- result = shapes.draw_line(row0, col0, row1, col1)
- self.assertTrue(self.isEqual(name, result, ref_coords))
-
- def test_width(self):
- """Test of line width"""
-
- lines = { # test_name: row0, col0, row1, col1, width, ref
- 'horizontal w=2':
- (0, 0, 0, 1, 2, ((0, 1, 0, 1),
- (0, 0, 1, 1))),
- 'horizontal w=3':
- (0, 0, 0, 1, 3, ((-1, 0, 1, -1, 0, 1),
- (0, 0, 0, 1, 1, 1))),
- 'vertical w=2':
- (0, 0, 1, 0, 2, ((0, 0, 1, 1),
- (0, 1, 0, 1))),
- 'vertical w=3':
- (0, 0, 1, 0, 3, ((0, 0, 0, 1, 1, 1),
- (-1, 0, 1, -1, 0, 1))),
- 'diagonal w=3':
- (0, 0, 1, 1, 3, ((-1, 0, 1, 0, 1, 2),
- (0, 0, 0, 1, 1, 1))),
- '1st octant w=3':
- (0, 0, 1, 2, 3,
- numpy.array(((-1, 0), (0, 0), (1, 0),
- (0, 1), (1, 1), (2, 1),
- (0, 2), (1, 2), (2, 2))).T),
- '2nd octant w=3':
- (0, 0, 2, 1, 3,
- numpy.array(((0, -1), (0, 0), (0, 1),
- (1, 0), (1, 1), (1, 2),
- (2, 0), (2, 1), (2, 2))).T),
- }
-
- for test_name, (row0, col0, row1, col1, width, ref) in lines.items():
- with self.subTest(msg=test_name,
- pt0=(row0, col0), pt1=(row1, col1), width=width):
- result = shapes.draw_line(row0, col0, row1, col1, width)
- self.assertTrue(self.isEqual(test_name, result, ref))
-
- def isEqual(self, test_name, result, ref):
- """Test equality of two numpy arrays and log them if different"""
- is_equal = numpy.all(numpy.equal(result, ref))
- if not is_equal:
- _logger.debug('%s failed with result != ref:',
- test_name)
- _logger.debug('result:\n%s', str(result))
- _logger.debug('ref:\n%s', str(ref))
- return is_equal
-
-
-class TestCircleFill(ParametricTestCase):
- """Tests for circle filling"""
-
- def testCircle(self):
- """Test circle_fill with different input parameters"""
-
- square3x3 = numpy.array(((-1, -1, -1, 0, 0, 0, 1, 1, 1),
- (-1, 0, 1, -1, 0, 1, -1, 0, 1)))
-
- tests = [
- # crow, ccol, radius, ref_coords = (ref_rows, ref_cols)
- (0, 0, 1, ((0,), (0,))),
- (10, 15, 1, ((10,), (15,))),
- (0, 0, 1.5, square3x3),
- (5, 10, 2, (5 + square3x3[0], 10 + square3x3[1])),
- (10, 20, 3.5, (
- 10 + numpy.array((-3, -3, -3,
- -2, -2, -2, -2, -2,
- -1, -1, -1, -1, -1, -1, -1,
- 0, 0, 0, 0, 0, 0, 0,
- 1, 1, 1, 1, 1, 1, 1,
- 2, 2, 2, 2, 2,
- 3, 3, 3)),
- 20 + numpy.array((-1, 0, 1,
- -2, -1, 0, 1, 2,
- -3, -2, -1, 0, 1, 2, 3,
- -3, -2, -1, 0, 1, 2, 3,
- -3, -2, -1, 0, 1, 2, 3,
- -2, -1, 0, 1, 2,
- -1, 0, 1)))),
- ]
-
- for crow, ccol, radius, ref_coords in tests:
- with self.subTest(crow=crow, ccol=ccol, radius=radius):
- coords = shapes.circle_fill(crow, ccol, radius)
- is_equal = numpy.all(numpy.equal(coords, ref_coords))
- if not is_equal:
- _logger.debug('result:\n%s', str(coords))
- _logger.debug('ref:\n%s', str(ref_coords))
- self.assertTrue(is_equal)
-
-
-class TestEllipseFill(unittest.TestCase):
- """Tests for ellipse filling"""
-
- def testPoint(self):
- args = [1, 1, 1, 1]
- result = shapes.ellipse_fill(*args)
- expected = numpy.array(([1], [1]))
- numpy.testing.assert_array_equal(result, expected)
-
- def testTranslatedPoint(self):
- args = [10, 10, 1, 1]
- result = shapes.ellipse_fill(*args)
- expected = numpy.array(([10], [10]))
- numpy.testing.assert_array_equal(result, expected)
-
- def testEllipse(self):
- args = [0, 0, 20, 10]
- rows, cols = shapes.ellipse_fill(*args)
- self.assertEqual(len(rows), 617)
- self.assertEqual(rows.mean(), 0)
- self.assertAlmostEqual(rows.std(), 10.025575, places=3)
- self.assertEqual(len(cols), 617)
- self.assertEqual(cols.mean(), 0)
- self.assertAlmostEqual(cols.std(), 4.897325, places=3)
-
- def testTranslatedEllipse(self):
- args = [0, 0, 20, 10]
- expected_rows, expected_cols = shapes.ellipse_fill(*args)
- args = [10, 50, 20, 10]
- rows, cols = shapes.ellipse_fill(*args)
- numpy.testing.assert_allclose(rows, expected_rows + 10)
- numpy.testing.assert_allclose(cols, expected_cols + 50)
-
-
-def suite():
- test_suite = unittest.TestSuite()
- for testClass in (TestPolygonFill, TestDrawLine, TestCircleFill, TestEllipseFill):
- test_suite.addTest(
- unittest.defaultTestLoader.loadTestsFromTestCase(testClass))
- return test_suite
-
-
-if __name__ == '__main__':
- unittest.main(defaultTest='suite')