From 471319845457a8e787146ab87d38944cdf11d784 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Tue, 22 Sep 2026 19:30:24 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 170 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 170 insertions(+) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index d2617b5..669c29e 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -18543,6 +18543,176 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( DEBUG_EXTRA("Fallback by center face : %zu", fallbackByCenterFace); DEBUG_EXTRA("================================================="); + // ================================================================ +// ★★★ 连通域强制统一视角(消除色差碎片)★★★ +// ================================================================ +{ + TD_TIMER_START(); + + const FIndex NF = (FIndex)faceToView.size(); + if (NF == 0) return true; + + // 1. 构建面片邻接关系(只考虑有效面片) + std::vector> adjList(NF); + for (FIndex fid = 0; fid < NF; ++fid) { + if (faceToView[fid] == NO_ID) continue; + if (fid >= (FIndex)scene.mesh.faceFaces.size()) continue; + + const Mesh::FaceFaces& neighbors = scene.mesh.faceFaces[fid]; + for (int k = 0; k < 3; ++k) { + FIndex nb = neighbors[k]; + if (nb == NO_ID || nb >= NF) continue; + if (faceToView[nb] == NO_ID) continue; + adjList[fid].push_back(nb); + } + } + + // 2. 连通域标记(BFS) + std::vector component(NF, -1); + int numComponents = 0; + + for (FIndex fid = 0; fid < NF; ++fid) { + if (faceToView[fid] == NO_ID) continue; + if (component[fid] >= 0) continue; + + std::queue q; + q.push(fid); + component[fid] = numComponents; + + while (!q.empty()) { + FIndex cur = q.front(); q.pop(); + for (FIndex nb : adjList[cur]) { + if (component[nb] < 0) { + component[nb] = numComponents; + q.push(nb); + } + } + } + numComponents++; + } + + DEBUG_EXTRA("Connected components: %d components from %d faces", numComponents, NF); + + // 3. 统计每个连通域的视角分布 + struct CompInfo { + std::vector faces; + std::map viewVotes; + std::map viewScoreSum; + std::map viewAvgScore; + }; + std::vector comps(numComponents); + + for (FIndex fid = 0; fid < NF; ++fid) { + if (faceToView[fid] == NO_ID) continue; + int c = component[fid]; + if (c < 0) continue; + + IIndex vid = faceToView[fid]; + float score = faceScores[fid]; + + comps[c].faces.push_back(fid); + comps[c].viewVotes[vid]++; + comps[c].viewScoreSum[vid] += score; + } + + for (int c = 0; c < numComponents; ++c) { + for (auto& vs : comps[c].viewScoreSum) { + IIndex vid = vs.first; + int votes = comps[c].viewVotes[vid]; + comps[c].viewAvgScore[vid] = vs.second / votes; + } + } + + // 4. 每个连通域选最佳视角 + int changedFaces = 0; + int smallComponents = 0; + + for (int c = 0; c < numComponents; ++c) { + auto& comp = comps[c]; + if (comp.faces.empty()) continue; + + // 小连通域合并 + if ((int)comp.faces.size() < 5) { + smallComponents++; + + std::map neighborCompVotes; + for (FIndex fid : comp.faces) { + for (FIndex nb : adjList[fid]) { + int nc = component[nb]; + if (nc >= 0 && nc != c) { + neighborCompVotes[nc]++; + } + } + } + + if (!neighborCompVotes.empty()) { + auto bestNb = std::max_element(neighborCompVotes.begin(), neighborCompVotes.end(), + [](const auto& a, const auto& b) { return a.second < b.second; }); + + int targetComp = bestNb->first; + IIndex targetView = NO_ID; + int maxVotes = 0; + for (const auto& vv : comps[targetComp].viewVotes) { + if (vv.second > maxVotes) { + maxVotes = vv.second; + targetView = vv.first; + } + } + + if (targetView != NO_ID) { + for (FIndex fid : comp.faces) { + if (faceToView[fid] != targetView) { + faceToView[fid] = targetView; + faceScores[fid] = comps[targetComp].viewAvgScore[targetView]; + changedFaces++; + } + } + } + continue; + } + } + + // 正常连通域选视角 + IIndex bestView = NO_ID; + float bestWeightedScore = -FLT_MAX; + int totalFaces = (int)comp.faces.size(); + + for (const auto& vv : comp.viewVotes) { + IIndex vid = vv.first; + int votes = vv.second; + float avgScore = comp.viewAvgScore[vid]; + + float voteRatio = (float)votes / totalFaces; + float normalizedScore = std::max(0.0f, std::min(1.0f, avgScore)); + float weightedScore = 0.6f * voteRatio + 0.4f * normalizedScore; + + if (weightedScore > bestWeightedScore) { + bestWeightedScore = weightedScore; + bestView = vid; + } + } + + if (bestView == NO_ID) continue; + + for (FIndex fid : comp.faces) { + if (faceToView[fid] != bestView) { + faceToView[fid] = bestView; + faceScores[fid] = comp.viewAvgScore[bestView]; + changedFaces++; + } + } + } + + DEBUG_EXTRA("Component unification: %d faces changed, %d small components merged (%s)", + changedFaces, smallComponents, TD_TIMER_GET_FMT().c_str()); + + // ★★★ 注意:不要在这里更新 faceViews/faceViewWeights! + // faceViews 会在视图选择阶段的末尾,由 SelectBestViewsForVirtualFaces 根据 faceToView 重建 + // 如果在这里强行同步,会破坏虚拟面片与视图的映射关系,导致采样坐标错误 + + DEBUG_EXTRA("Unification complete: %d components processed.", numComponents); +} + return true; }