ByteDance-Seed / ByteDance-Seed/Depth-Anything-3

Misalignment in two scene reconstruction blocks using camera conditions

Open
#64 3 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
6.3k
Forks
702
PR merge metrics
No merged PRs in 30d

Description

I divided over 600 images into two blocks and fed them into DA3, using the camera parameters. I noticed that the point clouds were not completely overlapping; instead, there was visible misalignment.

However, I found that DA3 did not change the extrinsics. In theory, if the extrinsics remain unchanged, even when reconstructing point clouds in separate blocks, they should still overlap.

I don't know why this happen?

Sorry, I can't upload images here, but the code I used is as follows:
```
from depth_anything_3.api import DepthAnything3

import glob, os, torch
from tqdm import tqdm
from pathlib import Path
from scipy.spatial.transform import Rotation
import numpy as np
from depth_anything_3.utils.export.glb import _as_homogeneous44
import open3d as o3d
from utils.write_sfm import write_cameras_text, write_images_text
# Initialize the model with a model name

def pose_matrix_from_quaternion(pvec):
"""
Get 4x4 pose matrix from quaternion (t, q)
t = (tx, ty, tz)
q = (qw, qx, qy, qz)
"""
pose = np.eye(4, dtype=np.float32)
pose[:3, :3] = Rotation.from_quat(pvec[3:], scalar_first=True).as_matrix()
pose[:3, 3] = pvec[:3]
return pose

def load_scene(source_dir, sparse_dir = None, stride=1, adaptive=False, max_num=300):

def read_images_lines(image_lines, stride=1):
count_info = 0
extrinsics = [] # N,4,4 world-to-camera
intrinsics = [] # N 3,3
images_list = []

# Process each image
for i in tqdm(range(0, len(image_lines), 2)): # 从0开始,步长2(取0,2,4...行)
parts = image_lines[i].strip().split()
_, qw, qx, qy, qz, tx, ty, tz, camera_id, name = parts[:10]
camera_id = int(camera_id)
base_name = os.path.basename(name)

# Get camera parameters
camera_info = camera_params_dict[camera_id]
width = camera_info["width"]
height = camera_info["height"]
f, cx, cy = camera_info["params"]
fx, fy = f, f
intrinsic = [[fx, 0, cx], [0, fy, cy], [0, 0, 1]]

w2c_pose = pose_matrix_from_quaternion(
[
float(tx),
float(ty),
float(tz),
float(qw),
float(qx),
float(qy),
float(qz),
]
)
image_path = os.path.join(image_root_dir, base_name)
if count_info % stride == 0:
intrinsics.append(intrinsic)
extrinsics.append(w2c_pose)
images_list.append(image_path)
count_info += 1

intrinsics_array = np.stack(intrinsics, axis=0)
extrinsics_array = np.stack(extrinsics, axis=0)
return images_list, intrinsics_array, extrinsics_array

scene_dict = {}
if sparse_dir is None:
sparse_dir = os.path.join(source_dir, "sparse", "0")

cameras_txt = Path(sparse_dir) / "cameras.txt"
images_txt = Path(sparse_dir) / "images.txt"
image_root_dir = os.path.join(source_dir, "images")
# 读取cameras 参数
camera_params_dict = {}
with open(cameras_txt, "r") as f:
camera_lines = f.readlines()[3:] # Skip header
for line in camera_lines:
parts = line.strip().split()
camera_id = int(parts[0])
width = int(parts[2])
height = int(parts[3])
params = list(map(float, parts[4:]))
camera_params_dict[camera_id] = {
"width": width,
"height": height,
"params": params,
}

with open(images_txt, "r") as f:
image_lines = f.readlines()[4:]
images_num = len(image_lines)

if adaptive:
block_num = (images_num + max_num - 1) // max_num # 得到需要分块的数量
print(f"Adaptive block: {block_num}")
for i in tqdm(range(block_num), desc="Reading each block:"):
block_images_lines = image_lines[i * max_num : (i + 1) * max_num]
img_list, intrs, extr = read_images_lines(block_images_lines)
scene_dict[f"block_{i}"] = {
"images_list": img_list,
"intrinsics_array": intrs,
"extrinsics_array": extr,
}
else:
print(f"Reading scene..")
img_list, intrs, extr = read_images_lines(image_lines)
scene_dict[f"block_0"] = {
"images_list": img_list,
"intrinsics_array": intrs,
"extrinsics_array": extr,
}

return scene_dict

model = DepthAnything3.from_pretrained("./weight", local_files_only=True).to("cuda") # Move to GPU

root_dir = ""

output_dir = ""

datanames = [""]

num_max_points = 5_000_000
conf_thresh_percentile = 50
colmap_stride = 1
output_special = "default"

infer_gs = False
if_save_depth = True
merge_ply = False
save_cameras_txt = True
save_images_txt = True

if infer_gs:
export_format = "npz-ply-gs_ply"
else:
export_format = "ply"

for dataname in datanames:
example_path = os.path.join(root_dir, dataname)

if output_special is None:
export_dir = os.path.join(output_dir, dataname)
else:
export_dir = os.path.join(output_dir, dataname, output_special)

scene_dict = load_scene(example_path, stride=colmap_stride, adaptive=True)
block_dir_list = []
for block_n, block_data in scene_dict.items():
print(f"正在处理块:{block_n}")
images_list = block_data["images_list"]
# 访问当前 block 的内参和外参
intrinsics_array = block_data["intrinsics_array"]
extrinsics_array = block_data["extrinsics_array"]

assert intrinsics_array.shape[0] == extrinsics_array.shape[0] and extrinsics_array.shape[0] == len(images_list)
print(f"Processing {len(images_list)} images in {example_path}")
block_export_dir = os.path.join(export_dir, block_n)
block_dir_list.append(block_export_dir)
print(f"Save prediction to {block_export_dir}")

prediction = model.inference(
image=images_list,
extrinsics=extrinsics_array, # Optional
intrinsics=intrinsics_array, # Optional
align_to_input_ext_scale=True, # Whether to align predicted poses to input scale
infer_gs=infer_gs, # Enable Gaussian branch for gs exports
process_res=504,
process_res_method="upper_bound_resize",
export_dir=block_export_dir, # Optional
export_format=export_format,
export_feat_layers=[], # List of layer indices to export features from
conf_thresh_percentile=conf_thresh_percentile, # Confidence threshold percentile for depth map in GLB export
num_max_points=num_max_points, # Maximum number of points to export in GLB export
show_cameras=True, # Whether to show cameras in GLB export
export_kwargs={} # Optional, additional arguments to export functions. export_format:key:val, see 'Parameters/Export Parameters' for details
)

if merge_ply:
merged_pcd = o3d.geometry.PointCloud()
output_ply_path = os.path.join(export_dir, f"scene_merge.ply")
for block_dir in block_dir_list:
ply_path = os.path.join(block_dir, "scene.ply")
pcd = o3d.io.read_point_cloud(ply_path)
merged_pcd += pcd

o3d.io.write_point_cloud(output_ply_path, merged_pcd)
print(f"Merged pc in {output_ply_path}")

```

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.