Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 3 additions & 7 deletions apps/src/ReorderTexture.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -335,16 +335,12 @@ auto main(int argc, char* argv[]) -> int
auto reader = graph.insertNode<MeshReadNode>();
reader->path = inputPath;

// We don't support RGBA textures
auto convert = graph.insertNode<ColorConvertNode>();
convert->imageIn = reader->image;
convert->channels = 3;

// Reorder the texture
// Reorder the texture. ReorderUnorganizedTexture normalizes each input
// image to 8-bit, 3-channel internally, so no ColorConvertNode is needed.
auto reorder = graph.insertNode<ReorderTextureNode>();
reorder->meshIn = reader->mesh;
reorder->uvMapIn = reader->uvMap;
reorder->imageIn = convert->imageOut;
reorder->imagesIn = reader->images;
reorder->samplingOrigin = samplingOrigin;
reorder->samplingMode = sampleMode;
reorder->sampleRate = sampleRate;
Expand Down
2 changes: 1 addition & 1 deletion apps/src/SeamFlattening.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -364,7 +364,7 @@ auto main(int argc, const char* argv[]) -> int
ReorderUnorganizedTexture reorder;
reorder.setMesh(flat);
reorder.setUVMap(reader.uvMap);
reorder.setTextureMat(reader.texture);
reorder.setTextureMats(reader.textures);
reorder.setSamplingMode(ReorderUnorganizedTexture::SamplingMode::AutoUV);
const auto texture = reorder.compute();

Expand Down
2 changes: 1 addition & 1 deletion apps/src/TextureDewarp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,7 +128,7 @@ auto main(int argc, const char* argv[]) -> int
ReorderUnorganizedTexture reorder;
reorder.setMesh(flat);
reorder.setUVMap(reader.uvMap);
reorder.setTextureMat(reader.texture);
reorder.setTextureMats(reader.textures);
reorder.setSamplingMode(ReorderUnorganizedTexture::SamplingMode::AutoUV);
const auto texture = reorder.compute();

Expand Down
53 changes: 42 additions & 11 deletions core/include/rt/ReorderUnorganizedTexture.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

#include <optional>
#include <string>
#include <vector>

#include <opencv2/core.hpp>

Expand Down Expand Up @@ -107,8 +108,27 @@ class ReorderUnorganizedTexture
void setMesh(const Mesh::Pointer& mesh);
/** @brief Set the input UV map for the mesh */
void setUVMap(const UVMap& uv);
/** @brief Set the input, unorganized texture image */
void setTextureMat(const cv::Mat& img);

/**
* @brief Set the input, unorganized texture images
*
* The mesh may be textured by more than one image (a multi-chart UV map,
* e.g. a multi-material OBJ). Images are indexed by UV chart: the color for
* a face is sampled from `imgs[chart]`, where `chart` is the atlas chart
* index carried by the face's UV coordinates (see rt::UVMap). A
* single-texture mesh is simply the one-element case (all faces chart 0).
*
* Faces whose chart has no corresponding image (chart index out of range or
* an empty `cv::Mat`) are left uncolored in the output; compute() emits a
* single warning naming the affected chart(s).
*
* @note Each image is normalized to 8-bit, 3-channel (BGR) on input via
* rt::QuantizeImage + rt::ColorConvertImage. Higher bit depths and other
* channel layouts are not yet preserved through the reorder pipeline; see
* https://github.com/educelab/registration-toolkit/issues/19 for the
* tracking issue on native multi-bit-depth/channel support.
*/
void setTextureMats(const std::vector<cv::Mat>& imgs);

/** @copydoc samplingOrigin() */
void setSamplingOrigin(SamplingOrigin o);
Expand Down Expand Up @@ -193,9 +213,6 @@ class ReorderUnorganizedTexture
/** @brief Get the output UV map */
auto getUVMap() -> UVMap;

/** @brief Get the output texture image */
auto getTextureMat() -> cv::Mat;

/**
* @brief Get depth map
*
Expand Down Expand Up @@ -225,19 +242,33 @@ class ReorderUnorganizedTexture
void create_texture_camera_();

/**
* Bilinearly sample the input texture color for a ray hit on face @p cellId
* with barycentric intersection (@p interU, @p interV). Assumes the input
* texture is non-empty.
* Resolve the input texture image a face samples from. Returns the image
* for the face's UV chart, or nullptr if that chart has no usable image
* (chart index out of range or an empty image); the offending chart index
* is recorded in missingCharts_ for a single aggregated warning.
*/
[[nodiscard]] auto resolve_chart_image_(std::size_t cellId) const
-> const cv::Mat*;

/**
* Bilinearly sample @p img for a ray hit on face @p cellId with barycentric
* intersection (@p interU, @p interV). Assumes @p img is non-empty.
*/
[[nodiscard]] auto sample_surface_color_(
std::size_t cellId, double interU, double interV) const -> cv::Vec3b;
const cv::Mat& img, std::size_t cellId, double interU, double interV)
const -> cv::Vec3b;

/** Emit one aggregated warning for charts with no usable image, if any */
void report_missing_charts_() const;

/** Input mesh */
Mesh::Pointer inputMesh_;
/** Input UV map */
UVMap inputUV_;
/** Input texture image */
cv::Mat inputTexture_;
/** Input texture images, indexed by UV chart */
std::vector<cv::Mat> inputTextures_;
/** Chart indices encountered with no usable image (for warning) */
mutable std::vector<std::size_t> missingCharts_;

/** Sample origin */
SamplingOrigin sampleOrigin_{SamplingOrigin::TopLeft};
Expand Down
15 changes: 11 additions & 4 deletions core/include/rt/io/MeshIO.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/** @file */

#include <filesystem>
#include <vector>

#include <opencv2/core.hpp>

Expand All @@ -18,10 +19,16 @@ struct MeshReadResult {
Mesh::Pointer mesh;
/** Loaded UV map (empty if the file had no texture coordinates) */
UVMap uvMap;
/** Loaded texture image (empty if no texture was referenced/found) */
cv::Mat texture;
/** Resolved path to the texture image (empty if none) */
std::filesystem::path texturePath;
/**
* Loaded texture images, one per referenced material, indexed by UV chart.
* A referenced-but-missing image is kept as an empty `cv::Mat` so the vector
* stays aligned with the UV map's chart indices (chart i ↔ textures[i]).
* Empty when the file references no textures. Consumers that expect a single
* texture should use `textures.front()` (guarding on `textures.empty()`).
*/
std::vector<cv::Mat> textures;
/** Resolved paths to the texture images, aligned with @ref textures */
std::vector<std::filesystem::path> texturePaths;
};

/**
Expand Down
25 changes: 20 additions & 5 deletions core/src/MeshIO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,15 +104,30 @@ auto rt::ReadMesh(const fs::path& path) -> rt::MeshReadResult
// Flip v to the in-memory top-left invariant
result.uvMap = FlipV(result.uvMap);

// Resolve and load the first referenced texture, if any
if (not texturePaths.empty()) {
fs::path texPath = path.parent_path() / texturePaths.front().string();
// Resolve and load every referenced texture, keeping the vectors aligned
// with the UV map's chart indices (chart i ↔ textures[i]). A chart whose
// material declares no map_Kd (empty path) or whose image is missing is
// stored as an empty cv::Mat / empty path so alignment is preserved; the
// reorder step no-ops on empty charts.
result.textures.reserve(texturePaths.size());
result.texturePaths.reserve(texturePaths.size());
for (const auto& rel : texturePaths) {
// A material without a map_Kd comes through as an empty path. Keep the
// slot empty rather than resolving it against the OBJ's parent dir.
if (rel.empty()) {
result.texturePaths.emplace_back();
result.textures.emplace_back();
continue;
}
fs::path texPath = path.parent_path() / rel.string();
if (fs::exists(texPath)) {
result.texturePath = texPath;
result.texture = rt::ReadImage(texPath);
result.texturePaths.push_back(texPath);
result.textures.push_back(rt::ReadImage(texPath));
} else {
rt::logger()->warn(
"Referenced texture not found: {}", texPath.string());
result.texturePaths.emplace_back();
result.textures.emplace_back();
}
}

Expand Down
Loading
Loading