From 707a62057a936539290b4361cde6a2692482c362 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Sun, 30 Aug 2026 11:01:47 +0800 Subject: [PATCH] =?UTF-8?q?=E8=BF=9B=E4=B8=80=E6=AD=A5=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 | 344 +++++++++++++++++++------------------- 1 file changed, 172 insertions(+), 172 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 54a1a46..99eb2d2 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -768,6 +768,10 @@ public: unsigned nTextureSizeMultiple, Pixel8U colEmpty, Mesh::Image8U3Arr& outTextures); + void FeatherTextureSeams(Image8U3& texture, + const std::vector& texelPatchID, + int textureSize, + int featherRadius=3); void GlobalPatchColorAlignment(Image8U3& atlas, int textureSize); void LocalSeamBlending(Image8U3& atlas, int textureSize); std::vector patchAvgColor; // ← 直接声明 vector,不要加括号! @@ -826,42 +830,40 @@ public: std::vector m_texelScores; std::vector m_texelPatchID; // 每个 texel 属于哪个 rcPatch(-1 表示无) inline void SmoothVirtualFaceViews( - std::vector>& virtualFaceViews, - const Mesh::FaceFacesArr& faceFaces, // ← 改成这个类型 - int iterations = 2) - { - for (int iter = 0; iter < iterations; ++iter) { - auto newViews = virtualFaceViews; - for (size_t i = 0; i < virtualFaceViews.size(); ++i) { - if (virtualFaceViews[i].empty()) continue; - IIndex myView = virtualFaceViews[i][0]; - - std::map vote; - int validNeighbors = 0; - - // faceFaces[i] 是 TPoint3,用 .x .y .z 或 [0][1][2] 访问3个邻居 - const auto& ff = faceFaces[i]; - for (int k = 0; k < 3; ++k) { - FIndex nb = ff[k]; // ff.x / ff.y / ff.z 也行 - if (nb < (FIndex)virtualFaceViews.size() && !virtualFaceViews[nb].empty()) { - vote[virtualFaceViews[nb][0]]++; - validNeighbors++; - } - } - if (validNeighbors < 2) continue; + std::vector>& virtualFaceViews, + const Mesh::FaceFacesArr& faceFaces, + int iterations = 6) +{ + for (int iter = 0; iter < iterations; ++iter) { + auto newViews = virtualFaceViews; + for (size_t i = 0; i < virtualFaceViews.size(); ++i) { + if (virtualFaceViews[i].empty()) continue; + IIndex myView = virtualFaceViews[i][0]; + + std::map vote; + // 1-ring + const auto& ff = faceFaces[i]; + for (int k = 0; k < 3; ++k) { + FIndex nb = ff[k]; + if (nb < (FIndex)virtualFaceViews.size() && !virtualFaceViews[nb].empty()) + vote[virtualFaceViews[nb][0]]++; + } + if (vote.empty()) continue; - auto best = std::max_element(vote.begin(), vote.end(), - [](const std::pair& a, const std::pair& b) { - return a.second < b.second; - }); + 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 != myView && best->second >= 2) { - newViews[i][0] = best->first; - } - } - virtualFaceViews.swap(newViews); - } - } + // ★ 条件:多数票 > 我的票 + 至少 2 票 + int myVote = vote[myView]; + if (best->first != myView && best->second > myVote && best->second >= 2) { + newViews[i][0] = best->first; + } + } + virtualFaceViews.swap(newViews); + } +} float ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal, const Point3f& faceCenter, const Image& image); float EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal, @@ -14605,9 +14607,48 @@ void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize) DEBUG_EXTRA("Global alignment done: %d constraints (%s)", rows, TD_TIMER_GET_FMT().c_str()); } -// ============================================================ -// 3. RC 风格光栅化主函数(含接缝优化) -// ============================================================ +// 对每个 texel,如果它的 patchID 和邻居不同,说明是接缝 +// 在接缝处做 3x3 或 5x5 的高斯模糊/均值模糊 +void MeshTexture::FeatherTextureSeams(Image8U3& texture, + const std::vector& texelPatchID, + int textureSize, + int featherRadius) +{ + if (texture.empty() || texelPatchID.empty()) return; + + // 对整张图做高斯模糊 + Image8U3 blurred; + cv::GaussianBlur(texture, blurred, + cv::Size(2 * featherRadius + 1, 2 * featherRadius + 1), 0); + + // 逐像素检查接缝 + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + int idx = y * textureSize + x; + if (texelPatchID[idx] == NO_ID) continue; + + IIndex myPatch = texelPatchID[idx]; + bool isSeam = false; + + // 检查 4 邻域 + const int dx4[] = { -1, 1, 0, 0 }; + const int dy4[] = { 0, 0, -1, 1 }; + for (int d = 0; d < 4; ++d) { + int nx = x + dx4[d], ny = y + dy4[d]; + if (nx < 0 || ny < 0 || nx >= textureSize || ny >= textureSize) continue; + IIndex nb = texelPatchID[ny * textureSize + nx]; + if (nb != NO_ID && nb != myPatch) { + isSeam = true; + break; + } + } + + if (isSeam) { + texture(y, x) = blurred(y, x); + } + } + } +} // ============================================================ // 3. RC 风格光栅化主函数(含接缝优化) // ============================================================ @@ -14813,6 +14854,8 @@ bool MeshTexture::RasterizeVirtualFaces( SeamBlendingFromOriginalImages(atlas); DEBUG_EXTRA("Seam blending completed: %zu edges (%s)", seamEdges.size(), TD_TIMER_GET_FMT().c_str()); } + + // FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3); return true; } @@ -15753,30 +15796,30 @@ if (!g_avgColorsComputed) { // ========== 颜色一致性代价 ========== { const float lambda = 20.35f; - + // ========== 收集邻居颜色(修复版: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; - if (!scene.mesh.faceFaces.empty() && nb < scene.mesh.faceFaces.size()) { - const auto& nbrs = scene.mesh.faceFaces[nb]; - for (int ni = 0; ni < 3; ++ni) { - unsigned int nbrFace = nbrs[ni]; - if (nbrFace == 0xFFFFFFFF) continue; - if (nbrFace >= (unsigned int)faceNeighbors.size()) continue; - if (faceNeighbors[nbrFace].empty()) continue; - - unsigned int nbrView = (unsigned int)faceNeighbors[nbrFace][0]; - if (nbrView == 0xFFFFFFFF) continue; - if (nbrView < (unsigned int)g_avgColors.size()) { - neighborColors.push_back(g_avgColors[nbrView]); - } - } - } + + IIndex nbView = faceToView[nb]; + if (nbView == NO_ID) { + // fallback: 邻居还没分配 view,用第一个可见相机 + 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); + } + } } } + // ========== 修复结束 ========== float colorDiff = 0.0f; if (!neighborColors.empty()) { @@ -15842,131 +15885,66 @@ if (!g_avgColorsComputed) { // ---------- 2. 改进的 Patch 一致性传播 ---------- - const int PROPAGATION_ITER = 2; - 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::Face& neighbors = scene.mesh.faceFaces[fid]; + float currentScore = faceScores[fid]; - if (currentScore > 0.8f) continue; // 锁死高质量面 + // ★ 放宽:只锁死 > 0.95 的极高分明面,其余都允许被传播覆盖 + 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]]++; - } - } - - IIndex majorityView = faceToView[fid]; - int maxVote = 0; - for (const auto& v : vote) { - if (v.second > maxVote) { - maxVote = v.second; - majorityView = v.first; - } - } - - if (maxVote >= 3 && 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; - - // ========== 颜色一致性检查(安全闸,优先于评分)========== - bool colorBlocked = false; - { - cv::Vec3f majorityColor(0, 0, 0); - int colorCount = 0; - for (int k = 0; k < 3; ++k) { - FIndex nb = neighbors[k]; - if (nb == NO_ID || nb >= (FIndex)faceToView.size()) continue; - if (faceToView[nb] == majorityView) { - Point3f nbCenter = (scene.mesh.vertices[scene.mesh.faces[nb][0]] + - scene.mesh.vertices[scene.mesh.faces[nb][1]] + - scene.mesh.vertices[scene.mesh.faces[nb][2]]) / 3.0f; - // cv::Vec3f col = SampleColorAtPoint( - // images[majorityView].image, - // images[majorityView].camera, - // nbCenter - // ); - cv::Vec3f col = g_avgColors[majorityView]; - - if (col[0] >= 0) { - majorityColor += col; - colorCount++; - } - } - } + 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]]++; + } + } - if (colorCount > 0) { - majorityColor /= (float)colorCount; - Point3f curCenter = (scene.mesh.vertices[scene.mesh.faces[fid][0]] + - scene.mesh.vertices[scene.mesh.faces[fid][1]] + - scene.mesh.vertices[scene.mesh.faces[fid][2]]) / 3.0f; - // cv::Vec3f curColor = SampleColorAtPoint( - // images[faceToView[fid]].image, - // images[faceToView[fid]].camera, - // curCenter - // ); - cv::Vec3f curColor = g_avgColors[faceToView[fid]]; - if (curColor[0] >= 0) { - cv::Vec3f d = curColor - majorityColor; - float colorDiff = std::sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]) / 1.732f; - - if (fid % 50000 == 0) { - VERBOSE("[Propagate] face=%d majorityView=%d curView=%d colorDiff=%.3f", - fid, majorityView, faceToView[fid], colorDiff); - } + IIndex majorityView = faceToView[fid]; + int maxVote = 0; + for (const auto& v : vote) { + if (v.second > maxVote) { + maxVote = v.second; + majorityView = v.first; + } + } - if (colorDiff > 0.4f) { - colorBlocked = true; // ✅ 阻断传播 - } - } - } - } - // ========== 颜色检查结束 ========== - - // ✅ 只有颜色没被阻断时才考虑评分条件 - bool shouldPropagate = false; - if (!colorBlocked) { - if (currentScore < 0.3f) { - shouldPropagate = (majorityAvgScore > currentScore - 0.2f); - } else if (currentScore < 0.6f) { - shouldPropagate = (majorityAvgScore > currentScore - 0.1f); - } else { - shouldPropagate = (majorityAvgScore > currentScore + 0.05f); - } - if (currentScore > 0.6f && std::abs(majorityAvgScore - currentScore) < 0.1f) { - shouldPropagate = false; - } - } + // ★ 放宽:只要多数票 >= 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 (shouldPropagate) { - newFaceToView[fid] = majorityView; - newFaceScores[fid] = majorityAvgScore; - } - } - } - faceToView.swap(newFaceToView); - faceScores.swap(newFaceScores); - } + // ★ 简化:去掉颜色阻断,直接让评分说话 + // 只要多数view的平均分不低于我太多(差 < 0.3),就切换 + if (majorityAvgScore > currentScore - 0.3f) { + newFaceToView[fid] = majorityView; + newFaceScores[fid] = majorityAvgScore; + } + } + } + faceToView.swap(newFaceToView); + faceScores.swap(newFaceScores); + } // ---------- 3. 写回 ---------- for (size_t i = 0; i < virtualFaceMap.size(); ++i) { @@ -15982,6 +15960,28 @@ if (!g_avgColorsComputed) { } } + // 在写回循环之后,加这个: + 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()); DEBUG_EXTRA("Successfully assigned : %zu", successVF); @@ -20305,7 +20305,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi return false; - texture.SmoothVirtualFaceViews(texture.faceViews, mesh.faceFaces, 2); + texture.SmoothVirtualFaceViews(texture.faceViews, mesh.faceFaces, 6); // ✅ 4. RC 风格光栅化(Affine + Homography 混合) Mesh::Image8U3Arr textures; if (!texture.RasterizeVirtualFaces(