3 minute read

Poisson Surface Reconstruction์˜ ์›๋ฆฌ

Poisson surface reconstruction์€ ์ฃผ์–ด์ง„ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ๋กœ๋ถ€ํ„ฐ ์‚ผ๊ฐํ˜• ๋ฉ”์‰ฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š”๋ฐ, ์ด ๋ฐฉ๋ฒ•์€ ์ ์˜ ์œ„์น˜๋ฟ๋งŒ ์•„๋‹ˆ๋ผ ๊ฐ ์ ์˜ ๋…ธ๋ฉ€ ๋ฒกํ„ฐ ์ •๋ณด๋ฅผ ์‚ฌ์šฉํ•ฉ๋‹ˆ๋‹ค.

Poisson Surface Reconstruction์‹œ points์— ๋Œ€์‘ํ•˜๋Š” normals ์ •๋ณด ๋˜ํ•œ ์กด์žฌํ•ด์•ผํ•œ๋‹ค๋Š” ์ ์€ open3d์˜ poisson surface reconstruction official document์—๋„ ๋‚˜ํƒ€๋‚˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์ด๋Š” Poisson Surface Reconstruction์ด points์˜ normals๋กœ surface๋ฅผ ์ฐพ๊ธฐ ๋•Œ๋ฌธ์ž…๋‹ˆ๋‹ค.

image

์›๋ฆฌ

  1. ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ:
    • ์ ๋“ค์˜ ์ขŒํ‘œ (points)
    • ๊ฐ ์ ์—์„œ์˜ ์ƒ‰์ƒ ์ •๋ณด (colors)
    • ๊ฐ ์ ์—์„œ์˜ ๋…ธ๋ฉ€ ๋ฒกํ„ฐ (normals)
  2. Octree ์ƒ์„ฑ:
    • ์ฃผ์–ด์ง„ ๊นŠ์ด(depth)์— ๋”ฐ๋ผ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ์˜ฅํŠธ๋ฆฌ ๊ตฌ์กฐ๋กœ ๋ถ„ํ• ํ•ฉ๋‹ˆ๋‹ค. ๊นŠ์ด๊ฐ€ ๊นŠ์„์ˆ˜๋ก ๋” ์„ธ๋ฐ€ํ•œ ๊ตฌ์กฐ๊ฐ€ ์ƒ์„ฑ๋ฉ๋‹ˆ๋‹ค.
  3. Poisson ๋ฐฉ์ •์‹ ํ’€์ด:
    • ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ์˜ ๋…ธ๋ฉ€ ๋ฒกํ„ฐ๋ฅผ ์ด์šฉํ•ด Poisson ๋ฐฉ์ •์‹์„ ํ’‰๋‹ˆ๋‹ค. ์ด๋ฅผ ํ†ตํ•ด ์ ๋“ค์ด ๋‚˜ํƒ€๋‚ด๋Š” ํ‘œ๋ฉด์˜ ์—ฐ์†์ ์ธ ๊ตฌ์กฐ๋ฅผ ์ถ”์ •ํ•ฉ๋‹ˆ๋‹ค.
  4. ์‚ผ๊ฐํ˜• ๋ฉ”์‰ฌ ์ƒ์„ฑ:
    • Poisson ๋ฐฉ์ •์‹์˜ ํ•ด๋ฅผ ๋ฐ”ํƒ•์œผ๋กœ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ๋ฅผ ์‚ผ๊ฐํ˜• ๋ฉ”์‰ฌ๋กœ ๋ณ€ํ™˜ํ•ฉ๋‹ˆ๋‹ค. ์ด๋Š” ์ ๋“ค์ด ๋งค๋„๋Ÿฌ์šด ํ‘œ๋ฉด์„ ๋‚˜ํƒ€๋‚ด๋„๋ก ํ•ฉ๋‹ˆ๋‹ค.

Input Requirements

  • ํฌ์ธํŠธ ๋ฐ์ดํ„ฐ (3D ์ขŒํ‘œ)
  • ๋…ธ๋ฉ€ ๋ฐ์ดํ„ฐ (๊ฐ ์ ์—์„œ์˜ ํ‘œ๋ฉด ๋…ธ๋ฉ€ ๋ฒกํ„ฐ)
  • ์ถ”๊ฐ€์ ์œผ๋กœ ์ปฌ๋Ÿฌ ๋ฐ์ดํ„ฐ๋Š” ์‹œ๊ฐํ™”๋ฅผ ์œ„ํ•ด ์‚ฌ์šฉ๋  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ๊ฐ€ ์ถฉ๋ถ„ํžˆ ๋ฐ€์ง‘๋˜๊ณ , ๋…ธ๋ฉ€ ๋ฒกํ„ฐ๊ฐ€ ์ •ํ™•ํ•˜๊ฒŒ ๊ณ„์‚ฐ๋œ ๊ฒฝ์šฐ, Poisson surface reconstruction์„ ํ†ตํ•ด ๋งค๋„๋Ÿฝ๊ณ  ์ •ํ™•ํ•œ ํ‘œ๋ฉด์„ ์ƒ์„ฑํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

Poisson surface reconstruction์˜ ์„ฑ๊ณต ์—ฌ๋ถ€๋Š” ์ž…๋ ฅ๋œ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ์˜ ํ’ˆ์งˆ์— ํฌ๊ฒŒ ์˜์กดํ•ฉ๋‹ˆ๋‹ค. ํŠนํžˆ, ๋…ธ๋ฉ€ ๋ฒกํ„ฐ๊ฐ€ ์˜ฌ๋ฐ”๋ฅด๊ฒŒ ์„ค์ •๋˜์ง€ ์•Š์œผ๋ฉด ํ‘œ๋ฉด์˜ ์ •ํ™•๋„๊ฐ€ ๋–จ์–ด์งˆ ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.

์š”์•ฝ

Poisson surface reconstruction์€ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ์™€ ํ•ด๋‹น ๋ฐ์ดํ„ฐ์˜ ๋…ธ๋ฉ€ ๋ฒกํ„ฐ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์‚ผ๊ฐํ˜• ๋ฉ”์‰ฌ๋ฅผ ์ƒ์„ฑํ•˜๋Š” ๋ฐฉ๋ฒ•์ž…๋‹ˆ๋‹ค. ์ด ๊ณผ์ •์—์„œ ์˜ฅํŠธ๋ฆฌ ๊นŠ์ด(depth) ์„ค์ •์€ ์ƒ์„ฑ๋  ๋ฉ”์‰ฌ์˜ ์„ธ๋ฐ€ํ•จ์„ ์กฐ์ ˆํ•ฉ๋‹ˆ๋‹ค.

poisson reconstruction์„ o3d๋กœ ๊ตฌํ˜„์‹œ, points์™€ normals์„ ๋ชจ๋‘ ๊ฐ€์ง„ pcd๋ฅผ poisson reconstruction์„ ์ˆ˜ํ–‰ํ•˜๋Š” ํ•จ์ˆ˜์— ๋„ฃ์–ด์ค๋‹ˆ๋‹ค.

  • pcd์— ๋Œ€ํ•ด์„œ points, colors, normals ์ •๋ณด๋ฅผ ๋„ฃ์–ด์ฃผ๊ณ , outlier๋ฅผ ์ œ๊ฑฐํ•ด์ฃผ๊ณ ,o3d.geometry.TriangleMesh.create_from_point_cloud_poisson๋กœ pcd๋ฅผ ์ฃผ๋ฉด mesh์™€ densities๋ฅผ ๊ตฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.
  • ์•„๋ž˜ ์ฝ”๋“œ์—์„œ fg_pcd์— fg_points, fg_colors, fg_normals์„ Vector๋กœ ๋„ฃ์–ด์ฃผ๊ณ , fg_pcd์—์„œ outlier๋ฅผ ์ œ๊ฑฐํ•œ ๊ฒƒ์„ poisson ํ•จ์ˆ˜์— ๋„ฃ๊ณ , poisson_depth(=octree depth)๋ฅผ ์„ค์ •ํ•˜์—ฌ, fg_mesh์™€ fg_densities๋ฅผ ์–ป์Šต๋‹ˆ๋‹ค.
  • o3d_fg_mesh, o3d_fg_densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(fg_pcd, depth=poisson_depth)
  • fg_pcd์—์„œ ๋งˆ์ง€๋ง‰์œผ๋กœ low density value๋ฅผ ๊ฐ–๋Š” vertices๋Š” ์ œ๊ฑฐํ•ด์ค๋‹ˆ๋‹ค.
  • ์ •์˜์ƒ ์–ด๋–ค vertex๊ฐ€ low density value๋ฅผ ๊ฐ€์ง„๋‹ค๋Š” ๊ฒƒ์€ input point cloud์—์„œ ๊ทธ vertex๋ฅผ ์ฐธ์กฐํ•˜๋Š” points์ˆ˜๊ฐ€ ์ ๋‹ค๋Š” ์˜๋ฏธ์ž…๋‹ˆ๋‹ค.
  • ์ด ๋ง์€ mesh์˜ ๊ฒฝ๊ณ„์ชฝ์œผ๋กœ ๊ฐˆ์ˆ˜๋ก ์–ด๋–ค vertex์— ๋Œ€ํ•œ density๋Š” ๋‚ฎ์•„์ง์„ ์˜๋ฏธํ•ฉ๋‹ˆ๋‹ค.
  • ์ด๋ฅผ ์ž˜ ํ‘œํ˜„ํ•œ ๊ทธ๋ฆผ์ด ์žˆ์Šต๋‹ˆ๋‹ค. image image image

    vertices_to_remove = o3d_fg_densities < np.quantile(o3d_fg_densities, vertices_density_quantile)
    o3d_fg_mesh.remove_vertices_by_mask(vertices_to_remove) 
    
  • bg_pcd์— ๋Œ€ํ•ด์„œ๋„ ๋™์ผํ•˜๊ฒŒ ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.
# sugar_extractors/coarse_mesh.py
def extract_mesh_from_coarse_sugar(args):

...

               # ---Compute foreground mesh---
                CONSOLE.print("\n-----Foreground mesh-----")
                if fg_points.shape[0] > 0:
                    CONSOLE.print("Computing points, colors and normals...")
                    fg_pcd = o3d.geometry.PointCloud()
                    fg_pcd.points = o3d.utility.Vector3dVector(fg_points.double().cpu().numpy())
                    fg_pcd.colors = o3d.utility.Vector3dVector(fg_colors.double().cpu().numpy())
                    fg_pcd.normals = o3d.utility.Vector3dVector(fg_normals.double().cpu().numpy())

                    # outliers removal
                    cl, ind = fg_pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=20.)
                    CONSOLE.print("Cleaning Point Cloud...")
                    fg_pcd = fg_pcd.select_by_index(ind)

                    CONSOLE.print("Finished computing points, colors and normals.")

                    CONSOLE.print("Now computing mesh...")
                    o3d_fg_mesh, o3d_fg_densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
                        fg_pcd, depth=poisson_depth) #, width=0, scale=1.1, linear_fit=False)  # depth=10 should be the default value? 11 is good to (but it starts to make a big number of triangles)

                    if vertices_density_quantile > 0.:
                        CONSOLE.print("Removing vertices with low densities...")
                        vertices_to_remove = o3d_fg_densities < np.quantile(o3d_fg_densities, vertices_density_quantile)
                        o3d_fg_mesh.remove_vertices_by_mask(vertices_to_remove)
                else:
                    CONSOLE.print("\n[WARNING] Foreground is empty.")
                    o3d_fg_mesh = None
                
                # ---Compute background mesh---
                CONSOLE.print("\n-----Background mesh-----")
                if bg_points.shape[0] > 0:
                    CONSOLE.print("Computing points, colors and normals...")
                    bg_pcd = o3d.geometry.PointCloud()
                    bg_pcd.points = o3d.utility.Vector3dVector(bg_points.double().cpu().numpy())
                    bg_pcd.colors = o3d.utility.Vector3dVector(bg_colors.double().cpu().numpy())
                    bg_pcd.normals = o3d.utility.Vector3dVector(bg_normals.double().cpu().numpy())

                    # outliers removal
                    cl, ind = bg_pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=20.)
                    CONSOLE.print("Cleaning Point Cloud...")
                    bg_pcd = bg_pcd.select_by_index(ind)

                    CONSOLE.print("Finished computing points, colors and normals.")

                    CONSOLE.print("Now computing mesh...")
                    o3d_bg_mesh, o3d_bg_densities = o3d.geometry.TriangleMesh.create_from_point_cloud_poisson(
                        bg_pcd, depth=poisson_depth) #, width=0, scale=1.1, linear_fit=False)  # depth=10 should be the default value? 11 is good to (but it starts to make a big number of triangles)

                    if vertices_density_quantile > 0.:
                        CONSOLE.print("Removing vertices with low densities...")
                        vertices_to_remove = o3d_bg_densities < np.quantile(o3d_bg_densities, vertices_density_quantile)
                        o3d_bg_mesh.remove_vertices_by_mask(vertices_to_remove)
                else:
                    CONSOLE.print("\n[WARNING] Background is empty.")
                    o3d_bg_mesh = None
                
                CONSOLE.print("Finished computing meshes.")
                CONSOLE.print("Foreground mesh:", o3d_fg_mesh)
                CONSOLE.print("Background mesh:", o3d_bg_mesh)

pcd์—์„œ outlier removal์€ ๋‹ค์Œ๊ณผ ๊ฐ™์ด ์ง„ํ–‰ํ•ฉ๋‹ˆ๋‹ค.

Point cloud outlier removal

remove_statistical_outlier ํ•จ์ˆ˜

remove_statistical_outlier ํ•จ์ˆ˜๋Š” ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ(point cloud) ๋ฐ์ดํ„ฐ์—์„œ ํ†ต๊ณ„์ ์œผ๋กœ ์ด๋ก€์ ์ธ(outlier) ์ ๋“ค์„ ์ œ๊ฑฐํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” ๋‘ ๊ฐœ์˜ ์ž…๋ ฅ ๋งค๊ฐœ๋ณ€์ˆ˜๋ฅผ ๋ฐ›์Šต๋‹ˆ๋‹ค:

  • nb_neighbors: ์ฃผ์–ด์ง„ ์ ์— ๋Œ€ํ•ด ํ‰๊ท  ๊ฑฐ๋ฆฌ๋ฅผ ๊ณ„์‚ฐํ•˜๊ธฐ ์œ„ํ•ด ๊ณ ๋ คํ•  ์ด์›ƒ์˜ ์ˆ˜๋ฅผ ์ง€์ •ํ•ฉ๋‹ˆ๋‹ค.
  • std_ratio: ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ์ „๋ฐ˜์˜ ํ‰๊ท  ๊ฑฐ๋ฆฌ์˜ ํ‘œ์ค€ํŽธ์ฐจ๋ฅผ ๊ธฐ๋ฐ˜์œผ๋กœ ์ž„๊ณ„๊ฐ’ ์ˆ˜์ค€์„ ์„ค์ •ํ•  ์ˆ˜ ์žˆ๊ฒŒ ํ•ฉ๋‹ˆ๋‹ค. ์ด ๊ฐ’์ด ๋‚ฎ์„์ˆ˜๋ก ํ•„ํ„ฐ๊ฐ€ ๋” ๊ณต๊ฒฉ์ ์œผ๋กœ ์ž‘๋™ํ•ฉ๋‹ˆ๋‹ค.

์ด ํ•จ์ˆ˜๋Š” ๋‘ ๊ฐ€์ง€ ๋ฐ˜ํ™˜ ๊ฐ’์„ ๊ฐ€์ง‘๋‹ˆ๋‹ค:

  • cl: Clustered point cloud data
    • ํ†ต๊ณ„์ ์œผ๋กœ ์ด๋ก€์ ์ด์ง€ ์•Š์€ ์ ๋“ค๋กœ ๊ตฌ์„ฑ๋œ ์ƒˆ๋กœ์šด ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ
    • ์›๋ž˜ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ์—์„œ ์ด๋ก€์น˜๊ฐ€ ์ œ๊ฑฐ๋œ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ
  • ind: Index list of inliers
    • ์›๋ž˜ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ์—์„œ ์ด๋ก€์ ์ด์ง€ ์•Š์€ ์ ๋“ค์˜ ์ธ๋ฑ์Šค๋ฅผ ๋‚˜ํƒ€๋‚ด๋Š” ๋ฆฌ์ŠคํŠธ
    • ์ด ์ธ๋ฑ์Šค๋ฅผ ์‚ฌ์šฉํ•˜์—ฌ ์›๋ž˜ ํฌ์ธํŠธ ํด๋ผ์šฐ๋“œ ๋ฐ์ดํ„ฐ์—์„œ ์ •์ƒ์ ์ธ ์ ๋“ค์„ ์„ ํƒํ•  ์ˆ˜ ์žˆ์Œ

select_by_index ํ•จ์ˆ˜

  • select_by_index, which takes a binary mask to output only the selected points.

Leave a comment