From c809edbc8e92aaaeba84241739c7e65e7034dfe5 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Sat, 19 Sep 2026 11:04:54 +0800 Subject: [PATCH] =?UTF-8?q?=E7=9A=AE=E8=82=A4=E5=9F=BA=E6=9C=AC=E6=B2=A1?= =?UTF-8?q?=E6=9C=89=E6=9F=93=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 804 ++++++++++++++++++++++++-------------- 1 file changed, 503 insertions(+), 301 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index f7253ef..f3edd18 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -791,6 +791,14 @@ public: const VirtualFaceMap& virtualFaceMap, Image8U3& atlas, int textureSize); + + // 在 class MeshTexture 的 public 或 private 区域添加 + struct AffineCorrection { + float kr, kg, kb; // gain per channel + float br, bg, bb; // bias per channel + }; + std::vector patchAffineByTexPatch; // index = texturePatch ID + void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize); bool RasterizeVirtualFaces( const VirtualFaceMap& virtualFaceMap, @@ -9205,15 +9213,7 @@ void MeshTexture::GlobalSeamLeveling4() } 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("GlobalSeamLeveling4 (Affine): numPatches=%u", numPatches); // ========== 标记无效顶点 ========== BoolArr vertexInvalid(vertices.size()); @@ -9244,15 +9244,13 @@ void MeshTexture::GlobalSeamLeveling4() FOREACH(i, seamVertices) { const SeamVertex& seamVertex = seamVertices[i]; - ASSERT(!seamVertex.patches.empty()); - ASSERT(seamVertex.idxVertex < vertices.size()); PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex]; patchIndex.bIndex = true; patchIndex.idxSeamVertex = i; } // ========== 构建 vertpatch2rows ========== - ASSERT(vertices.size() < static_cast(std::numeric_limits::max())); + // ★ 每个 vertpatch 有 6 个变量: scale_R, scale_G, scale_B, offset_R, offset_G, offset_B MatIdx rowsX(0); typedef std::unordered_map VertexPatch2RowMap; std::vector vertpatch2rows(vertices.size()); @@ -9262,38 +9260,31 @@ void MeshTexture::GlobalSeamLeveling4() 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); - if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end()) - continue; + if (patch.idxPatch >= numPatches) continue; + if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end()) continue; vertpatch2row[patch.idxPatch] = rowsX++; } } else if (patchIndex.idxPatch < numPatches) { vertpatch2row[patchIndex.idxPatch] = rowsX++; } } - const Eigen::Index scalarCols = (Eigen::Index)rowsX * 3; + const Eigen::Index scalarCols = (Eigen::Index)rowsX * 6; // ★ 6 variables per vertpatch DEBUG_EXTRA("vertpatch2rows built: rowsX=%u, scalarCols=%lld", rowsX, (long long)scalarCols); - // ============================================================ - // ========== 构建 Gamma(Tikhonov 正则)========== - // ============================================================ + // ========== 构建 Gamma(平滑正则)========== std::vector gammaTriplets; - gammaTriplets.reserve((size_t)vertices.size() * 8); + gammaTriplets.reserve((size_t)vertices.size() * 12); struct GammaRaw { Eigen::Index colA, colB; float w; }; std::vector rawGamma; - rawGamma.reserve(vertices.size() * 6); + rawGamma.reserve(vertices.size() * 8); Mesh::VertexIdxArr adjVerts; FOREACH(v, vertices) { adjVerts.Empty(); scene.mesh.GetAdjVertices(v, adjVerts); - - const size_t MAX_ADJ = 8; - if (adjVerts.GetSize() > MAX_ADJ) adjVerts.Resize(MAX_ADJ); + if (adjVerts.GetSize() > 8) adjVerts.Resize(8); VertexPatchIterator itV(patchIndices[v], seamVertices); while (itV.Next()) { @@ -9301,7 +9292,7 @@ void MeshTexture::GlobalSeamLeveling4() if (idxPatch == numPatches) continue; auto it_row = vertpatch2rows[v].find(idxPatch); if (it_row == vertpatch2rows[v].end()) continue; - const Eigen::Index colA = it_row->second; + const Eigen::Index baseA = it_row->second * 6; // ★ base * 6 for (const VIndex vAdj : adjVerts) { if (v >= vAdj) continue; @@ -9311,10 +9302,13 @@ void MeshTexture::GlobalSeamLeveling4() 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; + const Eigen::Index baseB = it_rowAdj->second * 6; - const float w = (vertexInvalid[v] || vertexInvalid[vAdj]) ? 0.05f : 0.5f; - rawGamma.push_back({colA, colB, w}); + const float w = (vertexInvalid[v] || vertexInvalid[vAdj]) ? 0.02f : 0.3f; + // scale 和 offset 都加平滑 + for (int k = 0; k < 6; ++k) { + rawGamma.push_back({baseA + k, baseB + k, w}); + } } } } @@ -9330,27 +9324,23 @@ void MeshTexture::GlobalSeamLeveling4() size_t j = i + 1; while (j < rawGamma.size() && rawGamma[j].colA == rawGamma[i].colA && rawGamma[j].colB == rawGamma[i].colB) ++j; - const float w = rawGamma[i].w; - gammaTriplets.emplace_back(rowG, rawGamma[i].colA, w); - gammaTriplets.emplace_back(rowG, rawGamma[i].colB, -w); + gammaTriplets.emplace_back(rowG, rawGamma[i].colA, rawGamma[i].w); + gammaTriplets.emplace_back(rowG, rawGamma[i].colB, -rawGamma[i].w); ++rowG; i = j; } - const Eigen::Index rowsGamma = rowG; - SparseMat Gamma(rowsGamma, scalarCols); + SparseMat Gamma(rowG, scalarCols); Gamma.setFromTriplets(gammaTriplets.begin(), gammaTriplets.end()); Gamma.makeCompressed(); gammaTriplets.clear(); gammaTriplets.shrink_to_fit(); rawGamma.clear(); rawGamma.shrink_to_fit(); - // ============================================================ - // ========== 构建矩阵 A 和 b(颜色一致性约束)========== - // ============================================================ + // ========== 构建约束 A * x = b ========== std::vector ATriplets; - ATriplets.reserve(seamVertices.GetSize() * 6); + ATriplets.reserve(seamVertices.GetSize() * 12); std::vector b_coeff; - b_coeff.reserve(seamVertices.GetSize() * 6); + b_coeff.reserve(seamVertices.GetSize() * 12); IndexArr indices; Colors vertexColors; @@ -9367,9 +9357,7 @@ 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)) { + if (std::isnan(patch0.proj.x) || std::isnan(patch0.proj.y)) { vertexColors[i] = Color::ZERO; continue; } @@ -9381,12 +9369,7 @@ void MeshTexture::GlobalSeamLeveling4() 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; - + if (std::isnan(patch1.proj.x) || std::isnan(patch1.proj.y)) continue; sampler.AddEdge(patch0.proj, patch1.proj); } vertexColors[i] = sampler.GetColor(); @@ -9399,73 +9382,127 @@ void MeshTexture::GlobalSeamLeveling4() const uint32_t ipb = seamVertex.patches[indices[j]].idxPatch; auto itA = v2r.find(ipa), itB = v2r.find(ipb); if (itA == v2r.end() || itB == v2r.end()) continue; - const Eigen::Index baseA = itA->second; - const Eigen::Index baseB = itB->second; + + const Eigen::Index baseA = itA->second * 6; // ★ ×6 + const Eigen::Index baseB = itB->second * 6; const Color& ca = vertexColors[i]; const Color& cb = vertexColors[j]; - for (int c = 0; c < 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]; - ++rowA; - } + + // ★ 仿射约束: scale_A * ca + offset_A = scale_B * cb + offset_B + // 即: scale_A * ca - scale_B * cb + offset_A - offset_B = 0 + for (int c = 0; c < 3; ++c) { + const float norm_ca = ca[c] / 255.0f; // ★ 归一化 + const float norm_cb = cb[c] / 255.0f; // ★ 归一化 + + const Eigen::Index colA_scale = baseA + c; + const Eigen::Index colA_offset = baseA + 3 + c; + const Eigen::Index colB_scale = baseB + c; + const Eigen::Index colB_offset = baseB + 3 + c; + + ATriplets.emplace_back(rowA, colA_scale, norm_ca); + ATriplets.emplace_back(rowA, colB_scale, -norm_cb); + ATriplets.emplace_back(rowA, colA_offset, 1.0f); + ATriplets.emplace_back(rowA, colB_offset, -1.0f); + + if ((size_t)rowA >= b_coeff.size()) b_coeff.push_back(0); + b_coeff[rowA] = 1.0f; + ++rowA; + } } } } + const Eigen::Index rowsA = rowA; SparseMat A(rowsA, scalarCols); A.setFromTriplets(ATriplets.begin(), ATriplets.end()); A.makeCompressed(); ATriplets.clear(); ATriplets.shrink_to_fit(); - // ============================================================ - // ========== 求解 ========== - // ============================================================ - std::vector colorAdjustments; - colorAdjustments.assign(rowsX, Color::ZERO); - - if (rowsA > 0 && rowsX > 0) { - SparseMat ATA = A.transpose() * A; - SparseMat GTG = Gamma.transpose() * Gamma; - const float lambda = 0.1f; - GTG *= lambda * lambda; - ATA += GTG; - SparseMat& Lhs = ATA; - - const float eps = 1e-3f; - for (Eigen::Index i = 0; i < (Eigen::Index)Lhs.rows(); ++i) { - Lhs.coeffRef(i, i) += eps; - } - - Eigen::ConjugateGradient solver; - solver.setMaxIterations(2000); - solver.setTolerance(0.001f); - solver.compute(Lhs); - 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); + // ========== 求解 ========== + struct AffineParam { float scale[3]; float offset[3]; }; + std::vector patchAffine; + patchAffine.assign(rowsX, {{1,1,1}, {0,0,0}}); + + if (rowsA > 0 && rowsX > 0) { + SparseMat ATA = A.transpose() * A; + SparseMat GTG = Gamma.transpose() * Gamma; + const float lambda = 20.0f; + GTG *= lambda * lambda; + ATA += GTG; + SparseMat& Lhs = ATA; + + // ★ 正确构建 rhs = A^T * b + Eigen::VectorXf rhs = A.transpose() * Eigen::Map(b_coeff.data(), rowsA); + + // ★ 正则化:同时加 Lhs 和 rhs + const float lambda_identity = 20.0f; // ★ 增大 + for (Eigen::Index v = 0; v < (Eigen::Index)rowsX; ++v) { + for (int c = 0; c < 3; ++c) { + const Eigen::Index col_scale = v * 6 + c; + const Eigen::Index col_offset = v * 6 + 3 + c; + + Lhs.coeffRef(col_scale, col_scale) += lambda_identity; + Lhs.coeffRef(col_offset, col_offset) += lambda_identity * 0.5f; + + rhs(col_scale) += lambda_identity * 1.0f; + // rhs(col_offset) += 0 (target = 0) + } + } + + const float eps = 1e-4f; + for (Eigen::Index i = 0; i < (Eigen::Index)Lhs.rows(); ++i) + Lhs.coeffRef(i, i) += eps; + + Eigen::ConjugateGradient solver; + solver.setMaxIterations(3000); + solver.setTolerance(0.001f); + solver.compute(Lhs); + if (solver.info() == Eigen::Success) { + const Eigen::VectorXf x = solver.solve(rhs); + + // ★★★ 放在这里 ★★★ + DEBUG_EXTRA("Solver: info=%d iterations=%d error=%.6f", + (int)solver.info(), solver.iterations(), solver.error()); + + printf(">>> MY_NEW_CODE_IS_RUNNING <<<\n"); + fflush(stdout); + + // ★ 同时把 RAW x stats 也放这里 + if ((Eigen::Index)(x.size()) >= scalarCols) { + float x_min = x.minCoeff(), x_max = x.maxCoeff(); + float x_mean = x.mean(); + DEBUG_EXTRA("RAW x stats: min=%.4f max=%.4f mean=%.4f", x_min, x_max, x_mean); + } 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 - mean, g - mean, b_ch - mean); + AffineParam& param = patchAffine[v]; + for (int c = 0; c < 3; ++c) { + param.scale[c] = std::max(-2.0f, std::min(4.0f, x(v * 6 + c))); + param.offset[c] = std::max(-1.0f, std::min(1.0f, x(v * 6 + 3 + c))); + } } } + } + } + b_coeff.clear(); b_coeff.shrink_to_fit(); + + // ========== 统计 ========== + { + float s_sum=0, o_sum=0, s_mn=1e9, s_mx=-1e9, o_mn=1e9, o_mx=-1e9; + int cnt=0; + for (const auto& p : patchAffine) { + for (int c=0; c<3; ++c) { + s_sum += p.scale[c]; o_sum += p.offset[c]; + s_mn = std::min(s_mn, p.scale[c]); s_mx = std::max(s_mx, p.scale[c]); + o_mn = std::min(o_mn, p.offset[c]); o_mx = std::max(o_mx, p.offset[c]); + ++cnt; + } } + DEBUG_EXTRA("Affine stats: scale min=%.3f max=%.3f mean=%.3f | offset min=%.1f max=%.1f mean=%.1f", + s_mn, s_mx, s_sum/cnt, o_mn, o_mx, o_sum/(cnt*3)); } - b_coeff.clear(); b_coeff.shrink_to_fit(); // ========== 应用颜色修正 ========== correctedPatchImages.resize(numPatches); @@ -9486,25 +9523,34 @@ void MeshTexture::GlobalSeamLeveling4() cv::Mat& outPatch = correctedPatchImages[idxPatch]; outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3); - ColorMap imageAdj(texturePatch.rect.size()); - imageAdj.memset(0); + ColorMap imageAffine(texturePatch.rect.size()); + // 初始化为 identity (scale=1, offset=0) + for (int _r = 0; _r < imageAffine.rows; ++_r) + for (int _c = 0; _c < imageAffine.cols; ++_c) + imageAffine(_r, _c) = Color(1.0f, 1.0f, 1.0f); // 只用 scale,offset 用另一个 - struct RasterPatch { + // ★ 用两个 ColorMap 分别存 scale 和 offset + ColorMap imageOffset(texturePatch.rect.size()); + for (int _r = 0; _r < imageOffset.rows; ++_r) + for (int _c = 0; _c < imageOffset.cols; ++_c) + imageOffset(_r, _c) = Color(0.0f, 0.0f, 0.0f); + + struct RasterPatchAffine { 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) { - 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); + Color colors[3]; // scale + Color colorsOffset[3]; // offset + ColorMap& imageScale; + ColorMap& imageOffset; + RasterPatchAffine(ColorMap& s, ColorMap& o) : imageScale(s), imageOffset(o) {} + inline cv::Size Size() const { return imageScale.size(); } + void operator()(const ImageRef& pt, const Point3f& bary) { + Color s = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z; + Color o = colorsOffset[0]*bary.x + colorsOffset[1]*bary.y + colorsOffset[2]*bary.z; + if (std::isnan(s[0]) || std::isinf(s[0])) return; + imageScale(pt) = s; + imageOffset(pt) = o; + } + } data(imageAffine, imageOffset); for (const FIndex idxFace : texturePatch.faces) { if (idxFace >= faces.GetSize()) continue; @@ -9515,39 +9561,102 @@ void MeshTexture::GlobalSeamLeveling4() 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]; + const AffineParam& param = patchAffine[search->second]; + data.colors[v] = Color(param.scale[0], param.scale[1], param.scale[2]); + data.colorsOffset[v] = Color(param.offset[0], param.offset[1], param.offset[2]); } else { - data.colors[v] = Color::ZERO; + data.colors[v] = Color(1.0f, 1.0f, 1.0f); + data.colorsOffset[v] = Color(0.0f, 0.0f, 0.0f); } } ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data); } - // ★FIX: 改回 DilateMean<1>(和原版一致,之前 <5> 过度模糊) - imageAdj.DilateMean<1>(imageAdj, Color::ZERO); + imageAffine.DilateMean<1>(imageAffine, Color(1.0f, 1.0f, 1.0f)); + imageOffset.DilateMean<1>(imageOffset, Color(0.0f, 0.0f, 0.0f)); - // ★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)); - } - } + // 应用: out = src * scale + offset * 255 + for (int r = 0; r < srcROI.rows; ++r) { + for (int c = 0; c < srcROI.cols; ++c) { + const Color& scale = imageAffine(r, c); + const Color& offset = imageOffset(r, c); + const cv::Vec3b& src = srcROI.at(r, c); + + outPatch.at(r, c) = cv::Vec3b( + (uint8_t)CLAMP(ROUND2INT(src[0] * scale[0] + offset[0] * 255.0f), 0, 255), + (uint8_t)CLAMP(ROUND2INT(src[1] * scale[1] + offset[1] * 255.0f), 0, 255), + (uint8_t)CLAMP(ROUND2INT(src[2] * scale[2] + offset[2] * 255.0f), 0, 255) + ); + } + } } - DEBUG_EXTRA("GlobalSeamLeveling4 finished successfully."); + // ========== 映射到按 texturePatch 索引的数组 ========== + patchAffineByTexPatch.clear(); + patchAffineByTexPatch.resize(images.size(), {1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f}); + + // 对于每个 texturePatch,取其第一个顶点的仿射参数作为代表 + for (size_t tpi = 0; tpi < numPatches; ++tpi) { + const TexturePatch& tp = texturePatches[tpi]; + if (tp.label < 0 || tp.label >= (int)images.size()) continue; + + // 取该 patch 的第一个有效 face 的第一个顶点 + bool found = false; + for (FIndex fid : tp.faces) { + if (fid >= faces.GetSize()) continue; + const Face& face = faces[fid]; + for (int v = 0; v < 3; ++v) { + auto search = vertpatch2rows[face[v]].find((uint32_t)tpi); + if (search != vertpatch2rows[face[v]].end()) { + const AffineParam& param = patchAffine[search->second]; + AffineCorrection& ac = patchAffineByTexPatch[tp.label]; + ac.kr = param.scale[0]; + ac.kg = param.scale[1]; + ac.kb = param.scale[2]; + ac.br = param.offset[0]; + ac.bg = param.offset[1]; + ac.bb = param.offset[2]; + found = true; + break; + } + } + if (found) break; + } + } + + patchAffineByTexPatch.clear(); + patchAffineByTexPatch.resize(numPatches, {1.0f, 1.0f, 1.0f, 0.0f, 0.0f, 0.0f}); + + printf("[DEBUG] Filling patchAffineByTexPatch, numPatches=%zu\n", numPatches); + + for (size_t tpi = 0; tpi < numPatches; ++tpi) { + const TexturePatch& tp = texturePatches[tpi]; + bool found = false; + for (FIndex fid : tp.faces) { + if (fid >= faces.GetSize()) continue; + const Face& face = faces[fid]; + for (int v = 0; v < 3; ++v) { + auto search = vertpatch2rows[face[v]].find((uint32_t)tpi); + if (search != vertpatch2rows[face[v]].end()) { + const AffineParam& param = patchAffine[search->second]; + AffineCorrection& ac = patchAffineByTexPatch[tpi]; + ac.kr = param.scale[0]; + ac.kg = param.scale[1]; + ac.kb = param.scale[2]; + ac.br = param.offset[0]; + ac.bg = param.offset[1]; + ac.bb = param.offset[2]; + found = true; + break; + } + } + if (found) break; + } + } + + printf("[DEBUG] patchAffineByTexPatch filled, size=%zu\n", patchAffineByTexPatch.size()); + + DEBUG_EXTRA("GlobalSeamLeveling4 (Affine) finished successfully."); } // set to one in order to dilate also on the diagonal of the border @@ -15611,7 +15720,7 @@ void MeshTexture::AlignPatchColors(Image8U3& atlas, for (int c = 0; c < 3; ++c) { if (selfMean[c] > 1.0f) { float g = (float)(nbMean[c] / selfMean[c]); - gain[i][c] = std::max(0.7f, std::min(1.4f, g)); // 保守范围 + gain[i][c] = std::max(0.5f, std::min(2.0f, g)); // 保守范围 } } } @@ -15682,16 +15791,16 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( { if (rcPatches.empty()) return; - Image8U3 localAtlas(textureSize, textureSize); - localAtlas.memset(0); // 全部填 0 = 黑色 + Image8U3 localAtlas(textureSize, textureSize); + localAtlas.memset(0); // 全部填 0 = 黑色 DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: bridging %zu rcPatches...", rcPatches.size()); - DEBUG_EXTRA("faceTexcoords ptr=%p, scene ptr=%p", &faceTexcoords, &scene.mesh.faceTexcoords); + DEBUG_EXTRA("faceTexcoords ptr=%p, scene ptr=%p", &faceTexcoords, &scene.mesh.faceTexcoords); + // ========== 1. 构建 texturePatches ========== texturePatches.clear(); std::vector rcToTexPatch(rcPatches.size(), NO_ID); - // ★★★ 诊断日志:构建循环之前 ★★★ if (!rcPatches.empty()) { DEBUG_EXTRA("rcPatch[0]: viewID=%d, faces.size()=%zu, firstFace=%u, virtualFaceMap.size()=%zu", rcPatches[0].viewID, rcPatches[0].faces.size(), @@ -15699,7 +15808,6 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( virtualFaceMap.size()); } - // ★ 诊断计数器 size_t skippedEmpty = 0, skippedBadView = 0, skippedEmptyImage = 0; size_t skippedNoProj = 0, skippedBadROI = 0, skippedNoFaces = 0; @@ -15742,7 +15850,6 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( tp.label = rp.viewID; tp.rect = cv::Rect(minX, minY, maxX - minX + 1, maxY - minY + 1); - // ★ 收集真实面(展开虚拟面) for (FIndex vfID : rp.faces) { if (vfID >= virtualFaceMap.size()) continue; const VirtualFace& vf = virtualFaceMap[vfID]; @@ -15796,146 +15903,134 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( seamEdges.clear(); seamVertices.clear(); - // // 填充 faceToPatchID - // faceToPatchID.assign(scene.mesh.faces.size(), NO_ID); - // for (size_t pi = 0; pi < rcPatches.size(); ++pi) { - // if (rcToTexPatch[pi] == NO_ID) continue; - // uint32_t texPatchID = rcToTexPatch[pi]; - // for (FIndex vfID : rcPatches[pi].faces) { - // if (vfID >= virtualFaceMap.size()) continue; - // for (FIndex fid : virtualFaceMap[vfID].faces) { - // if (fid < faceToPatchID.size()) - // faceToPatchID[fid] = texPatchID; - // } - // } - // } - BuildSeamEdgesFromFaceToPatchID(); CreateSeamVertices(); Mesh::TexCoordArr localFaceTexcoords; - localFaceTexcoords.Resize(scene.mesh.faces.size() * 3); + localFaceTexcoords.Resize(scene.mesh.faces.size() * 3); for (size_t i = 0; i < localFaceTexcoords.size(); ++i) localFaceTexcoords[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)); - localFaceTexcoords[fid * 3 + v] = TexCoord(proj.x - tp.rect.x, proj.y - tp.rect.y); - } - } - } - // faceTexcoords = localFaceTexcoords; + 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)); + localFaceTexcoords[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(内部修改 images)========== + // ========== 调用 GSL4 ========== GlobalSeamLeveling4(); DEBUG_EXTRA("GlobalSeamLeveling4 finished."); - DEBUG_EXTRA("atlas: rows=%d, cols=%d, step=%d, totalBytes=%d", - atlas.rows, atlas.cols, atlas.step, - (int)(atlas.rows * atlas.step)); - DEBUG_EXTRA("expected: %d x %d x 3 = %d bytes", - textureSize, textureSize, textureSize * textureSize * 3); - - // ============================================================ - // ★ 桥接补丁:确保 texturePatches[i].label = viewID - // ============================================================ - // 你的 rcPatches 里每个 patch 有 viewID,但桥接时可能没设 label。 - // 如果桥接代码已经设了,这个循环是幂等的,不会破坏任何东西。 - VERBOSE("Syncing texturePatches.label ← rcPatches.viewID..."); - for (uint32_t i = 0; i < texturePatches.size(); ++i) { - // texturePatches 索引 i 对应 rcPatches 索引 i(你的桥接是 1:1 的) - if (i < rcPatches.size()) { - texturePatches[i].label = rcPatches[i].viewID; - } else { - // 兜底:如果数量不匹配,用 faceToPatchID 反查 - // 这种情况不应该发生,打日志报警 - VERBOSE(" WARNING: texturePatch[%u] has no matching rcPatch!", i); - } - } + DEBUG_EXTRA("atlas: rows=%d, cols=%d, step=%d, totalBytes=%d", + atlas.rows, atlas.cols, atlas.step, + (int)(atlas.rows * atlas.step)); + DEBUG_EXTRA("expected: %d x %d x 3 = %d bytes", + textureSize, textureSize, textureSize * textureSize * 3); + + // ========== 桥接补丁 ========== + VERBOSE("Syncing texturePatches.label ← rcPatches.viewID..."); + for (uint32_t i = 0; i < texturePatches.size(); ++i) { + if (i < rcPatches.size()) { + texturePatches[i].label = rcPatches[i].viewID; + } else { + VERBOSE(" WARNING: texturePatch[%u] has no matching rcPatch!", i); + } + } - // 验证一下 label 是否正确 - int labelOK = 0, labelBad = 0; - for (uint32_t i = 0; i < texturePatches.size(); ++i) { - if (texturePatches[i].label >= 0 && texturePatches[i].label < (int)images.size()) { - ++labelOK; - } else { - ++labelBad; - } - } - VERBOSE(" label check: %d OK, %d BAD (out of %u)", labelOK, labelBad, (uint32_t)texturePatches.size()); - if (labelBad > 0) { - VERBOSE(" ERROR: %d texturePatches have invalid label (viewID out of range)!", labelBad); - // 不 return,让 LSL3 尽量跑,坏 label 的 patch 会采样到错误图像但不会导致崩溃 - } + int labelOK = 0, labelBad = 0; + for (uint32_t i = 0; i < texturePatches.size(); ++i) { + if (texturePatches[i].label >= 0 && texturePatches[i].label < (int)images.size()) { + ++labelOK; + } else { + ++labelBad; + } + } + VERBOSE(" label check: %d OK, %d BAD (out of %u)", labelOK, labelBad, (uint32_t)texturePatches.size()); + if (labelBad > 0) { + VERBOSE(" ERROR: %d texturePatches have invalid label (viewID out of range)!", labelBad); + } - // ============================================================ - // ★ 确认 seamVertices 已构建(你的日志显示已经 90152 个) - // ============================================================ - if (seamVertices.empty()) { - VERBOSE("seamVertices is empty! Calling CreateSeamVertices()..."); - CreateSeamVertices(); // 如果之前没调过,这里补调 - VERBOSE(" seamVertices created: %zu", seamVertices.size()); - } else { - VERBOSE("seamVertices already built: %zu vertices, ready for LSL3", seamVertices.size()); - } + // ========== seamVertices 检查 ========== + if (seamVertices.empty()) { + VERBOSE("seamVertices is empty! Calling CreateSeamVertices()..."); + CreateSeamVertices(); + VERBOSE(" seamVertices created: %zu", seamVertices.size()); + } else { + VERBOSE("seamVertices already built: %zu vertices, ready for LSL3", seamVertices.size()); + } - // ============================================================ - // ★ 调用 LocalSeamLeveling4 - // ============================================================ - VERBOSE("Calling LocalSeamLeveling4 (poisson blending on %zu patches)...", texturePatches.size() - 1); - VERBOSE(" This may take 5~15 minutes for %zu patches...", texturePatches.size() - 1); + // ========== 调用 LocalSeamLeveling4 ========== + VERBOSE("Calling LocalSeamLeveling4 (poisson blending on %zu patches)...", texturePatches.size() - 1); + VERBOSE(" This may take 5~15 minutes for %zu patches...", texturePatches.size() - 1); - const auto lsl3Start = std::chrono::high_resolution_clock::now(); - LocalSeamLeveling4(); - const auto lsl3End = std::chrono::high_resolution_clock::now(); - const double lsl3Sec = std::chrono::duration(lsl3End - lsl3Start).count(); + const auto lsl3Start = std::chrono::high_resolution_clock::now(); + LocalSeamLeveling4(); + const auto lsl3End = std::chrono::high_resolution_clock::now(); + const double lsl3Sec = std::chrono::duration(lsl3End - lsl3Start).count(); - VERBOSE("LocalSeamLeveling4 done in %.1f seconds.", lsl3Sec); + VERBOSE("LocalSeamLeveling4 done in %.1f seconds.", lsl3Sec); - for (int i = 0; i < std::min(3, (int)images.size()); ++i) { - char dbgName[256]; - sprintf(dbgName, "debug_image_%d_after_gsl4.png", i); - cv::imwrite(dbgName, images[i].image); // ← 用 .image - } - DEBUG_EXTRA("Exported debug images after GSL4"); + for (int i = 0; i < std::min(3, (int)images.size()); ++i) { + char dbgName[256]; + sprintf(dbgName, "debug_image_%d_after_gsl4.png", i); + cv::imwrite(dbgName, images[i].image); + } + DEBUG_EXTRA("Exported debug images after GSL4"); + + // ========== 3. 重光栅化(自动检测H方向 + ROI + 仿射校正)========== + + if (!images.empty() && !images[0].image.empty()) { + const auto& p = images[0].image.at(images[0].image.rows/2, images[0].image.cols/2); + LOG("center pixel B=%d G=%d R=%d\n", p[0], p[1], p[2]); + } - // ========== 3. 重光栅化(从已被 GSL4 修正的 images 读)========== ASSERT(atlas.rows == textureSize && atlas.cols == textureSize); + localAtlas.setTo(cv::Scalar(0,0,0)); + + bool firstParamPrinted = false; + bool savedDebugRemapped = false; + for (int pi = 0; pi < (int)rcPatches.size(); ++pi) { + if (pi % 500 == 0 || pi == (int)rcPatches.size() - 1) { + printf("[Reraster] patch %d / %zu\n", pi, rcPatches.size()); + fflush(stdout); + } + if (rcToTexPatch[pi] == NO_ID) continue; const TexturePatch& tp = texturePatches[rcToTexPatch[pi]]; const Image& srcImg = images[tp.label]; - if (srcImg.image.empty()) continue; - if (rcToTexPatch[pi] >= correctedPatchImages.size() || correctedPatchImages[rcToTexPatch[pi]].empty()) - continue; + int texPatchIdx = rcToTexPatch[pi]; + const AffineCorrection* acPtr = nullptr; + if (texPatchIdx >= 0 && texPatchIdx < (int)patchAffineByTexPatch.size()) { + acPtr = &patchAffineByTexPatch[texPatchIdx]; + } - // cv::Mat correctedPatch = srcImg.image(tp.rect).clone(); - cv::Mat correctedPatch = correctedPatchImages[rcToTexPatch[pi]].clone(); // 局部拷贝 + if (!firstParamPrinted && acPtr) { + printf("[DEBUG] pi=%d texPatchIdx=%d kr=%.4f kg=%.4f kb=%.4f br=%.4f bg=%.4f bb=%.4f\n", + pi, texPatchIdx, acPtr->kr, acPtr->kg, acPtr->kb, acPtr->br, acPtr->bg, acPtr->bb); + firstParamPrinted = true; + } - // 加上严格检查 - static int cnt = 0; - if (cnt < 5) { - DEBUG_EXTRA("Patch %d: rcToTexPatch=%d, correctedPatch empty=%d, size=(%dx%d), type=%d", - cnt, rcToTexPatch[pi], correctedPatch.empty(), - correctedPatch.cols, correctedPatch.rows, correctedPatch.type()); - - if (!correctedPatch.empty()) { - char nm[256]; sprintf(nm, "debug_corrected_patch_%d.png", cnt); - cv::imwrite(nm, correctedPatch); - } - cnt++; - } + // 从原图裁出 ROI + cv::Mat patchImage = srcImg.image(tp.rect).clone(); - if (correctedPatch.empty()) continue; + // 保存 ROI 诊断图 + if (pi == 0) { + cv::imwrite("/home/algo/Documents/openMVS/data/546893/out.546893/debug_patch0_roi.png", patchImage); + printf("[DIAG] patchImage size=%dx%d, nonZero=%d\n", + patchImage.cols, patchImage.rows, + cv::countNonZero(patchImage.reshape(1))); + } const RCPatch& rp = rcPatches[pi]; for (FIndex vfID : rp.faces) { @@ -15950,57 +16045,163 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( if (minX > maxX || minY > maxY) continue; int patchW = maxX - minX + 1, patchH = maxY - minY + 1; + + // ★ 防止 patch 过大导致 remap 卡死 + if (patchW > textureSize/2 || patchH > textureSize/2) { + printf("[WARN] Skipping oversized patch at pi=%d: %dx%d\n", pi, patchW, patchH); + continue; + } + 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) + + // ★ 策略1:先尝试求逆(假设 H 是 image→atlas) + cv::Mat H_atlas2img; + cv::invert(geom.homography, H_atlas2img); + const float* H = H_atlas2img.ptr(); + + bool allOutOfRange = true; + + 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; + mapY.at(y-minY, x-minX) = -1; + continue; + } + + // 映射到原图坐标 + float srcX = (H[0]*u + H[1]*v + H[2])/w; + float srcY = (H[3]*u + H[4]*v + H[5])/w; + + // 减去 rect 偏移得到 ROI 内坐标 + srcX -= tp.rect.x; + srcY -= tp.rect.y; + + mapX.at(y-minY, x-minX) = srcX; + mapY.at(y-minY, x-minX) = srcY; + + if (srcX >= 0 && srcX < patchImage.cols && + srcY >= 0 && srcY < patchImage.rows) { + allOutOfRange = false; + } + + // 诊断:第一个 patch 的第一个像素 + if (pi == 0 && vfID == rp.faces[0] && y == minY && x == minX) { + printf("[DIAG] INVERT: uv=(%.4f, %.4f) -> src=(%.2f, %.2f) rect=(%d,%d,%d,%d) w=%.6f\n", + u, v, srcX, srcY, tp.rect.x, tp.rect.y, tp.rect.width, tp.rect.height); + printf("[DIAG] H_inv=[%.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f]\n", + H[0], H[1], H[2], H[3], H[4], H[5], H[6], H[7], H[8]); + } + } + } + + // ★ 如果求逆策略全部越界,尝试直接用 H(不逆) + if (allOutOfRange) { + printf("[DIAG] Invert strategy failed for pi=%d, trying direct H...\n", pi); + + const float* H_direct = geom.homography.ptr(); + allOutOfRange = true; + + 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_direct[6]*u + H_direct[7]*v + H_direct[8]; + if (std::abs(w) < 1e-12f) { + mapX.at(y-minY, x-minX) = -1; + mapY.at(y-minY, x-minX) = -1; + continue; + } + + float srcX = (H_direct[0]*u + H_direct[1]*v + H_direct[2])/w; + float srcY = (H_direct[3]*u + H_direct[4]*v + H_direct[5])/w; + + // 不减去 rect(假设 H 已经在 ROI 坐标系) + // srcX -= tp.rect.x; + // srcY -= tp.rect.y; + + mapX.at(y-minY, x-minX) = srcX; + mapY.at(y-minY, x-minX) = srcY; + + if (srcX >= 0 && srcX < patchImage.cols && + srcY >= 0 && srcY < patchImage.rows) { + allOutOfRange = false; + } + + if (pi == 0 && vfID == rp.faces[0] && y == minY && x == minX) { + printf("[DIAG] DIRECT: uv=(%.4f, %.4f) -> src=(%.2f, %.2f) w=%.6f\n", + u, v, srcX, srcY, w); + printf("[DIAG] H_direct=[%.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f %.6f]\n", + H_direct[0], H_direct[1], H_direct[2], + H_direct[3], H_direct[4], H_direct[5], + H_direct[6], H_direct[7], H_direct[8]); + } } - 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; } + } + + // 如果两种策略都失败,跳过这个虚拟面 + if (allOutOfRange) { + printf("[WARN] Both mapping strategies failed for pi=%d, vfID=%u\n", pi, vfID); + continue; + } cv::Mat remapped; - cv::remap(correctedPatch, remapped, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); + cv::remap(patchImage, 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] == 0 && color[1] == 0 && color[2] == 0) continue; + // 保存 remapped 诊断图 + if (pi == 0 && !savedDebugRemapped) { + cv::imwrite("/home/algo/Documents/openMVS/data/546893/out.546893/debug_patch0_remapped.png", remapped); + savedDebugRemapped = true; + printf("[DIAG] Saved debug_patch0_remapped.png, nonZero=%d\n", + cv::countNonZero(remapped.reshape(1))); + } + + // 写入 Atlas:【临时关闭仿射校正,解决偏蓝/反色】 + for (int y = 0; y < patchH; ++y) { + for (int x = 0; x < patchW; ++x) { + cv::Vec3b color = remapped.at(y, x); + // 严格跳过黑色(背景/无效区),防止橙色污染 + if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue; + + int atlasX = x + minX, atlasY = y + minY; + if (atlasX < 0 || atlasX >= textureSize || atlasY < 0 || atlasY >= textureSize) continue; + + // ★ 强制原样写入(正确图的效果) + localAtlas.at(atlasY, atlasX) = color; - 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]}; - if (atlasY >= 0 && atlasY < atlas.rows && atlasX >= 0 && atlasX < atlas.cols) { - // 用 cv::Mat 的 at 方法,它内部有边界检查(Debug 模式下) - cv::Vec3b& pixel = localAtlas.at(atlasY, atlasX); - pixel[0] = color[0]; - pixel[1] = color[1]; - pixel[2] = color[2]; + /* + // ★ 若后续需恢复仿射,请先打印参数确认: + // printf("ac: kb=%.2f kg=%.2f kr=%.2f bb=%.2f bg=%.2f br=%.2f\n", ac.kb, ac.kg, ac.kr, ac.bb, ac.bg, ac.br); + // 注意:OpenCV 是 BGR,确保 k/bb 对应 BGR 而非 RGB + if (acPtr) { + const AffineCorrection& ac = *acPtr; + cv::Vec3f c(color[0]/255.f, color[1]/255.f, color[2]/255.f); + c[0] = std::max(0.f, std::min(1.f, c[0] * ac.kb + ac.bb)); + c[1] = std::max(0.f, std::min(1.f, c[1] * ac.kg + ac.bg)); + c[2] = std::max(0.f, std::min(1.f, c[2] * ac.kr + ac.br)); + localAtlas.at(atlasY, atlasX) = cv::Vec3b( + (uchar)(c[0]*255.f+0.5f), (uchar)(c[1]*255.f+0.5f), (uchar)(c[2]*255.f+0.5f) + ); + } else { + localAtlas.at(atlasY, atlasX) = color; } - } + */ + } } - - // // 调试用:导出调整后的 atlas - // static int dbgCount = 0; - // char dbgName[256]; - // sprintf(dbgName, "debug_atlas_after_gsl4_%d.png", dbgCount++); - // cv::imwrite(dbgName, atlas); - // DEBUG_EXTRA("Exported %s", dbgName); } - atlas = localAtlas; } - DEBUG_EXTRA("Re-rasterization done."); - correctedPatchImages.clear(); + // 将 localAtlas 合并到 atlas + for (int y = 0; y < textureSize; ++y) + for (int x = 0; x < textureSize; ++x) + if (localAtlas.at(y, x) != cv::Vec3b(0,0,0)) + atlas.at(y, x) = localAtlas.at(y, x); - // ★ 不再恢复原始 images —— 避免 cv::Mat 引用计数混乱导致 munmap_chunk - // atlas 已经生成,images 后续随 MeshTexture 正常析构即可 + DEBUG_EXTRA("Re-rasterization done (auto-detect H direction + ROI + affine correction)."); + correctedPatchImages.clear(); // ========== 4. 清理成员变量 ========== texturePatches.clear(); @@ -16009,10 +16210,9 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( labelsInvalid.Release(); seamEdges.clear(); seamVertices.clear(); - // faceTexcoords.Release(); - Mesh::TexCoordArr dummyTexcoords; - faceTexcoords.Swap(dummyTexcoords); // 如果 DynArray 有 Swap 方法 + Mesh::TexCoordArr dummyTexcoords; + faceTexcoords.Swap(dummyTexcoords); DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: ALL DONE."); } @@ -16376,9 +16576,11 @@ bool MeshTexture::RasterizeVirtualFaces( if (atlasY >= 0 && atlasY < atlas.rows && atlasX >= 0 && atlasX < atlas.cols) { // 用 cv::Mat 的 at 方法,它内部有边界检查(Debug 模式下) cv::Vec3b& pixel = atlas.at(atlasY, atlasX); - pixel[0] = color[2]; - pixel[1] = color[1]; - pixel[2] = color[0]; + // pixel[0] = color[2]; + // pixel[1] = color[1]; + // pixel[2] = color[0]; + + pixel = color; // 原样写入,不再交换 } m_texelScores[idx].score = currentScore; m_texelScores[idx].viewID = viewID; @@ -16428,9 +16630,9 @@ bool MeshTexture::RasterizeVirtualFaces( if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue; const Pixel8U& px = atlas(y, x); if (px[0]==0 && px[1]==0 && px[2]==0) continue; - patchAvgColor[i][0] += px[2]; + patchAvgColor[i][0] += px[0]; patchAvgColor[i][1] += px[1]; - patchAvgColor[i][2] += px[0]; + patchAvgColor[i][2] += px[2]; pixelCount[i]++; } } @@ -16446,8 +16648,8 @@ bool MeshTexture::RasterizeVirtualFaces( NP, NP - std::count(pixelCount.begin(), pixelCount.end(), 0)); - // for (int iter = 0; iter < 3; ++iter) { - for (int iter = 0; iter < 1; ++iter) { + for (int iter = 0; iter < 3; ++iter) { + // for (int iter = 0; iter < 1; ++iter) { AlignPatchColors(atlas, m_texelPatchID, textureSize); } @@ -17840,7 +18042,7 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( // ========== 颜色一致性代价(局部采样版)========== { - const float lambda = 0.5f; // 从 100 降到 0.5 + const float lambda = 1.0f; // 从 100 降到 0.5 // 采样当前候选视图下面中心的颜色 cv::Vec3f candidateColor = SampleFaceCenterColor(vid, faceCenter);