Browse Source

模拟OpenMVS几何信息

ManualUV
hesuicong 2 weeks ago
parent
commit
f96aca73a1
  1. 733
      libs/MVS/SceneTexture.cpp

733
libs/MVS/SceneTexture.cpp

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

Loading…
Cancel
Save