diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index f4b2888..9525154 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -606,7 +606,7 @@ public: bool FaceViewSelectionWithVirtualFaces(unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, int nIgnoreMaskLabel, const IIndexArr& views, bool bUseExistingUV); - + bool ComputePureFaceVisibility(float fOutlierThreshold, int nIgnoreMaskLabel, const IIndexArr& views); bool GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, @@ -6948,7 +6948,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT // printf("FaceViewSelection3 2 scene.mesh.vertices.size=%d\n", scene.mesh.vertices.size()); bool bUseVirtualFaces(minCommonCameras > 0); - bUseVirtualFaces = false; + // bUseVirtualFaces = false; // list all views for each face FaceDataViewArr facesDatas; @@ -12784,7 +12784,7 @@ float MeshTexture::ComputeFaceDistance(FIndex fid1, FIndex fid2) Point3f diff = center2 - center1; return std::sqrt(diff.x * diff.x + diff.y * diff.y + diff.z * diff.z); } -// 判断面是否在视图中可见 + // 判断面是否在视图中可见 bool MeshTexture::IsFaceVisibleFromView(FIndex idxFace, int viewID) { @@ -14073,6 +14073,14 @@ bool MeshTexture::RasterizeVirtualFaces( DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); TD_TIMER_START(); + int totalVF = virtualFaceMap.size(); + int emptyVF = 0; + for (auto& vf : virtualFaceViews) + if (vf.empty()) ++emptyVF; + + DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)", + totalVF, emptyVF, 100.0f*emptyVF/totalVF); + if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) return false; @@ -14318,6 +14326,12 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int DEBUG_EXTRA("Texture Pipeline: Steps 3-4 (View Selection + VirtualFace Mapping)"); TD_TIMER_START(); + // 不再重新选择视图!直接使用已有的 faceViews + if (faceViews.empty()) { + DEBUG_EXTRA("ERROR: faceViews is empty! Call FaceViewSelection first."); + return false; + } + // 1. 验证输入 if (scene.mesh.faceTexcoords.empty()) { VERBOSE("error: mesh does not contain UV coordinates"); @@ -14348,74 +14362,8 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int DEBUG_EXTRA("Step 4: Forced 1-to-1 mapping: %zu virtual faces for %zu original faces", virtualFaceMap.size(), numFaces); - // 4.2 为每个虚拟面收集视图数据(用于可见性判断) - VirtualFaceDataArr virtualFaceDatas; - if (!ListCameraVirtualFaces(virtualFaceMap, virtualFaceDatas, fOutlierThreshold, nIgnoreMaskLabel, views, false)) { - return false; - } - - // 4.3 单视图选择:为每个虚拟面选择最佳视图 - std::vector> virtualFaceViews(virtualFaceMap.size()); - std::vector> virtualFaceViewWeights(virtualFaceMap.size()); - - size_t unassignedFaces = 0; - - #ifdef _USE_OPENMP - #pragma omp parallel for reduction(+:unassignedFaces) schedule(dynamic) - #endif - for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { - const FIndex fid = virtualFaceMap[idxVF].faces[0]; - const FaceDataArr& vfDatas = virtualFaceDatas[idxVF]; - - float bestScore = -1.0f; - IIndex bestViewID = IIndex(-1); - - // 获取面片中心(用于计算距离和视角) - const Face& face = scene.mesh.faces[fid]; - Point3f faceCenter = (scene.mesh.vertices[face[0]] + - scene.mesh.vertices[face[1]] + - scene.mesh.vertices[face[2]]) / 3.0f; - - const Normal& faceNormal = scene.mesh.faceNormals[fid]; - - // 遍历能看到这个面片的所有相机 - for (const FaceData& data : vfDatas) { - if (data.bInvalidFacesRelative) continue; - - const Image& image = images[data.idxView]; - - // 使用综合评分函数 - float score = ComputeComprehensiveScore(data, faceNormal, faceCenter, image); - - if (score > bestScore) { - bestScore = score; - bestViewID = data.idxView; - } - } - - // 如果找到了有效的视图 - if (bestViewID != IIndex(-1) && bestScore > 0.1f) { - virtualFaceViews[idxVF] = { bestViewID }; - virtualFaceViewWeights[idxVF] = { 1.0f }; // 单视图权重设为1 - } else { - // virtualFaceViews[idxVF].clear(); - // virtualFaceViewWeights[idxVF].clear(); - // ++unassignedFaces; - - float bestQuality = -1.0f; - for (const FaceData& data : vfDatas) { - if (data.quality > bestQuality) { - bestQuality = data.quality; - bestViewID = data.idxView; - bestScore = data.quality; - } - } - } - } - - DEBUG_EXTRA("Step 3-4 completed: %zu faces processed, %zu unassigned", - numFaces, unassignedFaces); - DEBUG_EXTRA("Proceeding to Step 5: Forward Rasterization"); + std::vector> virtualFaceViews = faceViews; + std::vector> virtualFaceViewWeights = faceViewWeights; // ========================================================== // 步骤5:正向光栅化引擎 @@ -14469,39 +14417,84 @@ bool MeshTexture::TextureWithExistingUVVirtualFaces(const IIndexArr& views, int return true; } - DEBUG_EXTRA("Unassigned faces: %zu / %zu", unassignedFaces, numFaces); + // DEBUG_EXTRA("Unassigned faces: %zu / %zu", unassignedFaces, numFaces); DEBUG_EXTRA("Texture generation failed in stable mode"); return false; } -bool MeshTexture::FaceViewSelectionWithVirtualFaces(unsigned minCommonCameras, float fOutlierThreshold, - float fRatioDataSmoothness, int nIgnoreMaskLabel, - const IIndexArr& views, bool bUseExistingUV) +bool MeshTexture::FaceViewSelectionWithVirtualFaces( + unsigned minCommonCameras, + float fOutlierThreshold, + float fRatioDataSmoothness, + int nIgnoreMaskLabel, + const IIndexArr& views, + bool bUseExistingUV) { DEBUG_EXTRA("Performing face view selection with virtual faces (bUseExistingUV=%d)", bUseExistingUV); - + + if (faceNeighbors.empty()) { + DEBUG_EXTRA("Initializing faceNeighbors via ComputePureFaceVisibility..."); + if (!ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views)) { + return false; + } + } + if (bUseExistingUV) { - // 使用现有UV的虚拟面选择策略 VirtualFaceMap virtualFaceMap; if (!CreateVirtualFacesForExistingUV(virtualFaceMap)) { - DEBUG_EXTRA("Failed to create virtual faces for existing UV"); return false; } - - DEBUG_EXTRA("Created %zu virtual faces", virtualFaceMap.size()); - - // 为虚拟面选择最佳视图 - if (!SelectBestViewsForVirtualFaces(virtualFaceMap, minCommonCameras, fOutlierThreshold, - fRatioDataSmoothness, nIgnoreMaskLabel, views)) { - DEBUG_EXTRA("Failed to select best views for virtual faces"); + + if (!SelectBestViewsForVirtualFaces( + virtualFaceMap, minCommonCameras, fOutlierThreshold, + fRatioDataSmoothness, nIgnoreMaskLabel, views)) { return false; } - + return true; - } else { - // 使用原始面片的虚拟面选择 - return FaceViewSelection(minCommonCameras, fOutlierThreshold, fRatioDataSmoothness, nIgnoreMaskLabel, views); } + + // 非 UV 模式:走原生流程 + return FaceViewSelection(minCommonCameras, fOutlierThreshold, + fRatioDataSmoothness, nIgnoreMaskLabel, views); +} + +bool MeshTexture::ComputePureFaceVisibility( + float fOutlierThreshold, + int nIgnoreMaskLabel, + const IIndexArr& views) +{ + DEBUG_EXTRA("Computing face visibility via ListCameraFaces (UV-safe)"); + + // 1. 调用 openMVS 原生的可见性计算 + FaceDataViewArr facesDatas; + const bool bUseVirtualFaces = false; // ✅ 关键:禁用虚拟面,保持 1:1 映射 + + if (!ListCameraFaces(facesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, bUseVirtualFaces)) { + DEBUG_EXTRA("ERROR: ListCameraFaces failed"); + return false; + } + + // 2. 从 facesDatas 中提取 faceNeighbors + // facesDatas[i] 是第 i 个面的候选视图数组(FaceDataArr) + // 每个 FaceData 有 .idxView 成员 + faceNeighbors.resize(facesDatas.size()); + for (size_t i = 0; i < facesDatas.size(); ++i) { + faceNeighbors[i].clear(); + for (const FaceData& fd : facesDatas[i]) { + faceNeighbors[i].push_back(fd.idxView); + } + } + + // 3. 统计 + size_t visibleFaces = 0; + for (const auto& v : faceNeighbors) + if (!v.empty()) + ++visibleFaces; + + DEBUG_EXTRA("Visibility done: %zu/%zu faces have views", + visibleFaces, facesDatas.size()); + return true; } bool MeshTexture::GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, @@ -14533,115 +14526,158 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, const IIndexArr& views) { DEBUG_EXTRA("Selecting best views for %zu virtual faces", virtualFaceMap.size()); - - // 为每个虚拟面计算最佳视图 + faceViews.resize(virtualFaceMap.size()); faceViewWeights.resize(virtualFaceMap.size()); - - // 检查 m_virtualFaceGeometries 是否已初始化 - if (m_virtualFaceGeometries.empty()) { - m_virtualFaceGeometries.Resize(virtualFaceMap.size()); - } else if (m_virtualFaceGeometries.size() < virtualFaceMap.size()) { + + if (m_virtualFaceGeometries.empty() || + m_virtualFaceGeometries.size() < virtualFaceMap.size()) { m_virtualFaceGeometries.Resize(virtualFaceMap.size()); } - - // 初始化每个虚拟面的视图数据数组 - for (auto& viewList : faceViews) { + + for (auto& viewList : faceViews) viewList.clear(); - } - for (auto& weightList : faceViewWeights) { + for (auto& weightList : faceViewWeights) weightList.clear(); - } - + + // ===== Debug 统计 ===== + size_t emptyCandidateViews = 0; + size_t emptyCommonViews = 0; + size_t fallbackByCenterFace = 0; + size_t successVF = 0; + 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 (faceNeighbors[faceID2].empty()) { + DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2); + } + + //------------------------------------------------------------------ + // 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()) { - for (size_t k = 0; k < faceNeighbors[faceID].size(); ++k) { - IIndex viewID = faceNeighbors[faceID][k]; - if (views.empty() || views.FindFirst(viewID) != NO_ID) { - candidateViews.insert(viewID); - faceViewCandidates[j].push_back(viewID); - } - } - } - } - - // 如果候选视图为空,跳过这个虚拟面 - if (candidateViews.empty()) { - continue; - } - - // 找到所有虚拟面都可见的视图 - std::vector commonViews; - for (IIndex viewID : candidateViews) { - bool allFacesVisible = true; - for (size_t j = 0; j < vf.faces.size() && allFacesVisible; ++j) { - if (std::find(faceViewCandidates[j].begin(), faceViewCandidates[j].end(), viewID) == faceViewCandidates[j].end()) { - allFacesVisible = false; + 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); } } - - if (allFacesVisible) { - commonViews.push_back(viewID); - } } - - // 如果共同视图太少,使用所有候选视图 - if (commonViews.size() < minCommonCameras) { - // 将所有候选视图转换为vector - faceViews[i].assign(candidateViews.begin(), candidateViews.end()); + + //------------------------------------------------------------------ + // 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; + } + + //------------------------------------------------------------------ + // 3. 【关键放松】不再强求“所有面片共视” + // 只要视图能看到任意一个面片即可 + //------------------------------------------------------------------ + std::vector usableViews( + candidateViews.begin(), candidateViews.end() + ); + + // (可选)按质量排序,取前 N 个 + // std::sort(usableViews.begin(), usableViews.end(), + // [&](IIndex a, IIndex b) { return Quality(a) > Quality(b); }); + + if (usableViews.size() < minCommonCameras) { + faceViews[i] = usableViews; } else { - // 使用共同视图 - faceViews[i] = commonViews; + // 仍尊重 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; + } + } continue; } - - // 分配相等的权重 - float weight = 1.0f / faceViews[i].size(); + + //------------------------------------------------------------------ + // 5. 权重分配(均等,最稳定) + //------------------------------------------------------------------ + float weight = 1.0f / static_cast(faceViews[i].size()); faceViewWeights[i].resize(faceViews[i].size(), weight); - - // 计算每个视图的质量 - for (size_t k = 0; k < faceViews[i].size(); ++k) { - IIndex viewID = faceViews[i][k]; - - // 创建FaceData - FaceData faceData; - faceData.idxView = viewID; - faceData.quality = weight; // 使用权重作为质量 - - #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA - // 可以在这里计算和设置颜色 - // 例如:faceData.color = CalculateMeanColor(viewID, vf); - #endif - - faceData.bInvalidFacesRelative = false; - - // 注意:m_virtualFaceGeometries 存储的是几何信息,不是视图数据 - // 如果您需要存储视图数据,可以: - // 1. 创建单独的成员变量 - // 2. 扩展 VirtualFaceGeometryData - // 3. 使用 faceViews 和 faceViewWeights - } - - #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA - if (fOutlierThreshold > 0) { - // DetectAndRemoveOutliers(...); - } - #endif + + ++successVF; } - - DEBUG_EXTRA("Best view selection completed for %zu virtual faces", virtualFaceMap.size()); + + //-------------------------------------------------------------------------- + // 6. Debug 汇总(非常重要) + //-------------------------------------------------------------------------- + DEBUG_EXTRA("====== Virtual Face View Selection Summary ======"); + DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size()); + DEBUG_EXTRA("Successfully assigned : %zu", successVF); + DEBUG_EXTRA("Empty candidates : %zu", emptyCandidateViews); + DEBUG_EXTRA("Fallback by center face : %zu", fallbackByCenterFace); + DEBUG_EXTRA("Empty after relaxation : %zu", emptyCommonViews); + DEBUG_EXTRA("Expected empty ratio : %.2f%%", + 100.0 * (emptyCandidateViews - fallbackByCenterFace) / virtualFaceMap.size()); + DEBUG_EXTRA("================================================="); + return true; } @@ -18633,29 +18669,48 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi DEBUG_EXTRA("TextureMesh bUseExistingUV=%d, UVMeshFile=%s", bUseExistingUV, strUVMeshFileName.c_str()); // 处理已有UV的情况 - if (bUseExistingUV && !strUVMeshFileName.empty()) { - // VERBOSE("1faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); - // // 使用预计算UV模式 - // if (!mesh.Load(MAKE_PATH_SAFE(strUVMeshFileName), true)) { - // VERBOSE("error: cannot load mesh file with UV coordinates"); - // return false; - // } - - VERBOSE("2faceTexcoords.size=%d, faces.size=%d", mesh.faceTexcoords.size(), mesh.faces.size() * 3); - // mesh.CheckUVValid(); - - // 确保网格包含UV坐标 - if (mesh.faceTexcoords.empty()) { - VERBOSE("error: the specified mesh does not contain UV coordinates"); - return false; - } + if (bUseExistingUV && !strUVMeshFileName.empty()) { + MeshTexture texture(*this, nResolutionLevel, nMinResolution); + + // ✅ 只用“纯可见性”,绝不碰 FaceViewSelection + if (!texture.ComputePureFaceVisibility( + fOutlierThreshold, + nIgnoreMaskLabel, + views)) { + return false; + } - // 使用虚拟面优化的纹理生成 - if (!texture.TextureWithExistingUVVirtualFaces(views, nIgnoreMaskLabel, fOutlierThreshold, nTextureSizeMultiple, colEmpty, fSharpnessWeight)){ - return false; - } - return true; - } + // ✅ 现在 faces 没动过,faceNeighbors 是对齐的 + MeshTexture::VirtualFaceMap virtualFaceMap; + if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) { + return false; + } + + if (!texture.SelectBestViewsForVirtualFaces( + virtualFaceMap, + 1, // 单视图就够了 + fOutlierThreshold, + fRatioDataSmoothness, + nIgnoreMaskLabel, + views)) { + return false; + } + + // 直接光栅化 + Mesh::Image8U3Arr textures; + if (!texture.RasterizeVirtualFaces( + virtualFaceMap, + texture.faceViews, + texture.faceViewWeights, + nTextureSizeMultiple, + colEmpty, + textures)) { + return false; + } + + mesh.texturesDiffuse = std::move(textures); + return true; + } // generate the texture image and atlas {