Browse Source

皮肤基本没有染色

ManualUV
hesuicong 7 days ago
parent
commit
c809edbc8e
  1. 610
      libs/MVS/SceneTexture.cpp

610
libs/MVS/SceneTexture.cpp

@ -791,6 +791,14 @@ public:
const VirtualFaceMap& virtualFaceMap, const VirtualFaceMap& virtualFaceMap,
Image8U3& atlas, Image8U3& atlas,
int textureSize); int textureSize);
// 在 class MeshTexture 的 public 或 private 区域添加
struct AffineCorrection {
float kr, kg, kb; // gain per channel
float br, bg, bb; // bias per channel
};
std::vector<AffineCorrection> patchAffineByTexPatch; // index = texturePatch ID
void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize); void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize);
bool RasterizeVirtualFaces( bool RasterizeVirtualFaces(
const VirtualFaceMap& virtualFaceMap, const VirtualFaceMap& virtualFaceMap,
@ -9205,15 +9213,7 @@ void MeshTexture::GlobalSeamLeveling4()
} }
const unsigned numPatches = (unsigned)(texturePatches.size() - 1); const unsigned numPatches = (unsigned)(texturePatches.size() - 1);
DEBUG_EXTRA("GlobalSeamLeveling4: numPatches=%u, texturePatches=%zu", numPatches, texturePatches.size()); DEBUG_EXTRA("GlobalSeamLeveling4 (Affine): numPatches=%u", numPatches);
// ========== 验证输入 ==========
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());
}
}
// ========== 标记无效顶点 ========== // ========== 标记无效顶点 ==========
BoolArr vertexInvalid(vertices.size()); BoolArr vertexInvalid(vertices.size());
@ -9244,15 +9244,13 @@ void MeshTexture::GlobalSeamLeveling4()
FOREACH(i, seamVertices) { FOREACH(i, seamVertices) {
const SeamVertex& seamVertex = seamVertices[i]; const SeamVertex& seamVertex = seamVertices[i];
ASSERT(!seamVertex.patches.empty());
ASSERT(seamVertex.idxVertex < vertices.size());
PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex]; PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex];
patchIndex.bIndex = true; patchIndex.bIndex = true;
patchIndex.idxSeamVertex = i; patchIndex.idxSeamVertex = i;
} }
// ========== 构建 vertpatch2rows ========== // ========== 构建 vertpatch2rows ==========
ASSERT(vertices.size() < static_cast<VIndex>(std::numeric_limits<MatIdx>::max())); // ★ 每个 vertpatch 有 6 个变量: scale_R, scale_G, scale_B, offset_R, offset_G, offset_B
MatIdx rowsX(0); MatIdx rowsX(0);
typedef std::unordered_map<uint32_t, MatIdx> VertexPatch2RowMap; typedef std::unordered_map<uint32_t, MatIdx> VertexPatch2RowMap;
std::vector<VertexPatch2RowMap> vertpatch2rows(vertices.size()); std::vector<VertexPatch2RowMap> vertpatch2rows(vertices.size());
@ -9262,38 +9260,31 @@ void MeshTexture::GlobalSeamLeveling4()
VertexPatch2RowMap& vertpatch2row = vertpatch2rows[i]; VertexPatch2RowMap& vertpatch2row = vertpatch2rows[i];
if (patchIndex.bIndex) { if (patchIndex.bIndex) {
const SeamVertex& seamVertex = seamVertices[patchIndex.idxSeamVertex]; const SeamVertex& seamVertex = seamVertices[patchIndex.idxSeamVertex];
ASSERT(seamVertex.idxVertex == i);
for (const SeamVertex::Patch& patch : seamVertex.patches) { for (const SeamVertex::Patch& patch : seamVertex.patches) {
ASSERT(patch.idxPatch != numPatches); if (patch.idxPatch >= numPatches) continue;
ASSERT(patch.idxPatch < numPatches); if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end()) continue;
if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end())
continue;
vertpatch2row[patch.idxPatch] = rowsX++; vertpatch2row[patch.idxPatch] = rowsX++;
} }
} else if (patchIndex.idxPatch < numPatches) { } else if (patchIndex.idxPatch < numPatches) {
vertpatch2row[patchIndex.idxPatch] = rowsX++; 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); DEBUG_EXTRA("vertpatch2rows built: rowsX=%u, scalarCols=%lld", rowsX, (long long)scalarCols);
// ============================================================ // ========== 构建 Gamma(平滑正则)==========
// ========== 构建 Gamma(Tikhonov 正则)==========
// ============================================================
std::vector<GSLTriplet> gammaTriplets; std::vector<GSLTriplet> gammaTriplets;
gammaTriplets.reserve((size_t)vertices.size() * 8); gammaTriplets.reserve((size_t)vertices.size() * 12);
struct GammaRaw { Eigen::Index colA, colB; float w; }; struct GammaRaw { Eigen::Index colA, colB; float w; };
std::vector<GammaRaw> rawGamma; std::vector<GammaRaw> rawGamma;
rawGamma.reserve(vertices.size() * 6); rawGamma.reserve(vertices.size() * 8);
Mesh::VertexIdxArr adjVerts; Mesh::VertexIdxArr adjVerts;
FOREACH(v, vertices) { FOREACH(v, vertices) {
adjVerts.Empty(); adjVerts.Empty();
scene.mesh.GetAdjVertices(v, adjVerts); scene.mesh.GetAdjVertices(v, adjVerts);
if (adjVerts.GetSize() > 8) adjVerts.Resize(8);
const size_t MAX_ADJ = 8;
if (adjVerts.GetSize() > MAX_ADJ) adjVerts.Resize(MAX_ADJ);
VertexPatchIterator itV(patchIndices[v], seamVertices); VertexPatchIterator itV(patchIndices[v], seamVertices);
while (itV.Next()) { while (itV.Next()) {
@ -9301,7 +9292,7 @@ void MeshTexture::GlobalSeamLeveling4()
if (idxPatch == numPatches) continue; if (idxPatch == numPatches) continue;
auto it_row = vertpatch2rows[v].find(idxPatch); auto it_row = vertpatch2rows[v].find(idxPatch);
if (it_row == vertpatch2rows[v].end()) continue; 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) { for (const VIndex vAdj : adjVerts) {
if (v >= vAdj) continue; if (v >= vAdj) continue;
@ -9311,10 +9302,13 @@ void MeshTexture::GlobalSeamLeveling4()
if (idxPatch != idxPatchAdj) continue; if (idxPatch != idxPatchAdj) continue;
auto it_rowAdj = vertpatch2rows[vAdj].find(idxPatchAdj); auto it_rowAdj = vertpatch2rows[vAdj].find(idxPatchAdj);
if (it_rowAdj == vertpatch2rows[vAdj].end()) continue; 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; const float w = (vertexInvalid[v] || vertexInvalid[vAdj]) ? 0.02f : 0.3f;
rawGamma.push_back({colA, colB, w}); // 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; size_t j = i + 1;
while (j < rawGamma.size() && rawGamma[j].colA == rawGamma[i].colA && rawGamma[j].colB == rawGamma[i].colB) while (j < rawGamma.size() && rawGamma[j].colA == rawGamma[i].colA && rawGamma[j].colB == rawGamma[i].colB)
++j; ++j;
const float w = rawGamma[i].w; gammaTriplets.emplace_back(rowG, rawGamma[i].colA, rawGamma[i].w);
gammaTriplets.emplace_back(rowG, rawGamma[i].colA, w); gammaTriplets.emplace_back(rowG, rawGamma[i].colB, -rawGamma[i].w);
gammaTriplets.emplace_back(rowG, rawGamma[i].colB, -w);
++rowG; ++rowG;
i = j; i = j;
} }
const Eigen::Index rowsGamma = rowG;
SparseMat Gamma(rowsGamma, scalarCols); SparseMat Gamma(rowG, scalarCols);
Gamma.setFromTriplets(gammaTriplets.begin(), gammaTriplets.end()); Gamma.setFromTriplets(gammaTriplets.begin(), gammaTriplets.end());
Gamma.makeCompressed(); Gamma.makeCompressed();
gammaTriplets.clear(); gammaTriplets.shrink_to_fit(); gammaTriplets.clear(); gammaTriplets.shrink_to_fit();
rawGamma.clear(); rawGamma.shrink_to_fit(); rawGamma.clear(); rawGamma.shrink_to_fit();
// ============================================================ // ========== 构建约束 A * x = b ==========
// ========== 构建矩阵 A 和 b(颜色一致性约束)==========
// ============================================================
std::vector<GSLTriplet> ATriplets; std::vector<GSLTriplet> ATriplets;
ATriplets.reserve(seamVertices.GetSize() * 6); ATriplets.reserve(seamVertices.GetSize() * 12);
std::vector<float> b_coeff; std::vector<float> b_coeff;
b_coeff.reserve(seamVertices.GetSize() * 6); b_coeff.reserve(seamVertices.GetSize() * 12);
IndexArr indices; IndexArr indices;
Colors vertexColors; Colors vertexColors;
@ -9367,9 +9357,7 @@ void MeshTexture::GlobalSeamLeveling4()
const TexturePatch& tpRef = texturePatches[patch0.idxPatch]; const TexturePatch& tpRef = texturePatches[patch0.idxPatch];
if (tpRef.label < 0 || tpRef.label >= (IIndex)images.size()) continue; 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)) {
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; vertexColors[i] = Color::ZERO;
continue; continue;
} }
@ -9381,12 +9369,7 @@ void MeshTexture::GlobalSeamLeveling4()
const auto idxPatch1 = seamVertex1.patches.Find(patch0.idxPatch); const auto idxPatch1 = seamVertex1.patches.Find(patch0.idxPatch);
if (idxPatch1 == SeamVertex::Patches::NO_INDEX) continue; if (idxPatch1 == SeamVertex::Patches::NO_INDEX) continue;
const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1]; const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1];
if (std::isnan(patch1.proj.x) || std::isnan(patch1.proj.y)) continue;
// ★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); sampler.AddEdge(patch0.proj, patch1.proj);
} }
vertexColors[i] = sampler.GetColor(); vertexColors[i] = sampler.GetColor();
@ -9399,74 +9382,128 @@ void MeshTexture::GlobalSeamLeveling4()
const uint32_t ipb = seamVertex.patches[indices[j]].idxPatch; const uint32_t ipb = seamVertex.patches[indices[j]].idxPatch;
auto itA = v2r.find(ipa), itB = v2r.find(ipb); auto itA = v2r.find(ipa), itB = v2r.find(ipb);
if (itA == v2r.end() || itB == v2r.end()) continue; 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& ca = vertexColors[i];
const Color& cb = vertexColors[j]; const Color& cb = vertexColors[j];
// ★ 仿射约束: 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) { for (int c = 0; c < 3; ++c) {
const Eigen::Index colA = baseA * 3 + c; const float norm_ca = ca[c] / 255.0f; // ★ 归一化
const Eigen::Index colB = baseB * 3 + c; const float norm_cb = cb[c] / 255.0f; // ★ 归一化
ATriplets.emplace_back(rowA, colA, 1.0f);
ATriplets.emplace_back(rowA, colB, -1.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); if ((size_t)rowA >= b_coeff.size()) b_coeff.push_back(0);
b_coeff[rowA] = cb[c] - ca[c]; b_coeff[rowA] = 1.0f;
++rowA; ++rowA;
} }
} }
} }
} }
const Eigen::Index rowsA = rowA; const Eigen::Index rowsA = rowA;
SparseMat A(rowsA, scalarCols); SparseMat A(rowsA, scalarCols);
A.setFromTriplets(ATriplets.begin(), ATriplets.end()); A.setFromTriplets(ATriplets.begin(), ATriplets.end());
A.makeCompressed(); A.makeCompressed();
ATriplets.clear(); ATriplets.shrink_to_fit(); ATriplets.clear(); ATriplets.shrink_to_fit();
// ============================================================
// ========== 求解 ========== // ========== 求解 ==========
// ============================================================ struct AffineParam { float scale[3]; float offset[3]; };
std::vector<Color> colorAdjustments; std::vector<AffineParam> patchAffine;
colorAdjustments.assign(rowsX, Color::ZERO); patchAffine.assign(rowsX, {{1,1,1}, {0,0,0}});
if (rowsA > 0 && rowsX > 0) { if (rowsA > 0 && rowsX > 0) {
SparseMat ATA = A.transpose() * A; SparseMat ATA = A.transpose() * A;
SparseMat GTG = Gamma.transpose() * Gamma; SparseMat GTG = Gamma.transpose() * Gamma;
const float lambda = 0.1f; const float lambda = 20.0f;
GTG *= lambda * lambda; GTG *= lambda * lambda;
ATA += GTG; ATA += GTG;
SparseMat& Lhs = ATA; SparseMat& Lhs = ATA;
const float eps = 1e-3f; // ★ 正确构建 rhs = A^T * b
for (Eigen::Index i = 0; i < (Eigen::Index)Lhs.rows(); ++i) { Eigen::VectorXf rhs = A.transpose() * Eigen::Map<Eigen::VectorXf>(b_coeff.data(), rowsA);
Lhs.coeffRef(i, i) += eps;
// ★ 正则化:同时加 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<SparseMat, Eigen::Lower> solver; Eigen::ConjugateGradient<SparseMat, Eigen::Lower> solver;
solver.setMaxIterations(2000); solver.setMaxIterations(3000);
solver.setTolerance(0.001f); solver.setTolerance(0.001f);
solver.compute(Lhs); solver.compute(Lhs);
if (solver.info() == Eigen::Success) { 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); 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) { 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) { for (Eigen::Index v = 0; v < (Eigen::Index)rowsX; ++v) {
const float r = x(v * 3 + 0); AffineParam& param = patchAffine[v];
const float g = x(v * 3 + 1); for (int c = 0; c < 3; ++c) {
const float b_ch = x(v * 3 + 2); 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)));
// ★ 恢复原版逻辑:计算均值并减去(消除亮度偏移,只保留色度调整) }
const float mean = (r + g + b_ch) / 3.0f;
colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean);
} }
} }
} }
} }
b_coeff.clear(); b_coeff.shrink_to_fit(); 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));
}
// ========== 应用颜色修正 ========== // ========== 应用颜色修正 ==========
correctedPatchImages.resize(numPatches); correctedPatchImages.resize(numPatches);
@ -9486,25 +9523,34 @@ void MeshTexture::GlobalSeamLeveling4()
cv::Mat& outPatch = correctedPatchImages[idxPatch]; cv::Mat& outPatch = correctedPatchImages[idxPatch];
outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3); outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3);
ColorMap imageAdj(texturePatch.rect.size()); ColorMap imageAffine(texturePatch.rect.size());
imageAdj.memset(0); // 初始化为 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; const TexCoord* tri;
Color colors[3]; Color colors[3]; // scale
ColorMap& image; Color colorsOffset[3]; // offset
RasterPatch(ColorMap& _image) : image(_image) {} ColorMap& imageScale;
inline cv::Size Size() const { return image.size(); } 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) { void operator()(const ImageRef& pt, const Point3f& bary) {
Color c = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z; Color s = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z;
// ★ 过滤 NaN/Inf 调整值 Color o = colorsOffset[0]*bary.x + colorsOffset[1]*bary.y + colorsOffset[2]*bary.z;
if (std::isnan(c[0]) || std::isnan(c[1]) || std::isnan(c[2]) || if (std::isnan(s[0]) || std::isinf(s[0])) return;
std::isinf(c[0]) || std::isinf(c[1]) || std::isinf(c[2])) { imageScale(pt) = s;
return; imageOffset(pt) = o;
}
image(pt) = c;
} }
} data(imageAdj); } data(imageAffine, imageOffset);
for (const FIndex idxFace : texturePatch.faces) { for (const FIndex idxFace : texturePatch.faces) {
if (idxFace >= faces.GetSize()) continue; if (idxFace >= faces.GetSize()) continue;
@ -9515,39 +9561,102 @@ void MeshTexture::GlobalSeamLeveling4()
ASSERT(face[v] < vertices.size()); ASSERT(face[v] < vertices.size());
auto search = vertpatch2rows[face[v]].find(idxPatch); auto search = vertpatch2rows[face[v]].find(idxPatch);
if (search != vertpatch2rows[face[v]].end()) { if (search != vertpatch2rows[face[v]].end()) {
ASSERT(search->second < (Eigen::Index)colorAdjustments.size()); const AffineParam& param = patchAffine[search->second];
data.colors[v] = colorAdjustments[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 { } 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); ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data);
} }
// ★FIX: 改回 DilateMean<1>(和原版一致,之前 <5> 过度模糊) imageAffine.DilateMean<1>(imageAffine, Color(1.0f, 1.0f, 1.0f));
imageAdj.DilateMean<1>(imageAdj, Color::ZERO); imageOffset.DilateMean<1>(imageOffset, Color(0.0f, 0.0f, 0.0f));
// ★FIX: 去掉 GAIN=10,Y/Cb/Cr 全部 1:1 微调(和原版 GSL3 一致) // 应用: out = src * scale + offset * 255
for (int r = 0; r < srcROI.rows; ++r) { for (int r = 0; r < srcROI.rows; ++r) {
for (int c = 0; c < srcROI.cols; ++c) { for (int c = 0; c < srcROI.cols; ++c) {
const Color& a = imageAdj(r, c); const Color& scale = imageAffine(r, c);
if (a == Color::ZERO) { const Color& offset = imageOffset(r, c);
outPatch.at<cv::Vec3b>(r, c) = srcROI.at<cv::Vec3b>(r, c); const cv::Vec3b& src = srcROI.at<cv::Vec3b>(r, c);
continue;
}
const Pixel8U& v = srcROI.at<Pixel8U>(r, c);
const Color col(RGB2YCBCR(Color(v)));
// ★ 直接加,不放大任何通道
const Color acol(YCBCR2RGB(Color(col + a)));
outPatch.at<cv::Vec3b>(r, c) = cv::Vec3b( outPatch.at<cv::Vec3b>(r, c) = cv::Vec3b(
(uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255), (uint8_t)CLAMP(ROUND2INT(src[0] * scale[0] + offset[0] * 255.0f), 0, 255),
(uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255), (uint8_t)CLAMP(ROUND2INT(src[1] * scale[1] + offset[1] * 255.0f), 0, 255),
(uint8_t)CLAMP(ROUND2INT(acol[2]), 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 // 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) { for (int c = 0; c < 3; ++c) {
if (selfMean[c] > 1.0f) { if (selfMean[c] > 1.0f) {
float g = (float)(nbMean[c] / selfMean[c]); 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)); // 保守范围
} }
} }
} }
@ -15687,11 +15796,11 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: bridging %zu rcPatches...", rcPatches.size()); 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 ========== // ========== 1. 构建 texturePatches ==========
texturePatches.clear(); texturePatches.clear();
std::vector<size_t> rcToTexPatch(rcPatches.size(), NO_ID); std::vector<size_t> rcToTexPatch(rcPatches.size(), NO_ID);
// ★★★ 诊断日志:构建循环之前 ★★★
if (!rcPatches.empty()) { if (!rcPatches.empty()) {
DEBUG_EXTRA("rcPatch[0]: viewID=%d, faces.size()=%zu, firstFace=%u, virtualFaceMap.size()=%zu", DEBUG_EXTRA("rcPatch[0]: viewID=%d, faces.size()=%zu, firstFace=%u, virtualFaceMap.size()=%zu",
rcPatches[0].viewID, rcPatches[0].faces.size(), rcPatches[0].viewID, rcPatches[0].faces.size(),
@ -15699,7 +15808,6 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
virtualFaceMap.size()); virtualFaceMap.size());
} }
// ★ 诊断计数器
size_t skippedEmpty = 0, skippedBadView = 0, skippedEmptyImage = 0; size_t skippedEmpty = 0, skippedBadView = 0, skippedEmptyImage = 0;
size_t skippedNoProj = 0, skippedBadROI = 0, skippedNoFaces = 0; size_t skippedNoProj = 0, skippedBadROI = 0, skippedNoFaces = 0;
@ -15742,7 +15850,6 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
tp.label = rp.viewID; tp.label = rp.viewID;
tp.rect = cv::Rect(minX, minY, maxX - minX + 1, maxY - minY + 1); tp.rect = cv::Rect(minX, minY, maxX - minX + 1, maxY - minY + 1);
// ★ 收集真实面(展开虚拟面)
for (FIndex vfID : rp.faces) { for (FIndex vfID : rp.faces) {
if (vfID >= virtualFaceMap.size()) continue; if (vfID >= virtualFaceMap.size()) continue;
const VirtualFace& vf = virtualFaceMap[vfID]; const VirtualFace& vf = virtualFaceMap[vfID];
@ -15796,20 +15903,6 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
seamEdges.clear(); seamVertices.clear(); 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(); BuildSeamEdgesFromFaceToPatchID();
CreateSeamVertices(); CreateSeamVertices();
@ -15829,11 +15922,10 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
} }
} }
} }
// faceTexcoords = localFaceTexcoords;
DEBUG_EXTRA("Bridge done: %zu texturePatches ready, calling GlobalSeamLeveling4...", numValidPatches); DEBUG_EXTRA("Bridge done: %zu texturePatches ready, calling GlobalSeamLeveling4...", numValidPatches);
// ========== 调用 GSL4(内部修改 images)========== // ========== 调用 GSL4 ==========
GlobalSeamLeveling4(); GlobalSeamLeveling4();
DEBUG_EXTRA("GlobalSeamLeveling4 finished."); DEBUG_EXTRA("GlobalSeamLeveling4 finished.");
@ -15843,24 +15935,16 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
DEBUG_EXTRA("expected: %d x %d x 3 = %d bytes", DEBUG_EXTRA("expected: %d x %d x 3 = %d bytes",
textureSize, textureSize, textureSize * textureSize * 3); textureSize, textureSize, textureSize * textureSize * 3);
// ============================================================ // ========== 桥接补丁 ==========
// ★ 桥接补丁:确保 texturePatches[i].label = viewID
// ============================================================
// 你的 rcPatches 里每个 patch 有 viewID,但桥接时可能没设 label。
// 如果桥接代码已经设了,这个循环是幂等的,不会破坏任何东西。
VERBOSE("Syncing texturePatches.label ← rcPatches.viewID..."); VERBOSE("Syncing texturePatches.label ← rcPatches.viewID...");
for (uint32_t i = 0; i < texturePatches.size(); ++i) { for (uint32_t i = 0; i < texturePatches.size(); ++i) {
// texturePatches 索引 i 对应 rcPatches 索引 i(你的桥接是 1:1 的)
if (i < rcPatches.size()) { if (i < rcPatches.size()) {
texturePatches[i].label = rcPatches[i].viewID; texturePatches[i].label = rcPatches[i].viewID;
} else { } else {
// 兜底:如果数量不匹配,用 faceToPatchID 反查
// 这种情况不应该发生,打日志报警
VERBOSE(" WARNING: texturePatch[%u] has no matching rcPatch!", i); VERBOSE(" WARNING: texturePatch[%u] has no matching rcPatch!", i);
} }
} }
// 验证一下 label 是否正确
int labelOK = 0, labelBad = 0; int labelOK = 0, labelBad = 0;
for (uint32_t i = 0; i < texturePatches.size(); ++i) { for (uint32_t i = 0; i < texturePatches.size(); ++i) {
if (texturePatches[i].label >= 0 && texturePatches[i].label < (int)images.size()) { if (texturePatches[i].label >= 0 && texturePatches[i].label < (int)images.size()) {
@ -15872,23 +15956,18 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
VERBOSE(" label check: %d OK, %d BAD (out of %u)", labelOK, labelBad, (uint32_t)texturePatches.size()); VERBOSE(" label check: %d OK, %d BAD (out of %u)", labelOK, labelBad, (uint32_t)texturePatches.size());
if (labelBad > 0) { if (labelBad > 0) {
VERBOSE(" ERROR: %d texturePatches have invalid label (viewID out of range)!", labelBad); VERBOSE(" ERROR: %d texturePatches have invalid label (viewID out of range)!", labelBad);
// 不 return,让 LSL3 尽量跑,坏 label 的 patch 会采样到错误图像但不会导致崩溃
} }
// ============================================================ // ========== seamVertices 检查 ==========
// ★ 确认 seamVertices 已构建(你的日志显示已经 90152 个)
// ============================================================
if (seamVertices.empty()) { if (seamVertices.empty()) {
VERBOSE("seamVertices is empty! Calling CreateSeamVertices()..."); VERBOSE("seamVertices is empty! Calling CreateSeamVertices()...");
CreateSeamVertices(); // 如果之前没调过,这里补调 CreateSeamVertices();
VERBOSE(" seamVertices created: %zu", seamVertices.size()); VERBOSE(" seamVertices created: %zu", seamVertices.size());
} else { } else {
VERBOSE("seamVertices already built: %zu vertices, ready for LSL3", seamVertices.size()); VERBOSE("seamVertices already built: %zu vertices, ready for LSL3", seamVertices.size());
} }
// ============================================================ // ========== 调用 LocalSeamLeveling4 ==========
// ★ 调用 LocalSeamLeveling4
// ============================================================
VERBOSE("Calling LocalSeamLeveling4 (poisson blending on %zu patches)...", texturePatches.size() - 1); VERBOSE("Calling LocalSeamLeveling4 (poisson blending on %zu patches)...", texturePatches.size() - 1);
VERBOSE(" This may take 5~15 minutes for %zu patches...", texturePatches.size() - 1); VERBOSE(" This may take 5~15 minutes for %zu patches...", texturePatches.size() - 1);
@ -15902,41 +15981,57 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
for (int i = 0; i < std::min(3, (int)images.size()); ++i) { for (int i = 0; i < std::min(3, (int)images.size()); ++i) {
char dbgName[256]; char dbgName[256];
sprintf(dbgName, "debug_image_%d_after_gsl4.png", i); sprintf(dbgName, "debug_image_%d_after_gsl4.png", i);
cv::imwrite(dbgName, images[i].image); // ← 用 .image cv::imwrite(dbgName, images[i].image);
} }
DEBUG_EXTRA("Exported debug images after GSL4"); DEBUG_EXTRA("Exported debug images after GSL4");
// ========== 3. 重光栅化(从已被 GSL4 修正的 images 读)========== // ========== 3. 重光栅化(自动检测H方向 + ROI + 仿射校正)==========
if (!images.empty() && !images[0].image.empty()) {
const auto& p = images[0].image.at<cv::Vec3b>(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]);
}
ASSERT(atlas.rows == textureSize && atlas.cols == textureSize); 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) { 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; if (rcToTexPatch[pi] == NO_ID) continue;
const TexturePatch& tp = texturePatches[rcToTexPatch[pi]]; const TexturePatch& tp = texturePatches[rcToTexPatch[pi]];
const Image& srcImg = images[tp.label]; const Image& srcImg = images[tp.label];
if (srcImg.image.empty()) continue; if (srcImg.image.empty()) continue;
if (rcToTexPatch[pi] >= correctedPatchImages.size() || correctedPatchImages[rcToTexPatch[pi]].empty()) int texPatchIdx = rcToTexPatch[pi];
continue; const AffineCorrection* acPtr = nullptr;
if (texPatchIdx >= 0 && texPatchIdx < (int)patchAffineByTexPatch.size()) {
acPtr = &patchAffineByTexPatch[texPatchIdx];
}
// cv::Mat correctedPatch = srcImg.image(tp.rect).clone(); if (!firstParamPrinted && acPtr) {
cv::Mat correctedPatch = correctedPatchImages[rcToTexPatch[pi]].clone(); // 局部拷贝 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;
}
// 加上严格检查 // 从原图裁出 ROI
static int cnt = 0; cv::Mat patchImage = srcImg.image(tp.rect).clone();
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()) { // 保存 ROI 诊断图
char nm[256]; sprintf(nm, "debug_corrected_patch_%d.png", cnt); if (pi == 0) {
cv::imwrite(nm, correctedPatch); cv::imwrite("/home/algo/Documents/openMVS/data/546893/out.546893/debug_patch0_roi.png", patchImage);
} printf("[DIAG] patchImage size=%dx%d, nonZero=%d\n",
cnt++; patchImage.cols, patchImage.rows,
cv::countNonZero(patchImage.reshape(1)));
} }
if (correctedPatch.empty()) continue;
const RCPatch& rp = rcPatches[pi]; const RCPatch& rp = rcPatches[pi];
for (FIndex vfID : rp.faces) { for (FIndex vfID : rp.faces) {
if (vfID >= m_virtualFaceGeometries.size()) continue; if (vfID >= m_virtualFaceGeometries.size()) continue;
@ -15950,57 +16045,163 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
if (minX > maxX || minY > maxY) continue; if (minX > maxX || minY > maxY) continue;
int patchW = maxX - minX + 1, patchH = maxY - minY + 1; 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); cv::Mat mapX(patchH, patchW, CV_32FC1), mapY(patchH, patchW, CV_32FC1);
const float* H = geom.homography.ptr<float>();
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<float>();
bool allOutOfRange = true;
for (int y = minY; y <= maxY; ++y) {
for (int x = minX; x <= maxX; ++x) { for (int x = minX; x <= maxX; ++x) {
float u = (float)x / textureSize, v = (float)y / textureSize; float u = (float)x / textureSize, v = (float)y / textureSize;
float w = H[6]*u + H[7]*v + H[8]; float w = H[6]*u + H[7]*v + H[8];
if (std::abs(w) < 1e-12f) { if (std::abs(w) < 1e-12f) {
mapX.at<float>(y-minY, x-minX) = -1; mapX.at<float>(y-minY, x-minX) = -1;
mapY.at<float>(y-minY, x-minX) = -1; continue; mapY.at<float>(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<float>(y-minY, x-minX) = srcX;
mapY.at<float>(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<float>();
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<float>(y-minY, x-minX) = -1;
mapY.at<float>(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<float>(y-minY, x-minX) = srcX;
mapY.at<float>(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<float>(y-minY, x-minX) = (H[0]*u + H[1]*v + H[2])/w - tp.rect.x; }
mapY.at<float>(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::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) // 保存 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) { for (int x = 0; x < patchW; ++x) {
cv::Vec3b color = remapped.at<cv::Vec3b>(y, x); cv::Vec3b color = remapped.at<cv::Vec3b>(y, x);
// 严格跳过黑色(背景/无效区),防止橙色污染
if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue; if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue;
int atlasX = x + minX, atlasY = y + minY; int atlasX = x + minX, atlasY = y + minY;
if (atlasX < 0 || atlasX >= textureSize || atlasY < 0 || atlasY >= textureSize) continue; 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 模式下) localAtlas.at<cv::Vec3b>(atlasY, atlasX) = color;
cv::Vec3b& pixel = localAtlas.at<cv::Vec3b>(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<cv::Vec3b>(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<cv::Vec3b>(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<cv::Vec3b>(y, x) != cv::Vec3b(0,0,0))
atlas.at<cv::Vec3b>(y, x) = localAtlas.at<cv::Vec3b>(y, x);
// ★ 不再恢复原始 images —— 避免 cv::Mat 引用计数混乱导致 munmap_chunk DEBUG_EXTRA("Re-rasterization done (auto-detect H direction + ROI + affine correction).");
// atlas 已经生成,images 后续随 MeshTexture 正常析构即可 correctedPatchImages.clear();
// ========== 4. 清理成员变量 ========== // ========== 4. 清理成员变量 ==========
texturePatches.clear(); texturePatches.clear();
@ -16009,10 +16210,9 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
labelsInvalid.Release(); labelsInvalid.Release();
seamEdges.clear(); seamEdges.clear();
seamVertices.clear(); seamVertices.clear();
// faceTexcoords.Release();
Mesh::TexCoordArr dummyTexcoords; Mesh::TexCoordArr dummyTexcoords;
faceTexcoords.Swap(dummyTexcoords); // 如果 DynArray 有 Swap 方法 faceTexcoords.Swap(dummyTexcoords);
DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: ALL DONE."); DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: ALL DONE.");
} }
@ -16376,9 +16576,11 @@ bool MeshTexture::RasterizeVirtualFaces(
if (atlasY >= 0 && atlasY < atlas.rows && atlasX >= 0 && atlasX < atlas.cols) { if (atlasY >= 0 && atlasY < atlas.rows && atlasX >= 0 && atlasX < atlas.cols) {
// 用 cv::Mat 的 at 方法,它内部有边界检查(Debug 模式下) // 用 cv::Mat 的 at 方法,它内部有边界检查(Debug 模式下)
cv::Vec3b& pixel = atlas.at<cv::Vec3b>(atlasY, atlasX); cv::Vec3b& pixel = atlas.at<cv::Vec3b>(atlasY, atlasX);
pixel[0] = color[2]; // pixel[0] = color[2];
pixel[1] = color[1]; // pixel[1] = color[1];
pixel[2] = color[0]; // pixel[2] = color[0];
pixel = color; // 原样写入,不再交换
} }
m_texelScores[idx].score = currentScore; m_texelScores[idx].score = currentScore;
m_texelScores[idx].viewID = viewID; m_texelScores[idx].viewID = viewID;
@ -16428,9 +16630,9 @@ bool MeshTexture::RasterizeVirtualFaces(
if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue; if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue;
const Pixel8U& px = atlas(y, x); const Pixel8U& px = atlas(y, x);
if (px[0]==0 && px[1]==0 && px[2]==0) continue; 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][1] += px[1];
patchAvgColor[i][2] += px[0]; patchAvgColor[i][2] += px[2];
pixelCount[i]++; pixelCount[i]++;
} }
} }
@ -16446,8 +16648,8 @@ bool MeshTexture::RasterizeVirtualFaces(
NP, NP - std::count(pixelCount.begin(), pixelCount.end(), 0)); NP, NP - std::count(pixelCount.begin(), pixelCount.end(), 0));
// for (int iter = 0; iter < 3; ++iter) { for (int iter = 0; iter < 3; ++iter) {
for (int iter = 0; iter < 1; ++iter) { // for (int iter = 0; iter < 1; ++iter) {
AlignPatchColors(atlas, m_texelPatchID, textureSize); 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); cv::Vec3f candidateColor = SampleFaceCenterColor(vid, faceCenter);

Loading…
Cancel
Save