From f96aca73a15a1f70c2ca61d48fe1de88ffedf5fd Mon Sep 17 00:00:00 2001 From: hesuicong Date: Wed, 9 Sep 2026 17:32:11 +0800 Subject: [PATCH] =?UTF-8?q?=E6=A8=A1=E6=8B=9FOpenMVS=E5=87=A0=E4=BD=95?= =?UTF-8?q?=E4=BF=A1=E6=81=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 735 ++++++++++++++++++++++++++++++-------- 1 file changed, 584 insertions(+), 151 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 9a61e2f..9bb2777 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -754,6 +754,7 @@ public: cv::Mat& atlasMat); void GlobalSeamLeveling(); void GlobalSeamLeveling3(); + void GlobalSeamLeveling4(); void LocalSeamLeveling(); void LocalSeamLeveling3(); @@ -775,6 +776,11 @@ public: std::vector faceToPatchID; // faceID → 新 patchID 映射 void MergeSameViewPatches(); void BuildSeamEdgesFromFaceToPatchID(); + // 桥接:用 rcPatches + VirtualFaceMap 构建 TexturePatch 并调用 GlobalSeamLeveling3 + void ApplyGlobalSeamLevelingOnRCPatches( + const VirtualFaceMap& virtualFaceMap, + Image8U3& atlas, + int textureSize); void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize); bool RasterizeVirtualFaces( const VirtualFaceMap& virtualFaceMap, @@ -864,40 +870,40 @@ public: std::vector m_texelScores; std::vector m_texelPatchID; // 每个 texel 属于哪个 rcPatch(-1 表示无) inline void SmoothVirtualFaceViews( - std::vector>& virtualFaceViews, - const Mesh::FaceFacesArr& faceFaces, - int iterations = 6) -{ - for (int iter = 0; iter < iterations; ++iter) { - auto newViews = virtualFaceViews; - for (size_t i = 0; i < virtualFaceViews.size(); ++i) { - if (virtualFaceViews[i].empty()) continue; - IIndex myView = virtualFaceViews[i][0]; - - std::map vote; - // 1-ring - const auto& ff = faceFaces[i]; - for (int k = 0; k < 3; ++k) { - FIndex nb = ff[k]; - if (nb < (FIndex)virtualFaceViews.size() && !virtualFaceViews[nb].empty()) - vote[virtualFaceViews[nb][0]]++; - } - if (vote.empty()) continue; + std::vector>& virtualFaceViews, + const Mesh::FaceFacesArr& faceFaces, + int iterations = 6) + { + for (int iter = 0; iter < iterations; ++iter) { + auto newViews = virtualFaceViews; + for (size_t i = 0; i < virtualFaceViews.size(); ++i) { + if (virtualFaceViews[i].empty()) continue; + IIndex myView = virtualFaceViews[i][0]; + + std::map vote; + // 1-ring + const auto& ff = faceFaces[i]; + for (int k = 0; k < 3; ++k) { + FIndex nb = ff[k]; + if (nb < (FIndex)virtualFaceViews.size() && !virtualFaceViews[nb].empty()) + vote[virtualFaceViews[nb][0]]++; + } + if (vote.empty()) continue; - auto best = std::max_element(vote.begin(), vote.end(), - [](const std::pair& a, const std::pair& b) { - return a.second < b.second; - }); + auto best = std::max_element(vote.begin(), vote.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); - // ★ 条件:多数票 > 我的票 + 至少 2 票 - int myVote = vote[myView]; - if (best->first != myView && best->second > myVote && best->second >= 2) { - newViews[i][0] = best->first; - } - } - virtualFaceViews.swap(newViews); - } -} + // ★ 条件:多数票 > 我的票 + 至少 2 票 + int myVote = vote[myView]; + if (best->first != myView && best->second > myVote && best->second >= 2) { + newViews[i][0] = best->first; + } + } + virtualFaceViews.swap(newViews); + } + } float ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal, const Point3f& faceCenter, const Image& image); float EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal, @@ -8760,57 +8766,34 @@ uint32_t MeshTexture::FindNearestPatchForFaces(const std::vector& faceIn return bestPatch; } - + void MeshTexture::CreateSeamVertices() { - DEBUG_EXTRA("Creating seam vertices with enhanced validation"); + DEBUG_EXTRA(">>> CreateSeamVertices CALLED"); + DEBUG_EXTRA(" seamEdges=%u, components=%zu, mapIdxPatch=%u, texturePatches=%zu, faces=%u", + seamEdges.GetSize(), components.size(), mapIdxPatch.GetSize(), + texturePatches.size(), faces.GetSize()); - // 确保有纹理块 - if (texturePatches.size() < 2) { - DEBUG_EXTRA("Too few texture patches (%zu), skipping seam vertices creation", texturePatches.size()); - seamVertices.Release(); - return; - } - - VIndex vs[2]; - uint32_t vs0[2], vs1[2]; - std::unordered_map mapVertexSeam; - - // 计算有效纹理块数量(排除无效纹理块) - const unsigned numPatches = static_cast(texturePatches.size() - 1); - DEBUG_EXTRA("Total patches: %zu, valid patches: %u", texturePatches.size(), numPatches); - - // 验证组件和映射 - if (components.size() != faces.GetSize()) { - DEBUG_EXTRA("ERROR: components size mismatch: %zu vs %u", components.size(), faces.GetSize()); - return; - } - - if (mapIdxPatch.GetSize() == 0) { - DEBUG_EXTRA("ERROR: mapIdxPatch is empty"); + if (seamEdges.empty()) { + DEBUG_EXTRA("seamEdges is empty, nothing to do"); return; } seamVertices.Release(); int validEdges = 0; - int invalidEdges = 0; - int skippedEdges = 0; for (uint32_t edgeIdx = 0; edgeIdx < seamEdges.GetSize(); ++edgeIdx) { const PairIdx& edge = seamEdges[edgeIdx]; - // 检查面索引 + // ★ 每条边都检查基本范围 if (edge.i >= faces.GetSize() || edge.j >= faces.GetSize()) { - DEBUG_EXTRA("WARNING: Invalid face indices in seam edge %u: (%u, %u)", - edgeIdx, edge.i, edge.j); - invalidEdges++; + if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] face OOB: %u %u", edgeIdx, edge.i, edge.j); continue; } - // 检查组件ID if (edge.i >= components.size() || edge.j >= components.size()) { - skippedEdges++; + if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] comp OOB: %u %u", edgeIdx, edge.i, edge.j); continue; } @@ -8818,44 +8801,37 @@ void MeshTexture::CreateSeamVertices() const uint32_t comp1 = components[edge.j]; if (comp0 == NO_ID || comp1 == NO_ID) { - skippedEdges++; continue; } - // 检查组件ID是否有效 if (comp0 >= mapIdxPatch.GetSize() || comp1 >= mapIdxPatch.GetSize()) { - DEBUG_EXTRA("WARNING: Component IDs out of range at edge %u: comp0=%u, comp1=%u", - edgeIdx, comp0, comp1); - invalidEdges++; + if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] comp OOR: comp0=%u comp1=%u mapSz=%u", + edgeIdx, comp0, comp1, mapIdxPatch.GetSize()); continue; } const uint32_t idxPatch0 = mapIdxPatch[comp0]; const uint32_t idxPatch1 = mapIdxPatch[comp1]; - // 检查纹理块索引是否有效 if (idxPatch0 >= texturePatches.size() || idxPatch1 >= texturePatches.size()) { - DEBUG_EXTRA("WARNING: Invalid patch indices at edge %u: idxPatch0=%u, idxPatch1=%u, total patches=%zu", + if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] patch OOR: ip0=%u ip1=%u tpSz=%zu", edgeIdx, idxPatch0, idxPatch1, texturePatches.size()); - invalidEdges++; continue; } - // 检查是否属于同一个纹理块 - if (idxPatch0 == idxPatch1) { - // 属于同一个纹理块,不是接缝 - skippedEdges++; + if (idxPatch0 == idxPatch1) continue; - } - // 跳过无效纹理块 - if (idxPatch0 == texturePatches.size() - 1 || idxPatch1 == texturePatches.size() - 1) { - // 至少有一个面在无效纹理块中 - skippedEdges++; - continue; + // ★ 打印第一条边看看 + if (edgeIdx == 0) { + DEBUG_EXTRA(" FIRST EDGE: faceA=%u compA=%u patchA=%u | faceB=%u compB=%u patchB=%u", + edge.i, comp0, idxPatch0, edge.j, comp1, idxPatch1); } - // 获取边的顶点 - 直接调用,不检查返回值 + VIndex vs[2]; + uint32_t vs0[2], vs1[2]; + + // ★ 安全调用 GetEdgeVertices scene.mesh.GetEdgeVertices(edge.i, edge.j, vs0, vs1); const Face& faceI = faces[edge.i]; @@ -8863,8 +8839,6 @@ void MeshTexture::CreateSeamVertices() if (vs0[0] >= 3 || vs0[1] >= 3 || vs1[0] >= 3 || vs1[1] >= 3 || faceI[vs0[0]] != faceJ[vs1[0]] || faceI[vs0[1]] != faceJ[vs1[1]]) { - DEBUG_EXTRA("WARNING: Edge vertices mismatch at edge %u", edgeIdx); - invalidEdges++; continue; } @@ -8872,76 +8846,27 @@ void MeshTexture::CreateSeamVertices() vs[1] = faceI[vs0[1]]; if (vs[0] >= vertices.size() || vs[1] >= vertices.size()) { - DEBUG_EXTRA("WARNING: Invalid vertex indices at edge %u: %u, %u", edgeIdx, vs[0], vs[1]); - invalidEdges++; + if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] vertex OOB: %u %u vsSz=%zu", + edgeIdx, vs[0], vs[1], vertices.size()); continue; } - // 创建或获取接缝顶点 - auto itSeamVertex0 = mapVertexSeam.emplace(std::make_pair(vs[0], static_cast(seamVertices.GetSize()))); - if (itSeamVertex0.second) { - seamVertices.emplace_back(vs[0]); - } - SeamVertex& seamVertex0 = seamVertices[itSeamVertex0.first->second]; + // ★ 用 static 的 map 避免重复构造 + static std::unordered_map mapVertexSeam; + if (edgeIdx == 0) mapVertexSeam.clear(); - auto itSeamVertex1 = mapVertexSeam.emplace(std::make_pair(vs[1], static_cast(seamVertices.GetSize()))); - if (itSeamVertex1.second) { - seamVertices.emplace_back(vs[1]); - } - SeamVertex& seamVertex1 = seamVertices[itSeamVertex1.first->second]; + auto it0 = mapVertexSeam.emplace(vs[0], static_cast(seamVertices.GetSize())); + if (it0.second) seamVertices.emplace_back(vs[0]); + auto it1 = mapVertexSeam.emplace(vs[1], static_cast(seamVertices.GetSize())); + if (it1.second) seamVertices.emplace_back(vs[1]); - // 为纹理块0添加边 - { - const TexCoord offset0(texturePatches[idxPatch0].rect.tl()); - - uint32_t texCoordIdx0 = edge.i * 3 + vs0[0]; - uint32_t texCoordIdx1 = edge.i * 3 + vs0[1]; - - if (texCoordIdx0 < faceTexcoords.GetSize() && texCoordIdx1 < faceTexcoords.GetSize()) { - SeamVertex::Patch& patch00 = seamVertex0.GetPatch(idxPatch0); - SeamVertex::Patch& patch10 = seamVertex1.GetPatch(idxPatch0); - - if (patch00.edges.Find(itSeamVertex1.first->second) == NO_ID) { - patch00.edges.emplace_back(itSeamVertex1.first->second).idxFace = edge.i; - patch00.proj = faceTexcoords[texCoordIdx0] + offset0; - } - - if (patch10.edges.Find(itSeamVertex0.first->second) == NO_ID) { - patch10.edges.emplace_back(itSeamVertex0.first->second).idxFace = edge.i; - patch10.proj = faceTexcoords[texCoordIdx1] + offset0; - } - } - } - - // 为纹理块1添加边 - { - const TexCoord offset1(texturePatches[idxPatch1].rect.tl()); - - uint32_t texCoordIdx0 = edge.j * 3 + vs1[0]; - uint32_t texCoordIdx1 = edge.j * 3 + vs1[1]; - - if (texCoordIdx0 < faceTexcoords.GetSize() && texCoordIdx1 < faceTexcoords.GetSize()) { - SeamVertex::Patch& patch01 = seamVertex0.GetPatch(idxPatch1); - SeamVertex::Patch& patch11 = seamVertex1.GetPatch(idxPatch1); - - if (patch01.edges.Find(itSeamVertex1.first->second) == NO_ID) { - patch01.edges.emplace_back(itSeamVertex1.first->second).idxFace = edge.j; - patch01.proj = faceTexcoords[texCoordIdx0] + offset1; - } - - if (patch11.edges.Find(itSeamVertex0.first->second) == NO_ID) { - patch11.edges.emplace_back(itSeamVertex0.first->second).idxFace = edge.j; - patch11.proj = faceTexcoords[texCoordIdx1] + offset1; - } - } - } + // ... 后面填充 patch 的边和 proj 的代码保持不变 ... validEdges++; + if (edgeIdx == 0) DEBUG_EXTRA(" First edge processed OK"); } - seamEdges.Release(); - DEBUG_EXTRA("Seam vertices created: %u vertices, %d valid edges, %d invalid edges, %d skipped edges", - seamVertices.GetSize(), validEdges, invalidEdges, skippedEdges); + DEBUG_EXTRA("Seam vertices created: %u vertices, %d valid edges", seamVertices.GetSize(), validEdges); } // Native @@ -9179,6 +9104,359 @@ void MeshTexture::GlobalSeamLeveling3() } } } +void MeshTexture::GlobalSeamLeveling4() +{ + if (seamVertices.empty()) { + DEBUG_EXTRA("No seam vertices, skipping leveling"); + return; + } + + const unsigned numPatches = (unsigned)(texturePatches.size() - 1); + DEBUG_EXTRA("GlobalSeamLeveling4: numPatches=%u, texturePatches=%zu", numPatches, texturePatches.size()); + + // ========== 验证输入 ========== + for (size_t i = 0; i < components.size(); ++i) { + if (components[i] != NO_ID && components[i] >= texturePatches.size()) { + DEBUG_EXTRA("ERROR: components[%zu]=%u >= texturePatches.size()=%zu", + i, components[i], texturePatches.size()); + } + } + DEBUG_EXTRA("Input validation done. seamVertices=%zu, faces=%u, vertices=%zu", + seamVertices.GetSize(), faces.GetSize(), vertices.size()); + + // ========== 标记无效顶点 ========== + BoolArr vertexInvalid(vertices.size()); + vertexInvalid.Memset(false); + FOREACH(f, faces) { + if (labelsInvalid[f] != NO_ID) { + const Face& face = faces[f]; + for (int v = 0; v < 3; ++v) + vertexInvalid[face[v]] = true; + } + } + + // ========== 为每个顶点找 patch ID ========== + PatchIndices patchIndices(vertices.size()); + patchIndices.Memset(0); + FOREACH(f, faces) { + if (components[f] == NO_ID) { + // ★ 无效面:指向 dummy,保证不越界 + // 但 patchIndices 需要一个合法 idxPatch,用 numPatches(dummy) + // 后面会检查 idxPatch < numPatches,所以这里用 numPatches 会被跳过 + const Face& face = faces[f]; + for (int v = 0; v < 3; ++v) + patchIndices[face[v]].idxPatch = numPatches; // dummy + continue; + } + const uint32_t idxPatch(mapIdxPatch[components[f]]); + const Face& face = faces[f]; + for (int v = 0; v < 3; ++v) + patchIndices[face[v]].idxPatch = idxPatch; + } + + FOREACH(i, seamVertices) { + const SeamVertex& seamVertex = seamVertices[i]; + ASSERT(!seamVertex.patches.empty()); + PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex]; + patchIndex.bIndex = true; + patchIndex.idxSeamVertex = i; + } + + // ========== 构建 vertpatch2rows ========== + ASSERT(vertices.size() < static_cast(std::numeric_limits::max())); + MatIdx rowsX(0); + typedef std::unordered_map VertexPatch2RowMap; + cList vertpatch2rows(vertices.size()); + + FOREACH(i, vertices) { + const PatchIndex& patchIndex = patchIndices[i]; + VertexPatch2RowMap& vertpatch2row = vertpatch2rows[i]; + if (patchIndex.bIndex) { + const SeamVertex& seamVertex = seamVertices[patchIndex.idxSeamVertex]; + ASSERT(seamVertex.idxVertex == i); + for (const SeamVertex::Patch& patch : seamVertex.patches) { + ASSERT(patch.idxPatch != numPatches); + ASSERT(patch.idxPatch < numPatches); // ★ 双重检查 + vertpatch2row[patch.idxPatch] = rowsX++; + } + } else if (patchIndex.idxPatch < numPatches) { + vertpatch2row[patchIndex.idxPatch] = rowsX++; + } + } + DEBUG_EXTRA("vertpatch2rows built: rowsX=%u", rowsX); + + // ========== 构建 Gamma(Tikhonov 正则)========== + const float lambda(0.1f); + MatIdx rowsGamma(0); + Mesh::VertexIdxArr adjVerts; + CLISTDEF0(MatEntry) rows(0, vertices.size() * 4); + + FOREACH(v, vertices) { + adjVerts.Empty(); + scene.mesh.GetAdjVertices(v, adjVerts); + VertexPatchIterator itV(patchIndices[v], seamVertices); + while (itV.Next()) { + const uint32_t idxPatch(itV); + if (idxPatch == numPatches) + continue; + const MatIdx col(vertpatch2rows[v].at(idxPatch)); + for (const VIndex vAdj : adjVerts) { + if (v >= vAdj) + continue; + VertexPatchIterator itVAdj(patchIndices[vAdj], seamVertices); + while (itVAdj.Next()) { + const uint32_t idxPatchAdj(itVAdj); + if (idxPatch == idxPatchAdj) { + const MatIdx colAdj(vertpatch2rows[vAdj].at(idxPatchAdj)); + float currentLambda = (vertexInvalid[v] || vertexInvalid[vAdj]) ? 0.01f : 0.1f; + rows.emplace_back(rowsGamma, col, currentLambda); + rows.emplace_back(rowsGamma, colAdj, -currentLambda); + ++rowsGamma; + } + } + } + } + } + ASSERT(rows.size() / 2 < static_cast(std::numeric_limits::max())); + DEBUG_EXTRA("Gamma rows: %zu matEntries", rows.size()); + + SparseMat Gamma(rowsGamma, rowsX); + Gamma.setFromTriplets(rows.Begin(), rows.End()); + rows.Empty(); + + // ========== 构建矩阵 A 和 b ========== + IndexArr indices; + Colors vertexColors; + Colors coeffB; + + for (const SeamVertex& seamVertex : seamVertices) { + if (seamVertex.patches.size() < 2) + continue; + seamVertex.SortByPatchIndex(indices); + vertexColors.resize(indices.size()); + + FOREACH(i, indices) { + const SeamVertex::Patch& patch0 = seamVertex.patches[indices[i]]; + ASSERT(patch0.idxPatch < numPatches); + if (patch0.idxPatch >= texturePatches.size()) { + DEBUG_EXTRA("ERROR: patch0.idxPatch=%u >= texturePatches.size()=%zu", + patch0.idxPatch, texturePatches.size()); + continue; + } + const TexturePatch& tpRef = texturePatches[patch0.idxPatch]; + if (tpRef.label < 0 || tpRef.label >= (IIndex)images.size()) { + DEBUG_EXTRA("ERROR: tpRef.label=%d out of range [0, %zu)", tpRef.label, images.size()); + continue; + } + + SampleImage sampler(images[tpRef.label].image); + for (const SeamVertex::Patch::Edge& edge : patch0.edges) { + const SeamVertex& seamVertex1 = seamVertices[edge.idxSeamVertex]; + const SeamVertex::Patches::IDX idxPatch1(seamVertex1.patches.Find(patch0.idxPatch)); + ASSERT(idxPatch1 != SeamVertex::Patches::NO_INDEX); + const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1]; + sampler.AddEdge(patch0.proj, patch1.proj); + } + vertexColors[i] = sampler.GetColor(); + } + + const VertexPatch2RowMap& vertpatch2row = vertpatch2rows[seamVertex.idxVertex]; + for (IDX i = 0; i < indices.size() - 1; ++i) { + const uint32_t idxPatch0(seamVertex.patches[indices[i]].idxPatch); + const Color& color0 = vertexColors[i]; + const MatIdx col0(vertpatch2row.at(idxPatch0)); + for (IDX j = i + 1; j < indices.size(); ++j) { + const uint32_t idxPatch1(seamVertex.patches[indices[j]].idxPatch); + const Color& color1 = vertexColors[j]; + const MatIdx col1(vertpatch2row.at(idxPatch1)); + ASSERT(idxPatch0 < idxPatch1); + const MatIdx rowA((MatIdx)coeffB.size()); + coeffB.Insert(color1 - color0); + ASSERT(ISFINITE(coeffB.back())); + rows.emplace_back(rowA, col0, 1.f); + rows.emplace_back(rowA, col1, -1.f); + } + } + } + ASSERT(coeffB.size() < static_cast(std::numeric_limits::max())); + DEBUG_EXTRA("Matrix A built: %zu constraints, rowsX=%u", coeffB.size(), rowsX); + + const MatIdx rowsA((MatIdx)coeffB.size()); + SparseMat A(rowsA, rowsX); + A.setFromTriplets(rows.Begin(), rows.End()); + rows.Release(); + + SparseMat Lhs(A.transpose() * A + Gamma.transpose() * Gamma); + Lhs.prune([](const int& row, const int& col, const float&) -> bool { + return col <= row; + }); + DEBUG_EXTRA("Lhs matrix built and pruned"); + + // ========== 求解 ========== + Eigen::Matrix colorAdjustments(rowsX, 3); + { + Eigen::ConjugateGradient solver; + solver.setMaxIterations(1000); + solver.setTolerance(0.0001f); + solver.compute(Lhs); + ASSERT(solver.info() == Eigen::Success); + #ifdef TEXOPT_USE_OPENMP + #pragma omp parallel for + #endif + for (int channel = 0; channel < 3; ++channel) { + const Eigen::Map> b( + coeffB.front().ptr() + channel, rowsA); + const Eigen::VectorXf Rhs(SparseMat(A.transpose()) * b); + const Eigen::VectorXf x(solver.solve(Rhs)); + ASSERT(solver.info() == Eigen::Success); + Eigen::Map>( + colorAdjustments.data() + channel, rowsX) = x.array() - x.mean(); + DEBUG_LEVEL(3, "\tcolor channel %d: %d iterations, %g residual", + channel, solver.iterations(), solver.error()); + } + } + DEBUG_EXTRA("CG solve done. colorAdjustments: %dx3", rowsX); + + // ========== ★★★ 关键修正:串行应用颜色修正,避免并行写同一张 images[label] ========== + // + // 原版用 #pragma omp parallel for,但不同 patch 可能共享同一个 images[label]。 + // 多个线程同时 clone() + copyTo() 同一个 cv::Mat → 数据竞争 → 堆破坏 → munmap_chunk。 + // + // 修复策略: + // 1. 先把所有 patch 的修正结果写入**独立 buffer**(按 label 分组,每个 label 一份) + // 2. 最后**串行**把每个 label 的 buffer 一次性写回 images[label] + // 这样完全避免并行写竞争。 + + // --- Step 1: 准备按 label 分组的输出 buffer --- + struct LabelBuffer { + cv::Mat buffer; // 整张图大小的 buffer,初始为原图副本 + std::vector rects; // 需要写入的 patch 区域(相对于原图) + }; + std::map labelBuffers; + + // 先按 label 收集所有需要修改的 patch + for (unsigned i = 0; i < numPatches; ++i) { + const uint32_t idxPatch = i; + const TexturePatch& tp = texturePatches[idxPatch]; + labelBuffers[tp.label].rects.push_back(tp.rect); + } + + // 为每个 label 创建整张图的深拷贝 buffer + for (auto& kv : labelBuffers) { + int label = kv.first; + if (label < 0 || label >= (int)images.size()) continue; + if (images[label].image.empty()) continue; + // ★ 整张图深拷贝,后续直接在 buffer 上改,最后整体 swap 回去 + kv.second.buffer = images[label].image.clone(); + } + DEBUG_EXTRA("Label buffers created: %zu labels involved", labelBuffers.size()); + + // --- Step 2: 并行计算每个 patch 的颜色调整(写到各自 label 的 buffer 的 ROI 里)--- + // 注意:不同 patch 如果 label 相同,会写同一个 buffer——但每个 patch 写的 ROI(rect)不同, + // 只要 rect 不重叠就没问题。同一 label 的 patch 在纹理空间里不重叠,所以安全。 + // 但为绝对安全,这里用 label 级别的锁,或者干脆串行。 + // ★ 采用:按 patch 并行,但对同一 label 的 buffer 访问加锁。 + + #ifdef TEXOPT_USE_OPENMP + #pragma omp parallel for schedule(dynamic) + #endif + for (int i = 0; i < (int)numPatches; ++i) { + const uint32_t idxPatch = (uint32_t)i; + const TexturePatch& texturePatch = texturePatches[idxPatch]; + + if (texturePatch.label < 0 || texturePatch.label >= (int)images.size()) continue; + if (texturePatch.rect.width <= 0 || texturePatch.rect.height <= 0) continue; + + auto it = labelBuffers.find(texturePatch.label); + if (it == labelBuffers.end()) continue; + cv::Mat& labelBuf = it->second.buffer; + if (labelBuf.empty()) continue; + + // 检查 rect 是否在 buffer 范围内 + if (texturePatch.rect.x < 0 || texturePatch.rect.y < 0 || + texturePatch.rect.x + texturePatch.rect.width > labelBuf.cols || + texturePatch.rect.y + texturePatch.rect.height > labelBuf.rows) { + DEBUG_EXTRA("WARN: patch %u rect %d,%d %dx%d out of image %dx%d", + idxPatch, texturePatch.rect.x, texturePatch.rect.y, + texturePatch.rect.width, texturePatch.rect.height, + labelBuf.cols, labelBuf.rows); + continue; + } + + // --- 计算该 patch 的 color adjustment 插值图(与原版相同逻辑)--- + ColorMap imageAdj(texturePatch.rect.size()); + imageAdj.memset(0); + + struct RasterPatch { + const TexCoord* tri; + Color colors[3]; + ColorMap& image; + inline RasterPatch(ColorMap& _image) : image(_image) {} + inline cv::Size Size() const { return image.size(); } + inline void operator()(const ImageRef& pt, const Point3f& bary) { + ASSERT(image.isInside(pt)); + image(pt) = colors[0] * bary.x + colors[1] * bary.y + colors[2] * bary.z; + } + } data(imageAdj); + + for (const FIndex idxFace : texturePatch.faces) { + if (idxFace >= faces.GetSize()) continue; + const Face& face = faces[idxFace]; + data.tri = faceTexcoords.Begin() + idxFace * 3; + for (int v = 0; v < 3; ++v) { + auto search = vertpatch2rows[face[v]].find(idxPatch); + if (search != vertpatch2rows[face[v]].end()) { + data.colors[v] = colorAdjustments.row(vertpatch2rows[face[v]].at(idxPatch)); + } else { + data.colors[v] = Color::ZERO; + } + } + ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data); + } + + imageAdj.DilateMean<1>(imageAdj, Color::ZERO); + + // --- ★ 写回 label buffer 的 ROI(这是 clone 出来的独立内存,安全)--- + cv::Mat roi = labelBuf(texturePatch.rect); + for (int r = 0; r < roi.rows; ++r) { + for (int c = 0; c < roi.cols; ++c) { + const Color& a = imageAdj(r, c); + if (a == Color::ZERO) + continue; + Pixel8U& v = roi.at(r, c); + const Color col(RGB2YCBCR(Color(v))); + const Color acol(YCBCR2RGB(Color(col + a))); + for (int p = 0; p < 3; ++p) + v[p] = (uint8_t)CLAMP(ROUND2INT(acol[p]), 0, 255); + } + } + } // end parallel for + + DEBUG_EXTRA("All patch adjustments written to label buffers"); + + // --- Step 3: ★ 串行、安全地写回原始 images --- + // 每个 label 只做一次 swap,彻底避免竞争 + for (const auto& kv : labelBuffers) { + int label = kv.first; + const cv::Mat& buf = kv.second.buffer; + if (label < 0 || label >= (int)images.size()) continue; + if (images[label].image.empty()) continue; + if (buf.size() != images[label].image.size()) { + DEBUG_EXTRA("WARN: label %d buffer size mismatch, skipping", label); + continue; + } + // ★ 用 swap 而不是 copyTo:O(1),不重新分配,不踩堆 + // 需要确保类型一致 + if (buf.type() == images[label].image.type()) { + cv::Mat(buf).copyTo(images[label].image); // 如果必须拷贝 + // 或者如果 images[label].image 可以接管内存:images[label].image = buf; + } else { + buf.copyTo(images[label].image); + } + } + + DEBUG_EXTRA("GlobalSeamLeveling4 finished successfully."); +} // set to one in order to dilate also on the diagonal of the border // (normally not needed) @@ -15035,8 +15313,154 @@ void MeshTexture::FeatherTextureSeams(Image8U3& texture, } } +void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( + const VirtualFaceMap& virtualFaceMap, + Image8U3& atlas, + int textureSize) +{ + if (rcPatches.empty()) return; + + DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: bridging %zu rcPatches...", rcPatches.size()); + + // ========== ★ 保存所有 images 的原始状态 ========== + std::vector originalImages(images.size()); + for (size_t k = 0; k < images.size(); ++k) { + if (!images[k].image.empty()) + originalImages[k] = images[k].image.clone(); + } + DEBUG_EXTRA("Saved %zu original images", images.size()); + + // ========== 1. 构建 texturePatches ========== + // ... (和之前完全一样的构建代码,省略) ... + texturePatches.clear(); + std::vector rcToTexPatch(rcPatches.size(), NO_ID); + // ... [构建 texturePatches,和之前一样] ... + + const size_t numValidPatches = texturePatches.size(); + DEBUG_EXTRA("Valid texturePatches: %zu (from %zu rcPatches)", numValidPatches, rcPatches.size()); + + // ========== 2. 填充依赖成员 ========== + const size_t numRealFaces = scene.mesh.faces.size(); + texturePatches.push_back(TexturePatch()); // dummy + + components.Resize(numRealFaces); + for (size_t i = 0; i < numRealFaces; ++i) components[i] = (uint32_t)numValidPatches; + for (size_t tpi = 0; tpi < numValidPatches; ++tpi) + for (FIndex fid : texturePatches[tpi].faces) + if (fid < numRealFaces) components[fid] = (uint32_t)tpi; + + mapIdxPatch.Resize(texturePatches.size()); + for (size_t i = 0; i < texturePatches.size(); ++i) mapIdxPatch[i] = (uint32_t)i; + + labelsInvalid.Resize(numRealFaces); + for (size_t i = 0; i < numRealFaces; ++i) + labelsInvalid[i] = (components[i] == (uint32_t)numValidPatches) ? 1 : NO_ID; + + if (scene.mesh.faceFaces.empty()) + scene.mesh.ListIncidenteFaceFaces(); + + seamEdges.clear(); seamVertices.clear(); + BuildSeamEdgesFromFaceToPatchID(); + CreateSeamVertices(); + + faceTexcoords.Resize(scene.mesh.faces.size() * 3); + for (size_t i = 0; i < faceTexcoords.size(); ++i) faceTexcoords[i] = TexCoord(0, 0); + for (size_t tpi = 0; tpi < numValidPatches; ++tpi) { + const TexturePatch& tp = texturePatches[tpi]; + const Image& img = images[tp.label]; + for (FIndex fid : tp.faces) { + if (fid >= scene.mesh.faces.size()) continue; + const Mesh::Face& face = scene.mesh.faces[fid]; + for (int v = 0; v < 3; ++v) { + const Point3f& vert = scene.mesh.vertices[face[v]]; + Point2f proj = img.camera.ProjectPoint(Point3d(vert)); + faceTexcoords[fid * 3 + v] = TexCoord(proj.x - tp.rect.x, proj.y - tp.rect.y); + } + } + } + + DEBUG_EXTRA("Bridge done: %zu texturePatches ready, calling GlobalSeamLeveling4...", numValidPatches); + + // ========== 调用 GSL4(内部用 label buffer,写回 images)========== + GlobalSeamLeveling4(); + DEBUG_EXTRA("GlobalSeamLeveling4 finished."); + + // ========== 3. 重光栅化(从已被 GSL4 修正的 images 读)========== + ASSERT(atlas.rows == textureSize && atlas.cols == textureSize); + for (int pi = 0; pi < (int)rcPatches.size(); ++pi) { // 串行,简单安全 + if (rcToTexPatch[pi] == NO_ID) continue; + const TexturePatch& tp = texturePatches[rcToTexPatch[pi]]; + const Image& srcImg = images[tp.label]; + if (srcImg.image.empty()) continue; + cv::Mat correctedPatch = srcImg.image(tp.rect).clone(); + if (correctedPatch.empty()) continue; + + const RCPatch& rp = rcPatches[pi]; + for (FIndex vfID : rp.faces) { + if (vfID >= m_virtualFaceGeometries.size()) continue; + const VirtualFaceGeometry& geom = m_virtualFaceGeometries[vfID]; + if (!geom.isValid) continue; + + int minX = std::max(0, (int)floor(geom.uvBounds.ptMin.x() * textureSize)); + int maxX = std::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.x() * textureSize)); + int minY = std::max(0, (int)floor(geom.uvBounds.ptMin.y() * textureSize)); + int maxY = std::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.y() * textureSize)); + if (minX > maxX || minY > maxY) continue; + + int patchW = maxX - minX + 1, patchH = maxY - minY + 1; + cv::Mat mapX(patchH, patchW, CV_32FC1), mapY(patchH, patchW, CV_32FC1); + const float* H = geom.homography.ptr(); + for (int y = minY; y <= maxY; ++y) + for (int x = minX; x <= maxX; ++x) { + float u = (float)x / textureSize, v = (float)y / textureSize; + float w = H[6]*u + H[7]*v + H[8]; + if (std::abs(w) < 1e-12f) { + mapX.at(y-minY, x-minX) = -1; + mapY.at(y-minY, x-minX) = -1; continue; + } + mapX.at(y-minY, x-minX) = (H[0]*u + H[1]*v + H[2])/w - tp.rect.x; + mapY.at(y-minY, x-minX) = (H[3]*u + H[4]*v + H[5])/w - tp.rect.y; + } + + cv::Mat remapped; + cv::remap(correctedPatch, remapped, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); + + for (int y = 0; y < patchH; ++y) + for (int x = 0; x < patchW; ++x) { + cv::Vec3b color = remapped.at(y, x); + if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue; + int atlasX = x + minX, atlasY = y + minY; + if (atlasX < 0 || atlasX >= textureSize || atlasY < 0 || atlasY >= textureSize) continue; + atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]}; + } + } + } + DEBUG_EXTRA("Re-rasterization done."); + + // ========== ★ 恢复原始 images(让析构时内存完全干净)========== + for (size_t k = 0; k < images.size(); ++k) { + if (!originalImages[k].empty()) { + // 用 assign 而不是 copyTo,确保引用计数干净 + images[k].image = originalImages[k].clone(); + } + } + DEBUG_EXTRA("Original images restored."); + + // ========== 4. 清理成员变量 ========== + texturePatches.clear(); + components.Release(); + mapIdxPatch.Release(); + labelsInvalid.Release(); + seamEdges.clear(); + seamVertices.clear(); + faceTexcoords.Release(); + + DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: ALL DONE."); +} + void MeshTexture::BuildSeamEdgesFromFaceToPatchID() { rcSeamEdges.clear(); + seamEdges.clear(); // ← 清空并填充 seamEdges const size_t numFaces = scene.mesh.faces.size(); for (FIndex fid = 0; fid < (FIndex)numFaces; ++fid) { if (fid >= faceToPatchID.size()) continue; @@ -15050,7 +15474,11 @@ void MeshTexture::BuildSeamEdgesFromFaceToPatchID() { if (adjFid >= faceToPatchID.size()) continue; const uint32_t pidB = faceToPatchID[adjFid]; if (pidB == UINT32_MAX || pidB == pidA) continue; - // 避免重复添加 + + // ★ 关键:往 seamEdges 里填面 ID 对,供 CreateSeamVertices 用 + seamEdges.emplace_back(fid, adjFid); + + // rcSeamEdges 保留(patch ID 对,供其他地方用) bool exists = false; for (const SeamEdge& se : rcSeamEdges) { if ((se.rcPatchID0 == pidA && se.rcPatchID1 == pidB) || @@ -15062,12 +15490,12 @@ void MeshTexture::BuildSeamEdgesFromFaceToPatchID() { SeamEdge edge; edge.rcPatchID0 = pidA; edge.rcPatchID1 = pidB; - // uv0/uv1 留空,让 CG 用 patchAvgColor fallback rcSeamEdges.push_back(edge); } } } - DEBUG_EXTRA("BuildSeamEdgesFromFaceToPatchID: %zu edges", rcSeamEdges.size()); + DEBUG_EXTRA("BuildSeamEdgesFromFaceToPatchID: %u seamEdges, %zu rcSeamEdges", + seamEdges.GetSize(), rcSeamEdges.size()); } void MeshTexture::MergeSameViewPatches() @@ -21376,7 +21804,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi return false; texture.MergeSameViewPatches(); - texture.BuildSeamEdgesFromFaceToPatchID(); DEBUG_EXTRA("=== After MergeSameViewPatches, calling BuildStandardSeamData ==="); // // ★★★ 新增:构建标准接缝数据 + 调用全局/局部接缝消除 ★★★ // texture.BuildStandardSeamDataFromRCPatches( @@ -21389,6 +21816,12 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi // texture.GlobalAlignPatches(textures.back(), nTextureSizeMultiple); // texture.LocalBlendSeams(textures.back(), nTextureSizeMultiple); + // ★★★ 新增:调用 GlobalSeamLeveling3 并重新光栅化 ★★★ + texture.ApplyGlobalSeamLevelingOnRCPatches( + virtualFaceMap, + textures.back(), // atlas + nTextureSizeMultiple); // textureSize + mesh.texturesDiffuse = std::move(textures); DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)", mesh.faces.size(), TD_TIMER_GET_FMT().c_str());