Exemplo n.º 1
0
    def test_mask_2(self):
        a = ChunkGrid(2, bool, False)
        a[0, 0, 0] = True
        a.ensure_chunk_at_index((1, 1, 1)).set_fill(True)
        a.set_value((2, 1, 1), True)

        b = a.astype(np.int8)

        adense = a.to_dense()
        bdense = b.to_dense()
        self.assertEqual(adense.shape, bdense.shape)
        self.assertEqual(str(adense.astype(np.int8)), str(bdense))

        mask = ChunkGrid(2, bool, False)
        mask.ensure_chunk_at_index((1, 0, 0)).set_fill(True)
        mask.ensure_chunk_at_index((0, 1, 0)).set_fill(True)
        mask.ensure_chunk_at_index((0, 0, 1))[0, 0, 0] = True

        b[mask] = 2
        bdense = b.to_dense()

        expected = np.zeros((4, 4, 4), dtype=np.int8)
        expected[0, 0, 0] = 1
        expected[2:4, 2:4, 2:4] = 1
        expected[2, 1, 1] = 1
        expected[2:4, 0:2, 0:2] = 2
        expected[0:2, 2:4, 0:2] = 2
        expected[0, 0, 2] = 2

        self.assertEqual(expected.shape, bdense.shape)
        self.assertEqual(str(expected), str(bdense))
Exemplo n.º 2
0
    def test_padding(self):
        grid = ChunkGrid(2, int, -1)
        grid.ensure_chunk_at_index((0, 0, 0)).set_fill(1)
        grid.ensure_chunk_at_index((0, 0, 1)).set_fill(2)
        grid.ensure_chunk_at_index((0, 1, 0)).set_fill(3)
        grid.ensure_chunk_at_index((0, 1, 1)).set_fill(4)

        t = grid.chunks[(0, 0, 1)]
        t.set_array(np.array([
            [(111, 112), (121, 122)],
            [(211, 212), (221, 222)]
        ]))
        expected1 = t.to_array()[:, :, 0]

        c = grid.chunks.get((0, 0, 0))
        pad = c.padding(grid, 1)
        actual = pad

        expected = np.ones((4, 4, 4), int) * -1
        expected[1:3, 1:3, 1:3] = 1
        expected[1:-1, 1:-1, -1] = expected1
        expected[1:-1, -1, 1:-1] = 3

        self.assertEqual(expected.shape, actual.shape)
        # self.assertTrue(np.all(actual == expected), f"Failure! \n{actual}\n-------\n{expected}")
        self.assertEqual(str(expected), str(actual))
Exemplo n.º 3
0
    def test_eq_int(self):
        a = ChunkGrid(2, int, 0)
        a.set_value((1, 1, 1), 1)
        a.ensure_chunk_at_index((1, 1, 1))

        result = a == 0
        dense = result.to_dense()

        expected = np.ones((4, 4, 4), dtype=bool)
        expected[1, 1, 1] = False

        self.assertEqual(expected.shape, dense.shape)
        self.assertEqual(str(expected), str(dense))
        self.assertTrue(result.any())
        self.assertFalse(result.all())
Exemplo n.º 4
0
    def test_filled(self):
        a = ChunkGrid(2, bool, False)
        a.ensure_chunk_at_index((0, 0, 0)).set_fill(True)

        res = dilate(a, steps=1)
        self.assertEqual(7, len(res.chunks))
        c0 = res.chunks.get((0, 0, 0))
        self.assertIsNotNone(c0)
        self.assertTrue(c0.is_filled())
        self.assertTrue(c0.value)

        expected = np.zeros((6, 6, 6), dtype=bool)
        expected[2:-2, 2:-2, 1:-1] = True
        expected[2:-2, 1:-1, 2:-2] = True
        expected[1:-1, 2:-2, 2:-2] = True
        self.assertEqual(str(expected), str(res.to_dense()))
Exemplo n.º 5
0
    def test_getitem(self):
        CS = 2
        shape = (CS * 3, CS * 3, CS * 3)
        soll = np.arange(shape[0] * shape[1] * shape[2]).reshape(shape)

        a = ChunkGrid(CS, int, -1)
        for u in range(shape[0] // CS):
            for v in range(shape[1] // CS):
                for w in range(shape[2] // CS):
                    x, y, z = u * CS, v * CS, w * CS
                    index = (u, v, w)
                    a.ensure_chunk_at_index(index).set_array(soll[x:x + CS, y:y + CS, z:z + CS])

        dense = a.to_dense()
        self.assertEqual(soll.shape, dense.shape)
        self.assertEqual(str(soll), str(dense))

        self.assertEqual(str(soll[1: shape[0] - 1, 1: shape[1] - 1, 1: shape[2] - 1]),
                         str(a[1: shape[0] - 1, 1: shape[1] - 1, 1: shape[2] - 1]))
Exemplo n.º 6
0
    def test_getitem_offset(self):
        CS = 2
        shape = (CS * 3, CS * 3, CS * 3)
        soll = np.arange(shape[0] * shape[1] * shape[2]).reshape(shape)
        offset_chunk = (-1, 0, 1)

        a = ChunkGrid(CS, int, -1)
        for u in range(shape[0] // CS):
            for v in range(shape[1] // CS):
                for w in range(shape[2] // CS):
                    index = np.add((u, v, w), offset_chunk)
                    x, y, z = np.multiply((u, v, w), CS)
                    a.ensure_chunk_at_index(index).set_array(soll[x:x + CS, y:y + CS, z:z + CS])

        offset_voxel = np.multiply(offset_chunk, CS)
        dense, off = a.to_dense(return_offset=True)
        self.assertEqual(soll.shape, dense.shape)
        self.assertEqual(str(soll), str(dense))
        self.assertEqual(list(offset_voxel), list(off))

        ox, oy, oz = off
        self.assertEqual(str(soll[1: shape[0] - 1, 1: shape[1] - 1, 1: shape[2] - 1]),
                         str(a[1 + ox: shape[0] - 1 + ox, 1 + oy: shape[1] - 1 + oy, 1 + oz: shape[2] - 1 + oz]))
    data_min, data_max = np.min(data, axis=0), np.max(data, axis=0)
    data_delta_max = np.max(data_max - data_min)

    resolution = 64

    grid = ChunkGrid(16, dtype=int, fill_value=0)
    scaled = (data - data_min) * resolution / data_delta_max
    assert scaled.shape[1] == 3

    grid[scaled] = 1

    # Add padding
    filled = set(tuple(c.index) for c in grid.chunks)
    extra = set(
        tuple(n) for i in grid.chunks.keys()
        for f, n in grid.iter_neighbors_indices(i))
    for e in extra:
        grid.ensure_chunk_at_index(e)

    fill_mask = flood_fill_at((7, 9, 7), grid == 0)
    grid[fill_mask] = 3

    ren = VoxelRender()
    fig = ren.make_figure()
    fig.add_trace(ren.grid_voxel(grid == 1, opacity=1.0, flatshading=True))
    fig.add_trace(ren.grid_wireframe(grid == 1, opacity=1.0, size=2.0))
    # fig.add_trace(ren.grid_voxel(grid == 3, opacity=0.1, flatshading=True))
    fig.add_trace(CloudRender().make_scatter(scaled, marker=dict(size=1)))
    fig.show()
def crust_fix(
        crust: ChunkGrid[np.bool8],
        outer_fill: ChunkGrid[np.bool8],
        crust_outer: ChunkGrid[np.bool8],
        crust_inner: ChunkGrid[np.bool8],
        min_distance: int = 1,
        data_pts: Optional[np.ndarray] = None,  # for plotting,
        export_path: Optional[str] = None):
    CHUNKSIZE = crust.chunk_size
    normal_kernel = make_normal_kernel()

    inv_outer_fill = ~outer_fill

    # Method cache (prevent lookup in loop)
    __grid_set_value = ChunkGrid.set_value
    __np_sum = np.sum

    print("\tCreate Normals: ")
    with timed("\t\tTime: "):
        # normal_zero = np.zeros(3, dtype=np.float32)
        normal_pos = np.array(list(crust_outer.where()))
        normal_val = np.full((len(normal_pos), 3), 0.0, dtype=np.float32)
        for n, p in enumerate(normal_pos):
            x, y, z = p
            mask: np.ndarray = outer_fill[x - 1:x + 2, y - 1:y + 2,
                                          z - 1:z + 2]
            normal_val[n] = __np_sum(normal_kernel[mask], axis=0)
        normal_val = (normal_val.T / np.linalg.norm(normal_val, axis=1)).T

    print("\tGrid Normals: ")
    with timed("\t\tTime: "):
        normals: ChunkGrid[np.float32] = ChunkGrid(
            CHUNKSIZE, np.dtype((np.float32, (3, ))), 0.0)
        normals[normal_pos] = normal_val

    print("\tRender Normal Propagation: ")
    with timed("\t\tTime: "):
        markers_outer = np.array([
            v for p, n in normals.items(mask=crust_outer)
            for v in (p, p + n, (np.nan, np.nan, np.nan))
        ],
                                 dtype=np.float32) + 0.5
        markers_outer_tips = np.array(
            [p + n for p, n in normals.items(mask=crust_outer)],
            dtype=np.float32) + 0.5

        ren = CloudRender()
        fig = ren.make_figure(title="Crust-Fix: Start Normal Propagation")
        fig.add_trace(
            ren.make_scatter(markers_outer,
                             marker=dict(opacity=0.5, ),
                             mode="lines",
                             name="Start normal"))
        fig.add_trace(
            ren.make_scatter(markers_outer_tips,
                             marker=dict(size=1, symbol='x'),
                             name="Start nromal end"))
        if data_pts is not None:
            fig.add_trace(
                ren.make_scatter(data_pts, opacity=0.1, size=1, name='Model'))
        if export_path:
            fig.write_html(os.path.join(export_path, "normal_start.html"))
        fig.show()

    print("\tNormal Propagation")
    with timed("\t\tTime: "):
        iterations = CHUNKSIZE * 2
        nfield = propagate_normals(iterations, normals, crust_outer,
                                   inv_outer_fill)
        field_reset_mask = outer_fill ^ crust_outer
        nfield[field_reset_mask] = 0
        nfield.cleanup(remove=True)

    # print("\tRender Normal Field: ")
    # with timed("\t\tTime: "):
    #
    #     markers_crust = np.array(
    #         [v for p, n in nfield.items(mask=crust) for v in (p, p + n, (np.nan, np.nan, np.nan))],
    #         dtype=np.float32) + 0.5
    #     markers_outer = np.array(
    #         [v for p, n in nfield.items(mask=crust_outer) for v in (p, p + n, (np.nan, np.nan, np.nan))],
    #         dtype=np.float32) + 0.5
    #     markers_outer_tips = np.array(
    #         [p + n for p, n in nfield.items(mask=crust_outer)],
    #         dtype=np.float32) + 0.5
    #
    #     ren = CloudRender()
    #     fig = ren.make_figure(title="Crust-Fix: Normal Field")
    #     fig.add_trace(ren.make_scatter(markers_outer, marker=dict(opacity=0.5, ), mode="lines", name="Start normal"))
    #     fig.add_trace(ren.make_scatter(markers_outer_tips, marker=dict(size=1, symbol='x'), name="Start normal end"))
    #     fig.add_trace(ren.make_scatter(markers_crust, marker=dict(opacity=0.5, ), mode="lines", name="Normal field"))
    #     if data_pts is not None:
    #         fig.add_trace(ren.make_scatter(data_pts, opacity=0.1, size=1, name='Model'))
    #     if export_path:
    #         fig.write_html(os.path.join(export_path, "normal_field.html"))
    #     fig.show()

    print("\tNormal cone: ")
    with timed("\t\tTime: "):
        medial = ChunkGrid(crust.chunk_size, np.bool8, False)
        cone_threshold: float = 0.5 * np.pi
        min_norm: float = 1e-15
        for chunk in nfield.chunks:
            padded = nfield.padding_at(chunk.index,
                                       1,
                                       corners=True,
                                       edges=True)
            cones = normal_cone_angles(padded, cone_threshold, min_norm)
            medial.ensure_chunk_at_index(chunk.index).set_array(cones.copy())
        medial.cleanup(remove=True)

    print("\tResult: ")
    with timed("\t\tTime: "):
        # Remove artifacts where the inner and outer crusts are touching
        artifacts_fix = outer_fill.copy().pad_chunks(1)
        artifacts_fix.fill_value = False
        artifacts_fix = ~dilate(artifacts_fix,
                                steps=max(0, min_distance) + 2) & ~outer_fill
        medial_cleaned = medial & artifacts_fix
        medial_cleaned.cleanup(remove=True)

    print("\tRender 2: ")
    with timed("\t\tTime: "):
        time.sleep(0.01)
        ren = VoxelRender()
        fig = ren.make_figure(title="Crust-Normal-Fix: Result before cleanup")
        print("Ren2-medial")
        fig.add_trace(ren.grid_voxel(medial, opacity=0.3, name='Medial'))
        # fig.add_trace(ren.grid_voxel(medial_cleaned, opacity=0.05, name='Fixed'))
        print("Ren2-crust_outer")
        fig.add_trace(ren.grid_voxel(crust_outer, opacity=0.05, name='Outer'))
        if data_pts is not None:
            print("Ren2-data_pts")
            fig.add_trace(CloudRender().make_scatter(data_pts,
                                                     opacity=0.2,
                                                     size=1,
                                                     name='Model'))
        print("Ren2-show")
        if export_path:
            fig.write_html(os.path.join(export_path, "medial.html"))
        fig.show()

    print("\tRender 3: ")
    with timed("\t\tTime: "):
        time.sleep(0.01)
        ren = VoxelRender()
        fig = ren.make_figure(title="Crust-Fix: Result after cleanup")
        # fig.add_trace(ren.grid_voxel(medial, opacity=0.3, name='Fixed'))
        print("Ren2-medial_cleaned")
        fig.add_trace(
            ren.grid_voxel(medial_cleaned, opacity=0.3, name='Medial-Cleaned'))
        print("Ren3-crust_outer")
        fig.add_trace(ren.grid_voxel(crust_outer, opacity=0.05, name='Outer'))
        if data_pts is not None:
            print("Ren3-data_pts")
            fig.add_trace(CloudRender().make_scatter(data_pts,
                                                     opacity=0.2,
                                                     size=1,
                                                     name='Model'))
        print("Ren3-show")
        if export_path:
            fig.write_html(os.path.join(export_path, "medial_cleaned.html"))
        fig.show()

    return medial_cleaned