|
|
|
|
@ -14846,8 +14846,9 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -14846,8 +14846,9 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// faceToView 映射
|
|
|
|
|
// faceToView 映射 + 面评分存储
|
|
|
|
|
std::vector<IIndex> faceToView(scene.mesh.faces.size(), NO_ID); |
|
|
|
|
std::vector<float> faceScores(scene.mesh.faces.size(), -FLT_MAX); // ✅ 存储每个面的选图评分
|
|
|
|
|
|
|
|
|
|
// ---------- 1. 初始视图分配 ----------
|
|
|
|
|
size_t emptyCandidateViews = 0; |
|
|
|
|
@ -14881,94 +14882,93 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -14881,94 +14882,93 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
faceViews[i].push_back(forcedView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
for (FIndex fid : vf.faces) { |
|
|
|
|
if (fid < faceToView.size()) |
|
|
|
|
if (fid < faceToView.size()) { |
|
|
|
|
faceToView[fid] = forcedView; |
|
|
|
|
faceScores[fid] = 1.0f; // 兜底视图给中等评分
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
++fallbackByCenterFace; |
|
|
|
|
} |
|
|
|
|
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 = 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; |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 1. 正面度评分
|
|
|
|
|
float s_normal = ComputeViewNormalScore(faceNormal, cam, faceCenter); |
|
|
|
|
if (s_normal <= 0.0f) continue; |
|
|
|
|
IIndex bestView = NO_ID; |
|
|
|
|
float bestScore = -FLT_MAX; |
|
|
|
|
|
|
|
|
|
// 2. 分辨率评分
|
|
|
|
|
float s_resolution = ComputeResolutionScore( |
|
|
|
|
cam, v0, v1, v2, |
|
|
|
|
img.image.cols, img.image.rows |
|
|
|
|
); |
|
|
|
|
for (IIndex vid : candidateViews) { |
|
|
|
|
if (vid >= (IIndex)images.size()) continue; |
|
|
|
|
|
|
|
|
|
// 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
|
|
|
|
|
); |
|
|
|
|
const Image& img = images[vid]; |
|
|
|
|
if (img.image.empty() || img.image.cols < 2 || img.image.rows < 2) |
|
|
|
|
continue; |
|
|
|
|
|
|
|
|
|
// 综合评分
|
|
|
|
|
float score = 1.0f * s_normal |
|
|
|
|
+ 0.7f * s_resolution |
|
|
|
|
- 0.3f * s_occlusion; |
|
|
|
|
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 |
|
|
|
|
); |
|
|
|
|
|
|
|
|
|
// 综合评分
|
|
|
|
|
float score = 1.0f * s_normal |
|
|
|
|
+ 0.7f * s_resolution |
|
|
|
|
- 0.3f * s_occlusion; |
|
|
|
|
|
|
|
|
|
if (score > bestScore) { |
|
|
|
|
bestScore = score; |
|
|
|
|
bestView = vid; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (score > bestScore) { |
|
|
|
|
bestScore = score; |
|
|
|
|
bestView = vid; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
faceViews[i].push_back(bestView); |
|
|
|
|
faceViewWeights[i].push_back(1.0f); |
|
|
|
|
if (bestView != NO_ID) { |
|
|
|
|
faceViews[i].push_back(bestView); |
|
|
|
|
faceViewWeights[i].push_back(bestScore); // ✅ 存储实际评分,不是固定值
|
|
|
|
|
|
|
|
|
|
for (FIndex fid : vf.faces) { |
|
|
|
|
if (fid < faceToView.size()) |
|
|
|
|
faceToView[fid] = bestView; |
|
|
|
|
for (FIndex fid : vf.faces) { |
|
|
|
|
if (fid < faceToView.size()) { |
|
|
|
|
faceToView[fid] = bestView; |
|
|
|
|
faceScores[fid] = bestScore; // ✅ 记录面的评分
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
++successVF; |
|
|
|
|
} |
|
|
|
|
++successVF; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ---------- 2. Patch 一致性传播 ----------
|
|
|
|
|
// ---------- 2. 改进的 Patch 一致性传播 ----------
|
|
|
|
|
if (scene.mesh.faceFaces.empty()) { |
|
|
|
|
scene.mesh.ListIncidenteFaceFaces(); |
|
|
|
|
} |
|
|
|
|
@ -14976,6 +14976,7 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -14976,6 +14976,7 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
const int PROPAGATION_ITER = 2; |
|
|
|
|
for (int iter = 0; iter < PROPAGATION_ITER; ++iter) { |
|
|
|
|
std::vector<IIndex> newFaceToView = faceToView; |
|
|
|
|
std::vector<float> newFaceScores = faceScores; // ✅ 同步更新评分
|
|
|
|
|
|
|
|
|
|
for (FIndex fid = 0; fid < (FIndex)faceToView.size(); ++fid) { |
|
|
|
|
if (faceToView[fid] == NO_ID) continue; |
|
|
|
|
@ -14983,6 +14984,14 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -14983,6 +14984,14 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
|
|
|
|
|
const Mesh::Face& neighbors = scene.mesh.faceFaces[fid]; |
|
|
|
|
|
|
|
|
|
// ✅ 获取当前面的评分
|
|
|
|
|
float currentScore = faceScores[fid]; |
|
|
|
|
|
|
|
|
|
// ✅ 如果当前视图质量已经很高(>0.8),锁死,不参与传播
|
|
|
|
|
if (currentScore > 0.8f) { |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::unordered_map<IIndex, int> vote; |
|
|
|
|
vote[faceToView[fid]] = 1; |
|
|
|
|
|
|
|
|
|
@ -15003,11 +15012,54 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -15003,11 +15012,54 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ 改进的传播条件
|
|
|
|
|
if (maxVote >= 3 && majorityView != faceToView[fid]) { |
|
|
|
|
newFaceToView[fid] = majorityView; |
|
|
|
|
// 获取多数视图在邻居中的平均评分
|
|
|
|
|
float majorityAvgScore = 0.0f; |
|
|
|
|
int count = 0; |
|
|
|
|
|
|
|
|
|
for (int k = 0; k < 3; ++k) { |
|
|
|
|
FIndex nb = neighbors[k]; |
|
|
|
|
if (nb == NO_ID) continue; |
|
|
|
|
if (nb < (FIndex)faceToView.size() && faceToView[nb] == majorityView) { |
|
|
|
|
majorityAvgScore += faceScores[nb]; |
|
|
|
|
count++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (count > 0) { |
|
|
|
|
majorityAvgScore /= count; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ 条件1:当前评分较低才考虑传播
|
|
|
|
|
// ✅ 条件2:多数视图的评分与当前评分相差不大(防止引入更差的视图)
|
|
|
|
|
// ✅ 条件3:或者多数视图明显更好
|
|
|
|
|
bool shouldPropagate = false; |
|
|
|
|
|
|
|
|
|
if (currentScore < 0.3f) { |
|
|
|
|
// 当前评分很低,只要多数视图不是特别差就接受
|
|
|
|
|
shouldPropagate = (majorityAvgScore > currentScore - 0.2f); |
|
|
|
|
} else if (currentScore < 0.6f) { |
|
|
|
|
// 当前评分中等,要求多数视图相当或更好
|
|
|
|
|
shouldPropagate = (majorityAvgScore > currentScore - 0.1f); |
|
|
|
|
} else { |
|
|
|
|
// 当前评分较高(0.6-0.8之间),要求多数视图明显更好
|
|
|
|
|
shouldPropagate = (majorityAvgScore > currentScore + 0.05f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ✅ 额外保护:如果当前评分已经不错,且多数视图没有显著优势,保持原样
|
|
|
|
|
if (currentScore > 0.6f && std::abs(majorityAvgScore - currentScore) < 0.1f) { |
|
|
|
|
shouldPropagate = false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (shouldPropagate) { |
|
|
|
|
newFaceToView[fid] = majorityView; |
|
|
|
|
newFaceScores[fid] = majorityAvgScore; // ✅ 更新为邻居的平均评分
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
}防止高质量视图被低质量邻居覆盖,从而减少局部染色 |
|
|
|
|
faceToView.swap(newFaceToView); |
|
|
|
|
faceScores.swap(newFaceScores); // ✅ 同步更新评分
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ---------- 3. 写回 ----------
|
|
|
|
|
@ -15018,8 +15070,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -15018,8 +15070,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
FIndex firstFace = vf.faces[0]; |
|
|
|
|
if (firstFace < faceToView.size() && faceToView[firstFace] != NO_ID) { |
|
|
|
|
IIndex consistentView = faceToView[firstFace]; |
|
|
|
|
float consistentScore = faceScores[firstFace]; // ✅ 使用更新后的评分
|
|
|
|
|
|
|
|
|
|
faceViews[i] = {consistentView}; |
|
|
|
|
faceViewWeights[i] = {1.0f}; |
|
|
|
|
faceViewWeights[i] = {consistentScore}; // ✅ 更新权重为实际评分
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
@ -18937,11 +18991,11 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
@@ -18937,11 +18991,11 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
|
|
|
|
if (!texture.CreateVirtualFacesForExistingUV(virtualFaceMap)) |
|
|
|
|
return false; |
|
|
|
|
|
|
|
|
|
// ✅ 2. 计算可见性(填充 faceNeighbors)
|
|
|
|
|
if (!texture.ComputePureFaceVisibility( |
|
|
|
|
fOutlierThreshold, nIgnoreMaskLabel, views)) { |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
// // ✅ 2. 计算可见性(填充 faceNeighbors)
|
|
|
|
|
// if (!texture.ComputePureFaceVisibility(
|
|
|
|
|
// fOutlierThreshold, nIgnoreMaskLabel, views)) {
|
|
|
|
|
// return false;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// ✅ 3. 选择最佳视图(带 patch 一致性)
|
|
|
|
|
if (!texture.SelectBestViewsForVirtualFaces( |
|
|
|
|
|