|
|
|
|
@ -630,6 +630,17 @@ public:
@@ -630,6 +630,17 @@ public:
|
|
|
|
|
unsigned minCommonCameras, float fOutlierThreshold, |
|
|
|
|
float fRatioDataSmoothness, int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views); |
|
|
|
|
float ComputeViewNormalScore(const Mesh::Normal& faceNormal, const Camera& camera, const Point3f& faceCenter); |
|
|
|
|
float ComputeResolutionScore( |
|
|
|
|
const Camera& camera, |
|
|
|
|
const Point3f& v0, |
|
|
|
|
const Point3f& v1, |
|
|
|
|
const Point3f& v2, |
|
|
|
|
int imageWidth, |
|
|
|
|
int imageHeight |
|
|
|
|
); |
|
|
|
|
float ComputeOcclusionPenalty(const cv::Point2f& p0, const cv::Point2f& p1, const cv::Point2f& p2, |
|
|
|
|
int imageWidth, int imageHeight, int border = 8); |
|
|
|
|
|
|
|
|
|
bool ComputeVirtualFaceGeometry(const VirtualFaceMap& virtualFaceMap); |
|
|
|
|
|
|
|
|
|
@ -14606,6 +14617,90 @@ bool MeshTexture::GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool
@@ -14606,6 +14617,90 @@ bool MeshTexture::GenerateTextureWithVirtualFaces(bool bGlobalSeamLeveling, bool
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float MeshTexture::ComputeViewNormalScore( |
|
|
|
|
const Mesh::Normal& faceNormal, |
|
|
|
|
const Camera& camera, |
|
|
|
|
const Point3f& faceCenter) |
|
|
|
|
{ |
|
|
|
|
// ✅ 转换为 double 避免 float/double 混用
|
|
|
|
|
Point3d C(camera.C); |
|
|
|
|
Point3d FC(faceCenter); |
|
|
|
|
|
|
|
|
|
// 视线方向:相机 → 面中心(手动计算)
|
|
|
|
|
Point3d viewDir( |
|
|
|
|
C.x - FC.x, |
|
|
|
|
C.y - FC.y, |
|
|
|
|
C.z - FC.z |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
float vlen = std::sqrt(viewDir.x*viewDir.x + |
|
|
|
|
viewDir.y*viewDir.y + |
|
|
|
|
viewDir.z*viewDir.z); |
|
|
|
|
if (vlen < 1e-8f) return 0.0f; |
|
|
|
|
viewDir /= vlen; |
|
|
|
|
|
|
|
|
|
// 面法线
|
|
|
|
|
Point3f normal = faceNormal; |
|
|
|
|
float nlen = std::sqrt(normal.x*normal.x + |
|
|
|
|
normal.y*normal.y + |
|
|
|
|
normal.z*normal.z); |
|
|
|
|
if (nlen < 1e-8f) return 0.0f; |
|
|
|
|
normal /= nlen; |
|
|
|
|
|
|
|
|
|
float cosTheta = viewDir.x*normal.x + |
|
|
|
|
viewDir.y*normal.y + |
|
|
|
|
viewDir.z*normal.z; |
|
|
|
|
return (cosTheta > 0.0f) ? cosTheta : 0.0f; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float MeshTexture::ComputeResolutionScore( |
|
|
|
|
const Camera& camera, |
|
|
|
|
const Point3f& v0, |
|
|
|
|
const Point3f& v1, |
|
|
|
|
const Point3f& v2, |
|
|
|
|
int imageWidth, |
|
|
|
|
int imageHeight) |
|
|
|
|
{ |
|
|
|
|
Point2d p0 = camera.ProjectPoint(Point3d(v0)); |
|
|
|
|
Point2d p1 = camera.ProjectPoint(Point3d(v1)); |
|
|
|
|
Point2d p2 = camera.ProjectPoint(Point3d(v2)); |
|
|
|
|
|
|
|
|
|
auto inImage = [&](const Point2f& p) { |
|
|
|
|
return p.x >= 0 && p.x < imageWidth && |
|
|
|
|
p.y >= 0 && p.y < imageHeight; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
if (!inImage(p0) || !inImage(p1) || !inImage(p2)) |
|
|
|
|
return 0.0f; |
|
|
|
|
|
|
|
|
|
float area = |
|
|
|
|
std::abs((p1.x - p0.x) * (p2.y - p0.y) - |
|
|
|
|
(p2.x - p0.x) * (p1.y - p0.y)) * 0.5f; |
|
|
|
|
|
|
|
|
|
if (area < 1.0f) return 0.0f; |
|
|
|
|
return std::log2(area + 1.0f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
float MeshTexture::ComputeOcclusionPenalty( |
|
|
|
|
const cv::Point2f& p0, |
|
|
|
|
const cv::Point2f& p1, |
|
|
|
|
const cv::Point2f& p2, |
|
|
|
|
int imageWidth, |
|
|
|
|
int imageHeight, |
|
|
|
|
int border) |
|
|
|
|
{ |
|
|
|
|
auto inside = [&](const cv::Point2f& p) { |
|
|
|
|
return p.x >= border && p.x < imageWidth - border && |
|
|
|
|
p.y >= border && p.y < imageHeight - border; |
|
|
|
|
}; |
|
|
|
|
|
|
|
|
|
int count = 0; |
|
|
|
|
if (!inside(p0)) count++; |
|
|
|
|
if (!inside(p1)) count++; |
|
|
|
|
if (!inside(p2)) count++; |
|
|
|
|
|
|
|
|
|
return float(count) / 3.0f; // [0, 1]
|
|
|
|
|
} |
|
|
|
|
// ============================================================
|
|
|
|
|
// 5. SelectBestViewsForVirtualFaces(带 patch 一致性传播)
|
|
|
|
|
// ============================================================
|
|
|
|
|
@ -14674,8 +14769,75 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -14674,8 +14769,75 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ 获取面的顶点(必须在循环内定义!)
|
|
|
|
|
const Mesh::Face& face = scene.mesh.faces[faceID]; |
|
|
|
|
const Point3f& v0 = scene.mesh.vertices[face[0]]; |
|
|
|
|
const Point3f& v1 = scene.mesh.vertices[face[1]]; |
|
|
|
|
const Point3f& v2 = scene.mesh.vertices[face[2]]; |
|
|
|
|
|
|
|
|
|
// ✅ 计算面中心(手动计算)
|
|
|
|
|
Point3f faceCenter = (v0 + v1 + v2) / 3.0f; |
|
|
|
|
|
|
|
|
|
Point3f faceNormal = scene.mesh.faceNormals[faceID]; |
|
|
|
|
// 归一化
|
|
|
|
|
float norm = std::sqrt(faceNormal.x * faceNormal.x + |
|
|
|
|
faceNormal.y * faceNormal.y + |
|
|
|
|
faceNormal.z * faceNormal.z); |
|
|
|
|
if (norm > FLT_EPSILON) { |
|
|
|
|
faceNormal.x /= norm; |
|
|
|
|
faceNormal.y /= norm; |
|
|
|
|
faceNormal.z /= norm; |
|
|
|
|
} else { |
|
|
|
|
// 退化三角形,使用默认法线
|
|
|
|
|
faceNormal = Point3f(0, 0, 1); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 选第一个候选(简单策略,后续可优化为角度最优)
|
|
|
|
|
IIndex bestView = *candidateViews.begin(); |
|
|
|
|
// IIndex bestView = *candidateViews.begin();
|
|
|
|
|
IIndex bestView = NO_ID; |
|
|
|
|
float bestScore = -FLT_MAX; |
|
|
|
|
|
|
|
|
|
for (IIndex vid : candidateViews) { |
|
|
|
|
if (vid >= (IIndex)images.size()) continue; |
|
|
|
|
|
|
|
|
|
const Image& img = images[vid]; |
|
|
|
|
if (img.image.empty() || img.image.cols < 2 || img.image.rows < 2) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
const Camera& cam = img.camera; |
|
|
|
|
|
|
|
|
|
// 1. 正面度评分
|
|
|
|
|
float s_normal = ComputeViewNormalScore(faceNormal, cam, faceCenter); |
|
|
|
|
if (s_normal <= 0.0f) continue; |
|
|
|
|
|
|
|
|
|
// 2. 分辨率评分
|
|
|
|
|
float s_resolution = ComputeResolutionScore( |
|
|
|
|
cam, v0, v1, v2, |
|
|
|
|
img.image.cols, img.image.rows |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// 3. 遮挡惩罚
|
|
|
|
|
Point2f p0 = cam.ProjectPoint(Point3d(v0)); |
|
|
|
|
Point2f p1 = cam.ProjectPoint(Point3d(v1)); |
|
|
|
|
Point2f p2 = cam.ProjectPoint(Point3d(v2)); |
|
|
|
|
float s_occlusion = ComputeOcclusionPenalty( |
|
|
|
|
p0, p1, p2, |
|
|
|
|
img.image.cols, img.image.rows, |
|
|
|
|
8 // border pixels
|
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// 综合评分
|
|
|
|
|
float score = 1.0f * s_normal |
|
|
|
|
+ 0.7f * s_resolution |
|
|
|
|
- 0.3f * s_occlusion; |
|
|
|
|
|
|
|
|
|
if (score > bestScore) { |
|
|
|
|
bestScore = score; |
|
|
|
|
bestView = vid; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
faceViews[i].push_back(bestView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
|
|
|
|
|
|