From 453622d3f26f4fae1b54d2e67caa5d3f3e62eb48 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Thu, 17 Sep 2026 11:47:14 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BB=8E=E8=A7=86=E5=9B=BE=E9=80=89=E6=8B=A9?= =?UTF-8?q?=E5=92=8C=E5=85=89=E6=A0=85=E5=8C=96=E8=BF=9B=E4=B8=80=E6=AD=A5?= =?UTF-8?q?=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 | 396 ++++++++++++++++---------------------- 1 file changed, 166 insertions(+), 230 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index f9730a7..f7253ef 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -655,6 +655,7 @@ public: float fRatioDataSmoothness, int nIgnoreMaskLabel, const IIndexArr& views); // 简单的 view smoothing:如果某个面的 view 和多数邻居不同,且分数差距不大,就改成邻居的 view + cv::Vec3f SampleFaceCenterColor(IIndex vid, const Point3f& faceCenter); float ComputeViewNormalScore(const Mesh::Normal& faceNormal, const Camera& camera, const Point3f& faceCenter); float ComputeResolutionScore( @@ -15970,7 +15971,8 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( { for (int x = 0; x < patchW; ++x) { cv::Vec3b color = remapped.at(y, x); - if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue; + if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue; + int atlasX = x + minX, atlasY = y + minY; if (atlasX < 0 || atlasX >= textureSize || atlasY < 0 || atlasY >= textureSize) continue; // atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]}; @@ -16357,8 +16359,7 @@ bool MeshTexture::RasterizeVirtualFaces( for (int x = 0; x < patchW; ++x) { cv::Vec3b color = patch.at(y, x); - // if (color[0]==0 && color[1]==0 && color[2]==0) continue; - if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue; + if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue; int atlasX = x + minX, atlasY = y + minY; if (atlasX<0||atlasX>=textureSize||atlasY<0||atlasY>=textureSize) continue; @@ -17667,92 +17668,44 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive( return textureSize; } +// 替换掉 g_avgColors[vid] 的用法 +cv::Vec3f MeshTexture::SampleFaceCenterColor(IIndex vid, const Point3f& faceCenter) { + const Image& img = images[vid]; + if (img.image.empty()) return cv::Vec3f(-1, -1, -1); + Point2f proj = img.camera.ProjectPoint(Point3d(faceCenter)); + int px = (int)(proj.x + 0.5f), py = (int)(proj.y + 0.5f); + if (px < 0 || py < 0 || px >= img.image.cols || py >= img.image.rows) + return cv::Vec3f(-1, -1, -1); + const cv::Vec3b& c = img.image.at(py, px); + return cv::Vec3f(c[2]/255.0f, c[1]/255.0f, c[0]/255.0f); // BGR→RGB, normalize +} + // ============================================================ // 5. SelectBestViewsForVirtualFaces(修复版) // ============================================================ +// ============================================================ +// 5. SelectBestViewsForVirtualFaces(局部采样颜色版) +// ============================================================ +// 前置条件:MeshTexture 类中需声明并定义 SampleFaceCenterColor: +// cv::Vec3f SampleFaceCenterColor(IIndex vid, const Point3f& faceCenter) const; +// 实现见下方附录 +// ============================================================ bool MeshTexture::SelectBestViewsForVirtualFaces( VirtualFaceMap& virtualFaceMap, unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, int nIgnoreMaskLabel, const IIndexArr& views) { - VERBOSE("4[EnterSelect] faceFaces.empty=%d faceFaces.size=%d faceNeighbors.empty=%d", + VERBOSE("4[EnterSelect] faceFaces.empty=%d faceFaces.size=%d faceNeighbors.empty=%d", (int)scene.mesh.faceFaces.empty(), (int)scene.mesh.faceFaces.size(), (int)faceNeighbors.empty()); DEBUG_EXTRA("Selecting best views for %zu virtual faces", virtualFaceMap.size()); - static std::vector g_avgColors; - - // ===== 从磁盘加载图像,计算全局平均颜色 ===== - static bool g_avgColorsComputed = false; - // if (!g_avgColorsComputed) { - // const size_t numViews = images.size(); // ← 你的变量名 - // g_avgColors.resize(numViews, cv::Vec3f(-1, -1, -1)); - - // for (size_t vid = 0; vid < numViews; ++vid) { - // const std::string& imgPath = images[vid].name; // ← 你的字段名 - - // cv::Mat img = cv::imread(imgPath, cv::IMREAD_COLOR); - // if (img.empty()) { - // VERBOSE("[AvgColor] view %zu: cannot load '%s'", vid, imgPath.c_str()); - // continue; - // } - - // // 直接转 float 算均值,不 resize - // cv::Mat imgF; - // img.convertTo(imgF, CV_32FC3, 1.0 / 255.0); - // cv::Scalar mean = cv::mean(imgF); - - // g_avgColors[vid] = cv::Vec3f(mean[0], mean[1], mean[2]); - - // VERBOSE("[AvgColor] view %zu: mean=(%.3f,%.3f,%.3f)", - // vid, mean[0], mean[1], mean[2]); - // } - - // g_avgColorsComputed = true; - // VERBOSE("[AvgColor] Done: [0]=(%.3f,%.3f,%.3f)", - // g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2]); - // } - g_avgColorsComputed = false; - if (!g_avgColorsComputed) { - g_avgColors.assign(images.size(), cv::Vec3f(-1, -1, -1)); - for (size_t vid = 0; vid < images.size(); ++vid) { - const cv::Mat& img = images[vid].image; // ★ 直接用内存图像 - if (img.empty()) continue; - cv::Mat imgF; - img.convertTo(imgF, CV_32FC3, 1.0 / 255.0); - cv::Scalar mean = cv::mean(imgF); - g_avgColors[vid] = cv::Vec3f(mean[0], mean[1], mean[2]); // BGR - } - g_avgColorsComputed = true; - VERBOSE("[AvgColor] Done: [0]=(%.3f,%.3f,%.3f), [1]=(%.3f,%.3f,%.3f)", - g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2], - g_avgColors[1][0], g_avgColors[1][1], g_avgColors[1][2]); - } - - if (g_avgColors.empty()) { - g_avgColors.resize(images.size(), cv::Vec3f(-1, -1, -1)); - for (size_t v = 0; v < images.size(); ++v) { - const cv::Mat& m = images[v].image; - if (m.empty()) continue; - cv::Scalar meanColor = cv::mean(m); - g_avgColors[v] = cv::Vec3f(meanColor[0] / 255.0f, meanColor[1] / 255.0f, meanColor[2] / 255.0f); - } - VERBOSE("[AvgColor] Precomputed average colors for %zu images", images.size()); - - // 调试:打印 g_avgColors 内容 - VERBOSE("[AvgColorDebug] g_avgColors.size=%d, [0]=(%.1f,%.1f,%.1f), [1]=(%.1f,%.1f,%.1f), [78]=(%.1f,%.1f,%.1f)", - (int)g_avgColors.size(), - g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2], - g_avgColors[1][0], g_avgColors[1][1], g_avgColors[1][2], - g_avgColors[78][0], g_avgColors[78][1], g_avgColors[78][2]); - } - + // ---------- 初始化 ---------- faceViews.resize(virtualFaceMap.size()); faceViewWeights.resize(virtualFaceMap.size()); - for (auto& v : faceViews) v.clear(); for (auto& w : faceViewWeights) w.clear(); @@ -17766,46 +17719,45 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( std::vector faceToView(scene.mesh.faces.size(), NO_ID); std::vector faceScores(scene.mesh.faces.size(), -FLT_MAX); - if (scene.mesh.faceFaces.empty()) { + if (scene.mesh.faceFaces.empty()) { scene.mesh.ListIncidenteFaceFaces(); } - VERBOSE("5[AfterVis] faceFaces.empty=%d faceFaces.size=%d faceFaces[0]={%u,%u,%u}", - (int)scene.mesh.faceFaces.empty(), - (int)scene.mesh.faceFaces.size(), - scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][0], - scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][1], - scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][2]); - - // ★★★ 关键修复:从 faceFaces 构建 faceNeighbors(virtual face 邻接)★★★ - if (faceNeighbors.empty()) { - faceNeighbors.resize(virtualFaceMap.size()); - for (size_t fid = 0; fid < virtualFaceMap.size(); ++fid) { - if (fid >= scene.mesh.faceFaces.size()) break; - const Mesh::FaceFaces& ff = scene.mesh.faceFaces[fid]; // 原始面邻接 - for (int k = 0; k < 3; ++k) { - FIndex nb = ff[k]; - if (nb == NO_ID || nb >= (FIndex)virtualFaceMap.size()) continue; - // 去重插入 - bool exists = false; - for (IIndex existing : faceNeighbors[fid]) { - if ((FIndex)existing == nb) { exists = true; break; } - } - if (!exists) faceNeighbors[fid].push_back((IIndex)nb); - } - } - DEBUG_EXTRA("[SelectBest] faceNeighbors built: size=%zu, avg=%f", - faceNeighbors.size(), - faceNeighbors.size() > 0 ? - (double)std::accumulate(faceNeighbors.begin(), faceNeighbors.end(), 0ull, - [](size_t s, const auto& v) { return s + v.size(); }) / faceNeighbors.size() : 0); - } + VERBOSE("5[AfterVis] faceFaces.empty=%d faceFaces.size=%d faceFaces[0]={%u,%u,%u}", + (int)scene.mesh.faceFaces.empty(), + (int)scene.mesh.faceFaces.size(), + scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][0], + scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][1], + scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][2]); + + // ★★★ 从 faceFaces 构建 faceNeighbors(virtual face 邻接)★★★ + if (faceNeighbors.empty()) { + faceNeighbors.resize(virtualFaceMap.size()); + for (size_t fid = 0; fid < virtualFaceMap.size(); ++fid) { + if (fid >= scene.mesh.faceFaces.size()) break; + const Mesh::FaceFaces& ff = scene.mesh.faceFaces[fid]; + for (int k = 0; k < 3; ++k) { + FIndex nb = ff[k]; + if (nb == NO_ID || nb >= (FIndex)virtualFaceMap.size()) continue; + bool exists = false; + for (IIndex existing : faceNeighbors[fid]) { + if ((FIndex)existing == nb) { exists = true; break; } + } + if (!exists) faceNeighbors[fid].push_back((IIndex)nb); + } + } + DEBUG_EXTRA("[SelectBest] faceNeighbors built: size=%zu, avg=%f", + faceNeighbors.size(), + faceNeighbors.size() > 0 ? + (double)std::accumulate(faceNeighbors.begin(), faceNeighbors.end(), 0ull, + [](size_t s, const auto& v) { return s + v.size(); }) / faceNeighbors.size() : 0); + } // ---------- 1. 初始视图分配 ---------- size_t emptyCandidateViews = 0; size_t fallbackByCenterFace = 0; size_t successVF = 0; - + for (size_t i = 0; i < virtualFaceMap.size(); ++i) { const VirtualFace& vf = virtualFaceMap[i]; if (vf.faces.empty()) continue; @@ -17881,67 +17833,59 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( float s_occlusion = ComputeOcclusionPenalty( p0, p1, p2, img.image.cols, img.image.rows, 8); - float rawScore = 3.0f * s_normal // 正视角优先 - + 0.5f * s_resolution - - 0.1f * s_occlusion; + float rawScore = 3.0f * s_normal + + 0.5f * s_resolution + - 0.1f * s_occlusion; float score = rawScore; - // ========== 颜色一致性代价 ========== + // ========== 颜色一致性代价(局部采样版)========== { - const float lambda = 100.0f; - // ========== 收集邻居颜色(修复版:1-ring + faceToView)========== - std::vector neighborColors; - if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) { - const Mesh::Face& topoNeighbors = scene.mesh.faceFaces[faceID]; - for (int k = 0; k < 3; ++k) { - FIndex nb = topoNeighbors[k]; - if (nb == NO_ID || nb >= (FIndex)faceToView.size()) continue; - - IIndex nbView = faceToView[nb]; - if (nbView == NO_ID) { - // fallback: 邻居还没分配 view,用第一个可见相机 - if (nb < (FIndex)faceNeighbors.size() && !faceNeighbors[nb].empty()) { - nbView = faceNeighbors[nb][0]; + const float lambda = 0.5f; // 从 100 降到 0.5 + + // 采样当前候选视图下面中心的颜色 + cv::Vec3f candidateColor = SampleFaceCenterColor(vid, faceCenter); + if (candidateColor[0] < 0) { + // 投影失败,跳过颜色代价但不 return + // 保持 score 不变 + } else { + // 收集邻居颜色:已分配的面,采样邻居面中心在邻居视图下的颜色 + std::vector neighborColors; + if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) { + const Mesh::FaceFaces& topoNeighbors = scene.mesh.faceFaces[faceID]; + for (int k = 0; k < 3; ++k) { + FIndex nb = topoNeighbors[k]; + if (nb == NO_ID || nb >= (FIndex)faceToView.size()) continue; + + IIndex nbView = faceToView[nb]; + if (nbView == NO_ID) { + if (nb < (FIndex)faceNeighbors.size() && !faceNeighbors[nb].empty()) { + nbView = faceNeighbors[nb][0]; + } } - } - if (nbView != NO_ID && nbView < (IIndex)g_avgColors.size()) { - const cv::Vec3f& nc = g_avgColors[nbView]; - if (nc[0] >= 0) { // 有效颜色才收集 - neighborColors.push_back(nc); + if (nbView == NO_ID || nbView >= (IIndex)images.size()) continue; + + // 计算邻居面中心 + const Mesh::Face& nbFace = scene.mesh.faces[nb]; + Point3f nbCenter = (scene.mesh.vertices[nbFace[0]] + + scene.mesh.vertices[nbFace[1]] + + scene.mesh.vertices[nbFace[2]]) / 3.0f; + + cv::Vec3f nbColor = SampleFaceCenterColor(nbView, nbCenter); + if (nbColor[0] >= 0) { + neighborColors.push_back(nbColor); } } } - } - // ========== 修复结束 ========== - - float colorDiff = 0.0f; - if (!neighborColors.empty()) { - cv::Vec3f candidateColor = g_avgColors[vid]; - // cv::Vec3f candidateColor = SampleColorAtPoint(images[vid].image, images[vid].camera, faceCenter); - if (faceID == 50000) { - if (candidateColor[0] < 0) { - VERBOSE("[ColorDebug] face=50000 vid=%d -> candidateColor FAILED (val=%.1f,%.1f,%.1f)", - (int)vid, candidateColor[0], candidateColor[1], candidateColor[2]); - } - // 同时打印 g_avgColors[vid] 的原始值 - if (vid < g_avgColors.size()) { - VERBOSE("[ColorDebug] g_avgColors[%d] = (%.1f,%.1f,%.1f)", - (int)vid, g_avgColors[vid][0], g_avgColors[vid][1], g_avgColors[vid][2]); - } else { - VERBOSE("[ColorDebug] vid=%d OUT OF BOUNDS! g_avgColors.size=%d", - (int)vid, (int)g_avgColors.size()); - } - } - if (candidateColor[0] >= 0) { + + float colorDiff = 0.0f; + if (!neighborColors.empty()) { for (const auto& nc : neighborColors) { cv::Vec3f d = candidateColor - nc; colorDiff += std::sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]); } - // colorDiff = (colorDiff / neighborColors.size()) / 1.732f; colorDiff = colorDiff / neighborColors.size(); colorDiff = std::min(colorDiff, 1.0f); - // ✅ 修复:不再乘 score,避免放大效应 score -= lambda * colorDiff; } } @@ -17950,10 +17894,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( static int g_dbg = 0; g_dbg++; if (g_dbg <= 20 || faceID % 50000 == 0) { - VERBOSE("[Score] face=%d view=%d rawScore=%.3f nbrColors=%d " - "colorDiff=%.3f penalty=%.3f finalScore=%.3f", - faceID, vid, rawScore, (int)neighborColors.size(), - colorDiff, lambda * colorDiff, score); + VERBOSE("[Score] face=%d view=%d rawScore=%.3f finalScore=%.3f", + faceID, vid, rawScore, score); } } // ========== 颜色代价结束 ========== @@ -17978,67 +17920,61 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( } // ---------- 2. 改进的 Patch 一致性传播 ---------- + const int PROPAGATION_ITER = 6; + for (int iter = 0; iter < PROPAGATION_ITER; ++iter) { + std::vector newFaceToView = faceToView; + std::vector newFaceScores = faceScores; - const int PROPAGATION_ITER = 6; // 从2改到6 - 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; + 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]; + const Mesh::FaceFaces& neighbors = scene.mesh.faceFaces[fid]; + float currentScore = faceScores[fid]; - // ★ 放宽:只锁死 > 0.95 的极高分明面,其余都允许被传播覆盖 - if (currentScore > 0.95f) continue; + if (currentScore > 0.95f) continue; - std::unordered_map vote; - vote[faceToView[fid]] = 1; - for (int k = 0; k < 3; ++k) { - FIndex nb = neighbors[k]; - if (nb == NO_ID) continue; - if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) { - vote[faceToView[nb]]++; - } - } + std::unordered_map vote; + vote[faceToView[fid]] = 1; + for (int k = 0; k < 3; ++k) { + FIndex nb = neighbors[k]; + if (nb == NO_ID) continue; + if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) { + vote[faceToView[nb]]++; + } + } - IIndex majorityView = faceToView[fid]; - int maxVote = 0; - for (const auto& v : vote) { - if (v.second > maxVote) { - maxVote = v.second; - majorityView = v.first; - } - } + IIndex majorityView = faceToView[fid]; + int maxVote = 0; + for (const auto& v : vote) { + if (v.second > maxVote) { + maxVote = v.second; + majorityView = v.first; + } + } - // ★ 放宽:只要多数票 >= 2 且和我不一样,就考虑切换(原来是 >= 3) - if (maxVote >= 2 && majorityView != faceToView[fid]) { - // 计算多数view在邻居中的平均评分 - 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; + if (maxVote >= 2 && majorityView != faceToView[fid]) { + 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; - // ★ 简化:去掉颜色阻断,直接让评分说话 - // 只要多数view的平均分不低于我太多(差 < 0.3),就切换 - if (majorityAvgScore > currentScore - 0.5f) { - newFaceToView[fid] = majorityView; - newFaceScores[fid] = majorityAvgScore; - } - } - } - faceToView.swap(newFaceToView); - faceScores.swap(newFaceScores); - } + if (majorityAvgScore > currentScore - 0.5f) { + newFaceToView[fid] = majorityView; + newFaceScores[fid] = majorityAvgScore; + } + } + } + faceToView.swap(newFaceToView); + faceScores.swap(newFaceScores); + } // ---------- 3. 写回 ---------- for (size_t i = 0; i < virtualFaceMap.size(); ++i) { @@ -18054,27 +17990,27 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( } } - // 在写回循环之后,加这个: - for (int iter = 0; iter < 3; ++iter) { - auto newFaceToView = faceToView; - for (size_t i = 0; i < faceToView.size(); ++i) { - if (faceToView[i] == NO_ID) continue; - std::map vote; - const auto& ff = scene.mesh.faceFaces[i]; - for (int k = 0; k < 3; ++k) { - FIndex nb = ff[k]; - if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) - vote[faceToView[nb]]++; - } - if (vote.size() <= 1) continue; - auto best = std::max_element(vote.begin(), vote.end(), - [](const std::pair& a, const std::pair& b) { - return a.second < b.second; }); - if (best->first != faceToView[i] && best->second >= 2) - newFaceToView[i] = best->first; - } - faceToView.swap(newFaceToView); - } + // 后处理:再跑几轮简单投票 + for (int iter = 0; iter < 3; ++iter) { + auto newFaceToView = faceToView; + for (size_t i = 0; i < faceToView.size(); ++i) { + if (faceToView[i] == NO_ID) continue; + std::map vote; + const auto& ff = scene.mesh.faceFaces[i]; + for (int k = 0; k < 3; ++k) { + FIndex nb = ff[k]; + if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) + vote[faceToView[nb]]++; + } + if (vote.size() <= 1) continue; + auto best = std::max_element(vote.begin(), vote.end(), + [](const std::pair& a, const std::pair& b) { + return a.second < b.second; }); + if (best->first != faceToView[i] && best->second >= 2) + newFaceToView[i] = best->first; + } + faceToView.swap(newFaceToView); + } DEBUG_EXTRA("====== Virtual Face View Selection Summary ======"); DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size());