diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 89c81f2..cfc9951 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -14606,10 +14606,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, m_virtualFaceGeometries.Resize(virtualFaceMap.size()); } - for (auto& viewList : faceViews) - viewList.clear(); - for (auto& weightList : faceViewWeights) - weightList.clear(); + for (auto& viewList : faceViews) viewList.clear(); + for (auto& weightList : faceViewWeights) weightList.clear(); // ===== Debug 统计 ===== size_t emptyCandidateViews = 0; @@ -14617,128 +14615,142 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, size_t fallbackByCenterFace = 0; size_t successVF = 0; + // ✅ 面片 → 视图 映射表 + std::vector faceToView(scene.mesh.faces.size(), NO_ID); + + // ---------------------------------------------------------------- + // 1. 初始视图分配 + // ---------------------------------------------------------------- for (size_t i = 0; i < virtualFaceMap.size(); ++i) { const VirtualFace& vf = virtualFaceMap[i]; - FIndex faceID2 = vf.faces[0]; - - if (faceID2 >= faceNeighbors.size()) { - DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2); - continue; - } + if (vf.faces.empty()) continue; - if (faceNeighbors[faceID2].empty()) { - // DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2); - } + FIndex faceID2 = vf.faces[0]; + if (faceID2 >= faceNeighbors.size()) { + DEBUG_EXTRA("FATAL: faceID %u out of range!", faceID2); + continue; + } - //------------------------------------------------------------------ - // 1. 收集每个面片的候选视图 - //------------------------------------------------------------------ + // 收集候选视图 std::unordered_set candidateViews; - std::vector> faceViewCandidates(vf.faces.size()); - - for (size_t j = 0; j < vf.faces.size(); ++j) { - FIndex faceID = vf.faces[j]; - if (faceID >= faceNeighbors.size()) - continue; - + for (FIndex faceID : vf.faces) { + if (faceID >= faceNeighbors.size()) continue; for (IIndex viewID : faceNeighbors[faceID]) { if (views.empty() || views.FindFirst(viewID) != NO_ID) { candidateViews.insert(viewID); - faceViewCandidates[j].push_back(viewID); } } } - //------------------------------------------------------------------ - // 2. 【终极兜底】候选视图为空 → 强制分配一个视图 - // 物理遮挡不重要,逻辑可见性最重要 - //------------------------------------------------------------------ - if (candidateViews.empty()) { - ++emptyCandidateViews; - - // 找一个可用的相机ID - // 优先级:views参数指定的 > 所有images中的第一个 - IIndex forcedView = NO_ID; - - if (!views.empty()) { - forcedView = views[0]; - } else if (!images.empty()) { - forcedView = 0; - } - - if (forcedView != NO_ID) { - faceViews[i].push_back(forcedView); - faceViewWeights[i].push_back(1.0f); - ++fallbackByCenterFace; - - // 降低日志级别,避免刷屏,用 DEBUG 而非 EXTRA - // DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)", - // i, forcedView); - continue; - } - - // 实在没救(连images都没有),这才跳过 - DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i); - continue; - } + // ------------------------------------------------------------ + // 2. 兜底:候选视图为空 + // ------------------------------------------------------------ + if (candidateViews.empty()) { + ++emptyCandidateViews; - //------------------------------------------------------------------ - // 3. 【关键放松】不再强求“所有面片共视” - // 只要视图能看到任意一个面片即可 - //------------------------------------------------------------------ - std::vector usableViews( - candidateViews.begin(), candidateViews.end() - ); + IIndex forcedView = NO_ID; + if (!views.empty()) { + forcedView = views[0]; + } else if (!images.empty()) { + forcedView = 0; + } - // (可选)按质量排序,取前 N 个 - // std::sort(usableViews.begin(), usableViews.end(), - // [&](IIndex a, IIndex b) { return Quality(a) > Quality(b); }); + if (forcedView != NO_ID) { + faceViews[i].push_back(forcedView); + faceViewWeights[i].push_back(1.0f); - if (usableViews.size() < minCommonCameras) { - faceViews[i] = usableViews; - } else { - // 仍尊重 minCommonCameras,但不强求共视 - faceViews[i].assign( - usableViews.begin(), - usableViews.begin() + minCommonCameras - ); - } - - //------------------------------------------------------------------ - // 4. 【二次兜底】usableViews 为空(理论上不会发生) - //------------------------------------------------------------------ - if (faceViews[i].empty()) { - ++emptyCommonViews; - - if (!vf.faces.empty()) { - FIndex centerFace = vf.faces[0]; - if (centerFace < faceNeighbors.size() && - !faceNeighbors[centerFace].empty()) { - - IIndex bestView = faceNeighbors[centerFace][0]; - faceViews[i].push_back(bestView); - faceViewWeights[i].push_back(1.0f); - - DEBUG_EXTRA("VF[%zu] empty usableViews -> force view %d", - i, bestView); - continue; + for (FIndex fid : vf.faces) { + if (fid < faceToView.size()) + faceToView[fid] = forcedView; } + + ++fallbackByCenterFace; + continue; } + + DEBUG_EXTRA("VF[%zu] truly hopeless: no images available", i); continue; } - //------------------------------------------------------------------ - // 5. 权重分配(均等,最稳定) - //------------------------------------------------------------------ - float weight = 1.0f / static_cast(faceViews[i].size()); - faceViewWeights[i].resize(faceViews[i].size(), weight); + // ------------------------------------------------------------ + // 3. 选择最佳视图(简单策略:第一个候选) + // ------------------------------------------------------------ + IIndex bestView = *candidateViews.begin(); + + faceViews[i].push_back(bestView); + faceViewWeights[i].push_back(1.0f); + + for (FIndex fid : vf.faces) { + if (fid < faceToView.size()) + faceToView[fid] = bestView; + } ++successVF; } - //-------------------------------------------------------------------------- - // 6. Debug 汇总(非常重要) - //-------------------------------------------------------------------------- + // ---------------------------------------------------------------- + // 4. 【轻量 Patch 一致性传播】 + // ---------------------------------------------------------------- + if (scene.mesh.faceFaces.empty()) { + scene.mesh.ListIncidenteFaceFaces(); + } + + const int PROPAGATION_ITER = 2; + for (int iter = 0; iter < PROPAGATION_ITER; ++iter) { + std::vector newFaceToView = faceToView; + + for (FIndex fid = 0; fid < (FIndex)faceToView.size(); ++fid) { + if (faceToView[fid] == NO_ID) continue; + if (fid >= (FIndex)scene.mesh.faceFaces.size()) continue; + + const Mesh::Face& neighbors = scene.mesh.faceFaces[fid]; + + std::unordered_map vote; + vote[faceToView[fid]] = 1; + + for (int k = 0; k < 3; ++k) { + FIndex nb = neighbors[k]; + if (nb == NO_ID) continue; + if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) { + vote[faceToView[nb]]++; + } + } + + IIndex majorityView = faceToView[fid]; + int maxVote = 0; + for (const auto& v : vote) { + if (v.second > maxVote) { + maxVote = v.second; + majorityView = v.first; + } + } + + if (maxVote >= 3 && majorityView != faceToView[fid]) { + newFaceToView[fid] = majorityView; + } + } + + faceToView.swap(newFaceToView); + } + + // ---------------------------------------------------------------- + // 5. 根据传播后的结果,更新 virtual face 的视图 + // ---------------------------------------------------------------- + for (size_t i = 0; i < virtualFaceMap.size(); ++i) { + const VirtualFace& vf = virtualFaceMap[i]; + if (vf.faces.empty() || faceViews[i].empty()) continue; + + FIndex firstFace = vf.faces[0]; + if (firstFace < faceToView.size() && faceToView[firstFace] != NO_ID) { + IIndex consistentView = faceToView[firstFace]; + faceViews[i] = {consistentView}; + faceViewWeights[i] = {1.0f}; + } + } + + // ---------------------------------------------------------------- + // 6. Debug 汇总 + // ---------------------------------------------------------------- DEBUG_EXTRA("====== Virtual Face View Selection Summary ======"); DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size()); DEBUG_EXTRA("Successfully assigned : %zu", successVF);