Add Metashape support (#989)

Add metashape support
This commit is contained in:
Matthew Tancik authored and GitHub committed 2022-11-20 14:55:41 -08:00
1 parent b829499c12
commit 4065bb4733
2 files changed
+206

No files matched your search

+113
View File
@@ -0,0 +1,113 @@
# Copyright 2022 The Nerfstudio Team. All rights reserved.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
"""Helper utils for processing metashape data into the nerfstudio format."""
import json
import xml.etree.ElementTree as ET
from pathlib import Path
from typing import Dict
import numpy as np
from rich.console import Console
from nerfstudio.process_data.process_data_utils import CAMERA_MODELS
CONSOLE = Console(width=120)
def _find_distortion_param(calib_xml: ET.Element, param_name: str):
param = calib_xml.find(param_name)
if param is not None:
return float(param.text) # type: ignore
return 0.0
def metashape_to_json(
image_filename_map: Dict[str, Path],
xml_filename: Path,
output_dir: Path,
) -> str:
"""Convert Metashape data into a nerfstudio dataset.
Args:
image_filename_map: Mapping of original image filenames to their saved locations.
xml_filename: Path to the metashap cameras xml file.
output_dir: Path to the output directory.
Returns:
Summary of the conversion.
"""
xml_tree = ET.parse(xml_filename)
root = xml_tree.getroot()
chunk = root[0]
sensors = chunk.find("sensors")
# TODO Add support for per-frame intrinsics
if sensors is None or len(sensors) != 1:
raise ValueError("Only one sensor is supported for now")
sensor = sensors.find("sensor")
data = {}
assert sensor is not None, "Sensor not found in Metashape XML"
resolution = sensor.find("resolution")
assert resolution is not None, "Resolution not found in Metashape xml"
data["w"] = int(resolution.get("width")) # type: ignore
data["h"] = int(resolution.get("height")) # type: ignore
calib = sensor.find("calibration")
assert calib is not None, "Calibration not found in Metashape xml"
data["fl_x"] = float(calib.find("f").text) # type: ignore
data["fl_y"] = float(calib.find("f").text) # type: ignore
data["cx"] = float(calib.find("cx").text) + data["w"] / 2.0 # type: ignore
data["cy"] = float(calib.find("cy").text) + data["h"] / 2.0 # type: ignore
data["k1"] = _find_distortion_param(calib, "k1")
data["k2"] = _find_distortion_param(calib, "k2")
data["k3"] = _find_distortion_param(calib, "k3")
data["k4"] = _find_distortion_param(calib, "k4")
data["p1"] = _find_distortion_param(calib, "p1")
data["p2"] = _find_distortion_param(calib, "p2")
data["camera_model"] = CAMERA_MODELS["perspective"].value
frames = []
cameras = chunk.find("cameras")
assert cameras is not None, "Cameras not found in Metashape xml"
for camera in cameras:
frame = {}
frame["file_path"] = image_filename_map[camera.get("label")].as_posix() # type: ignore
t = [float(x) for x in camera.find("transform").text.split()] # type: ignore
transform = np.array(
[
[t[8], -t[9], -t[10], t[11]],
[t[0], -t[1], -t[2], t[3]],
[t[4], -t[5], -t[6], t[7]],
[t[12], -t[13], -t[14], t[15]],
]
)
frame["transform_matrix"] = transform.tolist()
frames.append(frame)
data["frames"] = frames
with open(output_dir / "transforms.json", "w", encoding="utf-8") as f:
json.dump(data, f, indent=4)
summary = f"Final dataset is {len(data['frames'])} frames."
return summary