色差进一步优化
This commit is contained in:
1 parent
990fff69c5
commit
e68ecd01db
1 file changed
+205
-225
+205
-225
@@ -18043,65 +18043,57 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
||||
(int)faceNeighbors.empty());
|
||||
|
||||
DEBUG_EXTRA("Selecting best views for %zu virtual faces", virtualFaceMap.size());
|
||||
|
||||
// ---------- 准备禁用视图列表(禁用最后一个单独数字 = 最大值的【所有】相机) ----------
|
||||
std::unordered_set<IIndex> disabledViews;
|
||||
{
|
||||
struct ViewDigitPair {
|
||||
IIndex idx;
|
||||
int digit;
|
||||
};
|
||||
std::vector<ViewDigitPair> viewDigits;
|
||||
|
||||
for (IIndex idx = 0; idx < (IIndex)images.size(); ++idx) {
|
||||
const Image& img = images[idx];
|
||||
if (!img.IsValid()) continue;
|
||||
// ====================================================================
|
||||
// 优化1:提前禁用视图筛选(不变,但移到前面)
|
||||
// ====================================================================
|
||||
std::unordered_set<IIndex> disabledViews;
|
||||
{
|
||||
struct ViewDigitPair { IIndex idx; int digit; };
|
||||
std::vector<ViewDigitPair> viewDigits;
|
||||
|
||||
// 提取文件名(不含路径和扩展名)
|
||||
std::string path = img.name;
|
||||
size_t lastSlash = path.find_last_of("/\\");
|
||||
size_t start = (lastSlash == std::string::npos) ? 0 : lastSlash + 1;
|
||||
size_t lastDot = path.find_last_of('.');
|
||||
if (lastDot == std::string::npos) lastDot = path.size();
|
||||
std::string strName = path.substr(start, lastDot - start);
|
||||
for (IIndex idx = 0; idx < (IIndex)images.size(); ++idx) {
|
||||
const Image& img = images[idx];
|
||||
if (!img.IsValid()) continue;
|
||||
|
||||
// 提取最后一个 _ 前面的最后一个数字字符
|
||||
int digit = -1;
|
||||
size_t lastUnderscore = strName.find_last_of('_');
|
||||
if (lastUnderscore != std::string::npos && lastUnderscore > 0) {
|
||||
for (size_t pos = lastUnderscore - 1; ; --pos) {
|
||||
if (std::isdigit(strName[pos])) {
|
||||
digit = strName[pos] - '0';
|
||||
break;
|
||||
}
|
||||
if (pos == 0) break;
|
||||
}
|
||||
}
|
||||
std::string path = img.name;
|
||||
size_t lastSlash = path.find_last_of("/\\");
|
||||
size_t start = (lastSlash == std::string::npos) ? 0 : lastSlash + 1;
|
||||
size_t lastDot = path.find_last_of('.');
|
||||
if (lastDot == std::string::npos) lastDot = path.size();
|
||||
std::string strName = path.substr(start, lastDot - start);
|
||||
|
||||
if (digit >= 0) {
|
||||
viewDigits.push_back({idx, digit});
|
||||
}
|
||||
}
|
||||
int digit = -1;
|
||||
size_t lastUnderscore = strName.find_last_of('_');
|
||||
if (lastUnderscore != std::string::npos && lastUnderscore > 0) {
|
||||
for (size_t pos = lastUnderscore - 1; ; --pos) {
|
||||
if (std::isdigit(strName[pos])) {
|
||||
digit = strName[pos] - '0';
|
||||
break;
|
||||
}
|
||||
if (pos == 0) break;
|
||||
}
|
||||
}
|
||||
if (digit >= 0) viewDigits.push_back({idx, digit});
|
||||
}
|
||||
|
||||
// ★ 找到最大的 digit 值
|
||||
int maxDigit = -1;
|
||||
for (const auto& vd : viewDigits) {
|
||||
if (vd.digit > maxDigit) maxDigit = vd.digit;
|
||||
}
|
||||
int maxDigit = -1;
|
||||
for (const auto& vd : viewDigits)
|
||||
if (vd.digit > maxDigit) maxDigit = vd.digit;
|
||||
|
||||
// ★ 禁用【所有】digit == maxDigit 的视图(不再只禁前 N 个)
|
||||
for (const auto& vd : viewDigits) {
|
||||
if (vd.digit == maxDigit) {
|
||||
disabledViews.insert(vd.idx);
|
||||
DEBUG_EXTRA("[DisabledView] idx=%d name=%s digit=%d (maxDigit=%d)",
|
||||
vd.idx, images[vd.idx].name.c_str(), vd.digit, maxDigit);
|
||||
}
|
||||
}
|
||||
DEBUG_EXTRA("[DisabledView] %zu views disabled (all with digit=%d)",
|
||||
disabledViews.size(), maxDigit);
|
||||
}
|
||||
for (const auto& vd : viewDigits) {
|
||||
if (vd.digit == maxDigit) {
|
||||
disabledViews.insert(vd.idx);
|
||||
DEBUG_EXTRA("[DisabledView] idx=%d name=%s digit=%d",
|
||||
vd.idx, images[vd.idx].name.c_str(), vd.digit);
|
||||
}
|
||||
}
|
||||
DEBUG_EXTRA("[DisabledView] %zu views disabled", disabledViews.size());
|
||||
}
|
||||
|
||||
// ---------- 初始化 ----------
|
||||
// ====================================================================
|
||||
// 优化2:初始化 + 构建 faceNeighbors
|
||||
// ====================================================================
|
||||
faceViews.resize(virtualFaceMap.size());
|
||||
faceViewWeights.resize(virtualFaceMap.size());
|
||||
for (auto& v : faceViews) v.clear();
|
||||
@@ -18109,26 +18101,13 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
||||
|
||||
if (faceNeighbors.empty()) {
|
||||
DEBUG_EXTRA("faceNeighbors empty, running ComputePureFaceVisibility first");
|
||||
if (!ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views)) {
|
||||
if (!ComputePureFaceVisibility(fOutlierThreshold, nIgnoreMaskLabel, views))
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<IIndex> faceToView(scene.mesh.faces.size(), NO_ID);
|
||||
std::vector<float> faceScores(scene.mesh.faces.size(), -FLT_MAX);
|
||||
|
||||
if (scene.mesh.faceFaces.empty()) {
|
||||
if (scene.mesh.faceFaces.empty())
|
||||
scene.mesh.ListIncidenteFaceFaces();
|
||||
}
|
||||
|
||||
VERBOSE("5[AfterVis] faceFaces.empty=%d faceFaces.size=%d faceFaces[0]={%u,%u,%u}",
|
||||
(int)scene.mesh.faceFaces.empty(),
|
||||
(int)scene.mesh.faceFaces.size(),
|
||||
scene.mesh.faceFaces.empty() ? 0xFFFFFFFF : scene.mesh.faceFaces[0][0],
|
||||
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) {
|
||||
@@ -18138,20 +18117,56 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
||||
FIndex nb = ff[k];
|
||||
if (nb == NO_ID || nb >= (FIndex)virtualFaceMap.size()) continue;
|
||||
bool exists = false;
|
||||
for (IIndex existing : faceNeighbors[fid]) {
|
||||
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. 初始视图分配 ----------
|
||||
std::vector<IIndex> faceToView(scene.mesh.faces.size(), NO_ID);
|
||||
std::vector<float> faceScores(scene.mesh.faces.size(), -FLT_MAX);
|
||||
|
||||
// ====================================================================
|
||||
// 优化3:预计算所有面的中心和法线(避免重复计算)
|
||||
// ====================================================================
|
||||
std::vector<Point3f> precomputedCenters(scene.mesh.faces.size());
|
||||
std::vector<Point3f> precomputedNormals(scene.mesh.faces.size());
|
||||
for (size_t fid = 0; fid < scene.mesh.faces.size(); ++fid) {
|
||||
const Mesh::Face& face = scene.mesh.faces[fid];
|
||||
const Point3f& v0 = scene.mesh.vertices[face[0]];
|
||||
const Point3f& v1 = scene.mesh.vertices[face[1]];
|
||||
const Point3f& v2 = scene.mesh.vertices[face[2]];
|
||||
precomputedCenters[fid] = (v0 + v1 + v2) / 3.0f;
|
||||
|
||||
Point3f normal = scene.mesh.faceNormals[fid];
|
||||
float norm = std::sqrt(normal.x*normal.x + normal.y*normal.y + normal.z*normal.z);
|
||||
if (norm > FLT_EPSILON) {
|
||||
normal.x /= norm; normal.y /= norm; normal.z /= norm;
|
||||
} else {
|
||||
normal = Point3f(0, 0, 1);
|
||||
}
|
||||
precomputedNormals[fid] = normal;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 优化4:预计算每个候选视图的投影参数(避免重复创建Camera对象)
|
||||
// ====================================================================
|
||||
struct ViewCache {
|
||||
bool valid;
|
||||
float s_normal_cache; // 这里存不下,因为依赖面法线,所以只存图像指针
|
||||
const Image* img_ptr;
|
||||
};
|
||||
std::vector<ViewCache> viewCache(images.size());
|
||||
for (IIndex vid = 0; vid < (IIndex)images.size(); ++vid) {
|
||||
const Image& img = images[vid];
|
||||
viewCache[vid].valid = img.IsValid() && !img.image.empty() && img.image.cols >= 2 && img.image.rows >= 2;
|
||||
viewCache[vid].img_ptr = &img;
|
||||
}
|
||||
|
||||
// ====================================================================
|
||||
// 优化5:主循环 - 减少 SampleFaceCenterColor 调用次数
|
||||
// ====================================================================
|
||||
size_t emptyCandidateViews = 0;
|
||||
size_t fallbackByCenterFace = 0;
|
||||
size_t successVF = 0;
|
||||
@@ -18163,38 +18178,34 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
||||
FIndex faceID = vf.faces[0];
|
||||
if (faceID >= faceNeighbors.size()) continue;
|
||||
|
||||
std::unordered_set<IIndex> candidateViews;
|
||||
for (FIndex fid : vf.faces) {
|
||||
if (fid >= faceNeighbors.size()) continue;
|
||||
for (IIndex vid : faceNeighbors[fid]) {
|
||||
if (views.empty() || views.FindFirst(vid) != NO_ID) {
|
||||
// ★ 排除禁用视图(优先选择小数字的视图)
|
||||
if (disabledViews.count(vid) == 0) {
|
||||
candidateViews.insert(vid);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// --- 收集候选视图 ---
|
||||
std::unordered_set<IIndex> candidateViews;
|
||||
for (FIndex fid : vf.faces) {
|
||||
if (fid >= faceNeighbors.size()) continue;
|
||||
for (IIndex vid : faceNeighbors[fid]) {
|
||||
if ((views.empty() || views.FindFirst(vid) != NO_ID) &&
|
||||
!viewCache[vid].valid) continue;
|
||||
if (disabledViews.count(vid) == 0)
|
||||
candidateViews.insert(vid);
|
||||
}
|
||||
}
|
||||
|
||||
// ★ 如果候选视图为空,放宽限制允许禁用视图参与
|
||||
if (candidateViews.empty()) {
|
||||
for (FIndex fid : vf.faces) {
|
||||
if (fid >= faceNeighbors.size()) continue;
|
||||
for (IIndex vid : faceNeighbors[fid]) {
|
||||
if (views.empty() || views.FindFirst(vid) != NO_ID) {
|
||||
candidateViews.insert(vid);
|
||||
}
|
||||
}
|
||||
}
|
||||
// DEBUG_EXTRA("[Fallback] pi=%zu: allowed disabled views, got %zu candidates",
|
||||
// i, candidateViews.size());
|
||||
}
|
||||
if (candidateViews.empty()) {
|
||||
for (FIndex fid : vf.faces) {
|
||||
if (fid >= faceNeighbors.size()) continue;
|
||||
for (IIndex vid : faceNeighbors[fid]) {
|
||||
if ((views.empty() || views.FindFirst(vid) != NO_ID) &&
|
||||
viewCache[vid].valid)
|
||||
candidateViews.insert(vid);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (candidateViews.empty()) {
|
||||
++emptyCandidateViews;
|
||||
IIndex forcedView = (!views.empty()) ? views[0] :
|
||||
(!images.empty()) ? 0 : NO_ID;
|
||||
if (forcedView != NO_ID) {
|
||||
if (forcedView != NO_ID && forcedView < viewCache.size() && viewCache[forcedView].valid) {
|
||||
faceViews[i].push_back(forcedView);
|
||||
faceViewWeights[i].push_back(1.0f);
|
||||
for (FIndex fid : vf.faces) {
|
||||
@@ -18208,112 +18219,96 @@ 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;
|
||||
// --- 使用预计算的中心和法线 ---
|
||||
const Point3f& faceCenter = precomputedCenters[faceID];
|
||||
const Point3f& faceNormal = precomputedNormals[faceID];
|
||||
|
||||
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 = NO_ID;
|
||||
float bestScore = -FLT_MAX;
|
||||
// ★ 优化:先计算所有候选视图的几何得分,选出 Top-K,再算颜色
|
||||
struct GeoScore {
|
||||
IIndex vid;
|
||||
float score;
|
||||
};
|
||||
std::vector<GeoScore> geoResults;
|
||||
geoResults.reserve(candidateViews.size());
|
||||
|
||||
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 Image& img = *viewCache[vid].img_ptr;
|
||||
const Camera& cam = img.camera;
|
||||
|
||||
float s_normal = ComputeViewNormalScore(faceNormal, cam, faceCenter);
|
||||
if (s_normal <= 0.0f) continue;
|
||||
|
||||
const Mesh::Face& face = scene.mesh.faces[faceID];
|
||||
float s_resolution = ComputeResolutionScore(
|
||||
cam, v0, v1, v2, img.image.cols, img.image.rows);
|
||||
cam, scene.mesh.vertices[face[0]], scene.mesh.vertices[face[1]],
|
||||
scene.mesh.vertices[face[2]], img.image.cols, img.image.rows);
|
||||
|
||||
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);
|
||||
Point2f p0 = cam.ProjectPoint(Point3d(scene.mesh.vertices[face[0]]));
|
||||
Point2f p1 = cam.ProjectPoint(Point3d(scene.mesh.vertices[face[1]]));
|
||||
Point2f p2 = cam.ProjectPoint(Point3d(scene.mesh.vertices[face[2]]));
|
||||
float s_occlusion = ComputeOcclusionPenalty(p0, p1, p2, img.image.cols, img.image.rows, 8);
|
||||
|
||||
float rawScore = 3.0f * s_normal
|
||||
+ 0.5f * s_resolution
|
||||
- 0.1f * s_occlusion;
|
||||
float score = 3.0f * s_normal + 0.5f * s_resolution - 0.1f * s_occlusion;
|
||||
geoResults.push_back({vid, score});
|
||||
}
|
||||
|
||||
if (geoResults.empty()) continue;
|
||||
|
||||
// ★ 排序,只取 Top-5 算颜色代价
|
||||
std::sort(geoResults.begin(), geoResults.end(),
|
||||
[](const GeoScore& a, const GeoScore& b) { return a.score > b.score; });
|
||||
|
||||
const size_t TOP_K = 5;
|
||||
size_t numToCheck = std::min(TOP_K, geoResults.size());
|
||||
|
||||
IIndex bestView = NO_ID;
|
||||
float bestScore = -FLT_MAX;
|
||||
|
||||
for (size_t gi = 0; gi < numToCheck; ++gi) {
|
||||
IIndex vid = geoResults[gi].vid;
|
||||
float rawScore = geoResults[gi].score;
|
||||
float score = rawScore;
|
||||
|
||||
// ========== 颜色一致性代价(局部采样版)==========
|
||||
{
|
||||
const float lambda = 8.0f; // 从 100 降到 0.5
|
||||
// --- 颜色代价(只对 Top-5 计算)---
|
||||
const float lambda = 15.0f; // 从8降到3
|
||||
cv::Vec3f candidateColor = SampleFaceCenterColor(vid, faceCenter);
|
||||
if (candidateColor[0] >= 0) {
|
||||
std::vector<cv::Vec3f> neighborColors;
|
||||
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) {
|
||||
const Mesh::FaceFaces& topoNeighbors = scene.mesh.faceFaces[faceID];
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
FIndex nb = topoNeighbors[k];
|
||||
if (nb == NO_ID || nb >= (FIndex)faceToView.size()) continue;
|
||||
|
||||
// 采样当前候选视图下面中心的颜色
|
||||
cv::Vec3f candidateColor = SampleFaceCenterColor(vid, faceCenter);
|
||||
if (candidateColor[0] < 0) {
|
||||
// 投影失败,跳过颜色代价但不 return
|
||||
// 保持 score 不变
|
||||
} else {
|
||||
// 收集邻居颜色:已分配的面,采样邻居面中心在邻居视图下的颜色
|
||||
std::vector<cv::Vec3f> neighborColors;
|
||||
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) {
|
||||
const Mesh::FaceFaces& topoNeighbors = scene.mesh.faceFaces[faceID];
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
FIndex nb = topoNeighbors[k];
|
||||
if (nb == NO_ID || nb >= (FIndex)faceToView.size()) continue;
|
||||
|
||||
IIndex nbView = faceToView[nb];
|
||||
if (nbView == NO_ID) {
|
||||
if (nb < (FIndex)faceNeighbors.size() && !faceNeighbors[nb].empty()) {
|
||||
nbView = faceNeighbors[nb][0];
|
||||
}
|
||||
}
|
||||
if (nbView == NO_ID || nbView >= (IIndex)images.size()) continue;
|
||||
|
||||
// 计算邻居面中心
|
||||
const Mesh::Face& nbFace = scene.mesh.faces[nb];
|
||||
Point3f nbCenter = (scene.mesh.vertices[nbFace[0]] +
|
||||
scene.mesh.vertices[nbFace[1]] +
|
||||
scene.mesh.vertices[nbFace[2]]) / 3.0f;
|
||||
|
||||
cv::Vec3f nbColor = SampleFaceCenterColor(nbView, nbCenter);
|
||||
if (nbColor[0] >= 0) {
|
||||
neighborColors.push_back(nbColor);
|
||||
}
|
||||
IIndex nbView = faceToView[nb];
|
||||
if (nbView == NO_ID) {
|
||||
if (nb < (FIndex)faceNeighbors.size() && !faceNeighbors[nb].empty())
|
||||
nbView = faceNeighbors[nb][0];
|
||||
}
|
||||
}
|
||||
if (nbView == NO_ID || nbView >= (IIndex)images.size()) continue;
|
||||
|
||||
float colorDiff = 0.0f;
|
||||
if (!neighborColors.empty()) {
|
||||
for (const auto& nc : neighborColors) {
|
||||
cv::Vec3f d = candidateColor - nc;
|
||||
colorDiff += std::sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);
|
||||
}
|
||||
colorDiff = colorDiff / neighborColors.size();
|
||||
colorDiff = std::min(colorDiff, 1.0f);
|
||||
|
||||
score -= lambda * colorDiff;
|
||||
cv::Vec3f nbColor = SampleFaceCenterColor(nbView, precomputedCenters[nb]);
|
||||
if (nbColor[0] >= 0)
|
||||
neighborColors.push_back(nbColor);
|
||||
}
|
||||
}
|
||||
|
||||
// ---- 调试日志(前20个面 + 每5万面)----
|
||||
static int g_dbg = 0;
|
||||
g_dbg++;
|
||||
if (g_dbg <= 20 || faceID % 50000 == 0) {
|
||||
VERBOSE("[Score] face=%d view=%d rawScore=%.3f finalScore=%.3f",
|
||||
faceID, vid, rawScore, score);
|
||||
if (!neighborColors.empty()) {
|
||||
float colorDiff = 0.0f;
|
||||
for (const auto& nc : neighborColors) {
|
||||
cv::Vec3f d = candidateColor - nc;
|
||||
colorDiff += std::sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]);
|
||||
}
|
||||
colorDiff = colorDiff / neighborColors.size();
|
||||
colorDiff = std::min(colorDiff, 1.0f);
|
||||
|
||||
// ★ 加入阈值,小色差不惩罚
|
||||
const float thresh = 0.05f;
|
||||
if (colorDiff > thresh)
|
||||
score -= lambda * (colorDiff - thresh);
|
||||
}
|
||||
}
|
||||
// ========== 颜色代价结束 ==========
|
||||
|
||||
if (score > bestScore) {
|
||||
bestScore = score;
|
||||
@@ -18334,8 +18329,10 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
||||
}
|
||||
}
|
||||
|
||||
// ---------- 2. 改进的 Patch 一致性传播 ----------
|
||||
const int PROPAGATION_ITER = 6;
|
||||
// ====================================================================
|
||||
// 优化6:一致性传播(减少轮数,改为加权投票)
|
||||
// ====================================================================
|
||||
const int PROPAGATION_ITER = 3; // 从6降到3
|
||||
for (int iter = 0; iter < PROPAGATION_ITER; ++iter) {
|
||||
std::vector<IIndex> newFaceToView = faceToView;
|
||||
std::vector<float> newFaceScores = faceScores;
|
||||
@@ -18347,66 +18344,49 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
||||
const Mesh::FaceFaces& neighbors = scene.mesh.faceFaces[fid];
|
||||
float currentScore = faceScores[fid];
|
||||
|
||||
if (currentScore > 0.95f) continue;
|
||||
if (currentScore > 0.90f) continue;
|
||||
|
||||
// ★ 加权投票:按分数加权
|
||||
std::map<IIndex, float> weightedVote;
|
||||
weightedVote[faceToView[fid]] = currentScore;
|
||||
|
||||
std::unordered_map<IIndex, int> vote;
|
||||
vote[faceToView[fid]] = 1;
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
FIndex nb = neighbors[k];
|
||||
if (nb == NO_ID) continue;
|
||||
if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) {
|
||||
vote[faceToView[nb]]++;
|
||||
}
|
||||
if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID)
|
||||
weightedVote[faceToView[nb]] += faceScores[nb];
|
||||
}
|
||||
|
||||
IIndex majorityView = faceToView[fid];
|
||||
int maxVote = 0;
|
||||
for (const auto& v : vote) {
|
||||
if (v.second > maxVote) {
|
||||
maxVote = v.second;
|
||||
majorityView = v.first;
|
||||
}
|
||||
}
|
||||
auto best = std::max_element(weightedVote.begin(), weightedVote.end(),
|
||||
[](const std::pair<IIndex, float>& a, const std::pair<IIndex, float>& b) {
|
||||
return a.second < b.second; });
|
||||
|
||||
if (maxVote >= 2 && majorityView != faceToView[fid]) {
|
||||
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;
|
||||
|
||||
if (majorityAvgScore > currentScore - 0.5f) {
|
||||
newFaceToView[fid] = majorityView;
|
||||
newFaceScores[fid] = majorityAvgScore;
|
||||
}
|
||||
// ★ 需要显著更好才切换
|
||||
if (best->first != faceToView[fid] && best->second > currentScore * 1.5f) {
|
||||
newFaceToView[fid] = best->first;
|
||||
newFaceScores[fid] = best->second / 4.0f; // 除以邻居数
|
||||
}
|
||||
}
|
||||
faceToView.swap(newFaceToView);
|
||||
faceScores.swap(newFaceScores);
|
||||
}
|
||||
|
||||
// ---------- 3. 写回 ----------
|
||||
// ====================================================================
|
||||
// 优化7:写回 + 后处理(减少轮数)
|
||||
// ====================================================================
|
||||
for (size_t i = 0; i < virtualFaceMap.size(); ++i) {
|
||||
const VirtualFace& vf = virtualFaceMap[i];
|
||||
if (vf.faces.empty() || faceViews[i].empty()) continue;
|
||||
|
||||
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] = {consistentScore};
|
||||
faceViews[i] = {faceToView[firstFace]};
|
||||
faceViewWeights[i] = {faceScores[firstFace]};
|
||||
}
|
||||
}
|
||||
|
||||
// 后处理:再跑几轮简单投票
|
||||
for (int iter = 0; iter < 3; ++iter) {
|
||||
// 后处理:1轮足够
|
||||
for (int iter = 0; iter < 1; ++iter) {
|
||||
auto newFaceToView = faceToView;
|
||||
for (size_t i = 0; i < faceToView.size(); ++i) {
|
||||
if (faceToView[i] == NO_ID) continue;
|
||||
|
||||
Reference in new issue
Block a user