From 35319544dd8716a4177b39cec9be5bbd1b018a07 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Mon, 10 Aug 2026 11:27:19 +0800 Subject: [PATCH] =?UTF-8?q?=E9=98=B2=E6=AD=A2=E9=AB=98=E8=B4=A8=E9=87=8F?= =?UTF-8?q?=E8=A7=86=E5=9B=BE=E8=A2=AB=E4=BD=8E=E8=B4=A8=E9=87=8F=E9=82=BB?= =?UTF-8?q?=E5=B1=85=E8=A6=86=E7=9B=96=EF=BC=8C=E4=BB=8E=E8=80=8C=E5=87=8F?= =?UTF-8?q?=E5=B0=91=E5=B1=80=E9=83=A8=E6=9F=93=E8=89=B2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 208 ++++++++++++++++++++++++-------------- 1 file changed, 131 insertions(+), 77 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 69b2d42..19cddec 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -14846,8 +14846,9 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( } } - // faceToView 映射 + // faceToView 映射 + 面评分存储 std::vector faceToView(scene.mesh.faces.size(), NO_ID); + std::vector faceScores(scene.mesh.faces.size(), -FLT_MAX); // ✅ 存储每个面的选图评分 // ---------- 1. 初始视图分配 ---------- size_t emptyCandidateViews = 0; @@ -14881,94 +14882,93 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( faceViews[i].push_back(forcedView); faceViewWeights[i].push_back(1.0f); for (FIndex fid : vf.faces) { - if (fid < faceToView.size()) + if (fid < faceToView.size()) { faceToView[fid] = forcedView; + faceScores[fid] = 1.0f; // 兜底视图给中等评分 + } } ++fallbackByCenterFace; } continue; } - // ✅ 获取面的顶点(必须在循环内定义!) const Mesh::Face& face = scene.mesh.faces[faceID]; const Point3f& v0 = scene.mesh.vertices[face[0]]; const Point3f& v1 = scene.mesh.vertices[face[1]]; const Point3f& v2 = scene.mesh.vertices[face[2]]; - // ✅ 计算面中心(手动计算) Point3f faceCenter = (v0 + v1 + v2) / 3.0f; - Point3f faceNormal = scene.mesh.faceNormals[faceID]; - // 归一化 - float norm = std::sqrt(faceNormal.x * faceNormal.x + - faceNormal.y * faceNormal.y + - faceNormal.z * faceNormal.z); - if (norm > FLT_EPSILON) { - faceNormal.x /= norm; - faceNormal.y /= norm; - faceNormal.z /= norm; - } else { - // 退化三角形,使用默认法线 - faceNormal = Point3f(0, 0, 1); - } - - - // 选第一个候选(简单策略,后续可优化为角度最优) - // IIndex bestView = *candidateViews.begin(); - IIndex bestView = NO_ID; - float bestScore = -FLT_MAX; + Point3f faceNormal = scene.mesh.faceNormals[faceID]; + float norm = std::sqrt(faceNormal.x * faceNormal.x + + faceNormal.y * faceNormal.y + + faceNormal.z * faceNormal.z); + if (norm > FLT_EPSILON) { + faceNormal.x /= norm; + faceNormal.y /= norm; + faceNormal.z /= norm; + } else { + faceNormal = Point3f(0, 0, 1); + } - for (IIndex vid : candidateViews) { - if (vid >= (IIndex)images.size()) continue; - - const Image& img = images[vid]; - if (img.image.empty() || img.image.cols < 2 || img.image.rows < 2) - continue; - - const Camera& cam = img.camera; - - // 1. 正面度评分 - float s_normal = ComputeViewNormalScore(faceNormal, cam, faceCenter); - if (s_normal <= 0.0f) continue; - - // 2. 分辨率评分 - float s_resolution = ComputeResolutionScore( - cam, v0, v1, v2, - img.image.cols, img.image.rows - ); - - // 3. 遮挡惩罚 - Point2f p0 = cam.ProjectPoint(Point3d(v0)); - Point2f p1 = cam.ProjectPoint(Point3d(v1)); - Point2f p2 = cam.ProjectPoint(Point3d(v2)); - float s_occlusion = ComputeOcclusionPenalty( - p0, p1, p2, - img.image.cols, img.image.rows, - 8 // border pixels - ); - - // 综合评分 - float score = 1.0f * s_normal - + 0.7f * s_resolution - - 0.3f * s_occlusion; - - if (score > bestScore) { - bestScore = score; - bestView = vid; - } - } + IIndex bestView = NO_ID; + float bestScore = -FLT_MAX; - faceViews[i].push_back(bestView); - faceViewWeights[i].push_back(1.0f); + for (IIndex vid : candidateViews) { + if (vid >= (IIndex)images.size()) continue; + + const Image& img = images[vid]; + if (img.image.empty() || img.image.cols < 2 || img.image.rows < 2) + continue; + + const Camera& cam = img.camera; + + // 1. 正面度评分 + float s_normal = ComputeViewNormalScore(faceNormal, cam, faceCenter); + if (s_normal <= 0.0f) continue; + + // 2. 分辨率评分 + float s_resolution = ComputeResolutionScore( + cam, v0, v1, v2, + img.image.cols, img.image.rows + ); + + // 3. 遮挡惩罚 + Point2f p0 = cam.ProjectPoint(Point3d(v0)); + Point2f p1 = cam.ProjectPoint(Point3d(v1)); + Point2f p2 = cam.ProjectPoint(Point3d(v2)); + float s_occlusion = ComputeOcclusionPenalty( + p0, p1, p2, + img.image.cols, img.image.rows, + 8 + ); + + // 综合评分 + float score = 1.0f * s_normal + + 0.7f * s_resolution + - 0.3f * s_occlusion; + + if (score > bestScore) { + bestScore = score; + bestView = vid; + } + } - for (FIndex fid : vf.faces) { - if (fid < faceToView.size()) - faceToView[fid] = bestView; + if (bestView != NO_ID) { + faceViews[i].push_back(bestView); + faceViewWeights[i].push_back(bestScore); // ✅ 存储实际评分,不是固定值 + + for (FIndex fid : vf.faces) { + if (fid < faceToView.size()) { + faceToView[fid] = bestView; + faceScores[fid] = bestScore; // ✅ 记录面的评分 + } + } + ++successVF; } - ++successVF; } - // ---------- 2. Patch 一致性传播 ---------- + // ---------- 2. 改进的 Patch 一致性传播 ---------- if (scene.mesh.faceFaces.empty()) { scene.mesh.ListIncidenteFaceFaces(); } @@ -14976,12 +14976,21 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( const int PROPAGATION_ITER = 2; for (int iter = 0; iter < PROPAGATION_ITER; ++iter) { std::vector newFaceToView = faceToView; + std::vector newFaceScores = faceScores; // ✅ 同步更新评分 for (FIndex fid = 0; fid < (FIndex)faceToView.size(); ++fid) { if (faceToView[fid] == NO_ID) continue; if (fid >= (FIndex)scene.mesh.faceFaces.size()) continue; const Mesh::Face& neighbors = scene.mesh.faceFaces[fid]; + + // ✅ 获取当前面的评分 + float currentScore = faceScores[fid]; + + // ✅ 如果当前视图质量已经很高(>0.8),锁死,不参与传播 + if (currentScore > 0.8f) { + continue; + } std::unordered_map vote; vote[faceToView[fid]] = 1; @@ -15003,11 +15012,54 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( } } + // ✅ 改进的传播条件 if (maxVote >= 3 && majorityView != faceToView[fid]) { - newFaceToView[fid] = majorityView; + // 获取多数视图在邻居中的平均评分 + float majorityAvgScore = 0.0f; + int count = 0; + + for (int k = 0; k < 3; ++k) { + FIndex nb = neighbors[k]; + if (nb == NO_ID) continue; + if (nb < (FIndex)faceToView.size() && faceToView[nb] == majorityView) { + majorityAvgScore += faceScores[nb]; + count++; + } + } + + if (count > 0) { + majorityAvgScore /= count; + } + + // ✅ 条件1:当前评分较低才考虑传播 + // ✅ 条件2:多数视图的评分与当前评分相差不大(防止引入更差的视图) + // ✅ 条件3:或者多数视图明显更好 + bool shouldPropagate = false; + + if (currentScore < 0.3f) { + // 当前评分很低,只要多数视图不是特别差就接受 + shouldPropagate = (majorityAvgScore > currentScore - 0.2f); + } else if (currentScore < 0.6f) { + // 当前评分中等,要求多数视图相当或更好 + shouldPropagate = (majorityAvgScore > currentScore - 0.1f); + } else { + // 当前评分较高(0.6-0.8之间),要求多数视图明显更好 + shouldPropagate = (majorityAvgScore > currentScore + 0.05f); + } + + // ✅ 额外保护:如果当前评分已经不错,且多数视图没有显著优势,保持原样 + if (currentScore > 0.6f && std::abs(majorityAvgScore - currentScore) < 0.1f) { + shouldPropagate = false; + } + + if (shouldPropagate) { + newFaceToView[fid] = majorityView; + newFaceScores[fid] = majorityAvgScore; // ✅ 更新为邻居的平均评分 + } } - } + }防止高质量视图被低质量邻居覆盖,从而减少局部染色 faceToView.swap(newFaceToView); + faceScores.swap(newFaceScores); // ✅ 同步更新评分 } // ---------- 3. 写回 ---------- @@ -15018,8 +15070,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( FIndex firstFace = vf.faces[0]; if (firstFace < faceToView.size() && faceToView[firstFace] != NO_ID) { IIndex consistentView = faceToView[firstFace]; + float consistentScore = faceScores[firstFace]; // ✅ 使用更新后的评分 + faceViews[i] = {consistentView}; - faceViewWeights[i] = {1.0f}; + faceViewWeights[i] = {consistentScore}; // ✅ 更新权重为实际评分 } } @@ -18937,11 +18991,11 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) return false; - // ✅ 2. 计算可见性(填充 faceNeighbors) - if (!texture.ComputePureFaceVisibility( - fOutlierThreshold, nIgnoreMaskLabel, views)) { - return false; - } + // // ✅ 2. 计算可见性(填充 faceNeighbors) + // if (!texture.ComputePureFaceVisibility( + // fOutlierThreshold, nIgnoreMaskLabel, views)) { + // return false; + // } // ✅ 3. 选择最佳视图(带 patch 一致性) if (!texture.SelectBestViewsForVirtualFaces(