From 864f5cafb959ca2ce1bb1cc6ef67fcea7222dcdd Mon Sep 17 00:00:00 2001 From: hesuicong Date: Sat, 29 Aug 2026 17:43:50 +0800 Subject: [PATCH] =?UTF-8?q?=E5=8A=A0View=20Smoothing?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 155 +++++++++++++++++++++++++++----------- 1 file changed, 113 insertions(+), 42 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index e07f20a..54a1a46 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -648,6 +648,8 @@ public: unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, int nIgnoreMaskLabel, const IIndexArr& views); + // 简单的 view smoothing:如果某个面的 view 和多数邻居不同,且分数差距不大,就改成邻居的 view + float ComputeViewNormalScore(const Mesh::Normal& faceNormal, const Camera& camera, const Point3f& faceCenter); float ComputeResolutionScore( const Camera& camera, @@ -822,7 +824,44 @@ public: IIndex viewID = NO_ID; }; 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; + + 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); + } + } float ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal, const Point3f& faceCenter, const Image& image); float EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal, @@ -14390,7 +14429,7 @@ void MeshTexture::LocalSeamBlending(Image8U3& atlas, int textureSize) cv::Rect overlap = rcPatches[e.rcPatchID0].rect & rcPatches[e.rcPatchID1].rect; if (overlap.width <= 0 || overlap.height <= 0) continue; // 只取 overlap 的边界环(带状),宽度 ~8px - int bw = 8; + int bw = 16; cv::Rect inner(overlap.x + bw, overlap.y + bw, std::max(0, overlap.width - 2*bw), std::max(0, overlap.height - 2*bw)); @@ -14543,30 +14582,32 @@ void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize) } // 应用:对每个 patch 的像素加上偏移(加性,限制幅度) - const float maxAdj = 30.0f; // 8-bit 空间最大偏移 - #pragma omp parallel for - for (int i = 0; i < NP; ++i) { - const RCPatch& patch = rcPatches[i]; - float adjR = std::max(-maxAdj, std::min(maxAdj, xR(i))); - float adjG = std::max(-maxAdj, std::min(maxAdj, xG(i))); - float adjB = std::max(-maxAdj, std::min(maxAdj, xB(i))); - - for (int y = patch.rect.y; y < patch.rect.y + patch.rect.height; ++y) { - for (int x = patch.rect.x; x < patch.rect.x + patch.rect.width; ++x) { - if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue; - Pixel8U& px = atlas(y, x); - if (px[0]==0 && px[1]==0 && px[2]==0) continue; - // BGR 存储顺序 - px[2] = (uint8_t)CLAMP(px[2] + adjR, 0.f, 255.f); // R - px[1] = (uint8_t)CLAMP(px[1] + adjG, 0.f, 255.f); // G - px[0] = (uint8_t)CLAMP(px[0] + adjB, 0.f, 255.f); // B - } - } - } + const float maxAdj = 50.0f; // 8-bit 空间最大偏移 + // 应用:对每个像素,如果它属于 patch i,才加偏移 + #pragma omp parallel for + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + size_t idx = y * textureSize + x; + if (m_texelPatchID[idx] == NO_ID) continue; + + int pid = m_texelPatchID[idx]; + const float adjR = std::max(-maxAdj, std::min(maxAdj, xR(pid))); + const float adjG = std::max(-maxAdj, std::min(maxAdj, xG(pid))); + const float adjB = std::max(-maxAdj, std::min(maxAdj, xB(pid))); + + Pixel8U& px = atlas(y, x); + if (px[0]==0 && px[1]==0 && px[2]==0) continue; + px[2] = (uint8_t)CLAMP(px[2] + adjR, 0.f, 255.f); + px[1] = (uint8_t)CLAMP(px[1] + adjG, 0.f, 255.f); + px[0] = (uint8_t)CLAMP(px[0] + adjB, 0.f, 255.f); + } + } DEBUG_EXTRA("Global alignment done: %d constraints (%s)", rows, TD_TIMER_GET_FMT().c_str()); } - +// ============================================================ +// 3. RC 风格光栅化主函数(含接缝优化) +// ============================================================ // ============================================================ // 3. RC 风格光栅化主函数(含接缝优化) // ============================================================ @@ -14600,19 +14641,30 @@ bool MeshTexture::RasterizeVirtualFaces( atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); m_texelScores.assign(textureSize * textureSize, TexelScore{}); + // ★ 修复:m_texelPatchID 只初始化一次,移到循环外面 + m_texelPatchID.assign(textureSize * textureSize, NO_ID); + + VERBOSE("[Raster] Step 1: initializing buffers... textureSize=%d, scores=%zu, patchID=%zu", + textureSize, m_texelScores.size(), m_texelPatchID.size()); + if (!ComputeVirtualFaceGeometry(virtualFaceMap)) { DEBUG_EXTRA("Failed to compute virtual face geometries"); return false; } currentTextureSize = textureSize; - // 确保增益已估计(若外部未调用则在此兜底) if (!m_gainsEstimated) EstimateGlobalPhotometricCorrection(); - // 3. 逐三角形光栅化(直接写入,最高分胜出 + 光度校正) + VERBOSE("[Raster] Step 2: starting patch loop, total faces=%zu", virtualFaceMap.size()); + + // 3. 逐三角形光栅化 #ifdef _USE_OPENMP #pragma omp parallel for schedule(dynamic) #endif for (int i = 0; i < (int)virtualFaceMap.size(); ++i) { + + if (i % 10000 == 0) + VERBOSE("[Raster] processing face %d / %d", i, (int)virtualFaceMap.size()); + const VirtualFace& vf = virtualFaceMap[i]; const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i]; if (!geom.isValid || vf.faces.empty() || virtualFaceViews[i].empty()) continue; @@ -14631,6 +14683,13 @@ bool MeshTexture::RasterizeVirtualFaces( if (minX > maxX || minY > maxY) continue; int patchW = maxX - minX + 1, patchH = maxY - minY + 1; + // ★ Guard:跳过异常巨大的 patch + if (patchW > textureSize || patchH > textureSize || + (int64_t)patchW * patchH > (int64_t)textureSize * textureSize / 5) { + DEBUG_EXTRA("[Raster] face %d has HUGE rect: %dx%d, skipping", i, patchW, patchH); + continue; + } + cv::Mat mapX(patchH, patchW, CV_32FC1), mapY(patchH, patchW, CV_32FC1); const float* H = geom.homography.ptr(); for (int y = minY; y <= maxY; ++y) { @@ -14645,7 +14704,7 @@ bool MeshTexture::RasterizeVirtualFaces( cv::Mat patch; cv::remap(srcImg.image, patch, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); - // 应用光度校正(在 patch 上原地乘 gain) + // 应用光度校正 const cv::Vec3f& gain = (viewID < (IIndex)m_imageGains.size()) ? m_imageGains[viewID] : cv::Vec3f(1,1,1); const bool applyGain = (gain != cv::Vec3f(1,1,1)); if (applyGain) { @@ -14653,9 +14712,9 @@ bool MeshTexture::RasterizeVirtualFaces( for (int x = 0; x < patchW; ++x) { cv::Vec3b& p = patch.at(y, x); if (p[0]==0 && p[1]==0 && p[2]==0) continue; - p[0] = cv::saturate_cast(p[0]*gain[0]); // B - p[1] = cv::saturate_cast(p[1]*gain[1]); // G - p[2] = cv::saturate_cast(p[2]*gain[2]); // R + p[0] = cv::saturate_cast(p[0]*gain[0]); + p[1] = cv::saturate_cast(p[1]*gain[1]); + p[2] = cv::saturate_cast(p[2]*gain[2]); } } } @@ -14670,16 +14729,22 @@ bool MeshTexture::RasterizeVirtualFaces( int atlasX = x + minX, atlasY = y + minY; if (atlasX<0||atlasX>=textureSize||atlasY<0||atlasY>=textureSize) continue; size_t idx = atlasY * textureSize + atlasX; + + // ★ 已删除错误放在这里的 m_texelPatchID.assign(...) + if (currentScore > m_texelScores[idx].score) { atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]}; m_texelScores[idx].score = currentScore; m_texelScores[idx].viewID = viewID; + m_texelPatchID[idx] = i; // ★ 记录这个像素属于 patch i } } } } } + VERBOSE("[Raster] Step 3: rasterization done, building RC patches..."); + m_texelScores.clear(); DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); m_virtualFaceViewWeights = virtualFaceViewWeights; @@ -14701,21 +14766,25 @@ bool MeshTexture::RasterizeVirtualFaces( } DEBUG_EXTRA("Created %zu RC patches", rcPatches.size()); -// ★ 统计每个 patch 的平均色(光栅化完成后调用) + // ★ 统计每个 patch 的平均色 const int NP = (int)rcPatches.size(); patchAvgColor.assign(NP, Color(0,0,0)); std::vector pixelCount(NP, 0); for (int i = 0; i < NP; ++i) { + if ((int64_t)rcPatches[i].rect.width * rcPatches[i].rect.height > (int64_t)textureSize * textureSize / 5) { + DEBUG_EXTRA("[Raster] patch %d HUGE rect in avgColor, skipping", i); + continue; + } const RCPatch& patch = rcPatches[i]; for (int y = patch.rect.y; y < patch.rect.y + patch.rect.height; ++y) { for (int x = patch.rect.x; x < patch.rect.x + patch.rect.width; ++x) { if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue; const Pixel8U& px = atlas(y, x); - if (px[0]==0 && px[1]==0 && px[2]==0) continue; // 跳过空像素 - patchAvgColor[i][0] += px[2]; // R - patchAvgColor[i][1] += px[1]; // G - patchAvgColor[i][2] += px[0]; // B + if (px[0]==0 && px[1]==0 && px[2]==0) continue; + patchAvgColor[i][0] += px[2]; + patchAvgColor[i][1] += px[1]; + patchAvgColor[i][2] += px[0]; pixelCount[i]++; } } @@ -14727,16 +14796,16 @@ bool MeshTexture::RasterizeVirtualFaces( } } - DEBUG_EXTRA("Patch avg color computed: %d patches, %d with pixels", + DEBUG_EXTRA("Patch avg color computed: %d patches, %d with pixels", NP, NP - std::count(pixelCount.begin(), pixelCount.end(), 0)); // 5/6/7. 接缝 BuildSeamEdgesFromRCPatches(); if (!rcSeamEdges.empty()) { - DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu", - rcSeamEdges.size(), patchAvgColor.size()); - GlobalPatchColorAlignment(atlas, textureSize); // 先全局对齐 - LocalSeamBlending(atlas, textureSize); // 再局部软化 + DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu", + rcSeamEdges.size(), patchAvgColor.size()); + GlobalPatchColorAlignment(atlas, textureSize); + LocalSeamBlending(atlas, textureSize); } if (!seamEdges.empty()) { @@ -15683,7 +15752,7 @@ if (!g_avgColorsComputed) { // ========== 颜色一致性代价 ========== { - const float lambda = 200.35f; + const float lambda = 20.35f; std::vector neighborColors; if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) { @@ -15691,11 +15760,11 @@ if (!g_avgColorsComputed) { 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() && faceID < scene.mesh.faceFaces.size()) { - const SEACAVE::TPoint3& nbrs = scene.mesh.faceFaces[faceID]; + 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; // 边界边没有邻居(NO_ID) + if (nbrFace == 0xFFFFFFFF) continue; if (nbrFace >= (unsigned int)faceNeighbors.size()) continue; if (faceNeighbors[nbrFace].empty()) continue; @@ -20235,6 +20304,8 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi fRatioDataSmoothness, nIgnoreMaskLabel, views)) return false; + + texture.SmoothVirtualFaceViews(texture.faceViews, mesh.faceFaces, 2); // ✅ 4. RC 风格光栅化(Affine + Homography 混合) Mesh::Image8U3Arr textures; if (!texture.RasterizeVirtualFaces(