From c287fe0fc7d9e97c980440d661250a151a3c56e0 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Fri, 11 Sep 2026 18:36:49 +0800 Subject: [PATCH] =?UTF-8?q?=E9=A2=9C=E8=89=B2=E7=BA=A6=E6=9D=9F=E7=94=9F?= =?UTF-8?q?=E6=95=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 220 ++++++++++++++++++++++++-------------- 1 file changed, 142 insertions(+), 78 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 7edbf94..d41b966 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -1303,6 +1303,8 @@ public: // ---- 全局光度校正(per-image gain,3 通道独立)---- std::vector m_imageGains; // 每个视图一个 (gR,gG,gB) bool m_gainsEstimated = false; + + bool s_gainsApplied = false; bool EstimateGlobalPhotometricCorrection(); inline cv::Vec3f SampleColorAtPoint(const cv::Mat& img, const Camera& cam, const Point3f& pt3D) { @@ -9520,15 +9522,17 @@ void MeshTexture::GlobalSeamLeveling4() ASSERT(face[v] < vertpatch2rows.size()); } - // ★ UV [0,1] → patch 局部像素坐标 - const cv::Size patchSize = texturePatch.rect.size(); - TexCoord localTri[3]; - const TexCoord* uvTri = faceTexcoords.Begin() + idxFace * 3; - for (int v = 0; v < 3; ++v) { - localTri[v] = TexCoord(uvTri[v].x * patchSize.width, - uvTri[v].y * patchSize.height); - } - data.tri = localTri; + // // ★ UV [0,1] → patch 局部像素坐标 + // const cv::Size patchSize = texturePatch.rect.size(); + // TexCoord localTri[3]; + // const TexCoord* uvTri = faceTexcoords.Begin() + idxFace * 3; + // for (int v = 0; v < 3; ++v) { + // localTri[v] = TexCoord(uvTri[v].x * patchSize.width, + // uvTri[v].y * patchSize.height); + // } + // data.tri = localTri; + // 改回直接用,不乘 + data.tri = faceTexcoords.Begin() + idxFace * 3; for (int v = 0; v < 3; ++v) { ASSERT(face[v] < vertices.size()); @@ -9555,7 +9559,15 @@ void MeshTexture::GlobalSeamLeveling4() } const Pixel8U& v = srcROI.at(r, c); const Color col(RGB2YCBCR(Color(v))); - const Color acol(YCBCR2RGB(Color(col + a))); + + // const Color acol(YCBCR2RGB(Color(col + a))); + const float GAIN = 10.0f; // ★ 增益,可调(5~20) + const Color acol(YCBCR2RGB(Color( + col[0] + a[0] * 1.0f, // Y 微调 + col[1] + a[1] * GAIN, // Cb 大幅调 + col[2] + a[2] * GAIN // Cr 大幅调 + ))); + outPatch.at(r, c) = cv::Vec3b( (uint8_t)CLAMP(ROUND2INT(acol[0]), 0, 255), (uint8_t)CLAMP(ROUND2INT(acol[1]), 0, 255), @@ -15661,7 +15673,6 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( } } - atlas = localAtlas; // // 调试用:导出调整后的 atlas // static int dbgCount = 0; // char dbgName[256]; @@ -15669,6 +15680,7 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( // cv::imwrite(dbgName, atlas); // DEBUG_EXTRA("Exported %s", dbgName); } + atlas = localAtlas; } DEBUG_EXTRA("Re-rasterization done."); @@ -15934,6 +15946,7 @@ bool MeshTexture::RasterizeVirtualFaces( int textureSize = ComputeOptimalTextureSizeAdaptive(virtualFaceMap, virtualFaceViews, nTextureSizeMultiple); if (textureSize < 1024) textureSize = 1024; if (textureSize > 16384) textureSize = 16384; + textureSize = std::max(textureSize, 4096); // 至少 4096 // 2. atlas + score outTextures.emplace_back(textureSize, textureSize); @@ -17272,10 +17285,11 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive( const std::vector>& virtualFaceViews, unsigned nTextureSizeMultiple) { - if (virtualFaceMap.empty()) return 2048; + if (virtualFaceMap.empty()) + return (int)nTextureSizeMultiple; // 没数据就直接用上限 double totalPixels = 0.0; - double totalAreaUV = 0.0; + int validFaces = 0; for (size_t i = 0; i < virtualFaceMap.size(); ++i) { const VirtualFace& vf = virtualFaceMap[i]; @@ -17288,13 +17302,7 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive( const Image& img = images[viewID]; if (img.image.empty()) continue; - // UV 面积 - const AABB2f& uv = vf.uvBounds; - float uvArea = (uv.ptMax.x() - uv.ptMin.x()) * - (uv.ptMax.y() - uv.ptMin.y()); - if (uvArea <= 0.0f) continue; - - // 投影到图像,估算像素覆盖 + // 用任意一个面估算投影面积即可 const Mesh::Face& face = scene.mesh.faces[vf.faces[0]]; const Point3f& v0 = scene.mesh.vertices[face[0]]; const Point3f& v1 = scene.mesh.vertices[face[1]]; @@ -17304,42 +17312,38 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive( Point2d p1 = img.camera.ProjectPoint(Point3d(v1)); Point2d p2 = img.camera.ProjectPoint(Point3d(v2)); - // 图像空间三角形面积(像素) double imgArea = std::abs((p1.x - p0.x) * (p2.y - p0.y) - (p2.x - p0.x) * (p1.y - p0.y)) * 0.5; if (imgArea < 1.0) continue; - // 加权累加 totalPixels += imgArea; - totalAreaUV += uvArea; + ++validFaces; } - if (totalAreaUV <= 0.0) return 2048; - - // 核心公式: - // texelPerUV = sqrt(图像像素总面积 / UV总面积) - double texelsPerUV = std::sqrt(totalPixels / totalAreaUV); - - // 用 UV 包围盒大小推算最终纹理尺寸 - AABB2f globalUV(true); - for (const auto& vf : virtualFaceMap) - globalUV.Insert(vf.uvBounds); + if (validFaces == 0) + return (int)nTextureSizeMultiple; - float uvW = globalUV.ptMax.x() - globalUV.ptMin.x(); - float uvH = globalUV.ptMax.y() - globalUV.ptMin.y(); + // ★ 关键:目标 1 texel ≈ 1 image pixel + int textureSize = (int)std::sqrt(totalPixels); - int textureSize = static_cast( - std::max(uvW, uvH) * texelsPerUV - ); + // 可选:你想要更“锐利”可以乘以 1.x(如 1.25) + // textureSize = (int)(textureSize * 1.25); - // 对齐到 2^n,不超过最大限制 + // 对齐到 2 的幂 textureSize = RoundUpPowerOfTwo(textureSize); + + // ★ 上限必须是 nTextureSizeMultiple(8192) textureSize = std::min(textureSize, (int)nTextureSizeMultiple); - DEBUG_EXTRA("Adaptive texture size: %d (texels/UV: %.1f)", - textureSize, texelsPerUV); + // 防止过小 + if (textureSize < 1024) textureSize = 1024; + + DEBUG_EXTRA("Adaptive texture size: %d (totalPixels=%.0f, validFaces=%d, max=%u)", + textureSize, totalPixels, validFaces, nTextureSizeMultiple); + + DEBUG_EXTRA("totalPixels=%.0f → textureSize=%d", totalPixels, textureSize); return textureSize; } @@ -17361,36 +17365,52 @@ bool MeshTexture::SelectBestViewsForVirtualFaces( 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]); -} + // ===== 从磁盘加载图像,计算全局平均颜色 ===== + 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)); @@ -17438,6 +17458,30 @@ if (!g_avgColorsComputed) { 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; @@ -17525,7 +17569,7 @@ if (!g_avgColorsComputed) { // ========== 颜色一致性代价 ========== { - const float lambda = 0.0f; + const float lambda = 2.0f; // ========== 收集邻居颜色(修复版:1-ring + faceToView)========== std::vector neighborColors; if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) { @@ -17574,7 +17618,8 @@ if (!g_avgColorsComputed) { 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()) / 1.732f; + colorDiff = colorDiff / neighborColors.size(); colorDiff = std::min(colorDiff, 1.0f); // ✅ 修复:不再乘 score,避免放大效应 @@ -21916,6 +21961,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi const std::string& inputFileName, const std::string& meshFileName, bool bUseExistingUV, const std::string& strUVMeshFileName) { nTextureSizeMultiple = 8192; // 8192 4096 + maxTextureSize = 8192; // ← 加这行 // 预处理:计算网格拓扑和几何信息 if (!bOriginFaceview) @@ -22022,11 +22068,29 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi texture.EstimateGlobalPhotometricCorrection(); - // // ✅ 2. 计算可见性(填充 faceNeighbors) - // if (!texture.ComputePureFaceVisibility( - // fOutlierThreshold, nIgnoreMaskLabel, views)) { - // return false; - // } + if (!texture.s_gainsApplied && texture.m_gainsEstimated && !texture.m_imageGains.empty()) { + for (size_t vid = 0; vid < images.size(); ++vid) { + if (vid >= texture.m_imageGains.size()) break; + const cv::Vec3f& gain = texture.m_imageGains[vid]; + cv::Mat& img = images[vid].image; + for (int r = 0; r < img.rows; ++r) { + for (int c = 0; c < img.cols; ++c) { + cv::Vec3b& p = img.at(r, c); + 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]); + } + } + } + texture.s_gainsApplied = true; + DEBUG_EXTRA("Photometric gains applied to %zu images", images.size()); + } + + // ✅ 2. 计算可见性(填充 faceNeighbors) + if (!texture.ComputePureFaceVisibility( + fOutlierThreshold, nIgnoreMaskLabel, views)) { + return false; + } // ✅ 3. 选择最佳视图(带 patch 一致性) if (!texture.SelectBestViewsForVirtualFaces(