diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 4718995..97ec91a 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -461,6 +461,7 @@ public: bool CreateVirtualFaces6(FaceDataViewArr& facesDatas, FaceDataViewArr& ComputeAverageQuality, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const; bool CreateVirtualFaces61(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f); 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; static std::string GetFileNameWithoutExtension(std::string& strPath); float CalculateBrightnessScore(const Image& imageData) const; bool IsFaceVisibleRelaxed(const FaceDataArr& faceDatas, const IIndexArr& selectedCams, float visibleRatioThreshold) const; @@ -4305,6 +4306,381 @@ std::string MeshTexture::GetFileNameWithoutExtension(std::string& strPath) { } } +bool MeshTexture::CreateVirtualFaces63(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras, float thMaxNormalDeviation) const +{ + if (meshCurvatures.empty()) { + ComputeFaceCurvatures(); + } + + float thMaxColorDeviation = 130.0f; + const float QUALITY_THRESHOLD = 0.9f; // 质量阈值,可根据实际情况调整 + + 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()); + 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; + 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; + } + ++nTotalViewCount; + } + + // 计算中心面片的质量 + float centerFaceQuality = 0.0f; + int validViewCount = 0; + for (const FaceData& fd : centerFaceDatas) { + if (!fd.bInvalidFacesRelative) { + centerFaceQuality += fd.quality; + ++validViewCount; + } + } + if (validViewCount > 0) { + centerFaceQuality /= validViewCount; + } + + std::vector> sortedViews; + std::vector> sortedLuminViews; + std::vector> validViews; + sortedViews.reserve(centerFaceDatas.size()); + for (const FaceData& fd : centerFaceDatas) { + if (fd.bInvalidFacesRelative) { + 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(); + 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); + + const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; + std::vector> cameraAngles; // 存储相机索引和角度 + + for (IIndex idxView : selectedCams) { + const Image& imageData = images[idxView]; + const RMatrix& R = imageData.camera.R; + Point3f localForward(0.0f, 0.0f, -1.0f); + 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; + + 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) { + cameraAngles.emplace_back(idxView, angleDeg); + } + } else { + cameraAngles.emplace_back(idxView, angleDeg); + } + } + } + + // 按angleDeg从小到大排序 + std::sort(cameraAngles.begin(), cameraAngles.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; + }); + + IIndexArr filteredCams; + + // 核心优化:根据中心面片质量决定选择多少张视图 + if (centerFaceQuality >= QUALITY_THRESHOLD) { + // 质量高于阈值,尽量使用同一张视图(角度最小的) + if (!cameraAngles.empty()) { + // 只选择角度最小的一张视图 + filteredCams.push_back(cameraAngles[0].first); + } else if (!selectedCams.empty()) { + // 如果没有符合条件的相机,回退到原始选择中的第一个 + filteredCams.push_back(selectedCams[0]); + } + } else { + // 质量低于阈值,可以选择多张视图 + size_t count = std::min(cameraAngles.size(), static_cast(3)); + for (size_t i = 0; i < count; ++i) { + filteredCams.push_back(cameraAngles[i].first); + } + } + + if (filteredCams.empty()) { + selectedCams = filteredCams; + isVirtualFace[virtualFaceCenterFaceID] = false; + } else { + selectedCams = filteredCams; + isVirtualFace[virtualFaceCenterFaceID] = true; + } + + currentVirtualFaceQueue.AddTail(virtualFaceCenterFaceID); + queuedFaces.clear(); + do { + const FIndex currentFaceId = currentVirtualFaceQueue.GetHead(); + currentVirtualFaceQueue.PopHead(); + + const Normal& faceNormal = scene.mesh.faceNormals[currentFaceId]; + const float cosFaceToCenter(ComputeAngleN(normalCenter.ptr(), faceNormal.ptr())); + if (cosFaceToCenter < dynamicCosTh) + continue; + + ASSERT(!selectedCams.empty()); + if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams)) + continue; + + const Color& centerColor = faceColors[virtualFaceCenterFaceID]; + const Color& currentColor = faceColors[currentFaceId]; + float colorDistance = cv::norm(centerColor - currentColor); + if (colorDistance > 200.0f) { + // continue; // 可选:如果颜色差异太大,跳过 + } + + if (colorDistance > thMaxColorDeviation) { + // continue; // 可选:如果颜色差异太大,跳过 + } + + { + const auto posToErase = remainingFaces.FindFirst(currentFaceId); + ASSERT(posToErase != Mesh::FaceIdxArr::NO_INDEX); + remainingFaces.RemoveAtMove(posToErase); + selectedFaces[currentFaceId] = true; + virtualFace.push_back(currentFaceId); + } + + 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()); + + // 计算虚拟面质量并创建虚拟面 + 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); + 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 { + #if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA + #endif + ++processedFaces; + } + } + } + } + + int validViewsSize = validViews.size(); + if (bHasInvalidView) { + const Color medianColor = ComputeMedianColorAndQuality(sortedViews).color; + const float medianQuality = ComputeMedianColorAndQuality(sortedViews).quality; + const float medianLuminance = ComputeMedianLuminance(sortedViews); + const float colorMAD = ComputeColorMAD(sortedViews, medianColor); + const float luminanceMAD = ComputeLuminanceMAD(sortedViews, medianLuminance); + 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; + const Color& centerColor = faceColors[virtualFaceCenterFaceID]; + const Color& currentColor = faceColors[currentFaceId]; + float colorDistance2 = cv::norm(centerColor - currentColor); + if (colorDistance2 > thMaxColorDeviation) { + bColorSimilarity = false; + } + + if (cosFaceToCenter 0); + 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); + const float colorMAD = ComputeColorMAD(sortedViews, medianColor); + const float luminanceMAD = ComputeLuminanceMAD(sortedViews, medianLuminance); + const float maxColorDeviation = 0.01f * colorMAD; + 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); + validIndices.push_back(n); + } + + if (validIndices.empty()) { + virtualFaceData.quality = medianQuality; + virtualFaceData.color = medianColor; + } 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(); + } + } + } + ASSERT(!virtualFaceDatas.empty()); + } + virtualFacesDatas.emplace_back(std::move(virtualFaceDatas)); + virtualFaces.emplace_back(std::move(virtualFace)); + } while (!remainingFaces.empty()); + + return true; +} + bool MeshTexture::CreateVirtualFaces62(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras, float thMaxNormalDeviation) const { if (meshCurvatures.empty()) { @@ -7245,6 +7621,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); + // CreateVirtualFaces63(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());