Lagrange
Loading...
Searching...
No Matches
shared_utils.h
1/*
2 * Copyright 2025 Adobe. All rights reserved.
3 * This file is licensed to you under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License. You may obtain a copy
5 * of the License at http://www.apache.org/licenses/LICENSE-2.0
6 *
7 * Unless required by applicable law or agreed to in writing, software distributed under
8 * the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTATIONS
9 * OF ANY KIND, either express or implied. See the License for the specific language
10 * governing permissions and limitations under the License.
11 */
12#pragma once
13
14// NOTE: These shared utils are used in our CLI examples and Python bindings. They depend on the
15// lagrange::scene module. But we do not want to create a strong dependency between
16// lagrange::texproc and lagrange::scene, so this file is included directly via relative path in the
17// examples and Python bindings C++ files. To avoid confusion with internal src/ files, we place
18// this file in a separate "shared/" folder.
19
20#include <lagrange/Logger.h>
21#include <lagrange/scene/internal/shared_utils.h>
22#include <lagrange/texproc/TextureRasterizer.h>
23#include <lagrange/utils/fmt/format.h>
24
25#include <tbb/parallel_for.h>
26
27namespace lagrange::texproc {
28using scene::internal::Array3Df;
29using scene::internal::ConstView3Df;
30using scene::internal::View3Df;
31
46template <typename Scalar, typename Index>
47std::vector<std::pair<Array3Df, Array3Df>> rasterize_textures_from_renders(
48 const SurfaceMesh<Scalar, Index>& mesh,
49 std::optional<Array3Df> base_texture,
50 const std::vector<CameraTransforms>& cameras,
51 const std::vector<ConstView3Df>& renders,
52 const std::optional<size_t> tex_width,
53 const std::optional<size_t> tex_height,
54 const float low_confidence_ratio,
55 const std::optional<float> base_confidence)
56{
57 la_runtime_assert(!renders.empty(), "No rendered images to unproject");
58 for (const auto& render : renders) {
59 size_t img_width = render.extent(0);
60 size_t img_height = render.extent(1);
61 size_t img_channels = render.extent(2);
62 la_runtime_assert(img_width == renders.front().extent(0), "Render width must all be equal");
64 img_height == renders.front().extent(1),
65 "Render height must all be equal");
67 img_channels == renders.front().extent(2),
68 "Render num channels must all be equal");
69 }
71 renders.size() == cameras.size(),
72 "Number of renders must match number of cameras");
73
74 std::vector<std::pair<Array3Df, Array3Df>> textures_and_weights;
75
76 // Use base texture with a low confidence
77 if (base_confidence.has_value() && base_confidence.value() == 0) {
78 if (base_texture.has_value()) {
79 lagrange::logger().warn("Base confidence is 0, ignoring provided base texture.");
80 }
81 } else {
82 if (base_texture.has_value()) {
83 const float default_confidence = base_confidence.value_or(0.3f);
84 lagrange::logger().info(
85 "Using base texture with uniform confidence: {}",
86 default_confidence);
87 const auto base_image = base_texture.value();
89 base_image.extent(0),
90 base_image.extent(1),
91 1);
92 for (size_t i = 0; i < base_weights.extent(0); ++i) {
93 for (size_t j = 0; j < base_weights.extent(1); ++j) {
94 base_weights(i, j, 0) = default_confidence;
95 }
96 }
97 textures_and_weights.emplace_back(base_image, std::move(base_weights));
98 } else {
99 if (base_confidence.has_value()) {
100 lagrange::logger().warn(
101 "No base texture was provided. Ignoring user-provided base confidence: {}",
102 base_confidence.value());
103 }
104 }
105 }
106
107 // Check base texture size against expected size
108 TextureRasterizerOptions rasterizer_options;
109 if (textures_and_weights.empty()) {
110 rasterizer_options.width = tex_width.value_or(1024);
111 rasterizer_options.height = tex_height.value_or(1024);
112 lagrange::logger().info(
113 "No base texture found. Using rasterization size: {}x{}",
114 rasterizer_options.width,
115 rasterizer_options.height);
116 } else {
117 const auto& base_image = textures_and_weights.front().first;
118 la_runtime_assert(!tex_width.has_value() || base_image.extent(0) == tex_width.value());
119 la_runtime_assert(!tex_height.has_value() || base_image.extent(1) == tex_height.value());
120 rasterizer_options.width = base_image.extent(0);
121 rasterizer_options.height = base_image.extent(1);
123 renders.front().extent(2) == base_image.extent(2),
124 format(
125 "Input render image num channels (={}) must match base texture num channels "
126 "(={})",
127 renders.front().extent(2),
128 base_image.extent(2)));
129 lagrange::logger().info(
130 "Using base texture size for rasterization: {}x{}",
131 rasterizer_options.width,
132 rasterizer_options.height);
133 }
134
135 // Unproject render in texture space and generate confidence map for each camera
136 size_t offset = textures_and_weights.size();
137 textures_and_weights.resize(offset + cameras.size());
138 const TextureRasterizer<Scalar, Index> rasterizer(mesh, rasterizer_options);
139 lagrange::logger().info("Computing confidence maps for {} cameras", cameras.size());
140 tbb::parallel_for(size_t(0), cameras.size(), [&](size_t i) {
141 textures_and_weights[offset + i] =
142 rasterizer.weighted_texture_from_render(renders[i], cameras[i]);
143 });
144
145 // Filter confidence across all cameras at each pixel
146 lagrange::logger().info(
147 "Filtering low confidence values using ratio threshold: {}",
148 low_confidence_ratio);
149 filter_low_confidences(textures_and_weights, low_confidence_ratio);
150
151 return textures_and_weights;
152}
153
159template <typename Scalar, typename Index>
160std::vector<std::pair<Array3Df, Array3Df>> rasterize_textures_from_renders(
161 const lagrange::scene::Scene<Scalar, Index>& scene,
162 std::optional<Array3Df> base_texture_override,
163 const std::vector<ConstView3Df>& renders,
164 const std::optional<size_t> tex_width,
165 const std::optional<size_t> tex_height,
166 const float low_confidence_ratio,
167 const std::optional<float> base_confidence)
168{
169 auto [mesh, base_texture] = scene::internal::single_mesh_from_scene(scene);
170 auto cameras = scene::internal::camera_transforms_from_scene(scene);
171 lagrange::logger().info("Found {} cameras in the input scene", cameras.size());
172
173 if (base_texture_override.has_value()) {
174 if (base_texture.has_value()) {
175 lagrange::logger().warn(
176 "Input scene already contains a base texture. Overriding with user-provided "
177 "texture.");
178 }
179 base_texture = std::move(base_texture_override);
180 }
181
182 return rasterize_textures_from_renders(
183 mesh,
184 std::move(base_texture),
185 cameras,
186 renders,
187 tex_width,
188 tex_height,
189 low_confidence_ratio,
190 base_confidence);
191}
192
193} // namespace lagrange::texproc
Given a mesh with UVs, unproject rendered images into a UV texture and confidence map.
Definition TextureRasterizer.h:56
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:175
Array3D< T > create_image(size_t width, size_t height, size_t num_channels)
Create an image with the given dimensions and number of channels.
Definition Array3D.h:46
void filter_low_confidences(span< std::pair< image::experimental::Array3D< float >, image::experimental::Array3D< float > > > textures_and_weights, float low_ratio_threshold)
Discard low-confidence values.
Options for computing the texture map and confidence from a rendering.
Definition TextureRasterizer.h:28