第2回 Minecraft × PLY点群:見た目を整える外れ値除去&平滑化で効果を探る
🧹 PLY点群をMinecraftに配置する前の前処理
前回は、PLY点群をMinecraftのブロックで再現しました。
今回はより綺麗な再現を目指して、点群の前処理を行います。
3DGSなどから出力されるPLYは、点の飛びやばらつきがある場合があります。
そのままMinecraftに置くと、ブロックが散らばったり形が不自然になることがあります。
そこで、今回は以下の3ステップで前処理します。
- 外れ値除去(Outlier Removal)
- 平滑化(Smoothing)
- 両方同時(Denoise & Smooth)
前回の結果
scale=50, thinning=2

1️⃣ 外れ値除去:outlier_removed.py
import open3d as o3d
def remove_outliers(input_path, output_path):
pcd = o3d.io.read_point_cloud(input_path)
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
pcd_clean = pcd.select_by_index(ind)
o3d.io.write_point_cloud(output_path, pcd_clean)
print(f"Outliers removed. Saved to {output_path}")
if __name__ == "__main__":
remove_outliers("lego.ply", "outlier_removed.ply")
解説
-
PLYを読み込み、点群を取得
-
remove_statistical_outlier()で統計的に外れ値を除去-
nb_neighbors=20: 近傍点数 -
std_ratio=2.0: 標準偏差の閾値
-
-
外れ値を除いた点群を保存
外れ値が多い場合、Minecraftでブロックを置いたときに散らばるのを防げます。
結果
前回の記事のコードでMinecraft上に再現してみた結果
"outlier_removed.ply", scale=50, thinning=2

2️⃣ 平滑化:smoothed.py
import open3d as o3d
import numpy as np
def smooth_point_cloud(input_path, output_path, iterations=1):
pcd = o3d.io.read_point_cloud(input_path)
points = np.asarray(pcd.points)
for _ in range(iterations):
pcd_tree = o3d.geometry.KDTreeFlann(pcd)
new_points = []
for i in range(len(points)):
[_, idx, _] = pcd_tree.search_knn_vector_3d(pcd.points[i], 10)
neighbor_pts = points[idx, :]
avg_pt = np.mean(neighbor_pts, axis=0)
new_points.append(avg_pt)
points = np.array(new_points)
pcd.points = o3d.utility.Vector3dVector(points)
o3d.io.write_point_cloud(output_path, pcd)
print(f"Smoothed point cloud saved to {output_path}")
if __name__ == "__main__":
smooth_point_cloud("lego.ply", "smoothed.ply", iterations=3)
解説
- PLYを読み込み、座標をNumPy配列に変換
- KDTreeで近傍点を探索
- 近傍点の平均位置で点を移動 → 平滑化
- 指定回数(
iterations)繰り返すことで、より滑らかに
平滑化により、Minecraft上でのブロック配置がより滑らかで自然な形になります。
結果
"smoothed.ply", scale=50, thinning=2

3️⃣ 両方同時:denoised_smoothed.py
import open3d as o3d
import numpy as np
def denoise_and_smooth(input_path, output_path, iterations=3):
pcd = o3d.io.read_point_cloud(input_path)
cl, ind = pcd.remove_statistical_outlier(nb_neighbors=20, std_ratio=2.0)
pcd_clean = pcd.select_by_index(ind)
points = np.asarray(pcd_clean.points)
for _ in range(iterations):
pcd_tree = o3d.geometry.KDTreeFlann(pcd_clean)
new_points = []
for i in range(len(points)):
[_, idx, _] = pcd_tree.search_knn_vector_3d(pcd_clean.points[i], 10)
neighbor_pts = points[idx, :]
avg_pt = np.mean(neighbor_pts, axis=0)
new_points.append(avg_pt)
points = np.array(new_points)
pcd_clean.points = o3d.utility.Vector3dVector(points)
o3d.io.write_point_cloud(output_path, pcd_clean)
print(f"Denoised and smoothed point cloud saved to {output_path}")
if __name__ == "__main__":
denoise_and_smooth("lego.ply", "denoised_smoothed.ply", iterations=3)
解説
- まず統計的外れ値除去でノイズ点を削除
- その後、近傍点平均による平滑化を繰り返す
- 両方を同時に行うことで、よりクリーンで滑らかな点群を生成
- 保存したPLYを、前回紹介したMinecraftブロック化スクリプトに渡すことで、より自然な再現が可能
結果
"denoised_smoothed.ply", scale=50, thinning=2

💡 コメント
-
outlier_removed
外れ値を除去するとブロック数は減り、多少スッキリしますが、形状の再現性への大きなメリットはあまり感じられませんでした。 -
smoothed
平滑化によりノイズや飛び点は減りますが、削られすぎて形状が崩れる場合もあるため、もう少し工夫が必要だと感じた。 -
総評
PLYの前処理は、形状を保持しつつノイズを減らすという点で有効ですが、Minecraftでの再現ではもっと調整や適用範囲の工夫が必要だと感じた。
🎁 おまけ:スケール比較
前回記述したコードでは、scaleの値によってブロックのサイズ感や形状の認識が変わります。
今回はそれを使って、間引き率thinning=2で3種類のスケールを比較してみました。
1. scale=20, thinning=2
-
小さめのサイズ

-
コメント:これより小さいと形がわからなくなりそうです。
2. scale=30, thinning=2
- やや小さめサイズ
3. scale=80, thinning=2
- 大きめサイズ
- コメント:これ以上大きくするとデカすぎてMinecraft上で扱いにくいです。
💡 補足
スケールはMinecraft上での「見やすさ」と「建造の扱いやすさ」のバランスで調整する変数です。
小さすぎると形状が潰れ、大きすぎると建造負荷が上がります。
Discussion