- Implement sync_models.py for preparing and uploading ONNX artifacts. - Create build_node.py for building and validating Blender scripts. - Add docs.py for building and previewing documentation with Mike or MkDocs. - Introduce model_exports.py for exporting PyTorch models to ONNX format. - Develop pack.py for packaging the project with dependencies for different platforms. - Add test_sync_models.py to validate sync_models functionality. - Add test_tool_commands.py to ensure repository tools have correct command scripts. - Initialize tools package with __init__.py.
81 lines
2.9 KiB
Python
81 lines
2.9 KiB
Python
"""Load and scale Depth file artifacts shared by Blender workflows."""
|
|
|
|
import json
|
|
from pathlib import Path
|
|
|
|
import bpy
|
|
|
|
from .image_data import depth_data_name, image_empty_bounds
|
|
|
|
|
|
def load_depth_metadata(path):
|
|
"""Read and validate the compact camera metadata beside a Depth texture."""
|
|
metadata = json.loads(Path(path).read_text(encoding="utf-8"))
|
|
try:
|
|
image_size = tuple(int(value) for value in metadata["image_size"])
|
|
intrinsics = tuple(
|
|
tuple(float(value) for value in row)
|
|
for row in metadata["intrinsics"]
|
|
)
|
|
reference_depth = float(metadata["reference_depth"])
|
|
except (KeyError, TypeError, ValueError) as error:
|
|
raise ValueError("Depth metadata is invalid") from error
|
|
if (
|
|
len(image_size) != 2
|
|
or min(image_size) <= 0
|
|
or len(intrinsics) != 3
|
|
or any(len(row) != 3 for row in intrinsics)
|
|
or intrinsics[0][0] <= 0.0
|
|
or intrinsics[1][1] <= 0.0
|
|
or reference_depth <= 0.0
|
|
):
|
|
raise ValueError("Depth metadata is invalid")
|
|
return {
|
|
"image_size": image_size,
|
|
"intrinsics": intrinsics,
|
|
"reference_depth": reference_depth,
|
|
}
|
|
|
|
|
|
def load_depth_result_image(path, source_object, metadata):
|
|
"""Load, validate, name, configure, and pack a generated Depth texture."""
|
|
image = bpy.data.images.load(str(path), check_existing=False)
|
|
try:
|
|
if tuple(int(value) for value in image.size) != metadata["image_size"]:
|
|
raise ValueError("Depth texture dimensions do not match its metadata")
|
|
image.name = depth_data_name(source_object)
|
|
image.colorspace_settings.name = "Non-Color"
|
|
image.alpha_mode = "CHANNEL_PACKED"
|
|
image.pack()
|
|
return image
|
|
except Exception:
|
|
bpy.data.images.remove(image, do_unlink=True)
|
|
raise
|
|
|
|
|
|
def camera_depth_values(image):
|
|
"""Return top-down camera Z and validity from a loaded Depth texture."""
|
|
import numpy as np
|
|
|
|
width, height = (int(value) for value in image.size)
|
|
pixels = np.empty(width * height * 4, dtype=np.float32)
|
|
image.pixels.foreach_get(pixels)
|
|
pixels = np.flipud(pixels.reshape((height, width, 4)))
|
|
return pixels[..., 2], pixels[..., 3] > 0.5
|
|
|
|
|
|
def depth_uniform_scale(source_object, metadata, bounds=None):
|
|
"""Convert model camera units uniformly into Image Empty local distance."""
|
|
import numpy as np
|
|
|
|
intrinsics = metadata["intrinsics"]
|
|
width, height = metadata["image_size"]
|
|
reference_depth = metadata["reference_depth"]
|
|
x_min, x_max, y_min, y_max = bounds or image_empty_bounds(source_object)
|
|
plane_width = x_max - x_min
|
|
plane_height = y_max - y_min
|
|
fx, fy = float(intrinsics[0][0]), float(intrinsics[1][1])
|
|
scale_x = plane_width * fx / (max(width - 1, 1) * reference_depth)
|
|
scale_y = plane_height * fy / (max(height - 1, 1) * reference_depth)
|
|
return float(np.sqrt(scale_x * scale_y))
|