def test_tree_of_shapes_self_dual(self): np.random.seed(42) image = np.random.rand(25, 38) neg_image = -1 * image tree1, altitudes1 = hg.component_tree_tree_of_shapes_image2d(image) tree2, altitudes2 = hg.component_tree_tree_of_shapes_image2d(neg_image) self.assertTrue(hg.test_tree_isomorphism(tree1, tree2))
def test_tree_of_shapes_no_padding(self): image = np.asarray( ((1, 1, 1, 1, 1, 1), (1, 0, 0, 3, 3, 1), (1, 0, 1, 1, 3, 1), (1, 0, 0, 3, 3, 1), (1, 1, 1, 1, 1, 1)), dtype=np.int8) tree, altitudes = hg.component_tree_tree_of_shapes_image2d( image, 'none', False) ref_parents = np.asarray( (101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 101, 99, 99, 99, 101, 101, 101, 101, 100, 101, 101, 101, 101, 101, 99, 101, 101, 101, 101, 100, 101, 101, 101, 101, 101, 99, 101, 101, 101, 101, 100, 101, 101, 101, 101, 101, 99, 101, 101, 101, 101, 100, 100, 100, 101, 99, 99, 99, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101), dtype=np.int64) ref_altitudes = np.asarray( (1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 3, 3, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 0, 0, 0, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0, 1), dtype=np.int8) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes))
def test_tree_of_shapes_padding_0(self): image = np.asarray(((1, 1, 1), (1, -2, 3)), dtype=np.int32) tree, altitudes = hg.component_tree_tree_of_shapes_image2d(image, 'zero', False) ref_parents = np.asarray((66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 65, 65, 65, 66, 66, 66, 66, 65, 66, 66, 66, 65, 66, 66, 66, 66, 65, 66, 63, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 66, 66), dtype=np.int64) ref_altitudes = np.asarray((0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 3, 1, 0), dtype=np.int32) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes)) leaf_graph = hg.CptHierarchy.get_leaf_graph(tree) res_shape = hg.CptGridGraph.get_shape(leaf_graph) self.assertTrue(len(res_shape) == 2) self.assertTrue(res_shape[0] * res_shape[1] == tree.num_leaves()) self.assertTrue(res_shape[0] == (image.shape[0] + 2) * 2 - 1) self.assertTrue(res_shape[1] == (image.shape[1] + 2) * 2 - 1)
def patches_batch_tos_area_p(batch_image, i, j, l): tree, altitudes = hg.component_tree_tree_of_shapes_image2d( batch_image[i, :, :, 0]) area = hg.attribute_area(tree) batch_image[i, :, :, j + 1] = hg.reconstruct_leaf_data(tree, altitudes, area < l) return batch_image[i, :, :, j + 1]
def test_tree_of_shapes_no_padding_original_space(self): image = np.asarray(((1, 1, 1, 1, 1, 1), (1, 0, 0, 3, 3, 1), (1, 0, 1, 1, 3, 1), (1, 0, 0, 3, 3, 1), (1, 1, 1, 1, 1, 1)), dtype=np.float64) tree, altitudes = hg.component_tree_tree_of_shapes_image2d(image, 'none', True) ref_parents = np.asarray((32, 32, 32, 32, 32, 32, 32, 30, 30, 31, 31, 32, 32, 30, 32, 32, 31, 32, 32, 30, 30, 31, 31, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32), dtype=np.int64) ref_altitudes = np.asarray((1, 1, 1, 1, 1, 1, 1, 0, 0, 3, 3, 1, 1, 0, 1, 1, 3, 1, 1, 0, 0, 3, 3, 1, 1, 1, 1, 1, 1, 1, 0, 3, 1), dtype=np.float64) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes)) leaf_graph = hg.CptHierarchy.get_leaf_graph(tree) res_shape = hg.CptGridGraph.get_shape(leaf_graph) self.assertTrue(len(res_shape) == 2) self.assertTrue(res_shape[0] * res_shape[1] == tree.num_leaves()) self.assertTrue(res_shape[0] == image.shape[0]) self.assertTrue(res_shape[1] == image.shape[1])
def patches_batch_tos_area(batch_image, batch_size, lambdas): for i in range(batch_size): tree, altitudes = hg.component_tree_tree_of_shapes_image2d( batch_image[i, :, :, 0]) area = hg.attribute_area(tree) for j in range(len(lambdas)): batch_image[i, :, :, j + 1] = hg.reconstruct_leaf_data( tree, altitudes, area < lambdas[j]) return batch_image
def test_component_tree_multivariate_tree_of_shapes_image2d_sanity(self): image = np.asarray(((1, 1), (1, -2), (1, 7)), dtype=np.float64) tree, _ = hg.component_tree_tree_of_shapes_image2d(image, 'mean') image3d = np.dstack((image, image, image)) tree2 = hg.component_tree_multivariate_tree_of_shapes_image2d(image3d, 'mean') self.assertTrue(hg.test_tree_isomorphism(tree, tree2))
def test_tree_of_shapes_padding_0_original_space(self): image = np.asarray(((1, 1, 1), (1, -2, 3)), dtype=np.int32) tree, altitudes = hg.component_tree_tree_of_shapes_image2d( image, 'zero', True) ref_parents = np.asarray((8, 8, 8, 8, 6, 7, 9, 8, 9, 9), dtype=np.int64) ref_altitudes = np.asarray((1, 1, 1, 1, -2, 3, -2, 3, 1, 0), dtype=np.int32) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes))
def test_tree_of_shapes_padding_mean_original_space(self): image = np.asarray(((1, 1), (1, -2), (1, 7)), dtype=np.float64) tree, altitudes = hg.component_tree_tree_of_shapes_image2d( image, 'mean', True) ref_parents = np.asarray((8, 8, 8, 7, 8, 6, 9, 8, 9, 9), dtype=np.int64) ref_altitudes = np.asarray((1., 1., 1., -2., 1., 7., 7., -2., 1., 1.5), dtype=np.float64) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.allclose(altitudes, ref_altitudes))
def test_contour_length_tree_of_shapes(self): image = np.asarray( ((0, 0, 0, 0), (0, -2, 2, 0), (0, -1, 1, 0), (0, 0, 0, 0))) tree, altitudes = hg.component_tree_tree_of_shapes_image2d(image) res = hg.attribute_contour_length(tree) ref = np.asarray( (4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 6, 6, 16), dtype=np.float64) self.assertTrue(np.all(res == ref))
def test_tree_of_shapes_no_padding_original_space(self): image = np.asarray( ((1, 1, 1, 1, 1, 1), (1, 0, 0, 3, 3, 1), (1, 0, 1, 1, 3, 1), (1, 0, 0, 3, 3, 1), (1, 1, 1, 1, 1, 1)), dtype=np.float64) tree, altitudes = hg.component_tree_tree_of_shapes_image2d( image, 'none', True) ref_parents = np.asarray((32, 32, 32, 32, 32, 32, 32, 31, 31, 30, 30, 32, 32, 31, 32, 32, 30, 32, 32, 31, 31, 30, 30, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32), dtype=np.int64) ref_altitudes = np.asarray( (1, 1, 1, 1, 1, 1, 1, 0, 0, 3, 3, 1, 1, 0, 1, 1, 3, 1, 1, 0, 0, 3, 3, 1, 1, 1, 1, 1, 1, 1, 3, 0, 1), dtype=np.float64) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes))
def test_tree_of_shapes_no_immersion_no_padding_no_original_space(self): image = np.asarray(((1, 1, 1, 1, 1), (1, 0, 1, 2, 1), (1, 1, 1, 1, 1)), dtype=np.float64) tree, altitudes = hg.component_tree_tree_of_shapes_image2d(image, 'none', original_size=False, immersion=False) ref_parents = np.asarray((17, 17, 17, 17, 17, 17, 16, 17, 15, 17, 17, 17, 17, 17, 17, 17, 17, 17), dtype=np.int64) ref_altitudes = np.asarray((1, 1, 1, 1, 1, 1, 0, 1, 2, 1, 1, 1, 1, 1, 1, 2, 0, 1), dtype=np.float64) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.allclose(altitudes, ref_altitudes)) g = hg.CptHierarchy.get_leaf_graph(tree) s = hg.CptGridGraph.get_shape(g) self.assertTrue(s[0] * s[1] == tree.num_leaves())
def test_tree_of_shapes_padding_0(self): image = np.asarray(((1, 1, 1), (1, -2, 3)), dtype=np.int32) tree, altitudes = hg.component_tree_tree_of_shapes_image2d( image, 'zero', False) ref_parents = np.asarray( (66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 65, 65, 65, 65, 66, 66, 66, 66, 65, 66, 66, 66, 65, 66, 66, 66, 66, 65, 66, 63, 66, 64, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 66, 65, 66, 66), dtype=np.int64) ref_altitudes = np.asarray( (0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, -2, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -2, 3, 1, 0), dtype=np.int32) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes))
def test_tree_of_shapes_padding_0_original_space(self): image = np.asarray(((1, 1, 1), (1, -2, 3)), dtype=np.int32) tree, altitudes = hg.component_tree_tree_of_shapes_image2d(image, 'zero', True) ref_parents = np.asarray((7, 7, 7, 7, 8, 6, 7, 9, 9, 9), dtype=np.int64) ref_altitudes = np.asarray((1, 1, 1, 1, -2, 3, 3, 1, -2, 0), dtype=np.int32) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes)) leaf_graph = hg.CptHierarchy.get_leaf_graph(tree) res_shape = hg.CptGridGraph.get_shape(leaf_graph) self.assertTrue(len(res_shape) == 2) self.assertTrue(res_shape[0] * res_shape[1] == tree.num_leaves()) self.assertTrue(res_shape[0] == image.shape[0]) self.assertTrue(res_shape[1] == image.shape[1])
def test_tree_of_shapes_no_padding(self): image = np.asarray(((1, 1, 1, 1, 1, 1), (1, 0, 0, 3, 3, 1), (1, 0, 1, 1, 3, 1), (1, 0, 0, 3, 3, 1), (1, 1, 1, 1, 1, 1)), dtype=np.int8) tree, altitudes = hg.component_tree_tree_of_shapes_image2d(image, 'none', False) ref_parents = np.asarray((101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 100, 100, 100, 101, 99, 99, 99, 101, 101, 101, 101, 100, 101, 101, 101, 101, 101, 99, 101, 101, 101, 101, 100, 101, 101, 101, 101, 101, 99, 101, 101, 101, 101, 100, 101, 101, 101, 101, 101, 99, 101, 101, 101, 101, 100, 100, 100, 101, 99, 99, 99, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101, 101), dtype=np.int64) ref_altitudes = np.asarray((1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 3, 3, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 3, 1, 1, 1, 1, 0, 0, 0, 1, 3, 3, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 3, 0, 1), dtype=np.int8) self.assertTrue(np.all(tree.parents() == ref_parents)) self.assertTrue(np.all(altitudes == ref_altitudes)) leaf_graph = hg.CptHierarchy.get_leaf_graph(tree) res_shape = hg.CptGridGraph.get_shape(leaf_graph) self.assertTrue(len(res_shape) == 2) self.assertTrue(res_shape[0] * res_shape[1] == tree.num_leaves()) self.assertTrue(res_shape[0] == image.shape[0] * 2 - 1) self.assertTrue(res_shape[1] == image.shape[1] * 2 - 1)
def component_tree_multivariate_tree_of_shapes_image2d(image, padding='mean', original_size=True, immersion=True): """ Multivariate tree of shapes for a 2d multi-band image. This tree is defined as a fusion of the marginal trees of shapes. The method is described in: E. Carlinet. `A Tree of shapes for multivariate images <https://pastel.archives-ouvertes.fr/tel-01280131/file/TH2015PESC1118.pdf>`_. PhD Thesis, Université Paris-Est, 2015. The input :attr:`image` must be a 3d array of shape :math:`(height, width, channel)`. Note that the constructed hierarchy doesn't have natural altitudes associated to its node: as a node is generally a fusion of several marginal nodes, we can't associate a single canonical value from the original image to this node. The parameters :attr:`padding`, :attr:`original_size`, and :attr:`immersion` are forwarded to the function :func:`~higra.component_tree_tree_of_shapes_image2d`: please look at this function documentation for more details. :Complexity: The worst case runtime complexity of this method is :math:`\mathcal{O}(N^2D^2)` with :math:`N` the number of pixels and :math:`D` the number of bands. If the image pixel values are quantized on :math:`K<<N` different values (eg. with a color image in :math:`[0..255]^D`), then the worst case runtime complexity can be tightened to :math:`\mathcal{O}(NKD^2)`. :See: This function relies on :func:`~higra.tree_fusion_depth_map` to compute the fusion of the marinal trees. :param image: input *color* 2d image :param padding: possible values are `'none'`, `'zero'`, and `'mean'` (default = `'mean'`) :param original_size: remove all nodes corresponding to interpolated/padded pixels (default = `True`) :param immersion: performs a plain map continuous immersion fo the original image (default = `True`) :return: a tree (Concept :class:`~higra.CptHierarchy`) """ assert len( image.shape ) == 3, "This multivariate tree of shapes implementation only supports multichannel 2d images." ndim = image.shape[2] trees = tuple( hg.component_tree_tree_of_shapes_image2d( image[:, :, k], padding, original_size=False, immersion=immersion)[0] for k in range(ndim)) g = hg.CptHierarchy.get_leaf_graph(trees[0]) shape = hg.CptGridGraph.get_shape(g) depth_map = hg.tree_fusion_depth_map(trees) tree, altitudes = hg.component_tree_tree_of_shapes_image2d( np.reshape(depth_map, shape), padding="none", original_size=False, immersion=False) if original_size and (immersion or padding != "none"): deleted_vertices = np.ones((tree.num_leaves(), ), dtype=np.bool) deleted = np.reshape(deleted_vertices, shape) if immersion: if padding != "none": deleted[2:-2:2, 2:-2:2] = False else: deleted[0::2, 0::2] = False else: if padding != "none": deleted[1:-1, 1::-1] = False all_deleted = hg.accumulate_sequential(tree, deleted_vertices, hg.Accumulators.min) shape = (image.shape[0], image.shape[1]) else: all_deleted = np.zeros((tree.num_vertices(), ), dtype=np.bool) holes = altitudes < altitudes[tree.parents()] all_deleted = np.logical_or(all_deleted, holes) tree, _ = hg.simplify_tree(tree, all_deleted, process_leaves=True) g = hg.get_4_adjacency_graph(shape) hg.CptHierarchy.link(tree, g) return tree