"""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))