twanghcmut's picture
download
raw
5.78 kB
import json
from pathlib import Path
import cv2
import numpy as np
from .. import video
def load_reconstruction(folder):
from .mapanything import Reconstruction
folder = Path(folder)
meta = json.loads((folder / "manifest.json").read_text())
views = []
for index in range(meta["views"]):
with np.load(folder / f"view_{index:04d}.npz") as data:
views.append({key: data[key] for key in data.files})
return Reconstruction(
views,
np.asarray(meta["frame_indices"]),
np.asarray(meta["timestamps"]),
meta["model_id"],
)
def _label(image, text):
out = image.copy()
cv2.rectangle(out, (0, 0), (out.shape[1], 34), (18, 22, 30), -1)
cv2.putText(
out,
text,
(12, 24),
cv2.FONT_HERSHEY_SIMPLEX,
0.55,
(240, 245, 255),
1,
cv2.LINE_AA,
)
return out
def _colormap(values, mask, bounds, palette):
normalized = np.clip((values - bounds[0]) / max(bounds[1] - bounds[0], 1e-8), 0, 1)
normalized = np.nan_to_num(normalized)
colored = cv2.cvtColor(
cv2.applyColorMap((normalized * 255).astype(np.uint8), palette),
cv2.COLOR_BGR2RGB,
)
colored[~mask] = [18, 22, 30]
return colored
def _project(points, colors, pose, intrinsics, size):
width, height = size
camera_points = (points - pose[:3, 3]) @ pose[:3, :3]
valid = np.isfinite(camera_points).all(axis=1) & (camera_points[:, 2] > 0)
camera_points, colors = camera_points[valid], colors[valid]
pixels = camera_points @ intrinsics.T
pixels = np.rint(pixels[:, :2] / pixels[:, 2:]).astype(int)
valid = (
(pixels[:, 0] >= 0)
& (pixels[:, 0] < width)
& (pixels[:, 1] >= 0)
& (pixels[:, 1] < height)
)
pixels, colors, depth = pixels[valid], colors[valid], camera_points[valid, 2]
order = np.argsort(-depth)
out = np.full((height, width, 3), [18, 22, 30], dtype=np.uint8)
out[pixels[order, 1], pixels[order, 0]] = colors[order]
return out
def export_preview(result, folder):
folder = Path(folder)
masks = [v["mask"] for v in result.views]
depths = np.concatenate(
[v["depth_z"].reshape(m.shape)[m] for v, m in zip(result.views, masks)]
)
confidences = np.concatenate(
[v["conf"].reshape(m.shape)[m] for v, m in zip(result.views, masks)]
)
if not len(depths):
raise ValueError("Reconstruction has no valid geometry")
depth_range = np.percentile(depths, [2, 98])
confidence_range = np.percentile(confidences, [2, 98])
points = np.concatenate(
[v["pts3d"][::3, ::3][m[::3, ::3]] for v, m in zip(result.views, masks)]
)
colors = np.concatenate(
[v["img_no_norm"][::3, ::3][m[::3, ::3]] for v, m in zip(result.views, masks)]
)
panels, metrics = [], []
for i, (view, mask) in enumerate(zip(result.views, masks)):
height, width = mask.shape
depth = _colormap(
view["depth_z"].reshape(mask.shape), mask, depth_range, cv2.COLORMAP_TURBO
)
confidence = _colormap(
view["conf"].reshape(mask.shape),
mask,
confidence_range,
cv2.COLORMAP_VIRIDIS,
)
reprojection = _project(
points, colors, view["camera_poses"], view["intrinsics"], (width, height)
)
panel = np.concatenate(
(
np.concatenate(
(_label(view["img_no_norm"], "RGB"), _label(depth, "Metric depth")),
axis=1,
),
np.concatenate(
(
_label(confidence, "Confidence / validity"),
_label(reprojection, "Multi-view point cloud reprojection"),
),
axis=1,
),
),
axis=0,
)
panels.append(panel)
metrics.append(
{
"frame": int(result.frame_indices[i]),
"timestamp": float(result.timestamps[i]),
"valid_fraction": float(mask.mean()),
"median_depth_m": float(
np.median(view["depth_z"].reshape(mask.shape)[mask])
),
}
)
step = np.median(np.diff(result.timestamps)) if len(result.timestamps) > 1 else 1
video.write(folder / "reconstruction.mp4", panels, 1 / step)
cv2.imwrite(
str(folder / "preview.png"),
cv2.cvtColor(panels[len(panels) // 2], cv2.COLOR_RGB2BGR),
)
center = np.median(points, axis=0)
radius = max(float(np.percentile(np.linalg.norm(points - center, axis=1), 90)), 0.1)
orbit = []
for angle in np.linspace(0, 2 * np.pi, 90, endpoint=False):
eye = center + radius * np.array(
[1.8 * np.cos(angle), -0.9, 1.8 * np.sin(angle)]
)
forward = (center - eye) / np.linalg.norm(center - eye)
right = np.cross(forward, [0, -1, 0])
right /= np.linalg.norm(right)
down = np.cross(forward, right)
pose = np.eye(4)
pose[:3, :3] = np.column_stack((right, down, forward))
pose[:3, 3] = eye
rendered = _project(
points,
colors,
pose,
np.array([[600, 0, 480], [0, 600, 320], [0, 0, 1]]),
(960, 640),
)
orbit.append(_label(rendered, "MapAnything multi-view geometry"))
video.write(folder / "orbit.mp4", orbit, 30)
cv2.imwrite(
str(folder / "overview.png"), cv2.cvtColor(orbit[10], cv2.COLOR_RGB2BGR)
)
(folder / "metrics.json").write_text(
json.dumps({"depth_range_m": depth_range.tolist(), "views": metrics}, indent=2)
+ "\n"
)

Xet Storage Details

Size:
5.78 kB
·
Xet hash:
0f40707a011d22a6a52352a6cecc24b05076b8f7a68847677185c43718de5631

Xet efficiently stores files, intelligently splitting them into unique chunks and accelerating uploads and downloads. More info.