Browse Source

成功用OpenMVS的几何信息编译通过

ManualUV
hesuicong 2 weeks ago
parent
commit
d442e213d7
  1. 729
      libs/MVS/SceneTexture.cpp

729
libs/MVS/SceneTexture.cpp

@ -44,6 +44,12 @@ @@ -44,6 +44,12 @@
#include <sstream>
#include <filesystem>
#include <Eigen/Sparse>
#include <Eigen/SparseCholesky>
#include <vector>
#include <unordered_map>
#include <algorithm>
namespace fs = std::filesystem;
// namespace py = pybind11;
@ -731,7 +737,8 @@ public: @@ -731,7 +737,8 @@ public:
return p;
}
// ★ 只需新增这一行(其余类型取自你现有头文件)
using GSLTriplet = Eigen::Triplet<float, Eigen::Index>;
uint32_t FindNearestPatchForFaces(const std::vector<FIndex>& faceIndices);
void FixIsolatedComponents();
void ReinitializeSeamData();
@ -776,6 +783,7 @@ public: @@ -776,6 +783,7 @@ public:
std::vector<uint32_t> faceToPatchID; // faceID → 新 patchID 映射
void MergeSameViewPatches();
void BuildSeamEdgesFromFaceToPatchID();
std::vector<cv::Mat> correctedPatchImages; // GSL4 apply 输出,重光栅化消费
// 桥接:用 rcPatches + VirtualFaceMap 构建 TexturePatch 并调用 GlobalSeamLeveling3
void ApplyGlobalSeamLevelingOnRCPatches(
const VirtualFaceMap& virtualFaceMap,
@ -8766,7 +8774,6 @@ uint32_t MeshTexture::FindNearestPatchForFaces(const std::vector<FIndex>& faceIn @@ -8766,7 +8774,6 @@ uint32_t MeshTexture::FindNearestPatchForFaces(const std::vector<FIndex>& faceIn
return bestPatch;
}
void MeshTexture::CreateSeamVertices()
{
DEBUG_EXTRA(">>> CreateSeamVertices CALLED");
@ -8783,90 +8790,171 @@ void MeshTexture::CreateSeamVertices() @@ -8783,90 +8790,171 @@ void MeshTexture::CreateSeamVertices()
int validEdges = 0;
// ★ 用 map 缓存顶点→seamVertex 索引
std::unordered_map<VIndex, uint32_t> mapVertexSeam;
mapVertexSeam.reserve(vertices.size() / 4);
for (uint32_t edgeIdx = 0; edgeIdx < seamEdges.GetSize(); ++edgeIdx) {
const PairIdx& edge = seamEdges[edgeIdx];
// ★ 每条边都检查基本范围
if (edge.i >= faces.GetSize() || edge.j >= faces.GetSize()) {
if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] face OOB: %u %u", edgeIdx, edge.i, edge.j);
continue;
}
if (edge.i >= components.size() || edge.j >= components.size()) {
if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] comp OOB: %u %u", edgeIdx, edge.i, edge.j);
continue;
}
if (edge.i >= faces.GetSize() || edge.j >= faces.GetSize()) continue;
if (edge.i >= components.size() || edge.j >= components.size()) continue;
const uint32_t comp0 = components[edge.i];
const uint32_t comp1 = components[edge.j];
if (comp0 == NO_ID || comp1 == NO_ID) {
continue;
}
if (comp0 >= mapIdxPatch.GetSize() || comp1 >= mapIdxPatch.GetSize()) {
if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] comp OOR: comp0=%u comp1=%u mapSz=%u",
edgeIdx, comp0, comp1, mapIdxPatch.GetSize());
continue;
}
if (comp0 == NO_ID || comp1 == NO_ID) continue;
if (comp0 >= mapIdxPatch.GetSize() || comp1 >= mapIdxPatch.GetSize()) continue;
const uint32_t idxPatch0 = mapIdxPatch[comp0];
const uint32_t idxPatch1 = mapIdxPatch[comp1];
if (idxPatch0 >= texturePatches.size() || idxPatch1 >= texturePatches.size()) {
if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] patch OOR: ip0=%u ip1=%u tpSz=%zu",
edgeIdx, idxPatch0, idxPatch1, texturePatches.size());
continue;
}
if (idxPatch0 == idxPatch1)
continue;
if (idxPatch0 >= texturePatches.size() || idxPatch1 >= texturePatches.size()) continue;
if (idxPatch0 == idxPatch1) continue; // ★ 同一 patch,不是 seam
// ★ 打印第一条边看看
if (edgeIdx == 0) {
DEBUG_EXTRA(" FIRST EDGE: faceA=%u compA=%u patchA=%u | faceB=%u compB=%u patchB=%u",
edge.i, comp0, idxPatch0, edge.j, comp1, idxPatch1);
}
// ★ 获取这条 seam edge 的两个共享顶点
VIndex vs[2];
uint32_t vs0[2], vs1[2];
// ★ 安全调用 GetEdgeVertices
scene.mesh.GetEdgeVertices(edge.i, edge.j, vs0, vs1);
const Face& faceI = faces[edge.i];
const Face& faceJ = faces[edge.j];
if (vs0[0] >= 3 || vs0[1] >= 3 || vs1[0] >= 3 || vs1[1] >= 3 ||
faceI[vs0[0]] != faceJ[vs1[0]] || faceI[vs0[1]] != faceJ[vs1[1]]) {
continue;
}
if (vs0[0] >= 3 || vs0[1] >= 3 || vs1[0] >= 3 || vs1[1] >= 3) continue;
if (faceI[vs0[0]] != faceJ[vs1[0]] || faceI[vs0[1]] != faceJ[vs1[1]]) continue;
vs[0] = faceI[vs0[0]];
vs[1] = faceI[vs0[1]];
if (vs[0] >= vertices.size() || vs[1] >= vertices.size()) {
if (edgeIdx < 5) DEBUG_EXTRA(" edge[%u] vertex OOB: %u %u vsSz=%zu",
edgeIdx, vs[0], vs[1], vertices.size());
continue;
if (vs[0] >= vertices.size() || vs[1] >= vertices.size()) continue;
// ★★★ 关键:为两个端点各创建一个 seamVertex(如果还没有),
// 然后让每个端点记录"对面的 patch",形成 patches.size() >= 2 ★★★
// 先确保两个 seamVertex 存在
for (int endpoint = 0; endpoint < 2; ++endpoint) {
const VIndex vIdx = vs[endpoint];
auto it = mapVertexSeam.emplace(vIdx, static_cast<uint32_t>(seamVertices.GetSize()));
if (it.second) {
seamVertices.emplace_back(vIdx); // ★ SeamVertex(vIdx)
}
}
// ★ 用 static 的 map 避免重复构造
static std::unordered_map<VIndex, uint32_t> mapVertexSeam;
if (edgeIdx == 0) mapVertexSeam.clear();
// 端点 0:属于 patch0,需要记录"通过这条边能到达 patch1"
// 端点 1:属于 patch1,需要记录"通过这条边能到达 patch0"
// → 交叉:每个端点把"对面 patch"加进自己的 patches
auto it0 = mapVertexSeam.emplace(vs[0], static_cast<uint32_t>(seamVertices.GetSize()));
if (it0.second) seamVertices.emplace_back(vs[0]);
auto it1 = mapVertexSeam.emplace(vs[1], static_cast<uint32_t>(seamVertices.GetSize()));
if (it1.second) seamVertices.emplace_back(vs[1]);
for (int endpoint = 0; endpoint < 2; ++endpoint) {
const VIndex vIdx = vs[endpoint];
const uint32_t myPatch = (endpoint == 0) ? idxPatch0 : idxPatch1;
const uint32_t otherPatch = (endpoint == 0) ? idxPatch1 : idxPatch0;
// ... 后面填充 patch 的边和 proj 的代码保持不变 ...
SeamVertex& sv = seamVertices[mapVertexSeam[vIdx]];
// ★ 用 GetPatch:如果 otherPatch 已存在就返回已有,否则新建(自动去重)
SeamVertex::Patch& patch = sv.GetPatch(otherPatch);
// ★ 记录这条边:从 sv 出发,经过对面的 patch,到达另一个端点
// edge.idxSeamVertex = 另一个端点的 seamVertex 索引
const VIndex otherVIdx = vs[1 - endpoint];
const uint32_t otherSvIdx = mapVertexSeam[otherVIdx];
// 避免重复添加同一条边
bool edgeExists = false;
for (const auto& e : patch.edges) {
if (e.idxSeamVertex == otherSvIdx) { edgeExists = true; break; }
}
if (!edgeExists) {
patch.edges.emplace_back(otherSvIdx);
}
// ★ 计算 proj:该顶点在"对面 patch"纹理空间中的坐标
// proj 用 faceTexcoords 里对应的 UV(在 GSL4 前必须已填充 faceTexcoords)
// 这里用 faceJ(对面面)的顶点在 otherPatch 里的 texcoord
// —— 简化:用面中心投影,或直接留 0(GSL4 会用 SampleImage 重采样)
// 原版是在 AddEdge 后用 SampleImage 根据面片 UV 插值,这里先设为 0,
// 后续如果需要精确采样,再从 faceTexcoords 取
if (patch.proj == Point2f::ZERO) {
// 取该顶点在"对面面"的某个 texcoord 作为近似投影
// 找到 vIdx 在 faceJ 中的局部索引
for (int lv = 0; lv < 3; ++lv) {
if (faceJ[lv] == vIdx) {
// faceTexcoords 是 (fid*3 + v) 的布局
const TexCoord& tc = faceTexcoords[(endpoint == 0 ? edge.j : edge.i) * 3 + lv];
patch.proj = Point2f(tc.x, tc.y);
break;
}
}
}
// ★ 抑制 unused 警告
(void)myPatch;
}
validEdges++;
if (edgeIdx == 0) DEBUG_EXTRA(" First edge processed OK");
}
DEBUG_EXTRA("Seam vertices created: %u vertices, %d valid edges", seamVertices.GetSize(), validEdges);
// ★ 诊断:统计每个 seamVertex 的 patch 数量
size_t svSingle = 0, svMulti = 0, svMaxPatches = 0;
for (const SeamVertex& sv : seamVertices) {
if (sv.patches.size() < 2) svSingle++;
else { svMulti++; svMaxPatches = std::max(svMaxPatches, (size_t)sv.patches.size()); }
}
DEBUG_EXTRA(" patch stats: single(patches<2)=%zu, multi(>=2)=%zu, maxPatches=%zu",
svSingle, svMulti, svMaxPatches);
// ★★★ 截断 seamVertices 到合理规模(原版 OpenMVS 通常 < 50000)★★★
// 按 patches 数量排序,保留最重要的(patches 多的 = 色差严重的接缝)
const size_t MAX_SEAM_VERTICES = 40000; // ← 可调,先试 40000
if (seamVertices.GetSize() > MAX_SEAM_VERTICES) {
DEBUG_EXTRA(" Truncating seamVertices: %u -> %zu (keeping highest patch-count)",
seamVertices.GetSize(), MAX_SEAM_VERTICES);
// 建 (索引, patch数量) 对,按 patch 数量降序
std::vector<std::pair<uint32_t, size_t>> idxAndCount(seamVertices.GetSize());
for (uint32_t i = 0; i < seamVertices.GetSize(); ++i)
idxAndCount[i] = {i, seamVertices[i].patches.size()};
std::sort(idxAndCount.begin(), idxAndCount.end(),
[](const auto& a, const auto& b) { return a.second > b.second; });
// 选前 MAX_SEAM_VERTICES 个索引,排序回原始顺序以保持稳定性
std::vector<uint32_t> keep(idxAndCount.size());
for (size_t i = 0; i < MAX_SEAM_VERTICES; ++i) keep[i] = idxAndCount[i].first;
std::sort(keep.begin(), keep.begin() + MAX_SEAM_VERTICES);
// 重建 seamVertices(只保留选中的)
decltype(seamVertices) newSV;
newSV.Reserve(MAX_SEAM_VERTICES);
std::unordered_map<uint32_t, uint32_t> old2new; // 旧索引 → 新索引
for (size_t i = 0; i < MAX_SEAM_VERTICES; ++i) {
const uint32_t oldIdx = keep[i];
old2new[oldIdx] = (uint32_t)newSV.GetSize();
newSV.push_back(std::move(seamVertices[oldIdx]));
}
// ★★★ 关键:更新所有 Patch::edges 里的 idxSeamVertex 引用 ★★★
for (uint32_t i = 0; i < newSV.GetSize(); ++i) {
for (auto& patch : newSV[i].patches) {
for (auto& edge : patch.edges) {
auto it = old2new.find(edge.idxSeamVertex);
if (it != old2new.end())
edge.idxSeamVertex = it->second;
else
edge.idxSeamVertex = i; // 指向自己(会被忽略)
}
}
}
seamVertices = std::move(newSV);
DEBUG_EXTRA(" Truncated seamVertices: %u final", seamVertices.GetSize());
}
}
// Native
@ -9104,6 +9192,7 @@ void MeshTexture::GlobalSeamLeveling3() @@ -9104,6 +9192,7 @@ void MeshTexture::GlobalSeamLeveling3()
}
}
}
void MeshTexture::GlobalSeamLeveling4()
{
if (seamVertices.empty()) {
@ -9140,12 +9229,9 @@ void MeshTexture::GlobalSeamLeveling4() @@ -9140,12 +9229,9 @@ void MeshTexture::GlobalSeamLeveling4()
patchIndices.Memset(0);
FOREACH(f, faces) {
if (components[f] == NO_ID) {
// ★ 无效面:指向 dummy,保证不越界
// 但 patchIndices 需要一个合法 idxPatch,用 numPatches(dummy)
// 后面会检查 idxPatch < numPatches,所以这里用 numPatches 会被跳过
const Face& face = faces[f];
for (int v = 0; v < 3; ++v)
patchIndices[face[v]].idxPatch = numPatches; // dummy
patchIndices[face[v]].idxPatch = numPatches; // dummy,后面跳过
continue;
}
const uint32_t idxPatch(mapIdxPatch[components[f]]);
@ -9157,16 +9243,17 @@ void MeshTexture::GlobalSeamLeveling4() @@ -9157,16 +9243,17 @@ 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 ==========
// ========== 构建 vertpatch2rows(块列号 → 连续行)==========
ASSERT(vertices.size() < static_cast<VIndex>(std::numeric_limits<MatIdx>::max()));
MatIdx rowsX(0);
typedef std::unordered_map<uint32_t, MatIdx> VertexPatch2RowMap;
cList<VertexPatch2RowMap> vertpatch2rows(vertices.size());
std::vector<VertexPatch2RowMap> vertpatch2rows(vertices.size());
FOREACH(i, vertices) {
const PatchIndex& patchIndex = patchIndices[i];
@ -9176,186 +9263,219 @@ void MeshTexture::GlobalSeamLeveling4() @@ -9176,186 +9263,219 @@ void MeshTexture::GlobalSeamLeveling4()
ASSERT(seamVertex.idxVertex == i);
for (const SeamVertex::Patch& patch : seamVertex.patches) {
ASSERT(patch.idxPatch != numPatches);
ASSERT(patch.idxPatch < numPatches); // ★ 双重检查
ASSERT(patch.idxPatch < numPatches);
if (vertpatch2row.find(patch.idxPatch) != vertpatch2row.end())
continue; // 去重
vertpatch2row[patch.idxPatch] = rowsX++;
}
} else if (patchIndex.idxPatch < numPatches) {
vertpatch2row[patchIndex.idxPatch] = rowsX++;
}
}
DEBUG_EXTRA("vertpatch2rows built: rowsX=%u", rowsX);
const Eigen::Index scalarCols = (Eigen::Index)rowsX * 3; // 每块 3 个标量列 (RGB)
DEBUG_EXTRA("vertpatch2rows built: rowsX(blocks)=%u, scalarCols=%lld", rowsX, (long long)scalarCols);
// ========== 构建 Gamma(Tikhonov 正则)==========
const float lambda(0.1f);
MatIdx rowsGamma(0);
Mesh::VertexIdxArr adjVerts;
CLISTDEF0(MatEntry) rows(0, vertices.size() * 4);
// ============================================================
// ========== 构建 Gamma(Tikhonov 正则)========== [修复1: 去重]
// ============================================================
std::vector<GSLTriplet> gammaTriplets;
gammaTriplets.reserve((size_t)vertices.size() * 8);
struct GammaRaw { Eigen::Index colA, colB; float w; };
std::vector<GammaRaw> rawGamma;
rawGamma.reserve(vertices.size() * 6);
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);
VertexPatchIterator itV(patchIndices[v], seamVertices);
while (itV.Next()) {
const uint32_t idxPatch(itV);
if (idxPatch == numPatches)
continue;
const MatIdx col(vertpatch2rows[v].at(idxPatch));
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;
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) {
const MatIdx colAdj(vertpatch2rows[vAdj].at(idxPatchAdj));
float currentLambda = (vertexInvalid[v] || vertexInvalid[vAdj]) ? 0.01f : 0.1f;
rows.emplace_back(rowsGamma, col, currentLambda);
rows.emplace_back(rowsGamma, colAdj, -currentLambda);
++rowsGamma;
}
if (idxPatch != idxPatchAdj) continue; // 同一 patch 才正则化
auto it_rowAdj = vertpatch2rows[vAdj].find(idxPatchAdj);
if (it_rowAdj == vertpatch2rows[vAdj].end()) continue;
const Eigen::Index colB = it_rowAdj->second;
const float w = (vertexInvalid[v] || vertexInvalid[vAdj]) ? 0.05f : 0.5f;
rawGamma.push_back({colA, colB, w});
}
}
}
}
ASSERT(rows.size() / 2 < static_cast<IDX>(std::numeric_limits<MatIdx>::max()));
DEBUG_EXTRA("Gamma rows: %zu matEntries", rows.size());
SparseMat Gamma(rowsGamma, rowsX);
Gamma.setFromTriplets(rows.Begin(), rows.End());
rows.Empty();
// ★ 排序 + 去重合并(消除重复邻接 → 保证矩阵性质良好)
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);
});
Eigen::Index rowG = 0;
for (size_t i = 0; i < rawGamma.size(); ) {
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);
++rowG;
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(颜色一致性约束)==========
// ============================================================
std::vector<GSLTriplet> ATriplets;
ATriplets.reserve(seamVertices.GetSize() * 6);
std::vector<float> b_coeff;
b_coeff.reserve(seamVertices.GetSize() * 6);
// ========== 构建矩阵 A 和 b ==========
IndexArr indices;
Colors vertexColors;
Colors coeffB;
Eigen::Index rowA = 0;
for (const SeamVertex& seamVertex : seamVertices) {
if (seamVertex.patches.size() < 2)
continue;
if (seamVertex.patches.size() < 2) continue;
seamVertex.SortByPatchIndex(indices);
vertexColors.resize(indices.size());
FOREACH(i, indices) {
for (IDX i = 0; i < (IDX)indices.size(); ++i) {
const SeamVertex::Patch& patch0 = seamVertex.patches[indices[i]];
ASSERT(patch0.idxPatch < numPatches);
if (patch0.idxPatch >= texturePatches.size()) {
DEBUG_EXTRA("ERROR: patch0.idxPatch=%u >= texturePatches.size()=%zu",
patch0.idxPatch, texturePatches.size());
continue;
}
if (patch0.idxPatch >= numPatches) continue;
const TexturePatch& tpRef = texturePatches[patch0.idxPatch];
if (tpRef.label < 0 || tpRef.label >= (IIndex)images.size()) {
DEBUG_EXTRA("ERROR: tpRef.label=%d out of range [0, %zu)", tpRef.label, images.size());
continue;
}
if (tpRef.label < 0 || tpRef.label >= (IIndex)images.size()) continue;
SampleImage sampler(images[tpRef.label].image);
for (const SeamVertex::Patch::Edge& edge : patch0.edges) {
if (edge.idxSeamVertex >= seamVertices.GetSize()) continue; // 越界保护
const SeamVertex& seamVertex1 = seamVertices[edge.idxSeamVertex];
const SeamVertex::Patches::IDX idxPatch1(seamVertex1.patches.Find(patch0.idxPatch));
ASSERT(idxPatch1 != SeamVertex::Patches::NO_INDEX);
const auto idxPatch1 = seamVertex1.patches.Find(patch0.idxPatch);
if (idxPatch1 == SeamVertex::Patches::NO_INDEX) continue;
const SeamVertex::Patch& patch1 = seamVertex1.patches[idxPatch1];
sampler.AddEdge(patch0.proj, patch1.proj);
}
vertexColors[i] = sampler.GetColor();
}
const VertexPatch2RowMap& vertpatch2row = vertpatch2rows[seamVertex.idxVertex];
for (IDX i = 0; i < indices.size() - 1; ++i) {
const uint32_t idxPatch0(seamVertex.patches[indices[i]].idxPatch);
const Color& color0 = vertexColors[i];
const MatIdx col0(vertpatch2row.at(idxPatch0));
for (IDX j = i + 1; j < indices.size(); ++j) {
const uint32_t idxPatch1(seamVertex.patches[indices[j]].idxPatch);
const Color& color1 = vertexColors[j];
const MatIdx col1(vertpatch2row.at(idxPatch1));
ASSERT(idxPatch0 < idxPatch1);
const MatIdx rowA((MatIdx)coeffB.size());
coeffB.Insert(color1 - color0);
ASSERT(ISFINITE(coeffB.back()));
rows.emplace_back(rowA, col0, 1.f);
rows.emplace_back(rowA, col1, -1.f);
const VertexPatch2RowMap& v2r = vertpatch2rows[seamVertex.idxVertex];
for (IDX i = 0; i < (IDX)indices.size() - 1; ++i) {
for (IDX j = i + 1; j < (IDX)indices.size(); ++j) {
const uint32_t ipa = seamVertex.patches[indices[i]].idxPatch;
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 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 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;
}
}
}
ASSERT(coeffB.size() < static_cast<IDX>(std::numeric_limits<MatIdx>::max()));
DEBUG_EXTRA("Matrix A built: %zu constraints, rowsX=%u", coeffB.size(), rowsX);
const MatIdx rowsA((MatIdx)coeffB.size());
SparseMat A(rowsA, rowsX);
A.setFromTriplets(rows.Begin(), rows.End());
rows.Release();
SparseMat Lhs(A.transpose() * A + Gamma.transpose() * Gamma);
Lhs.prune([](const int& row, const int& col, const float&) -> bool {
return col <= row;
});
DEBUG_EXTRA("Lhs matrix built and pruned");
}
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());
// ========== 求解 ==========
Eigen::Matrix<float, Eigen::Dynamic, 3, Eigen::RowMajor> colorAdjustments(rowsX, 3);
{
// ============================================================
// ========== 求解:Lhs = AᵀA + λ²ΓᵀΓ,对角扰动保证正定 [修复2]
// ============================================================
std::vector<Color> 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; // ★ 正则化强度(可调)
GTG *= lambda * lambda;
ASSERT(ATA.rows() == GTG.rows() && ATA.cols() == GTG.cols()); // 尺寸一致性检查
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;
}
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<SparseMat, Eigen::Lower> solver;
solver.setMaxIterations(1000);
solver.setTolerance(0.0001f);
solver.setMaxIterations(2000);
solver.setTolerance(0.001f);
solver.compute(Lhs);
ASSERT(solver.info() == Eigen::Success);
#ifdef TEXOPT_USE_OPENMP
#pragma omp parallel for
#endif
for (int channel = 0; channel < 3; ++channel) {
const Eigen::Map<Eigen::VectorXf, Eigen::Unaligned, Eigen::Stride<0, 3>> b(
coeffB.front().ptr() + channel, rowsA);
const Eigen::VectorXf Rhs(SparseMat(A.transpose()) * b);
const Eigen::VectorXf x(solver.solve(Rhs));
ASSERT(solver.info() == Eigen::Success);
Eigen::Map<Eigen::VectorXf, Eigen::Unaligned, Eigen::Stride<0, 3>>(
colorAdjustments.data() + channel, rowsX) = x.array() - x.mean();
DEBUG_LEVEL(3, "\tcolor channel %d: %d iterations, %g residual",
channel, solver.iterations(), solver.error());
if (solver.info() != Eigen::Success) {
DEBUG_EXTRA("WARN: Lhs compute failed (info=%d), skipping solve", (int)solver.info());
} else {
// ★ b 与通道无关 → 一次求解,结果按 InnerStride<3> 分通道
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);
const float mean = (r + g + b_ch) / 3.0f; // 去均值
colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean);
}
} else {
DEBUG_EXTRA("WARN: solve not successful, using zero adjustments");
}
DEBUG_EXTRA("CG solve done. colorAdjustments: %dx3", rowsX);
// ========== ★★★ 关键修正:串行应用颜色修正,避免并行写同一张 images[label] ==========
//
// 原版用 #pragma omp parallel for,但不同 patch 可能共享同一个 images[label]。
// 多个线程同时 clone() + copyTo() 同一个 cv::Mat → 数据竞争 → 堆破坏 → munmap_chunk。
//
// 修复策略:
// 1. 先把所有 patch 的修正结果写入**独立 buffer**(按 label 分组,每个 label 一份)
// 2. 最后**串行**把每个 label 的 buffer 一次性写回 images[label]
// 这样完全避免并行写竞争。
// --- Step 1: 准备按 label 分组的输出 buffer ---
struct LabelBuffer {
cv::Mat buffer; // 整张图大小的 buffer,初始为原图副本
std::vector<cv::Rect> rects; // 需要写入的 patch 区域(相对于原图)
};
std::map<int, LabelBuffer> labelBuffers;
// 先按 label 收集所有需要修改的 patch
for (unsigned i = 0; i < numPatches; ++i) {
const uint32_t idxPatch = i;
const TexturePatch& tp = texturePatches[idxPatch];
labelBuffers[tp.label].rects.push_back(tp.rect);
}
// 为每个 label 创建整张图的深拷贝 buffer
for (auto& kv : labelBuffers) {
int label = kv.first;
if (label < 0 || label >= (int)images.size()) continue;
if (images[label].image.empty()) continue;
// ★ 整张图深拷贝,后续直接在 buffer 上改,最后整体 swap 回去
kv.second.buffer = images[label].image.clone();
} else {
DEBUG_EXTRA("WARN: no constraints (rowsA=%lld), skip solve", (long long)rowsA);
}
DEBUG_EXTRA("Label buffers created: %zu labels involved", labelBuffers.size());
DEBUG_EXTRA("solve done. colorAdjustments: %zux3", rowsX);
// --- Step 2: 并行计算每个 patch 的颜色调整(写到各自 label 的 buffer 的 ROI 里)---
// 注意:不同 patch 如果 label 相同,会写同一个 buffer——但每个 patch 写的 ROI(rect)不同,
// 只要 rect 不重叠就没问题。同一 label 的 patch 在纹理空间里不重叠,所以安全。
// 但为绝对安全,这里用 label 级别的锁,或者干脆串行。
// ★ 采用:按 patch 并行,但对同一 label 的 buffer 访问加锁。
// ========== 应用颜色修正(输出到 correctedPatchImages,不碰 images)==========
correctedPatchImages.resize(numPatches);
#ifdef TEXOPT_USE_OPENMP
#pragma omp parallel for schedule(dynamic)
@ -9367,23 +9487,14 @@ void MeshTexture::GlobalSeamLeveling4() @@ -9367,23 +9487,14 @@ void MeshTexture::GlobalSeamLeveling4()
if (texturePatch.label < 0 || texturePatch.label >= (int)images.size()) continue;
if (texturePatch.rect.width <= 0 || texturePatch.rect.height <= 0) continue;
auto it = labelBuffers.find(texturePatch.label);
if (it == labelBuffers.end()) continue;
cv::Mat& labelBuf = it->second.buffer;
if (labelBuf.empty()) continue;
// ★ 从原始 images 读(只读,不修改)
const cv::Mat& srcROI = images[texturePatch.label].image(texturePatch.rect);
if (srcROI.empty()) continue;
// 检查 rect 是否在 buffer 范围内
if (texturePatch.rect.x < 0 || texturePatch.rect.y < 0 ||
texturePatch.rect.x + texturePatch.rect.width > labelBuf.cols ||
texturePatch.rect.y + texturePatch.rect.height > labelBuf.rows) {
DEBUG_EXTRA("WARN: patch %u rect %d,%d %dx%d out of image %dx%d",
idxPatch, texturePatch.rect.x, texturePatch.rect.y,
texturePatch.rect.width, texturePatch.rect.height,
labelBuf.cols, labelBuf.rows);
continue;
}
// ★ 创建独立输出 buffer
cv::Mat& outPatch = correctedPatchImages[idxPatch];
outPatch = cv::Mat::zeros(texturePatch.rect.size(), CV_8UC3);
// --- 计算该 patch 的 color adjustment 插值图(与原版相同逻辑)---
ColorMap imageAdj(texturePatch.rect.size());
imageAdj.memset(0);
@ -9391,69 +9502,62 @@ void MeshTexture::GlobalSeamLeveling4() @@ -9391,69 +9502,62 @@ void MeshTexture::GlobalSeamLeveling4()
const TexCoord* tri;
Color colors[3];
ColorMap& image;
inline RasterPatch(ColorMap& _image) : image(_image) {}
RasterPatch(ColorMap& _image) : image(_image) {}
inline cv::Size Size() const { return image.size(); }
inline void operator()(const ImageRef& pt, const Point3f& bary) {
ASSERT(image.isInside(pt));
void operator()(const ImageRef& pt, const Point3f& bary) {
image(pt) = colors[0]*bary.x + colors[1]*bary.y + colors[2]*bary.z;
}
} data(imageAdj);
ASSERT(texturePatch.faces.size() < 1000000); // 合理范围
for (const FIndex idxFace : texturePatch.faces) {
if (idxFace >= faces.GetSize()) continue;
const Face& face = faces[idxFace];
// ★ 检查 faceTexcoords 访问
ASSERT(idxFace * 3 + 2 < faceTexcoords.GetSize());
// ★ 检查 face[v] 不越界
for (int v = 0; v < 3; ++v) {
ASSERT(face[v] < vertices.size());
ASSERT(face[v] < vertpatch2rows.size());
}
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()) {
data.colors[v] = colorAdjustments.row(vertpatch2rows[face[v]].at(idxPatch));
} else {
data.colors[v] = Color::ZERO;
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<1>(imageAdj, Color::ZERO);
// --- ★ 写回 label buffer 的 ROI(这是 clone 出来的独立内存,安全)---
cv::Mat roi = labelBuf(texturePatch.rect);
for (int r = 0; r < roi.rows; ++r) {
for (int c = 0; c < roi.cols; ++c) {
// ★ 把修正叠加到 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)
if (a == Color::ZERO) {
outPatch.at<cv::Vec3b>(r, c) = srcROI.at<cv::Vec3b>(r, c);
continue;
Pixel8U& v = roi.at<Pixel8U>(r, c);
}
const Pixel8U& v = srcROI.at<Pixel8U>(r, c);
const Color col(RGB2YCBCR(Color(v)));
const Color acol(YCBCR2RGB(Color(col + a)));
for (int p = 0; p < 3; ++p)
v[p] = (uint8_t)CLAMP(ROUND2INT(acol[p]), 0, 255);
}
}
} // end parallel for
DEBUG_EXTRA("All patch adjustments written to label buffers");
// --- Step 3: ★ 串行、安全地写回原始 images ---
// 每个 label 只做一次 swap,彻底避免竞争
for (const auto& kv : labelBuffers) {
int label = kv.first;
const cv::Mat& buf = kv.second.buffer;
if (label < 0 || label >= (int)images.size()) continue;
if (images[label].image.empty()) continue;
if (buf.size() != images[label].image.size()) {
DEBUG_EXTRA("WARN: label %d buffer size mismatch, skipping", label);
continue;
outPatch.at<cv::Vec3b>(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));
}
// ★ 用 swap 而不是 copyTo:O(1),不重新分配,不踩堆
// 需要确保类型一致
if (buf.type() == images[label].image.type()) {
cv::Mat(buf).copyTo(images[label].image); // 如果必须拷贝
// 或者如果 images[label].image 可以接管内存:images[label].image = buf;
} else {
buf.copyTo(images[label].image);
}
}
DEBUG_EXTRA("All patch adjustments applied (to local buffers)");
DEBUG_EXTRA("GlobalSeamLeveling4 finished successfully.");
}
@ -15320,21 +15424,94 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( @@ -15320,21 +15424,94 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
{
if (rcPatches.empty()) return;
DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: bridging %zu rcPatches...", rcPatches.size());
// ========== ★ 保存所有 images 的原始状态 ==========
std::vector<cv::Mat> originalImages(images.size());
for (size_t k = 0; k < images.size(); ++k) {
if (!images[k].image.empty())
originalImages[k] = images[k].image.clone();
}
DEBUG_EXTRA("Saved %zu original images", images.size());
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);
// ========== 1. 构建 texturePatches ==========
// ... (和之前完全一样的构建代码,省略) ...
texturePatches.clear();
std::vector<size_t> rcToTexPatch(rcPatches.size(), NO_ID);
// ... [构建 texturePatches,和之前一样] ...
// ★★★ 诊断日志:构建循环之前 ★★★
if (!rcPatches.empty()) {
DEBUG_EXTRA("rcPatch[0]: viewID=%d, faces.size()=%zu, firstFace=%u, virtualFaceMap.size()=%zu",
rcPatches[0].viewID, rcPatches[0].faces.size(),
rcPatches[0].faces.empty() ? 0 : rcPatches[0].faces[0],
virtualFaceMap.size());
}
// ★ 诊断计数器
size_t skippedEmpty = 0, skippedBadView = 0, skippedEmptyImage = 0;
size_t skippedNoProj = 0, skippedBadROI = 0, skippedNoFaces = 0;
for (size_t pi = 0; pi < rcPatches.size(); ++pi) {
const RCPatch& rp = rcPatches[pi];
if (rp.faces.empty()) { skippedEmpty++; continue; }
if (rp.viewID < 0 || rp.viewID >= (IIndex)images.size()) { skippedBadView++; continue; }
const Image& img = images[rp.viewID];
if (img.image.empty()) { skippedEmptyImage++; continue; }
int minX = INT_MAX, minY = INT_MAX, maxX = 0, maxY = 0;
bool hasValidProj = false;
for (FIndex vfID : rp.faces) {
if (vfID >= virtualFaceMap.size()) continue;
const VirtualFace& vf = virtualFaceMap[vfID];
for (FIndex fid : vf.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));
int px = (int)(proj.x + 0.5f), py = (int)(proj.y + 0.5f);
if (px < 0 || py < 0 || px >= img.image.cols || py >= img.image.rows) continue;
minX = std::min(minX, px); minY = std::min(minY, py);
maxX = std::max(maxX, px); maxY = std::max(maxY, py);
hasValidProj = true;
}
}
}
if (!hasValidProj) { skippedNoProj++; continue; }
minX = std::max(0, minX - 2); minY = std::max(0, minY - 2);
maxX = std::min(img.image.cols - 1, maxX + 2); maxY = std::min(img.image.rows - 1, maxY + 2);
if (minX >= maxX || minY >= maxY) { skippedBadROI++; continue; }
TexturePatch tp;
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];
for (FIndex fid : vf.faces) {
if (fid < scene.mesh.faces.size())
tp.faces.push_back(fid);
}
}
if (!tp.faces.empty()) {
auto& f = tp.faces; std::sort(f.begin(), f.end());
FIndex last = f[0]; size_t wi = 1;
for (size_t ri = 1; ri < f.size(); ++ri)
if (f[ri] != last) { last = f[ri]; f[wi++] = last; }
f.Resize(wi);
}
if (tp.faces.empty()) { skippedNoFaces++; continue; }
rcToTexPatch[pi] = texturePatches.size();
texturePatches.push_back(tp);
}
DEBUG_EXTRA("texturePatch filtering: total=%zu", rcPatches.size());
DEBUG_EXTRA(" skippedEmpty=%zu, skippedBadView=%zu, skippedEmptyImage=%zu",
skippedEmpty, skippedBadView, skippedEmptyImage);
DEBUG_EXTRA(" skippedNoProj=%zu, skippedBadROI=%zu, skippedNoFaces=%zu",
skippedNoProj, skippedBadROI, skippedNoFaces);
DEBUG_EXTRA(" → accepted=%zu", texturePatches.size());
const size_t numValidPatches = texturePatches.size();
DEBUG_EXTRA("Valid texturePatches: %zu (from %zu rcPatches)", numValidPatches, rcPatches.size());
@ -15363,8 +15540,9 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( @@ -15363,8 +15540,9 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
BuildSeamEdgesFromFaceToPatchID();
CreateSeamVertices();
faceTexcoords.Resize(scene.mesh.faces.size() * 3);
for (size_t i = 0; i < faceTexcoords.size(); ++i) faceTexcoords[i] = TexCoord(0, 0);
Mesh::TexCoordArr localFaceTexcoords;
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];
@ -15374,25 +15552,37 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( @@ -15374,25 +15552,37 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
for (int v = 0; v < 3; ++v) {
const Point3f& vert = scene.mesh.vertices[face[v]];
Point2f proj = img.camera.ProjectPoint(Point3d(vert));
faceTexcoords[fid * 3 + v] = TexCoord(proj.x - tp.rect.x, proj.y - tp.rect.y);
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(内部用 label buffer,写回 images)==========
// ========== 调用 GSL4(内部修改 images)==========
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);
// ========== 3. 重光栅化(从已被 GSL4 修正的 images 读)==========
ASSERT(atlas.rows == textureSize && atlas.cols == textureSize);
for (int pi = 0; pi < (int)rcPatches.size(); ++pi) { // 串行,简单安全
for (int pi = 0; pi < (int)rcPatches.size(); ++pi) {
if (rcToTexPatch[pi] == NO_ID) continue;
const TexturePatch& tp = texturePatches[rcToTexPatch[pi]];
const Image& srcImg = images[tp.label];
if (srcImg.image.empty()) continue;
cv::Mat correctedPatch = srcImg.image(tp.rect).clone();
if (rcToTexPatch[pi] >= correctedPatchImages.size() || correctedPatchImages[rcToTexPatch[pi]].empty())
continue;
// cv::Mat correctedPatch = srcImg.image(tp.rect).clone();
cv::Mat correctedPatch = correctedPatchImages[rcToTexPatch[pi]].clone(); // 局部拷贝
if (correctedPatch.empty()) continue;
const RCPatch& rp = rcPatches[pi];
@ -15431,20 +15621,25 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( @@ -15431,20 +15621,25 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue;
int atlasX = x + minX, atlasY = y + minY;
if (atlasX < 0 || atlasX >= textureSize || atlasY < 0 || atlasY >= textureSize) continue;
atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]};
}
// 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<cv::Vec3b>(atlasY, atlasX);
pixel[0] = color[0];
pixel[1] = color[1];
pixel[2] = color[2];
}
}
DEBUG_EXTRA("Re-rasterization done.");
// ========== ★ 恢复原始 images(让析构时内存完全干净)==========
for (size_t k = 0; k < images.size(); ++k) {
if (!originalImages[k].empty()) {
// 用 assign 而不是 copyTo,确保引用计数干净
images[k].image = originalImages[k].clone();
atlas = localAtlas;
}
}
DEBUG_EXTRA("Original images restored.");
DEBUG_EXTRA("Re-rasterization done.");
correctedPatchImages.clear();
// ★ 不再恢复原始 images —— 避免 cv::Mat 引用计数混乱导致 munmap_chunk
// atlas 已经生成,images 后续随 MeshTexture 正常析构即可
// ========== 4. 清理成员变量 ==========
texturePatches.clear();
@ -15453,7 +15648,10 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( @@ -15453,7 +15648,10 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
labelsInvalid.Release();
seamEdges.clear();
seamVertices.clear();
faceTexcoords.Release();
// faceTexcoords.Release();
Mesh::TexCoordArr dummyTexcoords;
faceTexcoords.Swap(dummyTexcoords); // 如果 DynArray 有 Swap 方法
DEBUG_EXTRA("ApplyGlobalSeamLevelingOnRCPatches: ALL DONE.");
}
@ -15806,7 +16004,14 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -15806,7 +16004,14 @@ bool MeshTexture::RasterizeVirtualFaces(
color[1] = cv::saturate_cast<uchar>(std::min(255.0f, color[1] * 1.05f));
color[2] = cv::saturate_cast<uchar>(std::min(255.0f, color[2] * 1.05f));
atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]};
// 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 = atlas.at<cv::Vec3b>(atlasY, atlasX);
pixel[0] = color[2];
pixel[1] = color[1];
pixel[2] = color[0];
}
m_texelScores[idx].score = currentScore;
m_texelScores[idx].viewID = viewID;
m_texelPatchID[idx] = i; // ★ 记录这个像素属于 patch i

Loading…
Cancel
Save