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>;
37Array3Df convert_from(
const scene::ImageBufferExperimental& image)
39 size_t nc = std::min(image.num_channels,
size_t(3));
42 auto copy_buffer = [&](
auto scalar) {
43 using T = std::decay_t<
decltype(scalar)>;
44 constexpr bool IsChar = std::is_integral_v<T> &&
sizeof(T) == 1;
46 auto rawbuf =
reinterpret_cast<const T*
>(image.data.data());
47 for (
size_t y = 0, i = 0; y < image.height; ++y) {
48 for (
size_t x = 0; x < image.width; ++x) {
49 for (
size_t c = 0; c < image.num_channels; ++c) {
54 if constexpr (IsChar) {
55 result(x, y, c) =
static_cast<float>(rawbuf[i++]) / 255.f;
57 result(x, y, c) = rawbuf[i++];
64 switch (image.element_type) {
65 case AttributeValueType::e_uint8_t: copy_buffer(uint8_t());
break;
66 case AttributeValueType::e_int8_t: copy_buffer(int8_t());
break;
67 case AttributeValueType::e_uint32_t: copy_buffer(uint32_t());
break;
68 case AttributeValueType::e_int32_t: copy_buffer(int32_t());
break;
69 case AttributeValueType::e_float: copy_buffer(
float());
break;
70 case AttributeValueType::e_double: copy_buffer(
double());
break;
71 default:
throw std::runtime_error(
"Unsupported image scalar type");
78template <
typename Scalar,
typename Index>
79std::tuple<SurfaceMesh<Scalar, Index>, std::optional<Array3Df>> single_mesh_from_scene(
80 const scene::Scene<Scalar, Index>& scene)
82 using ElementId = scene::ElementId;
85 std::vector<ElementId> mesh_node_ids;
86 for (ElementId node_id = 0; node_id < scene.nodes.size(); ++node_id) {
87 const auto& node = scene.nodes[node_id];
88 if (!node.meshes.empty()) {
89 mesh_node_ids.push_back(node_id);
93 if (mesh_node_ids.size() != 1) {
94 throw std::runtime_error(
96 "Input scene contains {} mesh nodes. Expected exactly 1 mesh node.",
97 mesh_node_ids.size()));
99 const auto& mesh_node = scene.nodes[mesh_node_ids.front()];
101 if (mesh_node.meshes.size() != 1) {
102 throw std::runtime_error(
104 "Input scene has a mesh node with {} instance per node. Expected "
105 "exactly 1 instance per node",
106 mesh_node.meshes.size()));
108 const auto& mesh_instance = mesh_node.meshes.front();
110 [[maybe_unused]]
const auto mesh_id = mesh_instance.mesh;
112 SurfaceMesh<Scalar, Index> mesh = scene.meshes[mesh_instance.mesh];
115 auto world_from_mesh =
116 scene::utils::compute_global_node_transform(scene, mesh_node_ids.front())
122 if (
auto num_mats = mesh_instance.materials.size(); num_mats != 1) {
124 "Mesh node has {} materials. Expected exactly 1 material. Ignoring materials.",
126 return {mesh, std::nullopt};
128 const auto& material = scene.materials[mesh_instance.materials.front()];
129 if (material.base_color_texture.texcoord != 0) {
131 "Mesh node material texcoord is {} != 0. Expected 0. Ignoring texcoord.",
132 material.base_color_texture.texcoord);
134 const auto texture_id = material.base_color_texture.index;
136 const auto& texture = scene.textures[texture_id];
138 const auto image_id = texture.image;
140 const auto& image_ = scene.images[image_id].image;
141 Array3Df image = convert_from(image_);
143 return {mesh, image};
146template <
typename Scalar,
typename Index>
147std::vector<CameraOptions> cameras_from_scene(
const scene::Scene<Scalar, Index>& scene)
149 using ElementId = scene::ElementId;
152 std::vector<CameraOptions> cameras;
153 for (ElementId node_id = 0; node_id < scene.nodes.size(); ++node_id) {
154 using namespace scene::utils;
155 const auto& node = scene.nodes[node_id];
156 if (!node.cameras.empty()) {
157 auto world_from_node = compute_global_node_transform(scene, node_id);
158 for (
auto camera_id : node.cameras) {
159 const auto& scene_camera = scene.cameras[camera_id];
161 camera.
view_transform = camera_view_transform(scene_camera, world_from_node);
162 camera.projection_transform = camera_projection_transform(scene_camera);
163 cameras.push_back(camera);
171template <
typename Scalar,
typename Index>
172std::vector<std::pair<Array3Df, Array3Df>> rasterize_textures_from_renders(
173 const lagrange::scene::Scene<Scalar, Index>& scene,
174 std::optional<Array3Df> base_texture_in,
175 const std::vector<View3Df>& renders,
176 const std::optional<size_t> tex_width,
177 const std::optional<size_t> tex_height,
178 const float low_confidence_ratio,
179 const std::optional<float> base_confidence)
182 auto [mesh, base_texture] = single_mesh_from_scene(scene);
183 auto cameras = cameras_from_scene(scene);
184 lagrange::logger().info(
"Found {} cameras in the input scene", cameras.size());
186 if (base_texture_in.has_value()) {
187 if (base_texture.has_value()) {
189 "Input scene already contains a base texture. Overriding with user-provided "
192 base_texture = std::move(base_texture_in);
197 for (
const auto& render : renders) {
198 size_t img_width = render.extent(0);
199 size_t img_height = render.extent(1);
200 size_t img_channels = render.extent(2);
201 la_runtime_assert(img_width == renders.front().extent(0),
"Render width must all be equal");
203 img_height == renders.front().extent(1),
204 "Render height must all be equal");
206 img_channels == renders.front().extent(2),
207 "Render num channels must all be equal");
210 renders.size() == cameras.size(),
211 "Number of renders must match number of cameras");
213 std::vector<std::pair<Array3Df, Array3Df>> textures_and_weights;
216 if (base_confidence.has_value() && base_confidence.value() == 0) {
217 if (base_texture.has_value()) {
219 "Base confidence is 0, ignoring base texture in the input scene.");
222 if (base_texture.has_value()) {
223 const float default_confidence = base_confidence.value_or(0.3f);
225 "Using base texture with uniform confidence: {}",
227 const auto base_image = base_texture.value();
229 base_image.extent(0),
230 base_image.extent(1),
232 for (
size_t i = 0; i < base_weights.extent(0); ++i) {
233 for (
size_t j = 0; j < base_weights.extent(1); ++j) {
234 base_weights(i, j, 0) = default_confidence;
237 textures_and_weights.emplace_back(base_image, std::move(base_weights));
239 if (base_confidence.has_value()) {
241 "No base texture was found in the input scene. Ignoring user-provided base "
243 base_confidence.value());
250 if (textures_and_weights.empty()) {
251 rasterizer_options.width = tex_width.value_or(1024);
252 rasterizer_options.height = tex_height.value_or(1024);
254 "No base texture found. Using rasterization size: {}x{}",
255 rasterizer_options.width,
256 rasterizer_options.height);
258 const auto& base_image = textures_and_weights.front().first;
259 la_runtime_assert(!tex_width.has_value() || base_image.extent(0) == tex_width.value());
260 la_runtime_assert(!tex_height.has_value() || base_image.extent(1) == tex_height.value());
261 rasterizer_options.width = base_image.extent(0);
262 rasterizer_options.height = base_image.extent(1);
264 renders.front().extent(2) == base_image.extent(2),
266 "Input render image num channels (={}) must match base texture num channels (={})",
267 renders.front().extent(2),
268 base_image.extent(2)));
270 "Using base texture size for rasterization: {}x{}",
271 rasterizer_options.width,
272 rasterizer_options.height);
276 size_t offset = textures_and_weights.size();
277 textures_and_weights.resize(offset + cameras.size());
279 lagrange::logger().info(
"Computing confidence maps for {} cameras", cameras.size());
280 tbb::parallel_for(
size_t(0), cameras.size(), [&](
size_t i) {
281 textures_and_weights[offset + i] =
282 rasterizer.weighted_texture_from_render(renders[i], cameras[i]);
287 "Filtering low confidence values using ratio threshold: {}",
288 low_confidence_ratio);
291 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:199
#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