From 7ffaf9cef25f69f4b71dcb74fcdb301919830448 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Mon, 24 Aug 2026 15:09:48 +0800 Subject: [PATCH] =?UTF-8?q?=E5=85=B3=E9=97=AD=E8=99=9A=E6=8B=9F=E9=9D=A2?= =?UTF-8?q?=E5=A2=9E=E9=95=BF=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 1596 +++++++++++++++++++++++-------------- 1 file changed, 1001 insertions(+), 595 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 13bb80f..734d156 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -471,7 +471,7 @@ struct MeshTexture { */ public: - MeshTexture(Scene& _scene, unsigned _nResolutionLevel=0, unsigned _nMinResolution=640, Mesh::FaceIdxArr* _curvatureRegions=nullptr); + MeshTexture(Scene& _scene, unsigned _nResolutionLevel=0, unsigned _nMinResolution=640); ~MeshTexture(); void ListVertexFaces(); @@ -512,8 +512,6 @@ public: float ComputeAverageQuality(const FaceDataArr& virtualFaceData) const; FaceDataArr MergeFaceData(const FaceDataArr& data1, const FaceDataArr& data2) const; - bool ShouldStopGrowthByCurvature(FIndex currentFace, FIndex seedFace, float centerCurvature) const; - bool CreateVirtualFaces7(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const; IIndexArr SelectBestViews(const FaceDataArr& faceDatas, FIndex fid, unsigned minCommonCameras, float ratioAngleToQuality) const; IIndexArr SelectBestViews2(const FaceDataArr& faceDatas, FIndex fid, unsigned minCommonCameras, float ratioAngleToQuality, FaceDataViewArr& facesDatas) const; @@ -733,7 +731,6 @@ public: ImageArr& images; Scene& scene; // the mesh vertices and faces - Mesh::FaceIdxArr* curvatureRegions; }; // creating an invalid mask for the given image corresponding to @@ -767,7 +764,7 @@ static Image8U DetectInvalidImageRegions(const Image8U3& image) return mask; } -MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nMinResolution, Mesh::FaceIdxArr* _curvatureRegions) +MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nMinResolution) : nResolutionLevel(_nResolutionLevel), nMinResolution(_nMinResolution), @@ -781,8 +778,7 @@ MeshTexture::MeshTexture(Scene& _scene, unsigned _nResolutionLevel, unsigned _nM faces(_scene.mesh.faces), images(_scene.images), scene(_scene), - alternativeTexture(nullptr), - curvatureRegions(nullptr) + alternativeTexture(nullptr) { } MeshTexture::~MeshTexture() @@ -4311,28 +4307,6 @@ float MeshTexture::ComputeAverageQuality(const FaceDataArr& virtualFaceData) con return count > 0 ? totalQuality / static_cast(count) : 0.0f; } - -bool MeshTexture::ShouldStopGrowthByCurvature(FIndex currentFace, FIndex seedFace, float centerCurvature) const -{ - // 软化逻辑(同上) - const float curvCurrent = meshCurvatures[currentFace]; - const float curvSeed = meshCurvatures[seedFace]; - const float curvDiff = std::abs(curvCurrent - curvSeed); - - float threshold; - if (centerCurvature < 0.1f) { - threshold = 0.3f; - } else if (centerCurvature < 0.2f) { - threshold = 0.15f; - } else if (centerCurvature < 0.4f) { - threshold = 0.08f; - } else { - threshold = 0.03f; - } - - return curvDiff > threshold; -} - MeshTexture::FaceDataArr MeshTexture::MergeFaceData(const FaceDataArr& data1, const FaceDataArr& data2) const { FaceDataArr mergedData; @@ -5883,488 +5857,31 @@ 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; - } - } - - if (scene.mesh.faceNormals.size() != faces.size()) { - DEBUG_EXTRA("CreateVirtualFaces65: faceNormals size mismatch"); - return false; - } - - if (faceFaces.size() != faces.size()) { - DEBUG_EXTRA("CreateVirtualFaces65: faceFaces size mismatch"); - return false; - } - - // ==================== 基础参数 ==================== - const float ratioAngleToQuality = 0.67f; - - Mesh::FaceIdxArr remainingFaces; - remainingFaces.resize(faces.size()); - for (FIndex i = 0; i < faces.size(); ++i) - remainingFaces[i] = i; - - std::vector selectedFaces(faces.size(), false); - cQueue currentVirtualFaceQueue; - std::unordered_set queuedFaces; - - // ==================== 安全辅助函数 ==================== - 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); - } - - // ==================== 预计算面片平均颜色 ==================== - Colors faceColors; - faceColors.resize(faces.size()); - for (Color& c : faceColors) - c = Color(0.0f, 0.0f, 0.0f); - - for (FIndex fid = 0; fid < faces.size(); ++fid) { - const FaceDataArr& fdatas = facesDatas[fid]; - if (fdatas.empty()) continue; - - 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); - } - - // ==================== 相机前向向量 ==================== - 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; - } - - DEBUG_EXTRA("开始稳定的虚拟面片创建逻辑"); - - // ==================== 主循环 ==================== - while (!remainingFaces.empty()) { - const FIndex startPos = RAND() % remainingFaces.size(); - const FIndex virtualFaceCenterFaceID = remainingFaces[startPos]; - - if (!SafeFace(virtualFaceCenterFaceID) || !SafeNormal(virtualFaceCenterFaceID)) - continue; - - // ---------- 动态法线阈值 ---------- - 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; - - const float dynamicCosTh = COS(FD2R(dynamicThreshold)); - const Normal* normalCenterPtr = SafeNormal(virtualFaceCenterFaceID); - if (!normalCenterPtr) continue; - const Normal& normalCenter = *normalCenterPtr; - - // ---------- 中心面片数据 ---------- - 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 selectedCams = SelectBestViews( - centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality); - - if (selectedCams.empty()) { - isVirtualFace[virtualFaceCenterFaceID] = false; - continue; - } - - // ---------- 主视图选择 ---------- - IIndex mainView = NO_ID; - float bestScore = -1.0f; - for (const FaceData& fd : centerFaceDatas) { - if (fd.bInvalidFacesRelative || fd.idxView >= cameraForwards.size()) - continue; - - 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; - } - } - - if (mainView == NO_ID) - mainView = selectedCams[0]; - - // ---------- 虚拟面片生长 ---------- - Mesh::FaceIdxArr virtualFace; - FaceDataArr virtualFaceDatas; - - struct FaceDepthInfo { - FIndex idx; - float avgDepth; - float curvature; - Color avgColor; - }; - std::vector virtualFaceInfo; - virtualFaceInfo.reserve(512); - - currentVirtualFaceQueue.AddTail(virtualFaceCenterFaceID); - queuedFaces.clear(); - - float maxDepthDiff = 0.0f; - constexpr size_t growthLimit = 5000; - - while (!currentVirtualFaceQueue.IsEmpty()) { - if (virtualFace.size() > growthLimit) - break; - - const FIndex currentFaceId = currentVirtualFaceQueue.GetHead(); - currentVirtualFaceQueue.PopHead(); - - // ===== 安全校验 ===== - const Face* curFace = SafeFace(currentFaceId); - const Normal* curNormal = SafeNormal(currentFaceId); - if (!curFace || !curNormal) - continue; - - // ----- 法线检查 ----- - const float cosFaceToCenter = - ComputeAngleN(normalCenter.ptr(), curNormal->ptr()); - if (cosFaceToCenter < dynamicCosTh) - continue; - - // ----- 曲率检查 ----- - if (ShouldStopGrowthByCurvature(currentFaceId, virtualFaceCenterFaceID, centerCurvature)) - continue; - - // ----- 可见性检查 ----- - if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams)) - continue; - - // ----- 深度连续性检查 ----- - 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; - - // ----- 几何连通性检查 ----- - bool connected = false; - for (const auto& info : virtualFaceInfo) { - const Face* prevFace = SafeFace(info.idx); - if (!prevFace) 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; - } - - if (!connected && virtualFaceInfo.size() > 3) - continue; - - // ----- 视角一致性检查 ----- - bool acceptable = false; - if (mainView < cameraForwards.size()) { - const Point3f& mainCamDir = cameraForwards[mainView]; - float mainCosAngle = mainCamDir.dot(Point3f(curNormal->x, curNormal->y, curNormal->z)); - - for (const FaceData& fd : facesDatas[currentFaceId]) { - if (fd.idxView >= cameraForwards.size()) - continue; - - if (fd.idxView == mainView) { - acceptable = true; - break; - } - - 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; - } - } - } - - if (!acceptable) - continue; - - // ----- 颜色一致性检查(手写距离,避免 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; - } - - // ===== 加入虚拟面 ===== - auto pos = remainingFaces.FindFirst(currentFaceId); - if (pos != Mesh::FaceIdxArr::NO_INDEX) { - remainingFaces.RemoveAtMove(pos); - selectedFaces[currentFaceId] = true; - virtualFace.push_back(currentFaceId); - - // 更新 maxDepthDiff(先算,再存) - for (const auto& info : virtualFaceInfo) { - float d = std::abs(info.avgDepth - currentAvgDepth); - maxDepthDiff = std::max(maxDepthDiff, d); - } - - FaceDepthInfo info; - info.idx = currentFaceId; - info.avgDepth = currentAvgDepth; - info.curvature = SafeCurvature(currentFaceId); - info.avgColor = faceColors[currentFaceId]; - virtualFaceInfo.push_back(info); - } - - // ----- 扩展邻居 ----- - const Mesh::FaceFaces* adj = SafeFaceAdj(currentFaceId); - if (!adj) continue; - - 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); - } - } - } - - // ---------- 虚拟面紧凑性裁剪 ---------- - 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); - } - } - - Mesh::FaceIdxArr compactFace; - for (FIndex fid : virtualFace) { - const Face* f = SafeFace(fid); - if (!f) continue; - - 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); - - if (norm(fc - centerPoint) < maxAllowedDiameter * 0.5f) - compactFace.push_back(fid); - } - - if (compactFace.size() >= 3) - virtualFace.swap(compactFace); - } - - // ---------- 生成虚拟面数据 ---------- - for (IIndex idxView : selectedCams) { - if (idxView >= images.size()) - continue; - - FaceData& vfd = virtualFaceDatas.emplace_back(); - vfd.idxView = idxView; - vfd.quality = 0.0f; - vfd.color = Color(0.0f, 0.0f, 0.0f); - - 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; - } - } - } - - if (cnt > 0) { - vfd.quality = totalQuality / static_cast(cnt); - vfd.color = totalColor / static_cast(cnt); - } - } - - if (!virtualFace.empty() && !virtualFaceDatas.empty()) { - virtualFaces.emplace_back(std::move(virtualFace)); - virtualFacesDatas.emplace_back(std::move(virtualFaceDatas)); - isVirtualFace[virtualFaceCenterFaceID] = true; - } - } - - DEBUG_EXTRA("结束稳定的虚拟面片创建逻辑"); - - return true; -} - -/** - * 计算图像亮度评分,专为纹理生成优化 - * @param imageData 输入图像(支持单通道和三通道) - * @return 亮度评分(0-1之间,越高表示亮度质量越好) - */ -float MeshTexture::CalculateBrightnessScore(const Image& imageData) const -{ - if (imageData.image.empty()) { - return 0.5f; // 默认中等评分 - } - - const cv::Mat& image = imageData.image; // 步骤1: 计算动态范围(整合自 ComputeDynamicRangeWithHist) - cv::Mat grayImage; - if (image.channels() > 1) { - cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY); // 转为灰度图 - } else { - grayImage = image.clone(); - } - - // 计算直方图 - int histSize = 256; // 0-255的亮度值 - float range[] = {0, 256}; - const float* histRange = {range}; - cv::Mat hist; - cv::calcHist(&grayImage, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange); - - // 动态范围:找到最小和最大非零亮度值 - float minVal = 255, maxVal = 0; - for (int i = 0; i < histSize; i++) { - if (hist.at(i) > 0) { - minVal = std::min(minVal, static_cast(i)); - maxVal = std::max(maxVal, static_cast(i)); - } } - float dynamicRange = (maxVal - minVal) / 255.0f; // 归一化到[0,1] - // 步骤2: 计算最终评分(整合自 CalculateFinalScore) - // 示例评分逻辑:结合动态范围和平均亮度 - cv::Scalar meanVal = cv::mean(grayImage); - float meanBrightness = meanVal[0] / 255.0f; // 归一化平均亮度 + // 如果夹角小于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; // 视图索引和覆盖的面片数量 - // 评分公式:动态范围和平均亮度的加权组合(权重可调整) - float finalScore = 0.6f * dynamicRange + 0.4f * meanBrightness; + DEBUG_EXTRA("开始新的虚拟面片创建逻辑: 基于视图排序"); - return finalScore; // 返回亮度分数 -} - -bool MeshTexture::CreateVirtualFaces7(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras, float thMaxNormalDeviation) const -{ - if (meshCurvatures.empty()) { - ComputeFaceCurvatures(); - } + 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; + } float thMaxColorDeviation = 130.0f; @@ -6474,29 +5991,940 @@ bool MeshTexture::CreateVirtualFaces7(FaceDataViewArr& facesDatas, FaceDataViewA remainingFaces.RemoveAtMove(posToErase); } else { IIndexArr selectedCams = SelectBestViews(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality); + // IIndexArr selectedCams = SelectBestViews2(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality, facesDatas); + + IIndexArr initialCams = selectedCams; - /* - // 获取中心面片的法线 (注意变量名是 normalCenter, 不是 centerNormal) - const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; - // 过滤selectedCams:只保留夹角小于30度的视图 - IIndexArr filteredCams; // 用于存储过滤后的视图索引 - for (IIndex idxView : selectedCams) { - const Image& imageData = images[idxView]; - // 计算相机在世界坐标系中的朝向向量(相机镜面法线) - const RMatrix& R = imageData.camera.R; // 请根据 R 的实际类型调整,可能是 Matrix3x3f 或其他 - // 相机局部坐标系中的向前向量 (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(因为 Point3f 可能没有 normalize() 成员函数) - float norm = std::sqrt(cameraForward.x * cameraForward.x + - cameraForward.y * cameraForward.y + - cameraForward.z * cameraForward.z); - if (norm > 0.0f) { + 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) + 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 + } + + // 动态调整 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; + + 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 < 0.4f) + // 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 Point3f& cameraForward = cameraForwards[idxView]; + + 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; + }); + + //* + 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; + + // 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; +} + + +// 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); + + // 修改方案 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 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()); +} + +/** + * 计算图像亮度评分,专为纹理生成优化 + * @param imageData 输入图像(支持单通道和三通道) + * @return 亮度评分(0-1之间,越高表示亮度质量越好) + */ +float MeshTexture::CalculateBrightnessScore(const Image& imageData) const +{ + if (imageData.image.empty()) { + return 0.5f; // 默认中等评分 + } + + const cv::Mat& image = imageData.image; // 步骤1: 计算动态范围(整合自 ComputeDynamicRangeWithHist) + cv::Mat grayImage; + if (image.channels() > 1) { + cv::cvtColor(image, grayImage, cv::COLOR_BGR2GRAY); // 转为灰度图 + } else { + grayImage = image.clone(); + } + + // 计算直方图 + int histSize = 256; // 0-255的亮度值 + float range[] = {0, 256}; + const float* histRange = {range}; + cv::Mat hist; + cv::calcHist(&grayImage, 1, 0, cv::Mat(), hist, 1, &histSize, &histRange); + + // 动态范围:找到最小和最大非零亮度值 + float minVal = 255, maxVal = 0; + for (int i = 0; i < histSize; i++) { + if (hist.at(i) > 0) { + minVal = std::min(minVal, static_cast(i)); + maxVal = std::max(maxVal, static_cast(i)); + } + } + float dynamicRange = (maxVal - minVal) / 255.0f; // 归一化到[0,1] + + // 步骤2: 计算最终评分(整合自 CalculateFinalScore) + // 示例评分逻辑:结合动态范围和平均亮度 + cv::Scalar meanVal = cv::mean(grayImage); + float meanBrightness = meanVal[0] / 255.0f; // 归一化平均亮度 + + // 评分公式:动态范围和平均亮度的加权组合(权重可调整) + float finalScore = 0.6f * dynamicRange + 0.4f * meanBrightness; + + return finalScore; // 返回亮度分数 +} + +bool MeshTexture::CreateVirtualFaces7(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector& isVirtualFace, unsigned minCommonCameras, float thMaxNormalDeviation) const +{ + if (meshCurvatures.empty()) { + ComputeFaceCurvatures(); + } + + 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); + + /* + // 获取中心面片的法线 (注意变量名是 normalCenter, 不是 centerNormal) + const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID]; + // 过滤selectedCams:只保留夹角小于30度的视图 + IIndexArr filteredCams; // 用于存储过滤后的视图索引 + for (IIndex idxView : selectedCams) { + const Image& imageData = images[idxView]; + // 计算相机在世界坐标系中的朝向向量(相机镜面法线) + const RMatrix& R = imageData.camera.R; // 请根据 R 的实际类型调整,可能是 Matrix3x3f 或其他 + // 相机局部坐标系中的向前向量 (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(因为 Point3f 可能没有 normalize() 成员函数) + 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; @@ -8659,15 +9087,10 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT const FIndex fAdj(adjFaces[i]); if (fAdj == NO_ID) continue; - const FIndex virtualAdj = mapFaceToVirtualFace[fAdj]; - if (virtualAdj == NO_ID) - continue; - if (virtualAdj >= virtualFaces.size()) - continue; - if (virtualAdj == idxVF) + if (mapFaceToVirtualFace[fAdj] == idxVF) continue; - if (vfNeighbors.Find(virtualAdj) == Mesh::FaceIdxArr::NO_INDEX) { - vfNeighbors.emplace_back(virtualAdj); + if (fAdj != idxFace && vfNeighbors.Find(mapFaceToVirtualFace[fAdj]) == Mesh::FaceIdxArr::NO_INDEX) { + vfNeighbors.emplace_back(mapFaceToVirtualFace[fAdj]); } } } @@ -8684,20 +9107,13 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT //* FOREACH(idxVirtualFace, virtualFaces) { const Mesh::FaceIdxArr& afaces = virtualFaceNeighbors[idxVirtualFace]; - for (FIndex idxVirtualFaceAdj : afaces) { - // ✅ 防御性检查 - if (idxVirtualFaceAdj == NO_ID) - continue; - if (idxVirtualFaceAdj >= virtualFaces.size()) - continue; + for (FIndex idxVirtualFaceAdj: afaces) { if (idxVirtualFace >= idxVirtualFaceAdj) continue; - const bool bInvisibleFace(virtualFacesDatas[idxVirtualFace].empty()); const bool bInvisibleFaceAdj(virtualFacesDatas[idxVirtualFaceAdj].empty()); if (bInvisibleFace || bInvisibleFaceAdj) continue; - boost::add_edge(idxVirtualFace, idxVirtualFaceAdj, graph); } } @@ -8706,7 +9122,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT // 这里通过深度图判断virtualFaces是否要为invalid // CheckInvalidFaces(virtualFacesDatas, fOutlierThreshold, nIgnoreMaskLabel, views, bUseVirtualFaces)) - // printf("virtualFacesDatas.size()=%d, facesDatas.size()=%d\n", virtualFacesDatas.size(), facesDatas.size()); + printf("virtualFacesDatas.size()=%d, facesDatas.size()=%d\n", virtualFacesDatas.size(), facesDatas.size()); ASSERT((Mesh::FIndex)boost::num_vertices(graph) == virtualFaces.size()); // assign the best view to each face @@ -10307,13 +10723,6 @@ void MeshTexture::GlobalSeamLeveling3() const MatIdx col1(vertpatch2row.at(idxPatch1)); ASSERT(idxPatch0 < idxPatch1); const MatIdx rowA((MatIdx)coeffB.size()); - - // // ★ 新增:如果亮度差太大,说明一个是高光一个是暗区,跳过这个约束 - // const float lum0 = GetLuminance(color0); // 或者用 Y 通道 - // const float lum1 = GetLuminance(color1); - // if (std::abs(lum0 - lum1) > 50.0f) // 阈值可调 - // continue; // 不添加这个接缝约束,避免传播高光 - coeffB.Insert(color1 - color0); ASSERT(ISFINITE(coeffB.back())); rows.emplace_back(rowA, col0, 1.f); @@ -10413,28 +10822,9 @@ void MeshTexture::GlobalSeamLeveling3() if (a == Color::ZERO) continue; Pixel8U& v = image.at(r,c); - // 替换原来的 RGB2YCBCR → 加减 → YCBCR2RGB 那段 - const float maxAdjY = 25.0f; - const float maxAdjC = 12.0f; - Color clamped_a; - clamped_a[0] = CLAMP(a[0], -maxAdjY, maxAdjY); - clamped_a[1] = CLAMP(a[1], -maxAdjC, maxAdjC); - clamped_a[2] = CLAMP(a[2], -maxAdjC, maxAdjC); - const Color col(RGB2YCBCR(Color(v))); - - // // ★ 新增:如果已经很亮且调整量是正向的,跳过提亮 - // if (col[0] > 210.0f && a[0] > 0.0f) { - // // 已经够亮了,不再加亮度(但允许降亮度,也允许调色度) - // clamped_a[0] = 0.0f; - // } else { - // clamped_a[0] = CLAMP(a[0], -maxAdjY, maxAdjY); - // } - // clamped_a[1] = CLAMP(a[1], -maxAdjC, maxAdjC); - // clamped_a[2] = CLAMP(a[2], -maxAdjC, maxAdjC); - - const Color acol(YCBCR2RGB(Color(col + clamped_a))); - for (int p = 0; p < 3; ++p) + const Color acol(YCBCR2RGB(Color(col+a))); + for (int p=0; p<3; ++p) v[p] = (uint8_t)CLAMP(ROUND2INT(acol[p]), 0, 255); } } @@ -10953,31 +11343,47 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel ASSERT(imageData.image.isInside(Point2f(aabb.ptMin))); ASSERT(imageData.image.isInside(Point2f(aabb.ptMax))); - // ===== 替换 GenerateTexture 中第一个循环里设置 rect 的代码 ===== - if (bOriginFaceview) { - texturePatch.rect.x = FLOOR2INT(aabb.ptMin[0]) - border; - texturePatch.rect.y = FLOOR2INT(aabb.ptMin[1]) - border; - texturePatch.rect.width = CEIL2INT(aabb.ptMax[0] - aabb.ptMin[0]) + border * 2; - texturePatch.rect.height = CEIL2INT(aabb.ptMax[1] - aabb.ptMin[1]) + border * 2; - } else { - texturePatch.rect.x = std::max(0, FLOOR2INT(aabb.ptMin[0]) - border); - texturePatch.rect.y = std::max(0, FLOOR2INT(aabb.ptMin[1]) - border); + if (bOriginFaceview) + { + texturePatch.rect.x = FLOOR2INT(aabb.ptMin[0])-border; + texturePatch.rect.y = FLOOR2INT(aabb.ptMin[1])-border; + texturePatch.rect.width = CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2; + texturePatch.rect.height = CEIL2INT(aabb.ptMax[1]-aabb.ptMin[1])+border*2; + } + else + { + texturePatch.rect.x = std::max(0, FLOOR2INT(aabb.ptMin[0])-border); + texturePatch.rect.y = std::max(0, FLOOR2INT(aabb.ptMin[1])-border); + // 限制尺寸不超过图像实际范围 const cv::Mat& img = images[texturePatch.label].image; - texturePatch.rect.width = CEIL2INT(aabb.ptMax[0] - aabb.ptMin[0]) + border * 2; - texturePatch.rect.height = CEIL2INT(aabb.ptMax[1] - aabb.ptMin[1]) + border * 2; - // 裁剪到图像范围内 - texturePatch.rect.width = std::min(texturePatch.rect.width, img.cols - texturePatch.rect.x); - texturePatch.rect.height = std::min(texturePatch.rect.height, img.rows - texturePatch.rect.y); + texturePatch.rect.width = std::min( + CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2, + img.cols - texturePatch.rect.x + ); + texturePatch.rect.height = std::min( + CEIL2INT(aabb.ptMax[1]-aabb.ptMin[1])+border*2, + img.rows - texturePatch.rect.y + ); } - // ★ 关键修复:确保 rect 至少 1x1,防止后续空 Mat 写越界 - if (texturePatch.rect.width <= 0 || texturePatch.rect.height <= 0) { - // 退化成一个 1x1 的 patch,避免后续崩溃 - texturePatch.rect.width = 1; - texturePatch.rect.height = 1; - texturePatch.rect.x = std::min(texturePatch.rect.x, images[texturePatch.label].image.cols - 1); - texturePatch.rect.y = std::min(texturePatch.rect.y, images[texturePatch.label].image.rows - 1); - } + /* + // 设置 texturePatch.rect 后添加边界检查 + texturePatch.rect.width = std::max(1, texturePatch.rect.width); // 宽度至少为1 + texturePatch.rect.height = std::max(1, texturePatch.rect.height); // 高度至少为1 + + // 检查ROI是否超出图像实际范围 + if (texturePatch.rect.x < 0) { + texturePatch.rect.width += texturePatch.rect.x; // 修正负起点 + texturePatch.rect.x = 0; + } + if (texturePatch.rect.y < 0) { + texturePatch.rect.height += texturePatch.rect.y; + texturePatch.rect.y = 0; + } + // 确保ROI不超出图像右/下边界 + texturePatch.rect.width = std::min(texturePatch.rect.width, imageData.image.cols - texturePatch.rect.x); + texturePatch.rect.height = std::min(texturePatch.rect.height, imageData.image.rows - texturePatch.rect.y); + //*/ ASSERT(imageData.image.isInside(texturePatch.rect.tl())); ASSERT(imageData.image.isInside(texturePatch.rect.br())); @@ -12321,7 +12727,6 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi int nIgnoreMaskLabel, int maxTextureSize, const IIndexArr& views, const SEACAVE::String& baseFileName, bool bOriginFaceview, const std::string& inputFileName, const std::string& meshFileName) { - Mesh::FaceIdxArr regionMap; if (!bOriginFaceview) { // 确保网格拓扑结构已计算 @@ -12340,10 +12745,11 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi mesh.ListBoundaryVertices(); // 计算边界顶点 } + Mesh::FaceIdxArr regionMap; this->SegmentMeshBasedOnCurvature(regionMap, 0.2f); // 曲率阈值设为0.2 } - MeshTexture texture(*this, nResolutionLevel, nMinResolution, ®ionMap); + MeshTexture texture(*this, nResolutionLevel, nMinResolution); // printf("baseFileName=%s\n", baseFileName.c_str());