diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 6823adc..69b2d42 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -641,6 +641,21 @@ public: ); float ComputeOcclusionPenalty(const cv::Point2f& p0, const cv::Point2f& p1, const cv::Point2f& p2, int imageWidth, int imageHeight, int border = 8); + int ComputeOptimalTextureSizeAdaptive( + const VirtualFaceMap& virtualFaceMap, + const std::vector>& virtualFaceViews, + unsigned nTextureSizeMultiple); + // 辅助函数:向上取整到最近的2的幂 + static int RoundUpPowerOfTwo(int x) { + if (x <= 0) return 1; + --x; + x |= x >> 1; + x |= x >> 2; + x |= x >> 4; + x |= x >> 8; + x |= x >> 16; + return x + 1; + } bool ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap); @@ -706,6 +721,12 @@ public: Pixel8U colEmpty, Mesh::Image8U3Arr& outTextures); + struct TexelScore { + float score = -1.0f; + IIndex viewID = NO_ID; + }; + std::vector m_texelScores; + float ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal, const Point3f& faceCenter, const Image& image); float EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal, @@ -14218,7 +14239,13 @@ bool MeshTexture::RasterizeVirtualFaces( if (uvWidth < 0.001f) uvWidth = 1.0f; if (uvHeight < 0.001f) uvHeight = 1.0f; - int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); + // int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); + int textureSize = ComputeOptimalTextureSizeAdaptive( + virtualFaceMap, virtualFaceViews, nTextureSizeMultiple); + + // 兜底 + if (textureSize < 1024) textureSize = 1024; + if (textureSize > 16384) textureSize = 16384; // -------------------------------------------------- // 2. 创建纹理 @@ -14227,6 +14254,9 @@ bool MeshTexture::RasterizeVirtualFaces( Image8U3& atlas = outTextures.back(); atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); + // ✅ 初始化评分缓冲 + m_texelScores.assign(textureSize * textureSize, TexelScore{}); + // ✅ 计算所有虚拟面的几何和映射矩阵 if (!ComputeVirtualFaceGeometry(virtualFaceMap)) { DEBUG_EXTRA("Failed to compute virtual face geometries"); @@ -14300,29 +14330,41 @@ bool MeshTexture::RasterizeVirtualFaces( cv::Scalar(0, 0, 0)); // ✅ 拷贝到 atlas(OpenMP critical 区) -#pragma omp critical - { - for (int y = 0; y < patchH; ++y) { - for (int x = 0; x < patchW; ++x) { - cv::Vec3b color = patch.at(y, x); - // 跳过无效像素(BORDER_CONSTANT 产生的黑色) - if (color[0] == 0 && color[1] == 0 && color[2] == 0) - continue; + #pragma omp critical + { + for (int y = 0; y < patchH; ++y) { + 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; - int atlasX = x + minX; - int atlasY = y + minY; + int atlasX = x + minX; + int atlasY = y + minY; + if (atlasX < 0 || atlasX >= textureSize || + atlasY < 0 || atlasY >= textureSize) + continue; - // ✅ 边界保护 - if (atlasX < 0 || atlasX >= textureSize || - atlasY < 0 || atlasY >= textureSize) - continue; + size_t idx = atlasY * textureSize + atlasX; + TexelScore& ts = m_texelScores[idx]; - atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]}; - } - } - } + // ✅ 获取当前虚拟面的评分 + float currentScore = virtualFaceViewWeights[i].empty() + ? -1.0f + : virtualFaceViewWeights[i][0]; + + // ✅ 只接受更高评分的写入 + if (currentScore > ts.score) { + atlas(atlasY, atlasX) = + Pixel8U{color[2], color[1], color[0]}; + ts.score = currentScore; + ts.viewID = viewID; + } + } + } + } } + m_texelScores.clear(); DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); return true; } @@ -14632,7 +14674,7 @@ float MeshTexture::ComputeViewNormalScore( C.y - FC.y, C.z - FC.z ); - + float vlen = std::sqrt(viewDir.x*viewDir.x + viewDir.y*viewDir.y + viewDir.z*viewDir.z); @@ -14701,6 +14743,84 @@ float MeshTexture::ComputeOcclusionPenalty( return float(count) / 3.0f; // [0, 1] } + +int MeshTexture::ComputeOptimalTextureSizeAdaptive( + const VirtualFaceMap& virtualFaceMap, + const std::vector>& virtualFaceViews, + unsigned nTextureSizeMultiple) +{ + if (virtualFaceMap.empty()) return 2048; + + double totalPixels = 0.0; + double totalAreaUV = 0.0; + + for (size_t i = 0; i < virtualFaceMap.size(); ++i) { + const VirtualFace& vf = virtualFaceMap[i]; + if (vf.faces.empty() || virtualFaceViews[i].empty()) + continue; + + IIndex viewID = virtualFaceViews[i][0]; + if (viewID >= (IIndex)images.size()) continue; + + 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]]; + const Point3f& v2 = scene.mesh.vertices[face[2]]; + + Point2d p0 = img.camera.ProjectPoint(Point3d(v0)); + 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; + } + + 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); + + float uvW = globalUV.ptMax.x() - globalUV.ptMin.x(); + float uvH = globalUV.ptMax.y() - globalUV.ptMin.y(); + + int textureSize = static_cast( + std::max(uvW, uvH) * texelsPerUV + ); + + // 对齐到 2^n,不超过最大限制 + textureSize = RoundUpPowerOfTwo(textureSize); + textureSize = std::min(textureSize, (int)nTextureSizeMultiple); + + DEBUG_EXTRA("Adaptive texture size: %d (texels/UV: %.1f)", + textureSize, texelsPerUV); + + return textureSize; +} + // ============================================================ // 5. SelectBestViewsForVirtualFaces(带 patch 一致性传播) // ============================================================