From b47e0ab82f2159466a9b83e6af852eaa9aa35adc Mon Sep 17 00:00:00 2001 From: hesuicong Date: Tue, 23 Jun 2026 15:53:04 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E8=AF=84=E5=88=86=E7=B3=BB?= =?UTF-8?q?=E7=BB=9F=E6=9D=A5=E5=A4=84=E7=90=86=E8=A7=86=E5=9B=BE=E9=80=89?= =?UTF-8?q?=E6=8B=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 973 +++++++++++++++++++++++++++++++++++++- 1 file changed, 970 insertions(+), 3 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index aa84713..cdbf62a 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -471,6 +471,8 @@ public: bool CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataViewArr& ComputeAverageQuality, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const; bool CreateVirtualFaces63(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const; bool CreateVirtualFaces64(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const; + bool CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const; + static std::string GetFileNameWithoutExtension(std::string& strPath); float CalculateBrightnessScore(const Image& imageData) const; bool IsFaceVisibleRelaxed(const FaceDataArr& faceDatas, const IIndexArr& selectedCams, float visibleRatioThreshold) const; @@ -6049,7 +6051,7 @@ bool MeshTexture::CreateVirtualFaces64(FaceDataViewArr& facesDatas, FaceDataView int nViewCoverage = 0; int nViewCoverageMax = 100; int nHit = 0; - int nHitMax = 1; + int nHitMax = 5; std::map mapProcessedViewIdx; //* for (const auto& [viewIdx, coverageCount] : viewCoverage) { @@ -6108,7 +6110,7 @@ bool MeshTexture::CreateVirtualFaces64(FaceDataViewArr& facesDatas, FaceDataView nViewCoverage = 0; nViewCoverageMax = 200; nHit = 0; - nHitMax = 5; + nHitMax = 1; for (size_t i = 0; i < sortedCams2.size(); ++i) { if (nViewCoverage>=nViewCoverageMax) @@ -6473,6 +6475,971 @@ bool MeshTexture::CreateVirtualFaces64(FaceDataViewArr& facesDatas, FaceDataView return true; } +bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras, float thMaxNormalDeviation) const +{ + if (meshCurvatures.empty()) { + ComputeFaceCurvatures(); + } + + // 如果夹角小于45度(cos(45°) ≈ 0.7071),则计入覆盖 + float fAngleThreshold1 = 0.6071; // 0.7071f + float fAngleThreshold2 = 0.8571; // 0.7071f + + // 初始化数据结构 + std::vector processedFaces(faces.size(), false); + std::vector> viewCoverage; // 视图索引和覆盖的面片数量 + + DEBUG_EXTRA("开始新的虚拟面片创建逻辑: 基于视图排序"); + + std::vector cameraForwards(images.size(), Point3f(0,0,-1)); + for (IIndex v = 0; v < images.size(); ++v) { + if (!images[v].IsValid()) continue; + const RMatrix& R = images[v].camera.R; + Point3f forward(-R(0,2), -R(1,2), -R(2,2)); + float norm = std::sqrt(forward.x*forward.x + forward.y*forward.y + forward.z*forward.z); + if (norm > 0) forward /= norm; + cameraForwards[v] = forward; + } + + //* + // 1. 计算每个视图的覆盖情况 + Util::Progress progress1(_T("计算视图覆盖"), images.size()); + for (IIndex idxView = 0; idxView < images.size(); ++idxView) { + + if (mapViewCoverageData.find(idxView) == mapViewCoverageData.end()) + { + // 插入新对象 + mapViewCoverageData.emplace(idxView, ViewCoverageData(idxView)); + } + + ViewCoverageData& viewData = mapViewCoverageData[idxView]; + + if (!images[idxView].IsValid()) { + ++progress1; + continue; + } + + int coverageCount = 0; + const Image& imageData = images[idxView]; + + // 计算相机方向 + const RMatrix& R = imageData.camera.R; + Point3f cameraForward( + -R(0,2), // 相机坐标系中-Z轴是向前方向(从相机指向场景) + -R(1,2), + -R(2,2) + ); + + // 归一化相机方向 + const float normCameraForward = std::sqrt( + cameraForward.x * cameraForward.x + + cameraForward.y * cameraForward.y + + cameraForward.z * cameraForward.z + ); + if (normCameraForward > 0) { + cameraForward /= normCameraForward; + } + + FaceDataViewArr facesDatasPreprocess; + facesDatasPreprocess.resize(faces.size()); + + // 遍历所有面片,统计满足条件的面片 + for (FIndex idxFace = 0; idxFace < faces.size(); ++idxFace) { + // 跳过已处理的面片 + if (processedFaces[idxFace]) { + continue; + } + + // 检查面片是否有视图数据 + if (facesDatas[idxFace].empty()) { + continue; + } + + // 检查视图是否能看到这个面片 + bool viewSeesFace = false; + float bestQuality = 0.0f; + + for (const FaceData& fd : facesDatas[idxFace]) { + if (fd.idxView == idxView && !fd.bInvalidFacesRelative) { + viewSeesFace = true; + if (fd.quality > bestQuality) { + bestQuality = fd.quality; + } + } + } + + if (!viewSeesFace) { + continue; + } + + // 检查面片法线与相机方向的夹角 + const Normal& faceNormal = scene.mesh.faceNormals[idxFace]; + const float cosAngle = cameraForward.dot(Point3f(faceNormal.x, faceNormal.y, faceNormal.z)); + + if (cosAngle > fAngleThreshold2) + { + coverageCount++; + + viewData.faceToIndexMap[idxFace] = idxFace; + } + + if (cosAngle > fAngleThreshold1) + { + // viewData.faceToIndexMap[idxFace] = idxFace; + } + } + + if (coverageCount > 0) { + viewCoverage.emplace_back(idxView, coverageCount); + } + + ++progress1; + } + progress1.close(); + + // 2. 按覆盖数量对视图排序(从高到低) + std::sort(viewCoverage.begin(), viewCoverage.end(), + [](const auto& a, const auto& b) { return a.second > b.second; }); + + // 打印排序结果 + DEBUG_EXTRA("视图覆盖统计(按覆盖数量从高到低排序):"); + int totalCoverage = 0; + for (const auto& [idxView, coverage] : viewCoverage) { + const Image& imageData = images[idxView]; + DEBUG_EXTRA("视图 %s (索引 %d): 覆盖 %d 个面片", + imageData.name.c_str(), idxView, coverage); + totalCoverage += coverage; + } + + DEBUG_EXTRA("排序后视图数量: %zu,最大覆盖: %d, 共%d", viewCoverage.size(), + viewCoverage.empty() ? 0 : viewCoverage[0].second, totalCoverage); + //*/ + + float thMaxColorDeviation = 130.0f; + + const float ratioAngleToQuality(0.67f); + const float cosMaxNormalDeviation(COS(FD2R(thMaxNormalDeviation))); + Mesh::FaceIdxArr remainingFaces(faces.size()); + std::iota(remainingFaces.begin(), remainingFaces.end(), 0); + std::vector selectedFaces(faces.size(), false); + cQueue currentVirtualFaceQueue; + std::unordered_set queuedFaces; + + // Precompute average color for each face + Colors faceColors; // 创建一个空列表 + faceColors.reserve(faces.size()); // 预分配空间(如果cList有reserve方法且您关心性能) + for (size_t i = 0; i < faces.size(); ++i) { + faceColors.push_back(Color::ZERO); // 逐个添加元素 + } + for (FIndex idxFace = 0; idxFace < faces.size(); ++idxFace) { + const FaceDataArr& faceDatas = facesDatas[idxFace]; + if (faceDatas.empty()) continue; + Color sumColor = Color::ZERO; + for (const FaceData& fd : faceDatas) { + sumColor += fd.color; + } + faceColors[idxFace] = sumColor / faceDatas.size(); + } + + do { + const FIndex startPos = RAND() % remainingFaces.size(); + const FIndex virtualFaceCenterFaceID = remainingFaces[startPos]; + + // 动态法线阈值 + const float centerCurvature = meshCurvatures[virtualFaceCenterFaceID]; + const float dynamicThreshold = (centerCurvature < 0.2f) ? 15.0f : 8.0f; // 曲率<0.2为平坦区域 + const float dynamicCosTh = COS(FD2R(dynamicThreshold)); + + ASSERT(currentVirtualFaceQueue.IsEmpty()); + const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; + const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID]; + + // 检查中心面片是否包含无效视图 + bool bHasInvalidView = false; + int nInvalidViewCount = 0; + int nTotalViewCount = 0; + for (const FaceData& faceData : centerFaceDatas) { + if (faceData.bInvalidFacesRelative) { + bHasInvalidView = true; + ++nInvalidViewCount; + // break; + } + ++nTotalViewCount; + } + + std::vector> sortedViews; + std::vector> sortedLuminViews; + std::vector> validViews; + sortedViews.reserve(centerFaceDatas.size()); + for (const FaceData& fd : centerFaceDatas) { + + if (fd.bInvalidFacesRelative) + { + // invalidView = fd.idxView; + // invalidQuality = fd.quality; + sortedViews.emplace_back(fd.quality, fd.color); + sortedLuminViews.emplace_back(MeshTexture::GetLuminance(fd.color), fd.color); + } + else + { + sortedViews.emplace_back(fd.quality, fd.color); + sortedLuminViews.emplace_back(MeshTexture::GetLuminance(fd.color), fd.color); + validViews.emplace_back(fd.quality, fd.color); + } + } + std::sort(sortedViews.begin(), sortedViews.end(), + [](const auto& a, const auto& b) { return a.first > b.first; }); + std::sort(validViews.begin(), validViews.end(), + [](const auto& a, const auto& b) { return a.first > b.first; }); + + int nSize = sortedViews.size(); + // int nSize = (sortedViews.size()>1) ? 1 : sortedViews.size(); + // 计算初始平均值 + float totalQuality = 0.0f; + Color totalColor(0,0,0); + for (int n = 0; n < nSize; ++n) { + totalQuality += sortedViews[n].first; + totalColor += sortedViews[n].second; + } + const float avgQuality = totalQuality / nSize; + const Color avgColor = totalColor / nSize; + + float totalLuminance = MeshTexture::GetLuminance(totalColor); + float avgLuminance = totalLuminance / nSize; + std::sort(sortedLuminViews.begin(), sortedLuminViews.end(), + [avgLuminance](const auto& a, const auto& b) { + float luminDistA = cv::norm(avgLuminance - a.first); + float luminDistB = cv::norm(avgLuminance - b.first); + return luminDistA < luminDistB; }); + + // select the common cameras + Mesh::FaceIdxArr virtualFace; + FaceDataArr virtualFaceDatas; + if (centerFaceDatas.empty()) { + virtualFace.emplace_back(virtualFaceCenterFaceID); + selectedFaces[virtualFaceCenterFaceID] = true; + const auto posToErase = remainingFaces.FindFirst(virtualFaceCenterFaceID); + ASSERT(posToErase != Mesh::FaceIdxArr::NO_INDEX); + remainingFaces.RemoveAtMove(posToErase); + } else { + IIndexArr selectedCams = SelectBestViews(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality); + // IIndexArr selectedCams = SelectBestViews2(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality, facesDatas); + + IIndexArr initialCams = selectedCams; + + IIndexArr filteredCams; + //* + int maxSupplementCount = 12; + std::vector candidates; + candidates.reserve(initialCams.size() + maxSupplementCount); + for (IIndex v : initialCams) candidates.push_back(v); + + int supplementCount = 0; + for (const auto& [viewIdx, cov] : viewCoverage) { + if (std::find(candidates.begin(), candidates.end(), viewIdx) == candidates.end()) { + candidates.push_back(viewIdx); + if (++supplementCount >= maxSupplementCount) break; + } + } + + std::vector validCandidates; + for (IIndex v : candidates) { + std::string strName = MeshTexture::GetFileNameWithoutExtension(images[v].name); + if (!scene.is_face_delete_edge(strName, virtualFaceCenterFaceID)) + { + validCandidates.push_back(v); + } + } + + // 评分结构 + struct ViewScore { + IIndex viewIdx; + float score; + }; + + // 计算每个候选视图的综合评分 + std::vector scores; + scores.reserve(validCandidates.size()); + const Point3f normalPt(normalCenter.x, normalCenter.y, normalCenter.z); + const float normalNorm = std::sqrt(normalPt.x*normalPt.x + normalPt.y*normalPt.y + normalPt.z*normalPt.z); + const Point3f normalUnit = (normalNorm > 0) ? normalPt / normalNorm : normalPt; + + for (IIndex v : validCandidates) { + // 角度得分:夹角越小得分越高(0°→1.0,90°→0.0) + const Point3f& camDir = cameraForwards[v]; + float cosAngle = camDir.dot(normalUnit); + float angleDeg = std::acos(std::clamp(cosAngle, -1.0f, 1.0f)) * 180.0f / M_PI; + float angleScore = 1.0f - (angleDeg / 90.0f); + + // 覆盖得分:视图覆盖的面片数量越多越好(对数归一化) + int coverage = 0; + auto it = std::find_if(viewCoverage.begin(), viewCoverage.end(), + [v](const auto& p){ return p.first == v; }); + if (it != viewCoverage.end()) coverage = it->second; + float coverageScore = std::log(1.0f + coverage) / std::log(1.0f + 1000.0f); // 假设最大覆盖1000 + + // 综合评分(角度权重0.7,覆盖权重0.3) + float score = 0.7f * angleScore + 0.3f * coverageScore; + + // 边缘惩罚:如果该视图将当前面片视为边缘,则分数减半 + std::string strName = MeshTexture::GetFileNameWithoutExtension(images[v].name); + if (scene.is_face_edge(strName, virtualFaceCenterFaceID)) { + score *= 0.5f; + } + + scores.push_back({v, score}); + } + + // 按评分降序排序 + std::sort(scores.begin(), scores.end(), + [](const ViewScore& a, const ViewScore& b) { return a.score > b.score; }); + + std::map mapProcessedViewIdx; + + printf("----------------\n"); + + // 选择 Top-K 个视图(例如 K=5) + const int kMaxViews = 12; + for (int i = 0; i < kMaxViews && i < scores.size(); ++i) { + int index = scores[i].viewIdx; + float score = scores[i].score; + + const Image& imageData = images[index]; + std::string strPath = imageData.name; + std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); + printf("Top-K index=%d, strName=%s, score=%f\n", index, strName.c_str(), score); + + auto it_view = mapViewCoverageData.find(index); + if (it_view == mapViewCoverageData.end()) { + continue; + } + + const ViewCoverageData& viewData = it_view->second; + if (viewData.faceToIndexMap.find(virtualFaceCenterFaceID) == viewData.faceToIndexMap.end()) + continue; + + // if (score < 1.0f) + // continue; + + filteredCams.push_back(index); + + mapProcessedViewIdx[index] = index; + } + //*/ + + printf("--------\n"); + + // 获取中心面片的法线 (注意变量名是 normalCenter, 不是 centerNormal) + const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; + + std::map mapSortedcams; + std::map mapSortedcams2; + for (IIndex idxView : selectedCams) + { + const Image& imageData = images[idxView]; + // 计算相机在世界坐标系中的朝向向量(相机镜面法线) + const RMatrix& R = imageData.camera.R; + // 相机局部坐标系中的向前向量 (0,0,-1) + Point3f localForward(0.0f, 0.0f, -1.0f); + // 手动计算矩阵乘法:cameraForward = R * localForward + Point3f cameraForward; + cameraForward.x = R(0,0) * localForward.x + R(0,1) * localForward.y + R(0,2) * localForward.z; + cameraForward.y = R(1,0) * localForward.x + R(1,1) * localForward.y + R(1,2) * localForward.z; + cameraForward.z = R(2,0) * localForward.x + R(2,1) * localForward.y + R(2,2) * localForward.z; + + // 手动归一化 cameraForward + float norm = std::sqrt(cameraForward.x * cameraForward.x + + cameraForward.y * cameraForward.y + + cameraForward.z * cameraForward.z); + if (norm > 0.0f) { + cameraForward.x /= norm; + cameraForward.y /= norm; + cameraForward.z /= norm; + } else { + cameraForward = Point3f(0, 0, -1); + } + + Point3f normalPoint(normalCenter.x, normalCenter.y, normalCenter.z); + float cosAngle = cameraForward.dot(normalPoint); + float angleDeg = std::acos(cosAngle) * 180.0f / M_PI; + + std::string strPath = imageData.name; + std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); + + if (!scene.is_face_delete_edge(strName, virtualFaceCenterFaceID)) { + + if (scene.is_face_edge(strName, virtualFaceCenterFaceID)) + { + if (angleDeg <= 40.0f) + { + mapSortedcams[idxView] = angleDeg; + } + } + else + { + mapSortedcams[idxView] = angleDeg; + } + } + + if (!scene.is_face_delete_edge(strName, virtualFaceCenterFaceID)) { + + if (scene.is_face_edge(strName, virtualFaceCenterFaceID)) + { + // if (angleDeg <= 30.0f) + { + mapSortedcams2[idxView] = angleDeg; + } + } + else + { + // if (angleDeg <= 80.0f) + { + mapSortedcams2[idxView] = angleDeg; + } + } + } + } + + // 将map中的元素放入vector以便排序 + std::vector> sortedCams; + sortedCams.reserve(mapSortedcams.size()); + for (const auto& pair : mapSortedcams) { + sortedCams.emplace_back(pair.first, pair.second); + } + + // 按angleDeg从小到大排序 + std::sort(sortedCams.begin(), sortedCams.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; // 按angleDeg排序 + }); + + // 将map中的元素放入vector以便排序 + std::vector> sortedCams2; + sortedCams2.reserve(mapSortedcams2.size()); + for (const auto& pair : mapSortedcams2) { + sortedCams2.emplace_back(pair.first, pair.second); + } + + // 按angleDeg从小到大排序 + std::sort(sortedCams2.begin(), sortedCams2.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + + // if (filteredCams.empty()) { + // size_t count = std::min(sortedCams.size(), static_cast(3)); + // for (size_t i = 0; i < count; ++i) { + + // // IIndex viewIdx = sortedCams[i].first; + // // float val = sortedCams[i].second; + // // const Image& imageData = images[viewIdx]; + // // std::string strPath = imageData.name; + // // std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); + // // if (strName!="94_2") + // // continue; + + // filteredCams.push_back(sortedCams[i].first); + // } + // } + + // if (filteredCams.empty()) { + // // 处理所有视图都被过滤的情况... + // // DEBUG_EXTRA("Warning: All views filtered for virtual face due to angle condition."); + + // selectedCams = SelectBestView(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality); + // isVirtualFace[virtualFaceCenterFaceID] = false; + + // } else { + // selectedCams = filteredCams; + // isVirtualFace[virtualFaceCenterFaceID] = true; + // } + + // if (filteredCams.empty()) { + // // 尝试从 initialCams 或 viewCoverage 中找一个视图 + // if (!initialCams.empty()) { + // filteredCams.push_back(initialCams[0]); + // } else if (!viewCoverage.empty()) { + // filteredCams.push_back(viewCoverage[0].first); + // } else { + // // 实在找不到任何视图,则作为孤立虚拟面片处理 + // virtualFace.push_back(virtualFaceCenterFaceID); + // selectedFaces[virtualFaceCenterFaceID] = true; + // const auto pos = remainingFaces.FindFirst(virtualFaceCenterFaceID); + // if (pos != Mesh::FaceIdxArr::NO_INDEX) { + // remainingFaces.RemoveAtMove(pos); + // } + // // 创建空的虚拟面片数据(或使用默认视图) + // FaceDataArr vfd; + // vfd.emplace_back().quality = 0; + // vfd.back().idxView = NO_ID; // 或任意有效视图 + // virtualFacesDatas.emplace_back(std::move(vfd)); + // virtualFaces.emplace_back(std::move(virtualFace)); + // continue; // 跳过后续区域增长 + // } + // selectedCams = filteredCams; + // isVirtualFace[virtualFaceCenterFaceID] = true; + // } else { + // selectedCams = filteredCams; + // isVirtualFace[virtualFaceCenterFaceID] = true; + // } + + //* + int nViewCoverage = 0; + int nViewCoverageMax = 100; + int nHit = 0; + int nHitMax = 5; + + for (const auto& [viewIdx, coverageCount] : viewCoverage) { + if (nViewCoverage>=nViewCoverageMax) + // if (nHit>nHitMax) + break; + + auto it_view = mapViewCoverageData.find(viewIdx); + if (it_view == mapViewCoverageData.end()) { + continue; + } + + const ViewCoverageData& viewData = it_view->second; + + const Image& imageData = images[viewIdx]; + std::string strPath = imageData.name; + std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); + // printf("strName=%s\n", strName.c_str()); + // if (strName!="94_2") + // continue; + + if (viewData.faceToIndexMap.find(virtualFaceCenterFaceID) != viewData.faceToIndexMap.end()) + { + if (!scene.is_face_delete_edge(strName, virtualFaceCenterFaceID)) { + if (scene.is_face_edge(strName, virtualFaceCenterFaceID)) + { + // if (angleDeg <= 40.0f) + { + // filteredCams.push_back(viewIdx); + } + } + else + { + // if (filteredCams.empty()) + { + // filteredCams.push_back(viewIdx); + } + } + } + + printf("oldpush1 viewIdx=%d\n", viewIdx); + if (mapProcessedViewIdx.count(viewIdx) > 0) + continue; + + // if (!scene.is_face_delete_edge2(strName, virtualFaceCenterFaceID)) + { + filteredCams.push_back(viewIdx); + mapProcessedViewIdx[viewIdx] = viewIdx; + ++nHit; + + if (nHit>=nHitMax) + break; + } + } + + ++nViewCoverage; + } + + printf("--------\n"); + + nViewCoverage = 0; + nViewCoverageMax = 200; + nHit = 0; + nHitMax = 5; + for (size_t i = 0; i < sortedCams2.size(); ++i) + { + if (nViewCoverage>=nViewCoverageMax) + // if (nHit>nHitMax) + break; + + IIndex viewIdx = sortedCams2[i].first; + float val = sortedCams2[i].second; + + printf("oldpush2 viewIdx=%d\n", viewIdx); + + if (mapProcessedViewIdx.count(viewIdx) > 0) + continue; + + const Image& imageData = images[viewIdx]; + std::string strPath = imageData.name; + std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); + + // if (strName!="94_2") + // continue; + + // if (!scene.is_face_delete_edge2(strName, virtualFaceCenterFaceID)) + { + filteredCams.push_back(viewIdx); + ++nHit; + + if (nHit>=nHitMax) + break; + } + + ++nViewCoverage; + } + + if (filteredCams.empty()) { + size_t count = std::min(sortedCams.size(), static_cast(3)); + for (size_t i = 0; i < count; ++i) { + + // IIndex viewIdx = sortedCams[i].first; + // float val = sortedCams[i].second; + // const Image& imageData = images[viewIdx]; + // std::string strPath = imageData.name; + // std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); + // if (strName!="94_2") + // continue; + + filteredCams.push_back(sortedCams[i].first); + } + } + + // 确保 selectedCams 是非 const 的,才能对其进行赋值 + // 例如,其声明应为:IIndexArr selectedCams = ...; (不能是 const IIndexArr) + if (filteredCams.empty()) { + // 处理所有视图都被过滤的情况... + // DEBUG_EXTRA("Warning: All views filtered for virtual face due to angle condition."); + + // selectedCams = SelectBestView(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality); + selectedCams = filteredCams; + isVirtualFace[virtualFaceCenterFaceID] = false; + + } else { + selectedCams = filteredCams; + isVirtualFace[virtualFaceCenterFaceID] = true; + } + + // 检查中心面片可见性 + // if (!IsFaceVisible(facesDatas[virtualFaceCenterFaceID], selectedCams)) { + // virtualFace.push_back(virtualFaceCenterFaceID); + // selectedFaces[virtualFaceCenterFaceID] = true; + // const auto posToErase = remainingFaces.FindFirst(virtualFaceCenterFaceID); + // if (posToErase != Mesh::FaceIdxArr::NO_INDEX) { + // remainingFaces.RemoveAtMove(posToErase); + // } + // FaceDataArr virtualFaceData; + // FaceData& vfd = virtualFaceData.emplace_back(); + // vfd.idxView = selectedCams[0]; // 此时 selectedCams 非空 + // vfd.quality = 0.0f; + // for (const FaceData& fd : facesDatas[virtualFaceCenterFaceID]) { + // if (fd.idxView == selectedCams[0]) { + // vfd.quality = fd.quality; + // break; + // } + // } + // virtualFacesDatas.emplace_back(std::move(virtualFaceData)); + // virtualFaces.emplace_back(std::move(virtualFace)); + // continue; + // } + //*/ + + currentVirtualFaceQueue.AddTail(virtualFaceCenterFaceID); + queuedFaces.clear(); + do { + const FIndex currentFaceId = currentVirtualFaceQueue.GetHead(); + currentVirtualFaceQueue.PopHead(); + + // check for condition to add in current virtual face + // normal angle smaller than thMaxNormalDeviation degrees + const Normal& faceNormal = scene.mesh.faceNormals[currentFaceId]; + const float cosFaceToCenter(ComputeAngleN(normalCenter.ptr(), faceNormal.ptr())); + if (cosFaceToCenter < dynamicCosTh) + continue; + + // check if current face is seen by all cameras in selectedCams + ASSERT(!selectedCams.empty()); + if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams)) + continue; + + // 3. 放宽颜色差异条件 + const Color& centerColor = faceColors[virtualFaceCenterFaceID]; + const Color& currentColor = faceColors[currentFaceId]; + float colorDistance = cv::norm(centerColor - currentColor); + if (colorDistance > 200.0f) { // 从200.0f放宽到250.0f + // continue; // 如果颜色差异太大,跳过 + } + + { + float colorDistance = cv::norm(centerColor - currentColor); + // printf("1colorDistance=%f\n", colorDistance); + if (colorDistance > thMaxColorDeviation) { + // printf("2colorDistance=%f\n", colorDistance); + // continue; // Skip if color difference is too large + } + } + + // remove it from remaining faces and add it to the virtual face + { + const auto posToErase = remainingFaces.FindFirst(currentFaceId); + ASSERT(posToErase != Mesh::FaceIdxArr::NO_INDEX); + remainingFaces.RemoveAtMove(posToErase); + selectedFaces[currentFaceId] = true; + virtualFace.push_back(currentFaceId); + } + // add all new neighbors to the queue + const Mesh::FaceFaces& ffaces = faceFaces[currentFaceId]; + for (int i = 0; i < 3; ++i) { + const FIndex fIdx = ffaces[i]; + if (fIdx == NO_ID) + continue; + if (!selectedFaces[fIdx] && queuedFaces.find(fIdx) == queuedFaces.end()) { + currentVirtualFaceQueue.AddTail(fIdx); + queuedFaces.emplace(fIdx); + } + } + } while (!currentVirtualFaceQueue.IsEmpty()); + + // compute virtual face quality and create virtual face + for (IIndex idxView: selectedCams) { + FaceData& virtualFaceData = virtualFaceDatas.emplace_back(); + virtualFaceData.quality = 0; + virtualFaceData.idxView = idxView; + #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + virtualFaceData.color = Point3f::ZERO; + #endif + + const Image& imageData = images[idxView]; + std::string strPath = imageData.name; + std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); + int invalidQuality = 0; + Color invalidColor = Point3f::ZERO; + unsigned processedFaces(0); + bool bInvalidFacesRelative = false; + int invalidCount = 0; + for (FIndex fid : virtualFace) { + const FaceDataArr& faceDatas = facesDatas[fid]; + for (FaceData& faceData: faceDatas) { + + int nViewCount = 0; + if (faceData.idxView == idxView) + { + for (const FaceData& fd : faceDatas) + { + if ( faceData.bInvalidFacesRelative) + { + ++nViewCount; + } + } + if (bHasInvalidView) + { + + ++processedFaces; + } + else + { + // virtualFaceData.quality += faceData.quality; + #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + // virtualFaceData.color += faceData.color; + #endif + ++processedFaces; + // break; + } + } + } + } + + float maxLuminance = 120.0f; + float minLuminance = 90.0f; + int validViewsSize = validViews.size(); + // bHasInvalidView = true; + if (bHasInvalidView) + { + // 使用鲁棒的统计方法计算颜色和亮度的中心值 + const Color medianColor = ComputeMedianColorAndQuality(sortedViews).color; + const float medianQuality = ComputeMedianColorAndQuality(sortedViews).quality; + const float medianLuminance = ComputeMedianLuminance(sortedViews); + + // 计算颜色和亮度的绝对中位差(MAD)作为偏差阈值 + const float colorMAD = ComputeColorMAD(sortedViews, medianColor); + const float luminanceMAD = ComputeLuminanceMAD(sortedViews, medianLuminance); + + // 基于MAD设置动态阈值(3倍MAD是统计学上常用的异常值阈值) + const float maxColorDeviation = 0.01f * colorMAD; + const float maxLuminanceDeviation = 0.01f * luminanceMAD; + + std::vector validIndices; + for (int n = 0; n < sortedViews.size(); ++n) { + const Color& viewColor = sortedViews[n].second; + const float viewLuminance = MeshTexture::GetLuminance(viewColor); + + const float colorDistance = cv::norm(viewColor - medianColor); + const float luminanceDistance = std::abs(viewLuminance - medianLuminance); + + if (colorDistance <= maxColorDeviation && + luminanceDistance <= maxLuminanceDeviation) + { + + if (scene.is_face_normal_visible_map(strName, virtualFaceCenterFaceID)) + validIndices.push_back(n); + } + else + { + const FIndex currentFaceId = currentVirtualFaceQueue.GetHead(); + const Normal& faceNormal = scene.mesh.faceNormals[currentFaceId]; + const float cosFaceToCenter(ComputeAngleN(normalCenter.ptr(), faceNormal.ptr())); + + bool bColorSimilarity = true; + // Check color similarity + const Color& centerColor = faceColors[virtualFaceCenterFaceID]; + const Color& currentColor = faceColors[currentFaceId]; + + float colorDistance = cv::norm(centerColor - currentColor); + // printf("1colorDistance=%f\n", colorDistance); + if (colorDistance > thMaxColorDeviation) { + // printf("2colorDistance=%f\n", colorDistance); + bColorSimilarity = false; + } + + // if ((cosFaceToCenter 0); + // virtualFaceData.quality /= processedFaces; + #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + // virtualFaceData.color /= processedFaces; + #endif + + virtualFaceData.quality = 0; + #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + virtualFaceData.color = Point3f::ZERO; + #endif + } + } + else + { + // 使用鲁棒的统计方法计算颜色和亮度的中心值 + const Color medianColor = ComputeMedianColorAndQuality(sortedViews).color; + const float medianQuality = ComputeMedianColorAndQuality(sortedViews).quality; + const float medianLuminance = ComputeMedianLuminance(sortedViews); + + // 计算颜色和亮度的绝对中位差(MAD)作为偏差阈值 + const float colorMAD = ComputeColorMAD(sortedViews, medianColor); + const float luminanceMAD = ComputeLuminanceMAD(sortedViews, medianLuminance); + + // 基于MAD设置动态阈值(3倍MAD是统计学上常用的异常值阈值) + const float maxColorDeviation = 0.01f * colorMAD; + // const float maxLuminanceDeviation = 0.01f * luminanceMAD; + const float maxLuminanceDeviation = 0.05f * luminanceMAD; + + std::vector validIndices; + for (int n = 0; n < sortedViews.size(); ++n) { + const Color& viewColor = sortedViews[n].second; + const float viewLuminance = MeshTexture::GetLuminance(viewColor); + + const float colorDistance = cv::norm(viewColor - medianColor); + const float luminanceDistance = std::abs(viewLuminance - medianLuminance); + + // if (colorDistance <= maxColorDeviation && + // luminanceDistance <= maxLuminanceDeviation) + // if (luminanceDistance <= maxLuminanceDeviation) + { + validIndices.push_back(n); + } + } + + if (validIndices.empty()) { + + virtualFaceData.quality = medianQuality; + virtualFaceData.color = medianColor; + + // virtualFaceData.quality = 0; + // #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + // virtualFaceData.color = Point3f::ZERO; + // #endif + } + else { + // 使用过滤后的视图重新计算平均值 + float totalQuality2 = 0.0f; + Color totalColor2 = Color(0,0,0); + for (int idx : validIndices) { + totalQuality2 += validViews[idx].first; + totalColor2 += validViews[idx].second; + } + virtualFaceData.quality = totalQuality2 / validIndices.size(); + virtualFaceData.color = totalColor2 / validIndices.size(); + + // virtualFaceData.quality = 0; + // #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + // virtualFaceData.color = Point3f::ZERO; + // #endif + } + } + + // virtualFaceData.bInvalidFacesRelative = (invalidCount > 1); + // virtualFaceData.bInvalidFacesRelative = (invalidCount > processedFaces * 2 / 3); + } + ASSERT(!virtualFaceDatas.empty()); + } + virtualFacesDatas.emplace_back(std::move(virtualFaceDatas)); + virtualFaces.emplace_back(std::move(virtualFace)); + } while (!remainingFaces.empty()); + + return true; +} + /** * 计算图像亮度评分,专为纹理生成优化 * @param imageData 输入图像(支持单通道和三通道) @@ -8777,7 +9744,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT // CreateVirtualFaces6(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras); // CreateVirtualFaces61(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras); // CreateVirtualFaces62(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras); - CreateVirtualFaces64(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras); + CreateVirtualFaces65(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras); TD_TIMER_STARTD(); // CreateVirtualFaces7(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras); DEBUG_EXTRA("CreateVirtualFaces completed: %s", TD_TIMER_GET_FMT().c_str());