14#include <lagrange/AttributeValueType.h>
15#include <lagrange/Logger.h>
16#include <lagrange/image/Array3D.h>
17#include <lagrange/image/View3D.h>
18#include <lagrange/scene/Scene.h>
19#include <lagrange/scene/scene_utils.h>
20#include <lagrange/texproc/TextureRasterizer.h>
21#include <lagrange/transform_mesh.h>
23#include <tbb/parallel_for.h>
31namespace lagrange::texproc {
33using Array3Df = image::experimental::Array3D<float>;
34using View3Df = image::experimental::View3D<float>;
36Array3Df convert_from(
const scene::ImageBufferExperimental& image)
38 size_t nc = std::min(image.num_channels,
size_t(3));
41 auto copy_buffer = [&](
auto scalar) {
42 using T = std::decay_t<
decltype(scalar)>;
43 constexpr bool IsChar = std::is_integral_v<T> &&
sizeof(T) == 1;
45 auto rawbuf =
reinterpret_cast<const T*
>(image.data.data());
46 for (
size_t y = 0, i = 0; y < image.height; ++y) {
47 for (
size_t x = 0; x < image.width; ++x) {
48 for (
size_t c = 0; c < image.num_channels; ++c) {
53 if constexpr (IsChar) {
54 result(x, y, c) =
static_cast<float>(rawbuf[i++]) / 255.f;
56 result(x, y, c) = rawbuf[i++];
63 switch (image.element_type) {
64 case AttributeValueType::e_uint8_t: copy_buffer(uint8_t());
break;
65 case AttributeValueType::e_int8_t: copy_buffer(int8_t());
break;
66 case AttributeValueType::e_uint32_t: copy_buffer(uint32_t());
break;
67 case AttributeValueType::e_int32_t: copy_buffer(int32_t());
break;
68 case AttributeValueType::e_float: copy_buffer(
float());
break;
69 case AttributeValueType::e_double: copy_buffer(
double());
break;
70 default:
throw std::runtime_error(
"Unsupported image scalar type");
77template <
typename Scalar,
typename Index>
78std::tuple<SurfaceMesh<Scalar, Index>, std::optional<Array3Df>> single_mesh_from_scene(
79 const scene::Scene<Scalar, Index>& scene)
81 using ElementId = scene::ElementId;
84 std::vector<ElementId> mesh_node_ids;
85 for (ElementId node_id = 0; node_id < scene.nodes.size(); ++node_id) {
86 const auto& node = scene.nodes[node_id];
87 if (!node.meshes.empty()) {
88 mesh_node_ids.push_back(node_id);
92 if (mesh_node_ids.size() != 1) {
93 throw std::runtime_error(
95 "Input scene contains {} mesh nodes. Expected exactly 1 mesh node.",
96 mesh_node_ids.size()));
98 const auto& mesh_node = scene.nodes[mesh_node_ids.front()];
100 if (mesh_node.meshes.size() != 1) {
101 throw std::runtime_error(
103 "Input scene has a mesh node with {} instance per node. Expected "
104 "exactly 1 instance per node",
105 mesh_node.meshes.size()));
107 const auto& mesh_instance = mesh_node.meshes.front();
109 [[maybe_unused]]
const auto mesh_id = mesh_instance.mesh;
111 SurfaceMesh<Scalar, Index> mesh = scene.meshes[mesh_instance.mesh];
114 auto world_from_mesh =
115 scene::utils::compute_global_node_transform(scene, mesh_node_ids.front())
121 if (
auto num_mats = mesh_instance.materials.size(); num_mats != 1) {
123 "Mesh node has {} materials. Expected exactly 1 material. Ignoring materials.",
125 return {mesh, std::nullopt};
127 const auto& material = scene.materials[mesh_instance.materials.front()];
128 if (material.base_color_texture.texcoord != 0) {
130 "Mesh node material texcoord is {} != 0. Expected 0. Ignoring texcoord.",
131 material.base_color_texture.texcoord);
133 const auto texture_id = material.base_color_texture.index;
135 const auto& texture = scene.textures[texture_id];
137 const auto image_id = texture.image;
139 const auto& image_ = scene.images[image_id].image;
140 Array3Df image = convert_from(image_);
142 return {mesh, image};
145template <
typename Scalar,
typename Index>
146std::vector<CameraOptions> cameras_from_scene(
const scene::Scene<Scalar, Index>& scene)
148 using ElementId = scene::ElementId;
151 std::vector<CameraOptions> cameras;
152 for (ElementId node_id = 0; node_id < scene.nodes.size(); ++node_id) {
153 using namespace scene::utils;
154 const auto& node = scene.nodes[node_id];
155 if (!node.cameras.empty()) {
156 auto world_from_node = compute_global_node_transform(scene, node_id);
157 for (
auto camera_id : node.cameras) {
158 const auto& scene_camera = scene.cameras[camera_id];
160 camera.
view_transform = camera_view_transform(scene_camera, world_from_node);
161 camera.projection_transform = camera_projection_transform(scene_camera);
162 cameras.push_back(camera);
170template <
typename Scalar,
typename Index>
171std::vector<std::pair<Array3Df, Array3Df>> rasterize_textures_from_renders(
172 const lagrange::scene::Scene<Scalar, Index>& scene,
173 std::optional<Array3Df> base_texture_in,
174 const std::vector<View3Df>& renders,
175 const std::optional<size_t> tex_width,
176 const std::optional<size_t> tex_height,
177 const float low_confidence_ratio,
178 const std::optional<float> base_confidence)
181 auto [mesh, base_texture] = single_mesh_from_scene(scene);
182 auto cameras = cameras_from_scene(scene);
183 lagrange::logger().info(
"Found {} cameras in the input scene", cameras.size());
185 if (base_texture_in.has_value()) {
186 if (base_texture.has_value()) {
188 "Input scene already contains a base texture. Overriding with user-provided "
191 base_texture = std::move(base_texture_in);
195 for (
const auto& render : renders) {
196 size_t img_width = render.extent(0);
197 size_t img_height = render.extent(1);
198 la_runtime_assert(img_width == renders.front().extent(0),
"Render width must all be equal");
200 img_height == renders.front().extent(1),
201 "Render height must all be equal");
204 renders.size() == cameras.size(),
205 "Number of renders must match number of cameras");
207 std::vector<std::pair<Array3Df, Array3Df>> textures_and_weights;
210 if (base_confidence.has_value() && base_confidence.value() == 0) {
211 if (base_texture.has_value()) {
213 "Base confidence is 0, ignoring base texture in the input scene.");
216 if (base_texture.has_value()) {
217 const float default_confidence = base_confidence.value_or(0.3f);
219 "Using base texture with uniform confidence: {}",
221 const auto base_image = base_texture.value();
223 base_image.extent(0),
224 base_image.extent(1),
226 for (
size_t i = 0; i < base_weights.extent(0); ++i) {
227 for (
size_t j = 0; j < base_weights.extent(1); ++j) {
228 base_weights(i, j, 0) = default_confidence;
231 textures_and_weights.emplace_back(base_image, std::move(base_weights));
233 if (base_confidence.has_value()) {
235 "No base texture was found in the input scene. Ignoring user-provided base "
237 base_confidence.value());
244 if (textures_and_weights.empty()) {
245 rasterizer_options.width = tex_width.value_or(1024);
246 rasterizer_options.height = tex_height.value_or(1024);
248 "No base texture found. Using rasterization size: {}x{}",
249 rasterizer_options.width,
250 rasterizer_options.height);
252 const auto& base_image = textures_and_weights.front().first;
253 la_runtime_assert(!tex_width.has_value() || base_image.extent(0) == tex_width.value());
254 la_runtime_assert(!tex_height.has_value() || base_image.extent(1) == tex_height.value());
255 rasterizer_options.width = base_image.extent(0);
256 rasterizer_options.height = base_image.extent(1);
258 "Using base texture size for rasterization: {}x{}",
259 rasterizer_options.width,
260 rasterizer_options.height);
264 size_t offset = textures_and_weights.size();
265 textures_and_weights.resize(offset + cameras.size());
267 lagrange::logger().info(
"Computing confidence maps for {} cameras", cameras.size());
268 tbb::parallel_for(
size_t(0), cameras.size(), [&](
size_t i) {
269 textures_and_weights[offset + i] =
270 rasterizer.weighted_texture_from_render(renders[i], cameras[i]);
275 "Filtering low confidence values using ratio threshold: {}",
276 low_confidence_ratio);
279 return textures_and_weights;
Given a mesh with UVs, unproject rendered images into a UV texture and confidence map.
Definition TextureRasterizer.h:72
LA_CORE_API spdlog::logger & logger()
Retrieves the current logger.
Definition Logger.cpp:40
SurfaceMesh< ToScalar, ToIndex > cast(const SurfaceMesh< FromScalar, FromIndex > &source_mesh, const AttributeFilter &convertible_attributes={}, std::vector< std::string > *converted_attributes_names=nullptr)
Cast a mesh to a mesh of different scalar and/or index type.
void transform_mesh(SurfaceMesh< Scalar, Index > &mesh, const Eigen::Transform< Scalar, Dimension, Eigen::Affine > &transform, const TransformOptions &options={})
Apply an affine transform to a mesh in-place.
Definition transform_mesh.cpp:198
#define la_runtime_assert(...)
Runtime assertion check.
Definition assert.h:174
#define la_debug_assert(...)
Debug assertion check.
Definition assert.h:194
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.
Parameters for computing the rendering of a mesh.
Definition TextureRasterizer.h:29
Eigen::Affine3f view_transform
Camera view transform (world space -> view space).
Definition TextureRasterizer.h:31
Options for computing the texture map and confidence from a rendering.
Definition TextureRasterizer.h:44