|
|
|
@ -641,6 +641,21 @@ public: |
|
|
|
); |
|
|
|
); |
|
|
|
float ComputeOcclusionPenalty(const cv::Point2f& p0, const cv::Point2f& p1, const cv::Point2f& p2, |
|
|
|
float ComputeOcclusionPenalty(const cv::Point2f& p0, const cv::Point2f& p1, const cv::Point2f& p2, |
|
|
|
int imageWidth, int imageHeight, int border = 8); |
|
|
|
int imageWidth, int imageHeight, int border = 8); |
|
|
|
|
|
|
|
int ComputeOptimalTextureSizeAdaptive( |
|
|
|
|
|
|
|
const VirtualFaceMap& virtualFaceMap, |
|
|
|
|
|
|
|
const std::vector<std::vector<IIndex>>& 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); |
|
|
|
bool ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap); |
|
|
|
|
|
|
|
|
|
|
|
@ -706,6 +721,12 @@ public: |
|
|
|
Pixel8U colEmpty, |
|
|
|
Pixel8U colEmpty, |
|
|
|
Mesh::Image8U3Arr& outTextures); |
|
|
|
Mesh::Image8U3Arr& outTextures); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
struct TexelScore { |
|
|
|
|
|
|
|
float score = -1.0f; |
|
|
|
|
|
|
|
IIndex viewID = NO_ID; |
|
|
|
|
|
|
|
}; |
|
|
|
|
|
|
|
std::vector<TexelScore> m_texelScores; |
|
|
|
|
|
|
|
|
|
|
|
float ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal, |
|
|
|
float ComputeComprehensiveScore(const FaceData& data, const Normal& faceNormal, |
|
|
|
const Point3f& faceCenter, const Image& image); |
|
|
|
const Point3f& faceCenter, const Image& image); |
|
|
|
float EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal, |
|
|
|
float EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal, |
|
|
|
@ -14218,7 +14239,13 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
if (uvWidth < 0.001f) uvWidth = 1.0f; |
|
|
|
if (uvWidth < 0.001f) uvWidth = 1.0f; |
|
|
|
if (uvHeight < 0.001f) uvHeight = 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. 创建纹理
|
|
|
|
// 2. 创建纹理
|
|
|
|
@ -14227,6 +14254,9 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
Image8U3& atlas = outTextures.back(); |
|
|
|
Image8U3& atlas = outTextures.back(); |
|
|
|
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ✅ 初始化评分缓冲
|
|
|
|
|
|
|
|
m_texelScores.assign(textureSize * textureSize, TexelScore{}); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 计算所有虚拟面的几何和映射矩阵
|
|
|
|
// ✅ 计算所有虚拟面的几何和映射矩阵
|
|
|
|
if (!ComputeVirtualFaceGeometry(virtualFaceMap)) { |
|
|
|
if (!ComputeVirtualFaceGeometry(virtualFaceMap)) { |
|
|
|
DEBUG_EXTRA("Failed to compute virtual face geometries"); |
|
|
|
DEBUG_EXTRA("Failed to compute virtual face geometries"); |
|
|
|
@ -14300,29 +14330,41 @@ bool MeshTexture::RasterizeVirtualFaces( |
|
|
|
cv::Scalar(0, 0, 0)); |
|
|
|
cv::Scalar(0, 0, 0)); |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 拷贝到 atlas(OpenMP critical 区)
|
|
|
|
// ✅ 拷贝到 atlas(OpenMP critical 区)
|
|
|
|
#pragma omp critical |
|
|
|
#pragma omp critical |
|
|
|
{ |
|
|
|
{ |
|
|
|
for (int y = 0; y < patchH; ++y) { |
|
|
|
for (int y = 0; y < patchH; ++y) { |
|
|
|
for (int x = 0; x < patchW; ++x) { |
|
|
|
for (int x = 0; x < patchW; ++x) { |
|
|
|
cv::Vec3b color = patch.at<cv::Vec3b>(y, x); |
|
|
|
cv::Vec3b color = patch.at<cv::Vec3b>(y, x); |
|
|
|
// 跳过无效像素(BORDER_CONSTANT 产生的黑色)
|
|
|
|
if (color[0] == 0 && color[1] == 0 && color[2] == 0) |
|
|
|
if (color[0] == 0 && color[1] == 0 && color[2] == 0) |
|
|
|
continue; |
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int atlasX = x + minX; |
|
|
|
int atlasX = x + minX; |
|
|
|
int atlasY = y + minY; |
|
|
|
int atlasY = y + minY; |
|
|
|
|
|
|
|
if (atlasX < 0 || atlasX >= textureSize || |
|
|
|
|
|
|
|
atlasY < 0 || atlasY >= textureSize) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
// ✅ 边界保护
|
|
|
|
size_t idx = atlasY * textureSize + atlasX; |
|
|
|
if (atlasX < 0 || atlasX >= textureSize || |
|
|
|
TexelScore& ts = m_texelScores[idx]; |
|
|
|
atlasY < 0 || atlasY >= textureSize) |
|
|
|
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
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()); |
|
|
|
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str()); |
|
|
|
return true; |
|
|
|
return true; |
|
|
|
} |
|
|
|
} |
|
|
|
@ -14632,7 +14674,7 @@ float MeshTexture::ComputeViewNormalScore( |
|
|
|
C.y - FC.y, |
|
|
|
C.y - FC.y, |
|
|
|
C.z - FC.z |
|
|
|
C.z - FC.z |
|
|
|
); |
|
|
|
); |
|
|
|
|
|
|
|
|
|
|
|
float vlen = std::sqrt(viewDir.x*viewDir.x + |
|
|
|
float vlen = std::sqrt(viewDir.x*viewDir.x + |
|
|
|
viewDir.y*viewDir.y + |
|
|
|
viewDir.y*viewDir.y + |
|
|
|
viewDir.z*viewDir.z); |
|
|
|
viewDir.z*viewDir.z); |
|
|
|
@ -14701,6 +14743,84 @@ float MeshTexture::ComputeOcclusionPenalty( |
|
|
|
|
|
|
|
|
|
|
|
return float(count) / 3.0f; // [0, 1]
|
|
|
|
return float(count) / 3.0f; // [0, 1]
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
int MeshTexture::ComputeOptimalTextureSizeAdaptive( |
|
|
|
|
|
|
|
const VirtualFaceMap& virtualFaceMap, |
|
|
|
|
|
|
|
const std::vector<std::vector<IIndex>>& 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<int>( |
|
|
|
|
|
|
|
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 一致性传播)
|
|
|
|
// 5. SelectBestViewsForVirtualFaces(带 patch 一致性传播)
|
|
|
|
// ============================================================
|
|
|
|
// ============================================================
|
|
|
|
|