3DGSは Blender データセットのトレーニングをサポートしており、主なデータ形式は以下の通りです:
<location>
|---images
| |---<image 0>
| |---<image 1>
| |---...
|---transformers_train.json
|---point3d.ply
データ準備#
通常、私たちが収集するデータセットはレーザースキャン装置から来ており、las 形式の点群ファイル、json 形式のカメラポーズ、および画像を提供します。
まず、las 形式の点群ファイルを処理し、バイナリの ply ファイルに変換する必要があります。
次に、transformers_train.json ファイルを確認し、主に 3dgs コードファイルの /sence/dataset_readers.py を対象に修正します。
def readNerfSyntheticInfo(path, white_background, eval, extension=".jpg"):
print("トレーニング変換を読み込み中")
train_cam_infos = readCamerasFromTransforms(path, "transforms_train.json", white_background, extension)
#print("テスト変換を読み込み中")
#test_cam_infos = readCamerasFromTransforms(path, "transforms_test.json", white_background, extension)
#if not eval:
# train_cam_infos.extend(test_cam_infos)
# test_cam_infos = []
test_cam_infos = []
nerf_normalization = getNerfppNorm(train_cam_infos)
ply_path = os.path.join(path, "points3d.ply")
if not os.path.exists(ply_path):
# このデータセットにはcolmapデータがないため、ランダムポイントから始めます
num_pts = 100_000
print(f"ランダム点群を生成中 ({num_pts})...")
# 合成Blenderシーンの範囲内にランダムポイントを作成します
xyz = np.random.random((num_pts, 3)) * 2.6 - 1.3
shs = np.random.random((num_pts, 3)) / 255.0
pcd = BasicPointCloud(points=xyz, colors=SH2RGB(shs), normals=np.zeros((num_pts, 3)))
storePly(ply_path, xyz, SH2RGB(shs) * 255)
try:
pcd = fetchPly(ply_path)
except:
pcd = None
scene_info = SceneInfo(point_cloud=pcd,
train_cameras=train_cam_infos,
test_cameras=test_cam_infos,
nerf_normalization=nerf_normalization,
ply_path=ply_path)
return scene_info
Test Transforms に関連する部分はコメントアウトされており、eval は行われないため、テストは不要です。extension を変更し、主に自分の画像入力形式を確認します。
def readCamerasFromTransforms(path, transformsfile, white_background, extension=".jpg"):
cam_infos = []
with open(os.path.join(path, transformsfile)) as json_file:
contents = json.load(json_file)
fovx = contents["camera_angle_x"]
frames = contents["frames"]
for idx, frame in enumerate(frames):
cam_name = os.path.join(path,"images", frame["file_path"])
# NeRFの'transform_matrix'はカメラから世界への変換です
c2w = np.array(frame["transform_matrix"])
# OpenGL/Blenderカメラ軸(Y上、Z後ろ)からCOLMAP(Y下、Z前)に変更します
c2w[:3, 1:3] *= -1
# 世界からカメラへの変換を取得し、R、Tを設定します
w2c = np.linalg.inv(c2w)
R = np.transpose(w2c[:3,:3]) # RはCUDAコードの'glm'のため転置されて保存されています
T = w2c[:3, 3]
image_path = os.path.join(path, cam_name)
image_name = Path(cam_name).stem
image = Image.open(image_path)
im_data = np.array(image.convert("RGBA"))
bg = np.array([1,1,1]) if white_background else np.array([0, 0, 0])
norm_data = im_data / 255.0
arr = norm_data[:,:,:3] * norm_data[:, :, 3:4] + bg * (1 - norm_data[:, :, 3:4])
image = Image.fromarray(np.array(arr*255.0, dtype=np.byte), "RGB")
fovy = focal2fov(fov2focal(fovx, image.size[0]), image.size[1])
FovY = fovy
FovX = fovx
cam_infos.append(CameraInfo(uid=idx, R=R, T=T, FovY=FovY, FovX=FovX, image=image,
image_path=image_path, image_name=image_name, width=image.size[0], height=image.size[1]))
return cam_infos
ここでは通常、自分の json ファイルの内容に基づいて変更を行います。主に注意すべき点は以下の通りです:
fovx = contents["camera_angle_x"]
cam_name = os.path.join(path,"images", frame["file_path"])
def fetchPly(path):
plydata = PlyData.read(path)
vertices = plydata['vertex']
positions = np.vstack([vertices['x'], vertices['y'], vertices['z']]).T
colors = np.vstack([vertices['red'], vertices['green'], vertices['blue']]).T / 255.0
normals = np.vstack([0,0,0]).T
return BasicPointCloud(points=positions, colors=colors, normals=normals)
この部分は ply ファイルに関連しており、通常、自分で収集したデータセットには点群法線 normals がないため、colmap 部分のコードに基づいて 0 に設定します。
一般的に、このように修正を行えば、コードは正常に動作し、レーザーポイントクラウドと GPS 高精度カメラポーズを組み合わせることで、colmap がカメラポーズを取得できない問題を解決できます。