diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index cf32d38..b67cd8f 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -5883,968 +5883,432 @@ void MeshTexture::CreateVirtualFaces64(const FaceDataViewArr& facesDatas, FaceDa } while (!remainingFaces.empty()); } -bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras, float thMaxNormalDeviation) const +bool MeshTexture::CreateVirtualFaces65( + FaceDataViewArr& facesDatas, + FaceDataViewArr& virtualFacesDatas, + VirtualFaceIdxsArr& virtualFaces, + std::vector& isVirtualFace, + unsigned minCommonCameras, + float thMaxNormalDeviation) const { + // ==================== 全局安全校验 ==================== + if (faces.empty() || vertices.empty()) { + DEBUG_EXTRA("CreateVirtualFaces65: faces or vertices empty"); + return false; + } + if (meshCurvatures.empty()) { ComputeFaceCurvatures(); + if (meshCurvatures.empty()) { + DEBUG_EXTRA("CreateVirtualFaces65: meshCurvatures empty after compute"); + return false; + } } - // 如果夹角小于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; - } + if (scene.mesh.faceNormals.size() != faces.size()) { + DEBUG_EXTRA("CreateVirtualFaces65: faceNormals size mismatch"); + return false; + } - 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; + if (faceFaces.size() != faces.size()) { + DEBUG_EXTRA("CreateVirtualFaces65: faceFaces size mismatch"); + return false; + } - // 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(); - } + // ==================== 基础参数 ==================== + const float ratioAngleToQuality = 0.67f; - do { - const FIndex startPos = RAND() % remainingFaces.size(); - const FIndex virtualFaceCenterFaceID = remainingFaces[startPos]; + Mesh::FaceIdxArr remainingFaces; + remainingFaces.resize(faces.size()); + for (FIndex i = 0; i < faces.size(); ++i) + remainingFaces[i] = i; - // 动态法线阈值 - const float centerCurvature = meshCurvatures[virtualFaceCenterFaceID]; - const float dynamicThreshold = (centerCurvature < 0.2f) ? 15.0f : 8.0f; // 曲率<0.2为平坦区域 - const float dynamicCosTh = COS(FD2R(dynamicThreshold)); + std::vector selectedFaces(faces.size(), false); + cQueue currentVirtualFaceQueue; + std::unordered_set queuedFaces; - ASSERT(currentVirtualFaceQueue.IsEmpty()); - const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; - const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID]; + // ==================== 安全辅助函数 ==================== + auto SafeFace = [&](FIndex fid) -> const Face* { + if (fid == NO_ID || fid >= faces.size()) return nullptr; + return &faces[fid]; + }; + + auto SafeNormal = [&](FIndex fid) -> const Normal* { + if (fid == NO_ID || fid >= scene.mesh.faceNormals.size()) + return nullptr; + return &scene.mesh.faceNormals[fid]; + }; + + auto SafeFaceAdj = [&](FIndex fid) -> const Mesh::FaceFaces* { + if (fid == NO_ID || fid >= faceFaces.size()) return nullptr; + return &faceFaces[fid]; + }; + + auto SafeVertex = [&](VIndex vid) -> const Point3f* { + if (vid == NO_ID || vid >= vertices.size()) return nullptr; + return &vertices[vid]; + }; + + auto SafeCurvature = [&](FIndex fid) -> float { + if (fid == NO_ID || fid >= meshCurvatures.size()) return 0.0f; + return meshCurvatures[fid]; + }; + + // ==================== 预计算面片平均深度 ==================== + std::vector faceAvgDepths(faces.size(), 0.0f); + for (FIndex fid = 0; fid < faces.size(); ++fid) { + const Face* f = SafeFace(fid); + if (!f) continue; + + float depthSum = 0.0f; + int valid = 0; + for (int i = 0; i < 3; ++i) { + const Point3f* v = SafeVertex((*f)[i]); + if (v) { + depthSum += v->z; + ++valid; + } + } + if (valid > 0) + faceAvgDepths[fid] = depthSum / static_cast(valid); + } - // 检查中心面片是否包含无效视图 - 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) { + // ==================== 预计算面片平均颜色 ==================== + Colors faceColors; + faceColors.resize(faces.size()); + for (Color& c : faceColors) + c = Color(0.0f, 0.0f, 0.0f); - 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; }); + for (FIndex fid = 0; fid < faces.size(); ++fid) { + const FaceDataArr& fdatas = facesDatas[fid]; + if (fdatas.empty()) continue; - 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; + Color sumColor(0.0f, 0.0f, 0.0f); + int cnt = 0; + for (const FaceData& fd : fdatas) { + if (!fd.bInvalidFacesRelative) { + sumColor += fd.color; + ++cnt; + } + } + if (cnt > 0) + faceColors[fid] = sumColor / static_cast(cnt); + } - 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; }); + // ==================== 相机前向向量 ==================== + std::vector cameraForwards(images.size(), Point3f(0.0f, 0.0f, -1.0f)); + 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 len = std::sqrt(forward.x * forward.x + forward.y * forward.y + forward.z * forward.z); + const float FLOAT_EPS = 1e-6f; + if (len > FLOAT_EPS) + forward /= len; + cameraForwards[v] = forward; + } - // 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); - - // ============ 新增:确定主视图 ============ - IIndex mainView = NO_ID; - float bestScore = -1.0f; + DEBUG_EXTRA("开始稳定的虚拟面片创建逻辑"); - for (const FaceData& fd : centerFaceDatas) { - if (fd.bInvalidFacesRelative) continue; + // ==================== 主循环 ==================== + while (!remainingFaces.empty()) { + const FIndex startPos = RAND() % remainingFaces.size(); + const FIndex virtualFaceCenterFaceID = remainingFaces[startPos]; - const Image& img = images[fd.idxView]; - const RMatrix& R = img.camera.R; - Point3f camDir(-R(0,2), -R(1,2), -R(2,2)); - float norm = std::sqrt(camDir.x*camDir.x + camDir.y*camDir.y + camDir.z*camDir.z); - if (norm > 0) camDir /= norm; + if (!SafeFace(virtualFaceCenterFaceID) || !SafeNormal(virtualFaceCenterFaceID)) + continue; - const Normal& n = scene.mesh.faceNormals[virtualFaceCenterFaceID]; - float cosAngle = camDir.dot(Point3f(n.x, n.y, n.z)); + // ---------- 动态法线阈值 ---------- + const float centerCurvature = SafeCurvature(virtualFaceCenterFaceID); + float dynamicThreshold; + if (centerCurvature < 0.05f) dynamicThreshold = 20.0f; + else if (centerCurvature < 0.15f) dynamicThreshold = 12.0f; + else if (centerCurvature < 0.30f) dynamicThreshold = 8.0f; + else dynamicThreshold = 5.0f; - // 评分:质量为主,角度为辅 - float score = fd.quality * 0.7f + cosAngle * 0.3f; - if (score > bestScore) { - bestScore = score; - mainView = fd.idxView; - } - } + const float dynamicCosTh = COS(FD2R(dynamicThreshold)); + const Normal* normalCenterPtr = SafeNormal(virtualFaceCenterFaceID); + if (!normalCenterPtr) continue; + const Normal& normalCenter = *normalCenterPtr; - // 保底:防止空虚拟面 - if (mainView == NO_ID && !selectedCams.empty()) { - mainView = selectedCams[0]; - } - // ========================================== + // ---------- 中心面片数据 ---------- + const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID]; + if (centerFaceDatas.empty()) { + virtualFaces.emplace_back(Mesh::FaceIdxArr{virtualFaceCenterFaceID}); + virtualFacesDatas.emplace_back(FaceDataArr{}); + selectedFaces[virtualFaceCenterFaceID] = true; + auto pos = remainingFaces.FindFirst(virtualFaceCenterFaceID); + if (pos != Mesh::FaceIdxArr::NO_INDEX) + remainingFaces.RemoveAtMove(pos); + continue; + } - IIndexArr initialCams = selectedCams; + // ---------- 视图选择 ---------- + IIndexArr selectedCams = SelectBestViews( + centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality); - IIndexArr filteredCams; - //* - int maxSupplementCount = 12; - std::vector candidates; - candidates.reserve(initialCams.size() + maxSupplementCount); - for (IIndex v : initialCams) candidates.push_back(v); + if (selectedCams.empty()) { + isVirtualFace[virtualFaceCenterFaceID] = false; + continue; + } - 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; - } - } + // ---------- 主视图选择 ---------- + IIndex mainView = NO_ID; + float bestScore = -1.0f; + for (const FaceData& fd : centerFaceDatas) { + if (fd.bInvalidFacesRelative || fd.idxView >= cameraForwards.size()) + continue; - 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); - } - } + const Point3f& camDir = cameraForwards[fd.idxView]; + const Normal* n = SafeNormal(virtualFaceCenterFaceID); + if (!n) continue; + float cosAngle = camDir.dot(Point3f(n->x, n->y, n->z)); + float score = fd.quality * 0.7f + cosAngle * 0.3f; + if (score > bestScore) { + bestScore = score; + mainView = fd.idxView; + } + } - // 评分结构 - struct ViewScore { - IIndex viewIdx; - float score; - }; + if (mainView == NO_ID) + mainView = selectedCams[0]; - // 计算每个候选视图的综合评分 - 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; + // ---------- 虚拟面片生长 ---------- + Mesh::FaceIdxArr virtualFace; + FaceDataArr virtualFaceDatas; - 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); + struct FaceDepthInfo { + FIndex idx; + float avgDepth; + float curvature; + Color avgColor; + }; + std::vector virtualFaceInfo; + virtualFaceInfo.reserve(512); - // 覆盖得分:视图覆盖的面片数量越多越好(对数归一化) - 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 + currentVirtualFaceQueue.AddTail(virtualFaceCenterFaceID); + queuedFaces.clear(); - // 综合评分(角度权重0.7,覆盖权重0.3) - float score = 0.7f * angleScore + 0.3f * coverageScore; + float maxDepthDiff = 0.0f; + constexpr size_t growthLimit = 5000; - // 边缘惩罚:如果该视图将当前面片视为边缘,则分数减半 - std::string strName = MeshTexture::GetFileNameWithoutExtension(images[v].name); - if (scene.is_face_edge(strName, virtualFaceCenterFaceID)) { - score *= 0.5f; - } + while (!currentVirtualFaceQueue.IsEmpty()) { + if (virtualFace.size() > growthLimit) + break; - scores.push_back({v, score}); - } + const FIndex currentFaceId = currentVirtualFaceQueue.GetHead(); + currentVirtualFaceQueue.PopHead(); - // 按评分降序排序 - std::sort(scores.begin(), scores.end(), - [](const ViewScore& a, const ViewScore& b) { return a.score > b.score; }); - - std::map mapProcessedViewIdx; + // ===== 安全校验 ===== + const Face* curFace = SafeFace(currentFaceId); + const Normal* curNormal = SafeNormal(currentFaceId); + if (!curFace || !curNormal) + continue; - // printf("----------------\n"); + // ----- 法线检查 ----- + const float cosFaceToCenter = + ComputeAngleN(normalCenter.ptr(), curNormal->ptr()); + if (cosFaceToCenter < dynamicCosTh) + continue; - // 选择 Top-K 个视图(例如 K=5) - int highScoreCount1 = 0; - int highScoreCount2 = 0; - for (const auto& s : scores) { - if (s.score > 1.3f) highScoreCount1++; // 阈值改为0.8 - if (s.score > 0.8f) highScoreCount2++; // 阈值改为0.8 - } + // ----- 曲率检查 ----- + if (ShouldStopGrowthByCurvature(currentFaceId, virtualFaceCenterFaceID, centerCurvature)) + continue; - // 动态调整 kMaxViews - int kMaxViews = 32; - if (highScoreCount1 >= 1) { - kMaxViews = 5; // 高分视图多 - } else if (highScoreCount2 >= 3) { - kMaxViews = 10; - } - - for (int i = 0; i < kMaxViews && i < scores.size(); ++i) { - int index = scores[i].viewIdx; - float score = scores[i].score; + // ----- 可见性检查 ----- + if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams)) + continue; - 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); + // ----- 深度连续性检查 ----- + if (currentFaceId >= faceAvgDepths.size()) + continue; + float currentAvgDepth = faceAvgDepths[currentFaceId]; + float centerAvgDepth = faceAvgDepths[virtualFaceCenterFaceID]; + float depthDiff = std::abs(currentAvgDepth - centerAvgDepth); + if (maxDepthDiff > 0.01f && depthDiff > 0.1f * maxDepthDiff) + continue; - 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; + // ----- 几何连通性检查 ----- + bool connected = false; + for (const auto& info : virtualFaceInfo) { + const Face* prevFace = SafeFace(info.idx); + if (!prevFace) continue; - // if (score < 0.4f) - // continue; + for (int i = 0; i < 3; ++i) { + for (int j = 0; j < 3; ++j) { + if ((*prevFace)[i] == (*curFace)[j]) { + connected = true; + break; + } + } + if (connected) break; + } + if (connected) break; + } - filteredCams.push_back(index); + if (!connected && virtualFaceInfo.size() > 3) + continue; - mapProcessedViewIdx[index] = index; - } - //*/ + // ----- 视角一致性检查 ----- + bool acceptable = false; + if (mainView < cameraForwards.size()) { + const Point3f& mainCamDir = cameraForwards[mainView]; + float mainCosAngle = mainCamDir.dot(Point3f(curNormal->x, curNormal->y, curNormal->z)); - // printf("--------\n"); + for (const FaceData& fd : facesDatas[currentFaceId]) { + if (fd.idxView >= cameraForwards.size()) + continue; - // 获取中心面片的法线 (注意变量名是 normalCenter, 不是 centerNormal) - const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; + if (fd.idxView == mainView) { + acceptable = true; + break; + } - std::map mapSortedcams; - std::map mapSortedcams2; - for (IIndex idxView : selectedCams) - { - const Image& imageData = images[idxView]; + const Point3f& camDir = cameraForwards[fd.idxView]; + float cosAngle = camDir.dot(Point3f(curNormal->x, curNormal->y, curNormal->z)); + float angleDiff = std::abs(std::acos(cosAngle) - std::acos(mainCosAngle)); + if (angleDiff < FD2R(70.0f)) { + acceptable = true; + break; + } + } + } - const Point3f& cameraForward = cameraForwards[idxView]; + if (!acceptable) + continue; - Point3f normalPoint(normalCenter.x, normalCenter.y, normalCenter.z); - float cosAngle = cameraForward.dot(normalPoint); - float angleDeg = std::acos(cosAngle) * 180.0f / M_PI; + // ----- 颜色一致性检查(手写距离,避免 cv::norm) ----- + if (virtualFaceCenterFaceID < faceColors.size() && currentFaceId < faceColors.size()) { + const Color& centerColor = faceColors[virtualFaceCenterFaceID]; + const Color& currentColor = faceColors[currentFaceId]; + const float dr = centerColor.x - currentColor.x; + const float dg = centerColor.y - currentColor.y; + const float db = centerColor.z - currentColor.z; + const float colorDistance = std::sqrt(dr * dr + dg * dg + db * db); + if (colorDistance > 250.0f) + continue; + } - std::string strPath = imageData.name; - std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath); - - if (!scene.is_face_delete_edge(strName, virtualFaceCenterFaceID)) { + // ===== 加入虚拟面 ===== + auto pos = remainingFaces.FindFirst(currentFaceId); + if (pos != Mesh::FaceIdxArr::NO_INDEX) { + remainingFaces.RemoveAtMove(pos); + selectedFaces[currentFaceId] = true; + virtualFace.push_back(currentFaceId); - if (scene.is_face_edge(strName, virtualFaceCenterFaceID)) - { - // if (angleDeg <= 40.0f) - { - mapSortedcams[idxView] = angleDeg; - } - } - else - { - mapSortedcams[idxView] = angleDeg; - } - } + // 更新 maxDepthDiff(先算,再存) + for (const auto& info : virtualFaceInfo) { + float d = std::abs(info.avgDepth - currentAvgDepth); + maxDepthDiff = std::max(maxDepthDiff, d); + } - if (!scene.is_face_delete_edge(strName, virtualFaceCenterFaceID)) { + FaceDepthInfo info; + info.idx = currentFaceId; + info.avgDepth = currentAvgDepth; + info.curvature = SafeCurvature(currentFaceId); + info.avgColor = faceColors[currentFaceId]; + virtualFaceInfo.push_back(info); + } - if (scene.is_face_edge(strName, virtualFaceCenterFaceID)) - { - // if (angleDeg <= 30.0f) - { - mapSortedcams2[idxView] = angleDeg; - } - } - else - { - // if (angleDeg <= 80.0f) - { - mapSortedcams2[idxView] = angleDeg; - } - } - } - } + // ----- 扩展邻居 ----- + const Mesh::FaceFaces* adj = SafeFaceAdj(currentFaceId); + if (!adj) continue; - // 将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; - }); - - //* - int nViewCoverage = 0; - int nViewCoverageMax = 100; - int nHit = 0; - int nHitMax = 100; - - for (const auto& [viewIdx, coverageCount] : viewCoverage) { - break; - 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 = 100; - for (size_t i = 0; i < sortedCams2.size(); ++i) - { - break; - 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; - } - - //*/ - - 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; - - // if (meshCurvatures[currentFaceId] > 0.2f) - // continue; - - // 软化曲率检查 - const float centerCurvature = meshCurvatures[virtualFaceCenterFaceID]; - if (ShouldStopGrowthByCurvature(currentFaceId, virtualFaceCenterFaceID, centerCurvature)) { - continue; - } - - // check if current face is seen by all cameras in selectedCams - ASSERT(!selectedCams.empty()); - if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams)) - continue; - - //* - // 获取主视图方向 - const Point3f& mainCamDir = cameraForwards[mainView]; - const Normal& n = scene.mesh.faceNormals[currentFaceId]; - float mainCosAngle = mainCamDir.dot(Point3f(n.x, n.y, n.z)); - - bool acceptable = false; - - for (const FaceData& fd : facesDatas[currentFaceId]) { - // if (fd.bInvalidFacesRelative) - // continue; - - const Point3f& camDir = cameraForwards[fd.idxView]; - float cosAngle = camDir.dot(Point3f(n.x, n.y, n.z)); - - // 如果是主视图,直接通过 - if (fd.idxView == mainView) { - acceptable = true; - break; - } - - // 如果不是主视图,但角度差不多(比如差10°以内) - float angleDiff = std::abs(std::acos(cosAngle) - std::acos(mainCosAngle)); - if (angleDiff < FD2R(70.0f)) - { // 10度以内 - acceptable = true; - break; - } - else - { - // DEBUG_EXTRA("!acceptable, angleDiff=%f, FD2R(50.0f)=%f\n", angleDiff, FD2R(50.0f)); - } - } - - if (!acceptable) - { - // DEBUG_EXTRA("!acceptable\n"); - 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); + for (int i = 0; i < 3; ++i) { + const FIndex nf = (*adj)[i]; + if (nf == NO_ID || nf >= faces.size()) + continue; + if (!selectedFaces[nf] && queuedFaces.find(nf) == queuedFaces.end()) { + currentVirtualFaceQueue.AddTail(nf); + queuedFaces.emplace(nf); + } + } + } - // 计算颜色和亮度的绝对中位差(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; + // ---------- 虚拟面紧凑性裁剪 ---------- + if (virtualFace.size() > 10) { + const float maxAllowedDiameter = 0.5f; + Point3f centerPoint(0.0f, 0.0f, 0.0f); + { + const Face* cf = SafeFace(virtualFaceCenterFaceID); + if (cf) { + int valid = 0; + for (int i = 0; i < 3; ++i) { + const Point3f* v = SafeVertex((*cf)[i]); + if (v) { + centerPoint += *v; + ++valid; + } + } + if (valid > 0) + centerPoint /= static_cast(valid); + } + } - 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); - } - } + Mesh::FaceIdxArr compactFace; + for (FIndex fid : virtualFace) { + const Face* f = SafeFace(fid); + if (!f) continue; - if (validIndices.empty()) { + Point3f fc(0.0f, 0.0f, 0.0f); + int valid = 0; + for (int i = 0; i < 3; ++i) { + const Point3f* v = SafeVertex((*f)[i]); + if (v) { + fc += *v; + ++valid; + } + } + if (valid == 0) continue; + fc /= static_cast(valid); - virtualFaceData.quality = medianQuality; - virtualFaceData.color = medianColor; + if (norm(fc - centerPoint) < maxAllowedDiameter * 0.5f) + compactFace.push_back(fid); + } - // 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(); + if (compactFace.size() >= 3) + virtualFace.swap(compactFace); + } - // virtualFaceData.quality = 0; - // #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA - // virtualFaceData.color = Point3f::ZERO; - // #endif - } - } + // ---------- 生成虚拟面数据 ---------- + for (IIndex idxView : selectedCams) { + if (idxView >= images.size()) + continue; - // 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()); + FaceData& vfd = virtualFaceDatas.emplace_back(); + vfd.idxView = idxView; + vfd.quality = 0.0f; + vfd.color = Color(0.0f, 0.0f, 0.0f); - return true; -} + float totalQuality = 0.0f; + Color totalColor(0.0f, 0.0f, 0.0f); + int cnt = 0; + for (FIndex fid : virtualFace) { + const FaceDataArr& fds = facesDatas[fid]; + for (const FaceData& fd : fds) { + if (fd.idxView == idxView && !fd.bInvalidFacesRelative) { + totalQuality += fd.quality; + totalColor += fd.color; + ++cnt; + } + } + } -// build virtual faces with: -// - similar normal -// - high percentage of common images that see them -void MeshTexture::CreateVirtualFaces8(const FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, unsigned minCommonCameras, float thMaxNormalDeviation) const -{ - 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; - do { - const FIndex startPos = RAND() % remainingFaces.size(); - const FIndex virtualFaceCenterFaceID = remainingFaces[startPos]; - ASSERT(currentVirtualFaceQueue.IsEmpty()); - const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; - const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID]; - // 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 { - const IIndexArr selectedCams = SelectBestViews(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality); - 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 < cosMaxNormalDeviation) - continue; - // check if current face is seen by all cameras in selectedCams - ASSERT(!selectedCams.empty()); - if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams)) - continue; - // 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 - unsigned processedFaces(0); - for (FIndex fid : virtualFace) { - const FaceDataArr& faceDatas = facesDatas[fid]; - for (FaceData& faceData: faceDatas) { - if (faceData.idxView == idxView) { - virtualFaceData.quality += faceData.quality; - #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA - virtualFaceData.color += faceData.color; - #endif - ++processedFaces; - break; - } - } - } - ASSERT(processedFaces > 0); + if (cnt > 0) { + vfd.quality = totalQuality / static_cast(cnt); + vfd.color = totalColor / static_cast(cnt); + } + } - // 修改方案 B:使用第 20% 分位(更鲁棒) - // 收集所有 quality,排序,取较低的那个 - std::vector qualities; - for (FIndex fid : virtualFace) { - const FaceDataArr& faceDatas = facesDatas[fid]; - for (FaceData& faceData: faceDatas) { - if (faceData.idxView == idxView) { - qualities.push_back(faceData.quality); - break; - } - } - } - std::sort(qualities.begin(), qualities.end()); - virtualFaceData.quality = qualities[qualities.size() * 0.2]; // 取第20% + if (!virtualFace.empty() && !virtualFaceDatas.empty()) { + virtualFaces.emplace_back(std::move(virtualFace)); + virtualFacesDatas.emplace_back(std::move(virtualFaceDatas)); + isVirtualFace[virtualFaceCenterFaceID] = true; + } + } - #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA - virtualFaceData.color /= processedFaces; - #endif - } - ASSERT(!virtualFaceDatas.empty()); - } - virtualFacesDatas.emplace_back(std::move(virtualFaceDatas)); - virtualFaces.emplace_back(std::move(virtualFace)); - } while (!remainingFaces.empty()); + return true; } /**