保留全局颜色优化
This commit is contained in:
1 parent
9665d1226a
commit
4ae6404044
1 file changed
+663
-146
+663
-146
@@ -740,6 +740,18 @@ public:
|
||||
void CreateSeamVertices();
|
||||
uint32_t FindOrCreateComponentForFace(FIndex faceIdx);
|
||||
uint32_t FindNearestPatchForComponent(uint32_t compID);
|
||||
void RunGlobalColorOptimization(cv::Mat& atlas, int textureSize, float lambda);
|
||||
// ----------------------------------------------------------------------------
|
||||
// 3.3 把求解出的调整量应用到 atlas
|
||||
// —— 在三角形内用重心坐标插值(对齐 OpenMVS RasterPatch bary 插值)
|
||||
// 调整量从 3 个顶点 (顶点,patch) 的调整向量插值到每个像素,再加到 RGB 上
|
||||
// ----------------------------------------------------------------------------
|
||||
void ApplyColorAdjustments(cv::Mat& atlasMat, const Eigen::VectorXf& x, const Eigen::Index rowsX);
|
||||
void GlobalSeamLeveling2(Eigen::Index* pRowsX,
|
||||
Eigen::SparseMatrix<float>* pA,
|
||||
Eigen::VectorXf* pb,
|
||||
float lambda,
|
||||
cv::Mat& atlasMat);
|
||||
void GlobalSeamLeveling();
|
||||
void GlobalSeamLeveling3();
|
||||
void LocalSeamLeveling();
|
||||
@@ -762,6 +774,7 @@ public:
|
||||
);
|
||||
std::vector<uint32_t> faceToPatchID; // faceID → 新 patchID 映射
|
||||
void MergeSameViewPatches();
|
||||
void BuildSeamEdgesFromFaceToPatchID();
|
||||
void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize);
|
||||
bool RasterizeVirtualFaces(
|
||||
const VirtualFaceMap& virtualFaceMap,
|
||||
@@ -774,6 +787,15 @@ public:
|
||||
const VirtualFaceMap& virtualFaceMap,
|
||||
const std::vector<std::vector<IIndex>>& virtualFaceViews,
|
||||
int textureSize);
|
||||
// ----------------------------------------------------------------------------
|
||||
// 3.1 从 rcSeamEdges 构建 SeamVert(对齐 OpenMVS seamVertices)
|
||||
// 每个 seam edge 的两个端点(mesh 顶点)被收集;若一个顶点属于多个 patch
|
||||
// (即位于 patch 边界),则成为 SeamVert。
|
||||
// 同时为每个 (顶点, patch) 分配唯一的行号 rowsX —— 对齐 OpenMVS 的
|
||||
// vertpatch2rows(变量索引的核心)。
|
||||
// ----------------------------------------------------------------------------
|
||||
bool BuildSeamVertsFromEdges();
|
||||
bool BuildSeamVertsFromFaceAdjacency();
|
||||
void GlobalAlignPatches(Image8U3& atlas, int textureSize);
|
||||
void LocalBlendSeams(Image8U3& atlas, int textureSize);
|
||||
void FeatherTextureSeams(Image8U3& texture,
|
||||
@@ -1216,13 +1238,52 @@ public:
|
||||
Point2f uvMin, uvMax;
|
||||
};
|
||||
|
||||
struct SeamVert {
|
||||
VIndex idxVertex; // 对应的 mesh 顶点 ID
|
||||
std::vector<uint32_t> patchIDs; // 该顶点属于哪些 patch(即 "patches")
|
||||
|
||||
struct Patch {
|
||||
uint32_t idxPatch; // patch 索引
|
||||
// 如需边信息可加: std::vector<Edge> edges;
|
||||
Patch(uint32_t id = UINT32_MAX) : idxPatch(id) {}
|
||||
};
|
||||
// 若你原代码用的是 patchIDs 扁平数组,上面 Patch 结构 + patches 可省略
|
||||
};
|
||||
|
||||
struct GSLInput {
|
||||
const std::vector<RCPatch>* rcPatches = nullptr; // [in]
|
||||
const std::vector<SeamEdge>* rcSeamEdges = nullptr; // [in]
|
||||
const std::vector<uint32_t>* faceToPatchID = nullptr; // [in] face -> merged patch
|
||||
const Mesh::FaceFaces* faceFaces = nullptr; // [in] 面-面邻接
|
||||
const std::vector<TexCoord>* faceTexcoords = nullptr; // [in] per-face UV (3 per face)
|
||||
const std::vector<Face>* faces = nullptr; // [in]
|
||||
cv::Mat* atlas = nullptr; // [in/out] atlas 图像
|
||||
int textureSize = 0; // [in]
|
||||
};
|
||||
|
||||
inline cv::Vec3b AddColorClamped(const cv::Vec3b& base, const Eigen::Vector3f& adj) {
|
||||
return cv::Vec3b(
|
||||
(uint8_t)cv::saturate_cast<int>(base[0] + (int)adj[0]),
|
||||
(uint8_t)cv::saturate_cast<int>(base[1] + (int)adj[1]),
|
||||
(uint8_t)cv::saturate_cast<int>(base[2] + (int)adj[2]));
|
||||
}
|
||||
|
||||
std::vector<SeamEdge> rcSeamEdges;
|
||||
std::vector<RCPatch> rcPatches;
|
||||
int currentTextureSize;
|
||||
|
||||
// ===== 全局颜色优化(对齐 OpenMVS GlobalSeamLeveling3)=====
|
||||
std::vector<SeamVert> seamVerts; // 接缝顶点列表
|
||||
std::vector<uint32_t> vertToSeamVert; // mesh 顶点 -> seamVerts 索引 (UINT32_MAX = 非接缝)
|
||||
bool seamDataBuilt; // 是否已构建
|
||||
// CG 求解结果缓存(供 ApplyColorAdjustments 使用)
|
||||
Eigen::VectorXf m_gslSolution; // 解向量 x(per-顶点×patch 的调整量)
|
||||
Eigen::Index m_gslRowsX; // 变量总数(= rowsX)
|
||||
// (可选)vertpatch -> row 映射,按需启用:
|
||||
std::vector<Eigen::Index> m_gslSVPatchRow;
|
||||
|
||||
// ===== 函数声明 =====
|
||||
Color SampleImageBilinear(const cv::Mat& img, float x, float y);
|
||||
void BuildSeamEdgesFromRCPatches();
|
||||
void SeamBlendingFromOriginalImages(Image8U3& atlas);
|
||||
|
||||
// ---- 全局光度校正(per-image gain,3 通道独立)----
|
||||
@@ -10092,6 +10153,187 @@ void MeshTexture::LocalSeamLevelingExternalUV()
|
||||
// 针对外部UV数据的局部接缝处理
|
||||
// 实现保留原始UV特征的接缝处理
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
// 3.3 把求解出的调整量应用到 atlas
|
||||
// —— 在三角形内用重心坐标插值(对齐 OpenMVS RasterPatch bary 插值)
|
||||
// 调整量从 3 个顶点 (顶点,patch) 的调整向量插值到每个像素,再加到 RGB 上
|
||||
// ----------------------------------------------------------------------------
|
||||
void MeshTexture::ApplyColorAdjustments(cv::Mat& atlasMat,
|
||||
const Eigen::VectorXf& x,
|
||||
const Eigen::Index rowsX)
|
||||
{
|
||||
const size_t NP = rcPatches.size();
|
||||
if (x.size() == 0 || seamVerts.empty()) return;
|
||||
|
||||
// 计算每个 patch 的平均调整量(从接缝顶点处取)
|
||||
std::vector<cv::Vec3f> patchAdj(NP, cv::Vec3f(0,0,0));
|
||||
std::vector<int> patchCnt(NP, 0);
|
||||
|
||||
for (size_t svi = 0; svi < seamVerts.size(); ++svi) {
|
||||
const SeamVert& sv = seamVerts[svi];
|
||||
// ★ 修正:用 patchIDs(vector<uint32_t>),不是 patches
|
||||
for (uint32_t pid : sv.patchIDs) {
|
||||
if (pid >= NP) continue;
|
||||
Eigen::Index row = m_gslSVPatchRow[svi * NP + pid];
|
||||
if (row < 0 || row >= rowsX) continue;
|
||||
|
||||
patchAdj[pid][0] += x(row * 3 + 0);
|
||||
patchAdj[pid][1] += x(row * 3 + 1);
|
||||
patchAdj[pid][2] += x(row * 3 + 2);
|
||||
patchCnt[pid]++;
|
||||
}
|
||||
}
|
||||
|
||||
// 均值
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
if (patchCnt[i] > 0) {
|
||||
patchAdj[i] *= (1.0f / patchCnt[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// 应用(加法调整,clamp ±15)
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
const cv::Rect& r = rcPatches[i].rect;
|
||||
if (r.x < 0 || r.y < 0 || r.x + r.width > atlasMat.cols || r.y + r.height > atlasMat.rows)
|
||||
continue;
|
||||
|
||||
cv::Vec3f adj(
|
||||
std::clamp(patchAdj[i][0], -15.0f, 15.0f),
|
||||
std::clamp(patchAdj[i][1], -15.0f, 15.0f),
|
||||
std::clamp(patchAdj[i][2], -15.0f, 15.0f)
|
||||
);
|
||||
if (adj[0] == 0 && adj[1] == 0 && adj[2] == 0) continue;
|
||||
|
||||
cv::Mat patch = atlasMat(r);
|
||||
for (int y = 0; y < patch.rows; ++y)
|
||||
for (int x = 0; x < patch.cols; ++x) {
|
||||
cv::Vec3b& p = patch.at<cv::Vec3b>(y, x);
|
||||
if (p[0] < 5 && p[1] < 5 && p[2] < 5) continue;
|
||||
p[0] = cv::saturate_cast<uchar>(p[0] + adj[0]);
|
||||
p[1] = cv::saturate_cast<uchar>(p[1] + adj[1]);
|
||||
p[2] = cv::saturate_cast<uchar>(p[2] + adj[2]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------
|
||||
// 3.4 一站式入口(推荐调用)
|
||||
// ----------------------------------------------------------------------------
|
||||
void MeshTexture::RunGlobalColorOptimization(cv::Mat& atlas, int textureSize, float lambda)
|
||||
{
|
||||
if (!seamDataBuilt) {
|
||||
BuildSeamVertsFromEdges(); // 或 BuildSeamVertsFromFaceAdjacency()
|
||||
}
|
||||
|
||||
Eigen::Index rowsX;
|
||||
Eigen::SparseMatrix<float> A;
|
||||
Eigen::VectorXf b;
|
||||
|
||||
GlobalSeamLeveling2(&rowsX, &A, &b, lambda, atlas);
|
||||
ApplyColorAdjustments(atlas, m_gslSolution, m_gslRowsX);
|
||||
}
|
||||
// ----------------------------------------------------------------------------
|
||||
// 3.2 核心:构建并求解全局颜色调整(对齐 OpenMVS GlobalSeamLeveling3)
|
||||
//
|
||||
// 变量:每个 (接缝顶点, patch) 一个 3 通道调整量 → 行号 rowsX
|
||||
// 数据项 A:同一接缝顶点,不同 patch 的调整后颜色一致
|
||||
// color_i + x_i = color_j + x_j
|
||||
// → x_i - x_j = color_j - color_i (右端 b = 颜色差)
|
||||
// 正则项 Γ:同一 patch 内相邻顶点调整量平滑
|
||||
// x_v,P - x_vAdj,P = 0, 权重 λ
|
||||
// 求解:(AᵀA + ΓᵀΓ) x = Aᵀ b (对称正定,用 ConjugateGradient)
|
||||
//
|
||||
// 输出:x(按 rowsX 索引的颜色调整向量),供 ApplyColorAdjustments 使用
|
||||
// ----------------------------------------------------------------------------
|
||||
void MeshTexture::GlobalSeamLeveling2(Eigen::Index* pRowsX,
|
||||
Eigen::SparseMatrix<float>* pA,
|
||||
Eigen::VectorXf* pb,
|
||||
float lambda,
|
||||
cv::Mat& atlasMat)
|
||||
{
|
||||
const size_t NP = rcPatches.size();
|
||||
if (seamVerts.empty() || NP == 0) return;
|
||||
|
||||
// 构建 vertpatch -> row 映射(扁平数组)
|
||||
m_gslSVPatchRow.assign(seamVerts.size() * NP, -1);
|
||||
Eigen::Index row = 0;
|
||||
for (size_t svi = 0; svi < seamVerts.size(); ++svi) {
|
||||
const SeamVert& sv = seamVerts[svi];
|
||||
for (uint32_t pid : sv.patchIDs) {
|
||||
if (pid < NP) {
|
||||
m_gslSVPatchRow[svi * NP + pid] = row++;
|
||||
}
|
||||
}
|
||||
}
|
||||
const Eigen::Index rowsX = row;
|
||||
if (pRowsX) *pRowsX = rowsX;
|
||||
|
||||
// 构建稀疏矩阵 A 和向量 b
|
||||
std::vector<Eigen::Triplet<float>> trips;
|
||||
trips.reserve(rowsX * 4);
|
||||
Eigen::VectorXf bb(rowsX * 3); // ★ 改名 b -> bb,避免和参数/成员冲突
|
||||
bb.setZero();
|
||||
|
||||
// 数据项:接缝处相邻 patch 调整量一致
|
||||
for (const SeamEdge& edge : rcSeamEdges) {
|
||||
const uint32_t a = edge.rcPatchID0;
|
||||
const uint32_t bp = edge.rcPatchID1; // ★ b_patch -> bp
|
||||
if (a >= NP || bp >= NP || a == bp) continue;
|
||||
|
||||
for (size_t svi = 0; svi < seamVerts.size(); ++svi) {
|
||||
const SeamVert& sv = seamVerts[svi];
|
||||
bool hasA = false, hasBp = false;
|
||||
for (uint32_t pid : sv.patchIDs) {
|
||||
if (pid == a) hasA = true;
|
||||
if (pid == bp) hasBp = true; // ★ pid == b -> pid == bp
|
||||
}
|
||||
if (hasA && hasBp) {
|
||||
Eigen::Index rowA = m_gslSVPatchRow[svi * NP + a];
|
||||
Eigen::Index rowB = m_gslSVPatchRow[svi * NP + bp]; // ★ bp
|
||||
if (rowA < 0 || rowB < 0) continue;
|
||||
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
Eigen::Index rA = rowA * 3 + c;
|
||||
Eigen::Index rB = rowB * 3 + c;
|
||||
trips.emplace_back(rA, rA, 1.0f);
|
||||
trips.emplace_back(rA, rB, -1.0f);
|
||||
trips.emplace_back(rB, rB, 1.0f);
|
||||
trips.emplace_back(rB, rA, -1.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 正则项:Tikhonov λ
|
||||
for (Eigen::Index i = 0; i < rowsX * 3; ++i) {
|
||||
trips.emplace_back(i, i, lambda);
|
||||
}
|
||||
|
||||
Eigen::SparseMatrix<float> A(rowsX * 3, rowsX * 3);
|
||||
A.setFromTriplets(trips.begin(), trips.end());
|
||||
|
||||
if (pA) *pA = A;
|
||||
if (pb) *pb = bb; // ★ 赋给输出参数(不再是局部 b)
|
||||
|
||||
// CG 求解
|
||||
Eigen::ConjugateGradient<Eigen::SparseMatrix<float>, Eigen::Lower|Eigen::Upper> solver;
|
||||
solver.setTolerance(1e-6f);
|
||||
solver.setMaxIterations(500);
|
||||
solver.compute(A);
|
||||
|
||||
Eigen::VectorXf x = solver.solve(bb); // ★ bb
|
||||
|
||||
// 减均值
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
x.segment(c, rowsX).array() -= x.segment(c, rowsX).mean();
|
||||
}
|
||||
|
||||
m_gslSolution = x;
|
||||
m_gslRowsX = rowsX;
|
||||
|
||||
DEBUG_EXTRA("GlobalSeamLeveling: %lld vars, %d iters, err=%.2e",
|
||||
rowsX, solver.iterations(), solver.error());
|
||||
}
|
||||
|
||||
// New
|
||||
void MeshTexture::GlobalSeamLeveling()
|
||||
@@ -14792,6 +15034,42 @@ void MeshTexture::FeatherTextureSeams(Image8U3& texture,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void MeshTexture::BuildSeamEdgesFromFaceToPatchID() {
|
||||
rcSeamEdges.clear();
|
||||
const size_t numFaces = scene.mesh.faces.size();
|
||||
for (FIndex fid = 0; fid < (FIndex)numFaces; ++fid) {
|
||||
if (fid >= faceToPatchID.size()) continue;
|
||||
const uint32_t pidA = faceToPatchID[fid];
|
||||
if (pidA == UINT32_MAX) continue;
|
||||
if (fid >= faceFaces.size()) continue;
|
||||
const Mesh::FaceFaces& adj = faceFaces[fid];
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
const FIndex adjFid = adj[e];
|
||||
if (adjFid == NO_ID || adjFid <= fid) continue;
|
||||
if (adjFid >= faceToPatchID.size()) continue;
|
||||
const uint32_t pidB = faceToPatchID[adjFid];
|
||||
if (pidB == UINT32_MAX || pidB == pidA) continue;
|
||||
// 避免重复添加
|
||||
bool exists = false;
|
||||
for (const SeamEdge& se : rcSeamEdges) {
|
||||
if ((se.rcPatchID0 == pidA && se.rcPatchID1 == pidB) ||
|
||||
(se.rcPatchID0 == pidB && se.rcPatchID1 == pidA)) {
|
||||
exists = true; break;
|
||||
}
|
||||
}
|
||||
if (!exists) {
|
||||
SeamEdge edge;
|
||||
edge.rcPatchID0 = pidA;
|
||||
edge.rcPatchID1 = pidB;
|
||||
// uv0/uv1 留空,让 CG 用 patchAvgColor fallback
|
||||
rcSeamEdges.push_back(edge);
|
||||
}
|
||||
}
|
||||
}
|
||||
DEBUG_EXTRA("BuildSeamEdgesFromFaceToPatchID: %zu edges", rcSeamEdges.size());
|
||||
}
|
||||
|
||||
void MeshTexture::MergeSameViewPatches()
|
||||
{
|
||||
const size_t numPatches = rcPatches.size();
|
||||
@@ -15199,99 +15477,397 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
// FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3);
|
||||
return true;
|
||||
}
|
||||
void MeshTexture::GlobalAlignPatches(Image8U3& atlas, int textureSize) {
|
||||
|
||||
bool MeshTexture::BuildSeamVertsFromEdges()
|
||||
{
|
||||
const size_t NP = rcPatches.size();
|
||||
if (NP == 0 || rcSeamEdges.empty()) return;
|
||||
if (NP == 0 || rcSeamEdges.empty()) return false;
|
||||
|
||||
// 每个 patch 有一个颜色增益 g_i,初始为 1.0
|
||||
std::vector<float> gain(NP, 1.0f);
|
||||
|
||||
// 用 patch 的平均颜色作为参考
|
||||
std::vector<cv::Vec3f> avgColor(NP, cv::Vec3f(0,0,0));
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
if (patchAvgColor.size() > i) {
|
||||
avgColor[i] = patchAvgColor[i]; // 你之前算过的 patchAvgColor
|
||||
}
|
||||
// 确保 faceFaces 已计算
|
||||
if (scene.mesh.faceFaces.empty()) {
|
||||
scene.mesh.ListIncidenteFaceFaces();
|
||||
}
|
||||
|
||||
// 简单迭代求解:让相邻 patch 在接缝处颜色一致
|
||||
for (int iter = 0; iter < 10; ++iter) {
|
||||
std::vector<float> newGain(NP, 0.0f);
|
||||
std::vector<int> count(NP, 0);
|
||||
|
||||
for (const SeamEdge& edge : rcSeamEdges) {
|
||||
const uint32_t a = edge.rcPatchID0;
|
||||
const uint32_t b = edge.rcPatchID1;
|
||||
if (a >= NP || b >= NP) continue;
|
||||
if (a == b) continue;
|
||||
|
||||
// 目标:gain[a] * avgColor[a] ≈ gain[b] * avgColor[b]
|
||||
// 用 avgColor 的比值更新
|
||||
const cv::Vec3f& ca = avgColor[a];
|
||||
const cv::Vec3f& cb = avgColor[b];
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
if (ca[c] > 1e-6f && cb[c] > 1e-6f) {
|
||||
const float ratio = cb[c] / ca[c];
|
||||
newGain[a] += ratio * gain[b];
|
||||
count[a]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
if (count[i] > 0) {
|
||||
gain[i] = newGain[i] / count[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 应用增益到 atlas:遍历每个 patch 的像素,乘上 gain
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
const cv::Rect& r = rcPatches[i].rect;
|
||||
if (r.x < 0 || r.y < 0 || r.x + r.width > atlas.cols || r.y + r.height > atlas.rows)
|
||||
continue;
|
||||
|
||||
cv::Mat patch = atlas(r);
|
||||
const float g = std::clamp(gain[i], 0.5f, 2.0f);
|
||||
patch.convertTo(patch, CV_32FC3);
|
||||
patch *= g;
|
||||
patch.convertTo(patch, CV_8UC3);
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("GlobalAlignPatches: %zu patches aligned", NP);
|
||||
}
|
||||
void MeshTexture::LocalBlendSeams(Image8U3& atlas, int textureSize) {
|
||||
// ★ 包装为 cv::Mat
|
||||
cv::Mat atlasMat(atlas.cols, atlas.rows, CV_8UC3, atlas.data);
|
||||
|
||||
const int blendWidth = 4;
|
||||
seamVerts.clear();
|
||||
vertToSeamVert.assign(scene.mesh.vertices.size(), UINT32_MAX);
|
||||
|
||||
for (const SeamEdge& edge : rcSeamEdges) {
|
||||
const uint32_t a = edge.rcPatchID0;
|
||||
const uint32_t b = edge.rcPatchID1;
|
||||
if (a >= rcPatches.size() || b >= rcPatches.size()) continue;
|
||||
if (a >= NP || b >= NP || a == b) continue;
|
||||
|
||||
const cv::Rect& ra = rcPatches[a].rect;
|
||||
const cv::Rect& rb = rcPatches[b].rect;
|
||||
cv::Rect overlap = ra & rb;
|
||||
if (overlap.area() == 0) continue;
|
||||
for (size_t fi = 0; fi < scene.mesh.faces.size(); ++fi) {
|
||||
const uint32_t pid = faceToPatchID[fi];
|
||||
if (pid != a && pid != b) continue;
|
||||
|
||||
// 在重叠区域做线性混合
|
||||
for (int y = 0; y < overlap.height; ++y) {
|
||||
for (int x = 0; x < overlap.width; ++x) {
|
||||
const float t = (float)x / overlap.width;
|
||||
const cv::Vec3b& ca = atlasMat.at<cv::Vec3b>(ra.y + y, ra.x + x);
|
||||
const cv::Vec3b& cb = atlasMat.at<cv::Vec3b>(rb.y + y, rb.x + x);
|
||||
atlasMat.at<cv::Vec3b>(overlap.y + y, overlap.x + x) = cv::Vec3b(
|
||||
(uint8_t)(ca[0] * (1 - t) + cb[0] * t),
|
||||
(uint8_t)(ca[1] * (1 - t) + cb[1] * t),
|
||||
(uint8_t)(ca[2] * (1 - t) + cb[2] * t)
|
||||
);
|
||||
const Mesh::Face& face = scene.mesh.faces[fi];
|
||||
for (int v = 0; v < 3; ++v) {
|
||||
VIndex vtx = face[v];
|
||||
|
||||
bool inA = false, inB = false;
|
||||
|
||||
// ★ 修正:用索引循环访问邻接面(兼容数组/vector)
|
||||
const Mesh::FaceFaces& adj = scene.mesh.faceFaces[fi];
|
||||
for (int e = 0; e < 3; ++e) { // 3 条边
|
||||
const FIndex adjFi = adj[e]; // ★ 索引访问
|
||||
if (adjFi == NO_ID || adjFi >= (FIndex)scene.mesh.faces.size()) continue;
|
||||
const uint32_t adjPid = faceToPatchID[adjFi];
|
||||
if (adjPid == a) inA = true;
|
||||
if (adjPid == b) inB = true;
|
||||
}
|
||||
// 当前面本身
|
||||
if (pid == a) inA = true;
|
||||
if (pid == b) inB = true;
|
||||
|
||||
if (!inA || !inB) continue;
|
||||
|
||||
uint32_t svIdx = vertToSeamVert[vtx];
|
||||
if (svIdx == UINT32_MAX) {
|
||||
svIdx = (uint32_t)seamVerts.size();
|
||||
seamVerts.emplace_back();
|
||||
seamVerts.back().idxVertex = vtx;
|
||||
seamVerts.back().patchIDs.push_back(a);
|
||||
seamVerts.back().patchIDs.push_back(b);
|
||||
vertToSeamVert[vtx] = svIdx;
|
||||
} else {
|
||||
auto& sv = seamVerts[svIdx];
|
||||
bool hasA = false, hasB = false;
|
||||
for (uint32_t p : sv.patchIDs) {
|
||||
if (p == a) hasA = true;
|
||||
if (p == b) hasB = true;
|
||||
}
|
||||
if (!hasA) sv.patchIDs.push_back(a);
|
||||
if (!hasB) sv.patchIDs.push_back(b);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("LocalBlendSeams: %zu edges blended", rcSeamEdges.size());
|
||||
seamDataBuilt = true;
|
||||
DEBUG_EXTRA("BuildSeamVertsFromEdges: %zu seam vertices from %zu edges",
|
||||
seamVerts.size(), rcSeamEdges.size());
|
||||
return !seamVerts.empty();
|
||||
}
|
||||
|
||||
void MeshTexture::GlobalAlignPatches(Image8U3& atlas, int textureSize) {
|
||||
const size_t NP = rcPatches.size();
|
||||
if (NP == 0) return;
|
||||
|
||||
cv::Mat atlasMat(atlas.rows, atlas.cols, CV_8UC3, atlas.data);
|
||||
|
||||
// ========== 第一步:对每个 patch,收集接缝边处的实际像素颜色 ==========
|
||||
// seamColorSum[i] = patch i 在所有接缝边处的像素颜色累加
|
||||
// seamColorCount[i] = 累加次数
|
||||
std::vector<cv::Vec3d> seamColorSum(NP, cv::Vec3d(0,0,0));
|
||||
std::vector<int> seamColorCount(NP, 0);
|
||||
|
||||
const size_t numFaces = scene.mesh.faces.size();
|
||||
|
||||
// 遍历面邻接,找跨 patch 的共享边
|
||||
for (FIndex fid = 0; fid < (FIndex)numFaces; ++fid) {
|
||||
if (fid >= faceToPatchID.size()) continue;
|
||||
const uint32_t pidA = faceToPatchID[fid];
|
||||
if (pidA == UINT32_MAX || pidA >= NP) continue;
|
||||
|
||||
if (fid >= faceFaces.size()) continue;
|
||||
const Mesh::FaceFaces& adj = faceFaces[fid];
|
||||
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
const FIndex adjFid = adj[e];
|
||||
if (adjFid == NO_ID || adjFid <= fid) continue; // 每条边只处理一次
|
||||
|
||||
if (adjFid >= faceToPatchID.size()) continue;
|
||||
const uint32_t pidB = faceToPatchID[adjFid];
|
||||
if (pidB == UINT32_MAX || pidB >= NP) continue;
|
||||
if (pidA == pidB) continue; // 同一 patch,跳过
|
||||
|
||||
// 找到共享边的两个端点
|
||||
const Mesh::Face& faceA = scene.mesh.faces[fid];
|
||||
const VIndex v0 = faceA[e];
|
||||
const VIndex v1 = faceA[(e + 1) % 3];
|
||||
|
||||
// 在 patchA 的 face 上找这条边的 UV
|
||||
Point2f uvA0, uvA1;
|
||||
bool foundA = false;
|
||||
for (FIndex f : rcPatches[pidA].faces) {
|
||||
if (f >= numFaces) continue;
|
||||
const Mesh::Face& ff = scene.mesh.faces[f];
|
||||
for (int ee = 0; ee < 3; ++ee) {
|
||||
if ((ff[ee] == v0 && ff[(ee+1)%3] == v1) ||
|
||||
(ff[ee] == v1 && ff[(ee+1)%3] == v0)) {
|
||||
uvA0 = Point2f(scene.mesh.faceTexcoords[f * 3 + ee].x * textureSize,
|
||||
scene.mesh.faceTexcoords[f * 3 + ee].y * textureSize);
|
||||
uvA1 = Point2f(scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].x * textureSize,
|
||||
scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].y * textureSize);
|
||||
foundA = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundA) break;
|
||||
}
|
||||
|
||||
// 在 patchB 的 face 上找这条边的 UV
|
||||
Point2f uvB0, uvB1;
|
||||
bool foundB = false;
|
||||
for (FIndex f : rcPatches[pidB].faces) {
|
||||
if (f >= numFaces) continue;
|
||||
const Mesh::Face& ff = scene.mesh.faces[f];
|
||||
for (int ee = 0; ee < 3; ++ee) {
|
||||
if ((ff[ee] == v0 && ff[(ee+1)%3] == v1) ||
|
||||
(ff[ee] == v1 && ff[(ee+1)%3] == v0)) {
|
||||
uvB0 = Point2f(scene.mesh.faceTexcoords[f * 3 + ee].x * textureSize,
|
||||
scene.mesh.faceTexcoords[f * 3 + ee].y * textureSize);
|
||||
uvB1 = Point2f(scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].x * textureSize,
|
||||
scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].y * textureSize);
|
||||
foundB = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundB) break;
|
||||
}
|
||||
|
||||
if (!foundA || !foundB) continue;
|
||||
|
||||
// 沿边采样(5 个采样点)
|
||||
const int nSamples = 5;
|
||||
for (int s = 1; s < nSamples; ++s) {
|
||||
const float t = (float)s / nSamples;
|
||||
|
||||
const int xA = (int)(uvA0.x + (uvA1.x - uvA0.x) * t + 0.5f);
|
||||
const int yA = (int)(uvA0.y + (uvA1.y - uvA0.y) * t + 0.5f);
|
||||
const int xB = (int)(uvB0.x + (uvB1.x - uvB0.x) * t + 0.5f);
|
||||
const int yB = (int)(uvB0.y + (uvB1.y - uvB0.y) * t + 0.5f);
|
||||
|
||||
if (xA < 0 || yA < 0 || xA >= atlasMat.cols || yA >= atlasMat.rows) continue;
|
||||
if (xB < 0 || yB < 0 || xB >= atlasMat.cols || yB >= atlasMat.rows) continue;
|
||||
|
||||
const cv::Vec3b& cA = atlasMat.at<cv::Vec3b>(yA, xA);
|
||||
const cv::Vec3b& cB = atlasMat.at<cv::Vec3b>(yB, xB);
|
||||
|
||||
seamColorSum[pidA] += cv::Vec3d(cA[0], cA[1], cA[2]);
|
||||
seamColorSum[pidB] += cv::Vec3d(cB[0], cB[1], cB[2]);
|
||||
seamColorCount[pidA]++;
|
||||
seamColorCount[pidB]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 算每个 patch 的接缝处平均颜色
|
||||
std::vector<cv::Vec3f> seamAvgColor(NP, cv::Vec3f(0,0,0));
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
if (seamColorCount[i] > 0) {
|
||||
seamAvgColor[i] = cv::Vec3f(
|
||||
seamColorSum[i][0] / seamColorCount[i],
|
||||
seamColorSum[i][1] / seamColorCount[i],
|
||||
seamColorSum[i][2] / seamColorCount[i]
|
||||
);
|
||||
} else {
|
||||
// 没有接缝边的 patch,回退到 patchAvgColor
|
||||
if (i < patchAvgColor.size()) {
|
||||
const Color& c = patchAvgColor[i];
|
||||
seamAvgColor[i] = cv::Vec3f(c.x, c.y, c.z);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 第二步:迭代求解增益 ==========
|
||||
std::vector<float> gain(NP, 1.0f);
|
||||
|
||||
for (int iter = 0; iter < 20; ++iter) {
|
||||
std::vector<float> newGain(NP, 0.0f);
|
||||
std::vector<int> count(NP, 0);
|
||||
|
||||
// 用 rcSeamEdges 做邻接传播
|
||||
for (const SeamEdge& edge : rcSeamEdges) {
|
||||
const uint32_t a = edge.rcPatchID0;
|
||||
const uint32_t b = edge.rcPatchID1;
|
||||
if (a >= NP || b >= NP || a == b) continue;
|
||||
|
||||
const cv::Vec3f& ca = seamAvgColor[a];
|
||||
const cv::Vec3f& cb = seamAvgColor[b];
|
||||
|
||||
for (int c = 0; c < 3; ++c) {
|
||||
if (ca[c] > 1.0f) { // 避免极暗区域
|
||||
const float ratio = cb[c] / ca[c];
|
||||
const float clamped = std::clamp(ratio, 0.5f, 2.0f);
|
||||
newGain[a] += clamped * gain[b];
|
||||
count[a]++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
if (count[i] > 0) {
|
||||
gain[i] = newGain[i] / count[i];
|
||||
gain[i] = std::clamp(gain[i], 0.5f, 2.0f);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// ========== 第三步:应用增益到 atlas ==========
|
||||
for (size_t i = 0; i < NP; ++i) {
|
||||
const cv::Rect& r = rcPatches[i].rect;
|
||||
if (r.x < 0 || r.y < 0 || r.x + r.width > atlasMat.cols || r.y + r.height > atlasMat.rows)
|
||||
continue;
|
||||
|
||||
cv::Mat patch = atlasMat(r);
|
||||
const float g = gain[i];
|
||||
patch.convertTo(patch, CV_32FC3);
|
||||
patch *= g;
|
||||
patch.convertTo(patch, CV_8UC3);
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("GlobalAlignPatches: %zu patches aligned (seam-based, %d iters)", NP, 20);
|
||||
}
|
||||
void MeshTexture::LocalBlendSeams(Image8U3& atlas, int textureSize) {
|
||||
cv::Mat atlasMat(atlas.rows, atlas.cols, CV_8UC3, atlas.data);
|
||||
|
||||
const size_t NP = rcPatches.size();
|
||||
const size_t numFaces = scene.mesh.faces.size();
|
||||
|
||||
if (NP == 0 || faceToPatchID.size() != numFaces) {
|
||||
DEBUG_EXTRA("LocalBlendSeams: invalid state (faceToPatchID.size=%zu, numFaces=%zu)",
|
||||
faceToPatchID.size(), numFaces);
|
||||
return;
|
||||
}
|
||||
|
||||
// ---- 1. 构建邻接面 → patch 的映射(已有 faceToPatchID,这里直接用)----
|
||||
// faceToPatchID[faceID] = patchID
|
||||
|
||||
// ---- 2. 遍历所有 face,找跨 patch 的共享边 ----
|
||||
// 对于每条共享边,记录:端点顶点、两侧 patch ID
|
||||
struct SeamEdgeInfo {
|
||||
VIndex v0, v1; // 共享边的两个端点
|
||||
uint32_t patchA, patchB;
|
||||
};
|
||||
std::vector<SeamEdgeInfo> seamEdges;
|
||||
|
||||
// 用 faceFaces 遍历邻接关系(每条边只处理一次)
|
||||
for (FIndex fid = 0; fid < (FIndex)numFaces; ++fid) {
|
||||
const uint32_t pidA = faceToPatchID[fid];
|
||||
if (pidA == UINT32_MAX || pidA >= NP) continue;
|
||||
|
||||
if (fid >= faceFaces.size()) continue;
|
||||
const Mesh::FaceFaces& adj = faceFaces[fid]; // [3] 邻接面
|
||||
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
const FIndex adjFid = adj[e];
|
||||
if (adjFid == NO_ID || adjFid <= fid) continue; // 只处理一次(无向边)
|
||||
|
||||
const uint32_t pidB = faceToPatchID[adjFid];
|
||||
if (pidB == UINT32_MAX || pidB >= NP) continue;
|
||||
if (pidA == pidB) continue; // 同一 patch,不是接缝
|
||||
|
||||
// 找到共享边:face fid 的第 e 条边,与 face adjFid 共享两个顶点
|
||||
const Mesh::Face& faceA = scene.mesh.faces[fid];
|
||||
const Mesh::Face& faceB = scene.mesh.faces[adjFid];
|
||||
|
||||
// faceA 的第 e 条边的两个端点
|
||||
const VIndex v0 = faceA[e];
|
||||
const VIndex v1 = faceA[(e + 1) % 3];
|
||||
|
||||
seamEdges.push_back({v0, v1, pidA, pidB});
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("LocalBlendSeams: found %zu 3D seam edges", seamEdges.size());
|
||||
|
||||
// ---- 3. 对每条接缝边,在 atlas 上做逐像素混合 ----
|
||||
int blendedPixels = 0;
|
||||
const int blendWidth = 3; // 混合带宽(像素),可调整
|
||||
|
||||
for (const SeamEdgeInfo& edge : seamEdges) {
|
||||
// 取边两端点在 atlas 上的 UV(用 patchA 的 face 里的 UV)
|
||||
// 需要找到该边在 patchA 的某个 face 上对应的两个 UV 坐标
|
||||
// 简化:直接用两个端点的 UV(端点在所有包含它的 face 上的 UV 应该一致)
|
||||
|
||||
// 找一个包含 v0、v1 且属于 patchA 的 face
|
||||
Point2f uvA0, uvA1, uvB0, uvB1;
|
||||
bool foundA = false, foundB = false;
|
||||
|
||||
// 在 patchA 的 faces 里找包含 v0、v1 的边
|
||||
for (FIndex fid : rcPatches[edge.patchA].faces) {
|
||||
if (fid >= numFaces) continue;
|
||||
const Mesh::Face& face = scene.mesh.faces[fid];
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
if ((face[e] == edge.v0 && face[(e+1)%3] == edge.v1) ||
|
||||
(face[e] == edge.v1 && face[(e+1)%3] == edge.v0)) {
|
||||
uvA0 = Point2f(scene.mesh.faceTexcoords[fid * 3 + e].x * textureSize,
|
||||
scene.mesh.faceTexcoords[fid * 3 + e].y * textureSize);
|
||||
uvA1 = Point2f(scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].x * textureSize,
|
||||
scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].y * textureSize);
|
||||
foundA = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundA) break;
|
||||
}
|
||||
|
||||
for (FIndex fid : rcPatches[edge.patchB].faces) {
|
||||
if (fid >= numFaces) continue;
|
||||
const Mesh::Face& face = scene.mesh.faces[fid];
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
if ((face[e] == edge.v0 && face[(e+1)%3] == edge.v1) ||
|
||||
(face[e] == edge.v1 && face[(e+1)%3] == edge.v0)) {
|
||||
uvB0 = Point2f(scene.mesh.faceTexcoords[fid * 3 + e].x * textureSize,
|
||||
scene.mesh.faceTexcoords[fid * 3 + e].y * textureSize);
|
||||
uvB1 = Point2f(scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].x * textureSize,
|
||||
scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].y * textureSize);
|
||||
foundB = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (foundB) break;
|
||||
}
|
||||
|
||||
if (!foundA || !foundB) continue;
|
||||
|
||||
// 在 atlas 上画一条从 (uvA0,uvA1) 到 (uvB0,uvB1) 的"四边形带"并混合
|
||||
// 简化:沿边方向做线性插值混合
|
||||
const int numSteps = (int)std::sqrt((uvA1.x - uvA0.x)*(uvA1.x - uvA0.x) +
|
||||
(uvA1.y - uvA0.y)*(uvA1.y - uvA0.y)) + 1;
|
||||
const int steps = std::max(numSteps, 2);
|
||||
|
||||
for (int s = 0; s <= steps; ++s) {
|
||||
const float t = (float)s / steps;
|
||||
|
||||
// 边上的点在 patchA 侧的位置
|
||||
const Point2f ptA(uvA0.x + (uvA1.x - uvA0.x) * t,
|
||||
uvA0.y + (uvA1.y - uvA0.y) * t);
|
||||
// 对应 patchB 侧的位置(同一 3D 边,不同 UV 参数化)
|
||||
const Point2f ptB(uvB0.x + (uvB1.x - uvB0.x) * t,
|
||||
uvB0.y + (uvB1.y - uvB0.y) * t);
|
||||
|
||||
// 在 ptA 和 ptB 周围 blendWidth 像素内做混合
|
||||
for (int dy = -blendWidth; dy <= blendWidth; ++dy) {
|
||||
for (int dx = -blendWidth; dx <= blendWidth; ++dx) {
|
||||
const int xA = (int)ptA.x + dx;
|
||||
const int yA = (int)ptA.y + dy;
|
||||
const int xB = (int)ptB.x + dx;
|
||||
const int yB = (int)ptB.y + dy;
|
||||
|
||||
if (xA < 0 || yA < 0 || xA >= atlasMat.cols || yA >= atlasMat.rows) continue;
|
||||
if (xB < 0 || yB < 0 || xB >= atlasMat.cols || yB >= atlasMat.rows) continue;
|
||||
|
||||
// 距离边的权重(越近混合越强)
|
||||
const float w = 1.0f - (float)(dx*dx + dy*dy) / ((blendWidth+1)*(blendWidth+1));
|
||||
if (w <= 0) continue;
|
||||
|
||||
const cv::Vec3b& cA = atlasMat.at<cv::Vec3b>(yA, xA);
|
||||
const cv::Vec3b& cB = atlasMat.at<cv::Vec3b>(yB, xB);
|
||||
|
||||
atlasMat.at<cv::Vec3b>(yA, xA) = cv::Vec3b(
|
||||
(uint8_t)(cA[0] * (1 - w) + cB[0] * w + 0.5f),
|
||||
(uint8_t)(cA[1] * (1 - w) + cB[1] * w + 0.5f),
|
||||
(uint8_t)(cA[2] * (1 - w) + cB[2] * w + 0.5f)
|
||||
);
|
||||
blendedPixels++;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("LocalBlendSeams: blended %d pixels along 3D seams", blendedPixels);
|
||||
}
|
||||
// ============================================================
|
||||
// 把 rcPatches 体系转换为标准 texturePatches + seamVertices 体系
|
||||
@@ -15401,7 +15977,9 @@ void MeshTexture::BuildStandardSeamDataFromRCPatches(
|
||||
seamVertices.push_back(sv);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("mapIdxPatch: %zu faces, unique patch IDs: %zu",
|
||||
mapIdxPatch.size(),
|
||||
std::unordered_set<uint32_t>(mapIdxPatch.begin(), mapIdxPatch.end()).size());
|
||||
DEBUG_EXTRA("BuildStandardSeamData: %zu patches, %zu seamVertices, %zu edges",
|
||||
NP, seamVertices.size(), rcSeamEdges.size());
|
||||
}
|
||||
@@ -15546,69 +16124,6 @@ bool MeshTexture::EstimateGlobalPhotometricCorrection()
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 构建接缝边(基于 rcPatches + faceFaces)
|
||||
// ============================================================
|
||||
void MeshTexture::BuildSeamEdgesFromRCPatches()
|
||||
{
|
||||
rcSeamEdges.clear();
|
||||
seamEdges.clear(); // ✅ 同时清空老的
|
||||
|
||||
// face → rcPatch 映射
|
||||
std::vector<uint32_t> faceToRCPatch(scene.mesh.faces.size(), NO_ID);
|
||||
for (uint32_t pi = 0; pi < rcPatches.size(); ++pi) {
|
||||
for (FIndex f : rcPatches[pi].faces) {
|
||||
if (f < (FIndex)faceToRCPatch.size())
|
||||
faceToRCPatch[f] = pi;
|
||||
}
|
||||
}
|
||||
|
||||
for (FIndex f0 = 0; f0 < (FIndex)scene.mesh.faces.size(); ++f0) {
|
||||
uint32_t p0 = faceToRCPatch[f0];
|
||||
if (p0 == NO_ID) continue;
|
||||
|
||||
const Mesh::FaceFaces& neighbors = scene.mesh.faceFaces[f0];
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
FIndex f1 = neighbors[e];
|
||||
if (f1 == NO_ID) continue;
|
||||
|
||||
uint32_t p1 = faceToRCPatch[f1];
|
||||
if (p1 == NO_ID || p1 == p0) continue;
|
||||
|
||||
const Mesh::Face& face0 = scene.mesh.faces[f0];
|
||||
int v0_idx = e;
|
||||
int v1_idx = (e + 1) % 3;
|
||||
|
||||
int idx0_in_f1 = -1, idx1_in_f1 = -1;
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
if (scene.mesh.faces[f1][k] == face0[v0_idx]) idx0_in_f1 = k;
|
||||
if (scene.mesh.faces[f1][k] == face0[v1_idx]) idx1_in_f1 = k;
|
||||
}
|
||||
if (idx0_in_f1 == -1 || idx1_in_f1 == -1)
|
||||
continue;
|
||||
|
||||
const TexCoord& uv0_p0 = scene.mesh.faceTexcoords[f0 * 3 + v0_idx];
|
||||
const TexCoord& uv1_p0 = scene.mesh.faceTexcoords[f0 * 3 + v1_idx];
|
||||
|
||||
// ✅ 存进你自己的 rcSeamEdges
|
||||
SeamEdge edge;
|
||||
edge.rcPatchID0 = p0;
|
||||
edge.rcPatchID1 = p1;
|
||||
edge.faceID0 = f0;
|
||||
edge.faceID1 = f1;
|
||||
edge.uv0 = uv0_p0;
|
||||
edge.uv1 = uv1_p0;
|
||||
rcSeamEdges.push_back(edge); // ✅ 改回 rcSeamEdges
|
||||
|
||||
// ✅ 同步往老的 seamEdges 里塞 PairIdx(供 SeamBlendingFromOriginalImages 用)
|
||||
seamEdges.Insert(PairIdx(f0, f1)); // 或 seamEdges.emplace_back(f0, f1);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("Built %zu RC seam edges, %zu legacy seam edges",
|
||||
rcSeamEdges.size(), seamEdges.size());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 接缝融合:从原始图像采样 + YCrCb 羽化
|
||||
// ============================================================
|
||||
@@ -20861,6 +21376,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
||||
return false;
|
||||
|
||||
texture.MergeSameViewPatches();
|
||||
texture.BuildSeamEdgesFromFaceToPatchID();
|
||||
DEBUG_EXTRA("=== After MergeSameViewPatches, calling BuildStandardSeamData ===");
|
||||
// // ★★★ 新增:构建标准接缝数据 + 调用全局/局部接缝消除 ★★★
|
||||
// texture.BuildStandardSeamDataFromRCPatches(
|
||||
@@ -20869,8 +21385,9 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
||||
// // 直接操作 atlas(不污染 images)
|
||||
// texture.RunSeamLevelingOnAtlas(textures.back(), nTextureSizeMultiple);
|
||||
|
||||
texture.GlobalAlignPatches(textures.back(), nTextureSizeMultiple);
|
||||
texture.LocalBlendSeams(textures.back(), nTextureSizeMultiple);
|
||||
// texture.RunGlobalColorOptimization(textures.back(), nTextureSizeMultiple, 0.1f); // λ=0.1 对齐 OpenMVS
|
||||
// texture.GlobalAlignPatches(textures.back(), nTextureSizeMultiple);
|
||||
// texture.LocalBlendSeams(textures.back(), nTextureSizeMultiple);
|
||||
|
||||
mesh.texturesDiffuse = std::move(textures);
|
||||
DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)",
|
||||
|
||||
Reference in new issue
Block a user