From e555b9c804899402eba5c206db8c97c0c055c7bc Mon Sep 17 00:00:00 2001 From: hesuicong Date: Tue, 15 Sep 2026 18:29:24 +0800 Subject: [PATCH] =?UTF-8?q?=E6=9C=80=E6=96=B0=E4=BC=98=E5=8C=96=E7=BB=93?= =?UTF-8?q?=E6=9E=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 252 +++++++++++++++++--------------------- 1 file changed, 110 insertions(+), 142 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index c5cf658..f9730a7 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -9213,8 +9213,6 @@ void MeshTexture::GlobalSeamLeveling4() 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()); @@ -9234,7 +9232,7 @@ void MeshTexture::GlobalSeamLeveling4() if (components[f] == NO_ID) { const Face& face = faces[f]; for (int v = 0; v < 3; ++v) - patchIndices[face[v]].idxPatch = numPatches; // dummy,后面跳过 + patchIndices[face[v]].idxPatch = numPatches; continue; } const uint32_t idxPatch(mapIdxPatch[components[f]]); @@ -9246,13 +9244,13 @@ void MeshTexture::GlobalSeamLeveling4() FOREACH(i, seamVertices) { const SeamVertex& seamVertex = seamVertices[i]; ASSERT(!seamVertex.patches.empty()); - ASSERT(seamVertex.idxVertex < vertices.size()); + ASSERT(seamVertex.idxVertex < vertices.size()); PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex]; patchIndex.bIndex = true; patchIndex.idxSeamVertex = i; } - // ========== 构建 vertpatch2rows(块列号 → 连续行)========== + // ========== 构建 vertpatch2rows ========== ASSERT(vertices.size() < static_cast(std::numeric_limits::max())); MatIdx rowsX(0); typedef std::unordered_map VertexPatch2RowMap; @@ -9268,18 +9266,18 @@ void MeshTexture::GlobalSeamLeveling4() ASSERT(patch.idxPatch != numPatches); ASSERT(patch.idxPatch < numPatches); if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end()) - continue; // 去重 + continue; vertpatch2row[patch.idxPatch] = rowsX++; } } else if (patchIndex.idxPatch < numPatches) { vertpatch2row[patchIndex.idxPatch] = rowsX++; } } - const Eigen::Index scalarCols = (Eigen::Index)rowsX * 3; // 每块 3 个标量列 (RGB) - DEBUG_EXTRA("vertpatch2rows built: rowsX(blocks)=%u, scalarCols=%lld", rowsX, (long long)scalarCols); + const Eigen::Index scalarCols = (Eigen::Index)rowsX * 3; + DEBUG_EXTRA("vertpatch2rows built: rowsX=%u, scalarCols=%lld", rowsX, (long long)scalarCols); // ============================================================ - // ========== 构建 Gamma(Tikhonov 正则)========== [修复1: 去重] + // ========== 构建 Gamma(Tikhonov 正则)========== // ============================================================ std::vector gammaTriplets; gammaTriplets.reserve((size_t)vertices.size() * 8); @@ -9293,7 +9291,7 @@ void MeshTexture::GlobalSeamLeveling4() adjVerts.Empty(); scene.mesh.GetAdjVertices(v, adjVerts); - const size_t MAX_ADJ = 8; // ★ 限制邻接度数,控制规模 + const size_t MAX_ADJ = 8; if (adjVerts.GetSize() > MAX_ADJ) adjVerts.Resize(MAX_ADJ); VertexPatchIterator itV(patchIndices[v], seamVertices); @@ -9305,11 +9303,11 @@ void MeshTexture::GlobalSeamLeveling4() const Eigen::Index colA = it_row->second; for (const VIndex vAdj : adjVerts) { - if (v >= vAdj) continue; // ★ 无向边只处理一次(去重关键) + if (v >= vAdj) continue; VertexPatchIterator itVAdj(patchIndices[vAdj], seamVertices); while (itVAdj.Next()) { const uint32_t idxPatchAdj(itVAdj); - if (idxPatch != idxPatchAdj) continue; // 同一 patch 才正则化 + if (idxPatch != idxPatchAdj) continue; auto it_rowAdj = vertpatch2rows[vAdj].find(idxPatchAdj); if (it_rowAdj == vertpatch2rows[vAdj].end()) continue; const Eigen::Index colB = it_rowAdj->second; @@ -9321,7 +9319,6 @@ void MeshTexture::GlobalSeamLeveling4() } } - // ★ 排序 + 去重合并(消除重复邻接 → 保证矩阵性质良好) std::sort(rawGamma.begin(), rawGamma.end(), [](const GammaRaw& a, const GammaRaw& b) { return a.colA < b.colA || (a.colA == b.colA && a.colB < b.colB); @@ -9339,16 +9336,12 @@ void MeshTexture::GlobalSeamLeveling4() i = j; } const Eigen::Index rowsGamma = rowG; - DEBUG_EXTRA("Gamma after dedup: triplets=%zu, rowsGamma=%lld, scalarCols=%lld", - gammaTriplets.size(), (long long)rowsGamma, (long long)scalarCols); SparseMat Gamma(rowsGamma, scalarCols); Gamma.setFromTriplets(gammaTriplets.begin(), gammaTriplets.end()); Gamma.makeCompressed(); gammaTriplets.clear(); gammaTriplets.shrink_to_fit(); rawGamma.clear(); rawGamma.shrink_to_fit(); - DEBUG_EXTRA("Gamma built: %lld x %lld, nnz=%lld", (long long)Gamma.rows(), - (long long)Gamma.cols(), (long long)Gamma.nonZeros()); // ============================================================ // ========== 构建矩阵 A 和 b(颜色一致性约束)========== @@ -9373,13 +9366,26 @@ void MeshTexture::GlobalSeamLeveling4() const TexturePatch& tpRef = texturePatches[patch0.idxPatch]; if (tpRef.label < 0 || tpRef.label >= (IIndex)images.size()) continue; + // ★FIX: 检查 patch0.proj 是否为 NaN/Inf(崩溃根源) + if (std::isnan(patch0.proj.x) || std::isnan(patch0.proj.y) || + std::isinf(patch0.proj.x) || std::isinf(patch0.proj.y)) { + vertexColors[i] = Color::ZERO; + continue; + } + SampleImage sampler(images[tpRef.label].image); for (const SeamVertex::Patch::Edge& edge : patch0.edges) { - if (edge.idxSeamVertex >= seamVertices.GetSize()) continue; // 越界保护 + if (edge.idxSeamVertex >= seamVertices.GetSize()) continue; const SeamVertex& seamVertex1 = seamVertices[edge.idxSeamVertex]; const auto idxPatch1 = seamVertex1.patches.Find(patch0.idxPatch); if (idxPatch1 == SeamVertex::Patches::NO_INDEX) continue; const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1]; + + // ★FIX: 检查 patch1.proj 是否为 NaN/Inf,防止 AddEdge 崩溃 + if (std::isnan(patch1.proj.x) || std::isnan(patch1.proj.y) || + std::isinf(patch1.proj.x) || std::isinf(patch1.proj.y)) + continue; + sampler.AddEdge(patch0.proj, patch1.proj); } vertexColors[i] = sampler.GetColor(); @@ -9398,185 +9404,147 @@ void MeshTexture::GlobalSeamLeveling4() const Color& ca = vertexColors[i]; const Color& cb = vertexColors[j]; for (int c = 0; c < 3; ++c) { - const Eigen::Index colA = baseA * 3 + c; // ★ 标量列 = base*3 + c + const Eigen::Index colA = baseA * 3 + c; const Eigen::Index colB = baseB * 3 + c; ATriplets.emplace_back(rowA, colA, 1.0f); ATriplets.emplace_back(rowA, colB, -1.0f); if ((size_t)rowA >= b_coeff.size()) b_coeff.push_back(0); - b_coeff[rowA] = cb[c] - ca[c]; // 差值方程 + b_coeff[rowA] = cb[c] - ca[c]; ++rowA; } } } } const Eigen::Index rowsA = rowA; - ASSERT(rowsA <= (Eigen::Index)b_coeff.size()); SparseMat A(rowsA, scalarCols); A.setFromTriplets(ATriplets.begin(), ATriplets.end()); A.makeCompressed(); ATriplets.clear(); ATriplets.shrink_to_fit(); - DEBUG_EXTRA("Matrix A built: %lld constraints, scalarCols=%lld, nnz=%lld", - (long long)rowsA, (long long)scalarCols, (long long)A.nonZeros()); // ============================================================ - // ========== 求解:Lhs = AᵀA + λ²ΓᵀΓ,对角扰动保证正定 [修复2] + // ========== 求解 ========== // ============================================================ std::vector colorAdjustments; colorAdjustments.assign(rowsX, Color::ZERO); if (rowsA > 0 && rowsX > 0) { - // ★ Lhs = AᵀA + λ² ΓᵀΓ SparseMat ATA = A.transpose() * A; SparseMat GTG = Gamma.transpose() * Gamma; - const float lambda = 0.5f; // ★ 正则化强度(可调) + const float lambda = 0.1f; GTG *= lambda * lambda; - ASSERT(ATA.rows() == GTG.rows() && ATA.cols() == GTG.cols()); // 尺寸一致性检查 ATA += GTG; SparseMat& Lhs = ATA; - // ★★★ 对角加扰动,严格保证正定 ★★★ - const float eps = 1e-3f; // 足够小不影响结果,保证正定 + const float eps = 1e-3f; for (Eigen::Index i = 0; i < (Eigen::Index)Lhs.rows(); ++i) { Lhs.coeffRef(i, i) += eps; } - DEBUG_EXTRA("Lhs built: %lld x %lld, nnz=%lld (diag += %g)", - (long long)Lhs.rows(), (long long)Lhs.cols(), - (long long)Lhs.nonZeros(), eps); - // ★ 用 ConjugateGradient(只需对称,不要求严格 SPD) Eigen::ConjugateGradient solver; solver.setMaxIterations(2000); solver.setTolerance(0.001f); solver.compute(Lhs); - if (solver.info() != Eigen::Success) { - DEBUG_EXTRA("WARN: Lhs compute failed (info=%d), skipping solve", (int)solver.info()); - } else { - // ★ b 与通道无关 → 一次求解,结果按 InnerStride<3> 分通道 + if (solver.info() == Eigen::Success) { Eigen::VectorXf b(rowsA); for (Eigen::Index k = 0; k < rowsA; ++k) b(k) = b_coeff[k]; const Eigen::VectorXf rhs = A.transpose() * b; const Eigen::VectorXf x = solver.solve(rhs); - DEBUG_EXTRA("solve: %d iterations, residual=%g", solver.iterations(), solver.error()); - - // ★ 从 x 提取 colorAdjustments(每块一个 Color,显式循环避免对齐问题) - if (solver.info() == Eigen::Success && (Eigen::Index)(x.size()) >= scalarCols) { - colorAdjustments.assign(rowsX, Color::ZERO); - for (Eigen::Index v = 0; v < (Eigen::Index)rowsX; ++v) { - const float r = x(v * 3 + 0); - const float g = x(v * 3 + 1); - const float b_ch = x(v * 3 + 2); + + if (solver.info() == Eigen::Success && (Eigen::Index)(x.size()) >= scalarCols) { + colorAdjustments.assign(rowsX, Color::ZERO); + for (Eigen::Index v = 0; v < (Eigen::Index)rowsX; ++v) { + const float r = x(v * 3 + 0); + const float g = x(v * 3 + 1); + const float b_ch = x(v * 3 + 2); + + // ★ 恢复原版逻辑:计算均值并减去(消除亮度偏移,只保留色度调整) const float mean = (r + g + b_ch) / 3.0f; - // colorAdjustments[v] = Color(r, g, b_ch); - colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean); - } - } else { - DEBUG_EXTRA("WARN: solve not successful, using zero adjustments"); - } + colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean); + } + } } - } else { - DEBUG_EXTRA("WARN: no constraints (rowsA=%lld), skip solve", (long long)rowsA); } - DEBUG_EXTRA("solve done. colorAdjustments: %zux3", rowsX); + b_coeff.clear(); b_coeff.shrink_to_fit(); - // ========== 应用颜色修正(输出到 correctedPatchImages,不碰 images)========== - correctedPatchImages.resize(numPatches); + // ========== 应用颜色修正 ========== + correctedPatchImages.resize(numPatches); - #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]; + #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; + if (texturePatch.label < 0 || texturePatch.label >= (int)images.size()) continue; + if (texturePatch.rect.width <= 0 || texturePatch.rect.height <= 0) continue; - // ★ 从原始 images 读(只读,不修改) - const cv::Mat& srcROI = images[texturePatch.label].image(texturePatch.rect); - if (srcROI.empty()) continue; + const cv::Mat& srcROI = images[texturePatch.label].image(texturePatch.rect); + if (srcROI.empty()) continue; - // ★ 创建独立输出 buffer - cv::Mat& outPatch = correctedPatchImages[idxPatch]; - outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3); + cv::Mat& outPatch = correctedPatchImages[idxPatch]; + outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3); - ColorMap imageAdj(texturePatch.rect.size()); - imageAdj.memset(0); + ColorMap imageAdj(texturePatch.rect.size()); + imageAdj.memset(0); - struct RasterPatch { - const TexCoord* tri; - Color colors[3]; - ColorMap& image; - RasterPatch(ColorMap& _image) : image(_image) {} - inline cv::Size Size() const { return image.size(); } + struct RasterPatch { + const TexCoord* tri; + Color colors[3]; + ColorMap& image; + RasterPatch(ColorMap& _image) : image(_image) {} + inline cv::Size Size() const { return image.size(); } void operator()(const ImageRef& pt, const Point3f& bary) { - image(pt) = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z; + Color c = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z; + // ★ 过滤 NaN/Inf 调整值 + if (std::isnan(c[0]) || std::isnan(c[1]) || std::isnan(c[2]) || + std::isinf(c[0]) || std::isinf(c[1]) || std::isinf(c[2])) { + return; + } + image(pt) = c; } - } data(imageAdj); - ASSERT(texturePatch.faces.size() < 1000000); // 合理范围 - for (const FIndex idxFace : texturePatch.faces) { - if (idxFace >= faces.GetSize()) continue; - const Face& face = faces[idxFace]; + } data(imageAdj); - ASSERT(idxFace * 3 + 2 < faceTexcoords.GetSize()); - for (int v = 0; v < 3; ++v) { - ASSERT(face[v] < vertices.size()); - ASSERT(face[v] < vertpatch2rows.size()); - } + for (const FIndex idxFace : texturePatch.faces) { + if (idxFace >= faces.GetSize()) continue; + const Face& face = faces[idxFace]; + data.tri = faceTexcoords.Begin() + idxFace * 3; - // // ★ UV [0,1] → patch 局部像素坐标 - // const cv::Size patchSize = texturePatch.rect.size(); - // TexCoord localTri[3]; - // const TexCoord* uvTri = faceTexcoords.Begin() + idxFace * 3; - // for (int v = 0; v < 3; ++v) { - // localTri[v] = TexCoord(uvTri[v].x * patchSize.width, - // uvTri[v].y * patchSize.height); - // } - // data.tri = localTri; - // 改回直接用,不乘 - data.tri = faceTexcoords.Begin() + idxFace * 3; + for (int v = 0; v < 3; ++v) { + ASSERT(face[v] < vertices.size()); + auto search = vertpatch2rows[face[v]].find(idxPatch); + if (search != vertpatch2rows[face[v]].end()) { + ASSERT(search->second < (Eigen::Index)colorAdjustments.size()); + data.colors[v] = colorAdjustments[search->second]; + } else { + data.colors[v] = Color::ZERO; + } + } + ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data); + } - for (int v = 0; v < 3; ++v) { - ASSERT(face[v] < vertices.size()); - auto search = vertpatch2rows[face[v]].find(idxPatch); - if (search != vertpatch2rows[face[v]].end()) - { - ASSERT(search->second < (Eigen::Index)colorAdjustments.size()); - data.colors[v] = colorAdjustments[search->second]; - } - else - data.colors[v] = Color::ZERO; - } - ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data); - } - imageAdj.DilateMean<5>(imageAdj, Color::ZERO); - - // ★ 把修正叠加到 outPatch(从 srcROI 读原始颜色) - for (int r = 0; r < srcROI.rows; ++r) { - for (int c = 0; c < srcROI.cols; ++c) { - const Color& a = imageAdj(r, c); - if (a == Color::ZERO) { - outPatch.at(r, c) = srcROI.at(r, c); - continue; - } - const Pixel8U& v = srcROI.at(r, c); - const Color col(RGB2YCBCR(Color(v))); + // ★FIX: 改回 DilateMean<1>(和原版一致,之前 <5> 过度模糊) + imageAdj.DilateMean<1>(imageAdj, Color::ZERO); - // const Color acol(YCBCR2RGB(Color(col + a))); - const float GAIN = 10.0f; // ★ 增益,可调(5~20) - const Color acol(YCBCR2RGB(Color( - col[0] + a[0] * 1.0f, // Y 微调 - col[1] + a[1] * GAIN, // Cb 大幅调 - col[2] + a[2] * GAIN // Cr 大幅调 - ))); - - outPatch.at(r, c) = cv::Vec3b( - (uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255), - (uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255), - (uint8_t)CLAMP(ROUND2INT(acol[2]), 0, 255)); - } - } - } - DEBUG_EXTRA("All patch adjustments applied (to local buffers)"); + // ★FIX: 去掉 GAIN=10,Y/Cb/Cr 全部 1:1 微调(和原版 GSL3 一致) + for (int r = 0; r < srcROI.rows; ++r) { + for (int c = 0; c < srcROI.cols; ++c) { + const Color& a = imageAdj(r, c); + if (a == Color::ZERO) { + outPatch.at(r, c) = srcROI.at(r, c); + continue; + } + const Pixel8U& v = srcROI.at(r, c); + const Color col(RGB2YCBCR(Color(v))); + // ★ 直接加,不放大任何通道 + const Color acol(YCBCR2RGB(Color(col + a))); + outPatch.at(r, c) = cv::Vec3b( + (uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255), + (uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255), + (uint8_t)CLAMP(ROUND2INT(acol[2]), 0, 255)); + } + } + } DEBUG_EXTRA("GlobalSeamLeveling4 finished successfully."); }