|
|
|
|
@ -655,6 +655,7 @@ public:
@@ -655,6 +655,7 @@ public:
|
|
|
|
|
float fRatioDataSmoothness, int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views); |
|
|
|
|
// 简单的 view smoothing:如果某个面的 view 和多数邻居不同,且分数差距不大,就改成邻居的 view
|
|
|
|
|
cv::Vec3f SampleFaceCenterColor(IIndex vid, const Point3f& faceCenter); |
|
|
|
|
|
|
|
|
|
float ComputeViewNormalScore(const Mesh::Normal& faceNormal, const Camera& camera, const Point3f& faceCenter); |
|
|
|
|
float ComputeResolutionScore( |
|
|
|
|
@ -15970,7 +15971,8 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
@@ -15970,7 +15971,8 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
|
|
|
|
|
{ |
|
|
|
|
for (int x = 0; x < patchW; ++x) { |
|
|
|
|
cv::Vec3b color = remapped.at<cv::Vec3b>(y, x); |
|
|
|
|
if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue; |
|
|
|
|
if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue; |
|
|
|
|
|
|
|
|
|
int atlasX = x + minX, atlasY = y + minY; |
|
|
|
|
if (atlasX < 0 || atlasX >= textureSize || atlasY < 0 || atlasY >= textureSize) continue; |
|
|
|
|
// atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]};
|
|
|
|
|
@ -16357,8 +16359,7 @@ bool MeshTexture::RasterizeVirtualFaces(
@@ -16357,8 +16359,7 @@ bool MeshTexture::RasterizeVirtualFaces(
|
|
|
|
|
for (int x = 0; x < patchW; ++x) { |
|
|
|
|
cv::Vec3b color = patch.at<cv::Vec3b>(y, x); |
|
|
|
|
|
|
|
|
|
// if (color[0]==0 && color[1]==0 && color[2]==0) continue;
|
|
|
|
|
if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue; |
|
|
|
|
if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue; |
|
|
|
|
|
|
|
|
|
int atlasX = x + minX, atlasY = y + minY; |
|
|
|
|
if (atlasX<0||atlasX>=textureSize||atlasY<0||atlasY>=textureSize) continue; |
|
|
|
|
@ -17667,92 +17668,44 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive(
@@ -17667,92 +17668,44 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive(
|
|
|
|
|
return textureSize; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 替换掉 g_avgColors[vid] 的用法
|
|
|
|
|
cv::Vec3f MeshTexture::SampleFaceCenterColor(IIndex vid, const Point3f& faceCenter) { |
|
|
|
|
const Image& img = images[vid]; |
|
|
|
|
if (img.image.empty()) return cv::Vec3f(-1, -1, -1); |
|
|
|
|
Point2f proj = img.camera.ProjectPoint(Point3d(faceCenter)); |
|
|
|
|
int px = (int)(proj.x + 0.5f), py = (int)(proj.y + 0.5f); |
|
|
|
|
if (px < 0 || py < 0 || px >= img.image.cols || py >= img.image.rows) |
|
|
|
|
return cv::Vec3f(-1, -1, -1); |
|
|
|
|
const cv::Vec3b& c = img.image.at<cv::Vec3b>(py, px); |
|
|
|
|
return cv::Vec3f(c[2]/255.0f, c[1]/255.0f, c[0]/255.0f); // BGR→RGB, normalize
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ============================================================
|
|
|
|
|
// 5. SelectBestViewsForVirtualFaces(修复版)
|
|
|
|
|
// ============================================================
|
|
|
|
|
// ============================================================
|
|
|
|
|
// 5. SelectBestViewsForVirtualFaces(局部采样颜色版)
|
|
|
|
|
// ============================================================
|
|
|
|
|
// 前置条件:MeshTexture 类中需声明并定义 SampleFaceCenterColor:
|
|
|
|
|
// cv::Vec3f SampleFaceCenterColor(IIndex vid, const Point3f& faceCenter) const;
|
|
|
|
|
// 实现见下方附录
|
|
|
|
|
// ============================================================
|
|
|
|
|
bool MeshTexture::SelectBestViewsForVirtualFaces( |
|
|
|
|
VirtualFaceMap& virtualFaceMap, |
|
|
|
|
unsigned minCommonCameras, float fOutlierThreshold, |
|
|
|
|
float fRatioDataSmoothness, int nIgnoreMaskLabel, |
|
|
|
|
const IIndexArr& views) |
|
|
|
|
{ |
|
|
|
|
VERBOSE("4[EnterSelect] faceFaces.empty=%d faceFaces.size=%d faceNeighbors.empty=%d", |
|
|
|
|
VERBOSE("4[EnterSelect] faceFaces.empty=%d faceFaces.size=%d faceNeighbors.empty=%d", |
|
|
|
|
(int)scene.mesh.faceFaces.empty(), |
|
|
|
|
(int)scene.mesh.faceFaces.size(), |
|
|
|
|
(int)faceNeighbors.empty()); |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("Selecting best views for %zu virtual faces", virtualFaceMap.size()); |
|
|
|
|
static std::vector<cv::Vec3f> g_avgColors; |
|
|
|
|
|
|
|
|
|
// ===== 从磁盘加载图像,计算全局平均颜色 =====
|
|
|
|
|
static bool g_avgColorsComputed = false; |
|
|
|
|
// if (!g_avgColorsComputed) {
|
|
|
|
|
// const size_t numViews = images.size(); // ← 你的变量名
|
|
|
|
|
// g_avgColors.resize(numViews, cv::Vec3f(-1, -1, -1));
|
|
|
|
|
|
|
|
|
|
// for (size_t vid = 0; vid < numViews; ++vid) {
|
|
|
|
|
// const std::string& imgPath = images[vid].name; // ← 你的字段名
|
|
|
|
|
|
|
|
|
|
// cv::Mat img = cv::imread(imgPath, cv::IMREAD_COLOR);
|
|
|
|
|
// if (img.empty()) {
|
|
|
|
|
// VERBOSE("[AvgColor] view %zu: cannot load '%s'", vid, imgPath.c_str());
|
|
|
|
|
// continue;
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// // 直接转 float 算均值,不 resize
|
|
|
|
|
// cv::Mat imgF;
|
|
|
|
|
// img.convertTo(imgF, CV_32FC3, 1.0 / 255.0);
|
|
|
|
|
// cv::Scalar mean = cv::mean(imgF);
|
|
|
|
|
|
|
|
|
|
// g_avgColors[vid] = cv::Vec3f(mean[0], mean[1], mean[2]);
|
|
|
|
|
|
|
|
|
|
// VERBOSE("[AvgColor] view %zu: mean=(%.3f,%.3f,%.3f)",
|
|
|
|
|
// vid, mean[0], mean[1], mean[2]);
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
// g_avgColorsComputed = true;
|
|
|
|
|
// VERBOSE("[AvgColor] Done: [0]=(%.3f,%.3f,%.3f)",
|
|
|
|
|
// g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2]);
|
|
|
|
|
// }
|
|
|
|
|
g_avgColorsComputed = false; |
|
|
|
|
if (!g_avgColorsComputed) { |
|
|
|
|
g_avgColors.assign(images.size(), cv::Vec3f(-1, -1, -1)); |
|
|
|
|
for (size_t vid = 0; vid < images.size(); ++vid) { |
|
|
|
|
const cv::Mat& img = images[vid].image; // ★ 直接用内存图像
|
|
|
|
|
if (img.empty()) continue; |
|
|
|
|
cv::Mat imgF; |
|
|
|
|
img.convertTo(imgF, CV_32FC3, 1.0 / 255.0); |
|
|
|
|
cv::Scalar mean = cv::mean(imgF); |
|
|
|
|
g_avgColors[vid] = cv::Vec3f(mean[0], mean[1], mean[2]); // BGR
|
|
|
|
|
} |
|
|
|
|
g_avgColorsComputed = true; |
|
|
|
|
VERBOSE("[AvgColor] Done: [0]=(%.3f,%.3f,%.3f), [1]=(%.3f,%.3f,%.3f)", |
|
|
|
|
g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2], |
|
|
|
|
g_avgColors[1][0], g_avgColors[1][1], g_avgColors[1][2]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (g_avgColors.empty()) { |
|
|
|
|
g_avgColors.resize(images.size(), cv::Vec3f(-1, -1, -1)); |
|
|
|
|
for (size_t v = 0; v < images.size(); ++v) { |
|
|
|
|
const cv::Mat& m = images[v].image; |
|
|
|
|
if (m.empty()) continue; |
|
|
|
|
cv::Scalar meanColor = cv::mean(m); |
|
|
|
|
g_avgColors[v] = cv::Vec3f(meanColor[0] / 255.0f, meanColor[1] / 255.0f, meanColor[2] / 255.0f); |
|
|
|
|
} |
|
|
|
|
VERBOSE("[AvgColor] Precomputed average colors for %zu images", images.size()); |
|
|
|
|
|
|
|
|
|
// 调试:打印 g_avgColors 内容
|
|
|
|
|
VERBOSE("[AvgColorDebug] g_avgColors.size=%d, [0]=(%.1f,%.1f,%.1f), [1]=(%.1f,%.1f,%.1f), [78]=(%.1f,%.1f,%.1f)", |
|
|
|
|
(int)g_avgColors.size(), |
|
|
|
|
g_avgColors[0][0], g_avgColors[0][1], g_avgColors[0][2], |
|
|
|
|
g_avgColors[1][0], g_avgColors[1][1], g_avgColors[1][2], |
|
|
|
|
g_avgColors[78][0], g_avgColors[78][1], g_avgColors[78][2]); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ---------- 初始化 ----------
|
|
|
|
|
faceViews.resize(virtualFaceMap.size()); |
|
|
|
|
faceViewWeights.resize(virtualFaceMap.size()); |
|
|
|
|
|
|
|
|
|
for (auto& v : faceViews) v.clear(); |
|
|
|
|
for (auto& w : faceViewWeights) w.clear(); |
|
|
|
|
|
|
|
|
|
@ -17766,40 +17719,39 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -17766,40 +17719,39 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
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) { |
|
|
|
|
if (fid >= scene.mesh.faceFaces.size()) break; |
|
|
|
|
const Mesh::FaceFaces& ff = scene.mesh.faceFaces[fid]; // 原始面邻接
|
|
|
|
|
for (int k = 0; k < 3; ++k) { |
|
|
|
|
FIndex nb = ff[k]; |
|
|
|
|
if (nb == NO_ID || nb >= (FIndex)virtualFaceMap.size()) continue; |
|
|
|
|
// 去重插入
|
|
|
|
|
bool exists = false; |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
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) { |
|
|
|
|
if (fid >= scene.mesh.faceFaces.size()) break; |
|
|
|
|
const Mesh::FaceFaces& ff = scene.mesh.faceFaces[fid]; |
|
|
|
|
for (int k = 0; k < 3; ++k) { |
|
|
|
|
FIndex nb = ff[k]; |
|
|
|
|
if (nb == NO_ID || nb >= (FIndex)virtualFaceMap.size()) continue; |
|
|
|
|
bool exists = false; |
|
|
|
|
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. 初始视图分配 ----------
|
|
|
|
|
size_t emptyCandidateViews = 0; |
|
|
|
|
@ -17881,67 +17833,59 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -17881,67 +17833,59 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
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 rawScore = 3.0f * s_normal |
|
|
|
|
+ 0.5f * s_resolution |
|
|
|
|
- 0.1f * s_occlusion; |
|
|
|
|
float score = rawScore; |
|
|
|
|
|
|
|
|
|
// ========== 颜色一致性代价 ==========
|
|
|
|
|
// ========== 颜色一致性代价(局部采样版)==========
|
|
|
|
|
{ |
|
|
|
|
const float lambda = 100.0f; |
|
|
|
|
// ========== 收集邻居颜色(修复版:1-ring + faceToView)==========
|
|
|
|
|
std::vector<cv::Vec3f> neighborColors; |
|
|
|
|
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) { |
|
|
|
|
const Mesh::Face& 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) { |
|
|
|
|
// fallback: 邻居还没分配 view,用第一个可见相机
|
|
|
|
|
if (nb < (FIndex)faceNeighbors.size() && !faceNeighbors[nb].empty()) { |
|
|
|
|
nbView = faceNeighbors[nb][0]; |
|
|
|
|
const float lambda = 0.5f; // 从 100 降到 0.5
|
|
|
|
|
|
|
|
|
|
// 采样当前候选视图下面中心的颜色
|
|
|
|
|
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)g_avgColors.size()) { |
|
|
|
|
const cv::Vec3f& nc = g_avgColors[nbView]; |
|
|
|
|
if (nc[0] >= 0) { // 有效颜色才收集
|
|
|
|
|
neighborColors.push_back(nc); |
|
|
|
|
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); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// ========== 修复结束 ==========
|
|
|
|
|
|
|
|
|
|
float colorDiff = 0.0f; |
|
|
|
|
if (!neighborColors.empty()) { |
|
|
|
|
cv::Vec3f candidateColor = g_avgColors[vid]; |
|
|
|
|
// cv::Vec3f candidateColor = SampleColorAtPoint(images[vid].image, images[vid].camera, faceCenter);
|
|
|
|
|
if (faceID == 50000) { |
|
|
|
|
if (candidateColor[0] < 0) { |
|
|
|
|
VERBOSE("[ColorDebug] face=50000 vid=%d -> candidateColor FAILED (val=%.1f,%.1f,%.1f)", |
|
|
|
|
(int)vid, candidateColor[0], candidateColor[1], candidateColor[2]); |
|
|
|
|
} |
|
|
|
|
// 同时打印 g_avgColors[vid] 的原始值
|
|
|
|
|
if (vid < g_avgColors.size()) { |
|
|
|
|
VERBOSE("[ColorDebug] g_avgColors[%d] = (%.1f,%.1f,%.1f)", |
|
|
|
|
(int)vid, g_avgColors[vid][0], g_avgColors[vid][1], g_avgColors[vid][2]); |
|
|
|
|
} else { |
|
|
|
|
VERBOSE("[ColorDebug] vid=%d OUT OF BOUNDS! g_avgColors.size=%d", |
|
|
|
|
(int)vid, (int)g_avgColors.size()); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
if (candidateColor[0] >= 0) { |
|
|
|
|
|
|
|
|
|
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()) / 1.732f;
|
|
|
|
|
colorDiff = colorDiff / neighborColors.size(); |
|
|
|
|
colorDiff = std::min(colorDiff, 1.0f); |
|
|
|
|
|
|
|
|
|
// ✅ 修复:不再乘 score,避免放大效应
|
|
|
|
|
score -= lambda * colorDiff; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
@ -17950,10 +17894,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -17950,10 +17894,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
static int g_dbg = 0; |
|
|
|
|
g_dbg++; |
|
|
|
|
if (g_dbg <= 20 || faceID % 50000 == 0) { |
|
|
|
|
VERBOSE("[Score] face=%d view=%d rawScore=%.3f nbrColors=%d " |
|
|
|
|
"colorDiff=%.3f penalty=%.3f finalScore=%.3f", |
|
|
|
|
faceID, vid, rawScore, (int)neighborColors.size(), |
|
|
|
|
colorDiff, lambda * colorDiff, score); |
|
|
|
|
VERBOSE("[Score] face=%d view=%d rawScore=%.3f finalScore=%.3f", |
|
|
|
|
faceID, vid, rawScore, score); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
// ========== 颜色代价结束 ==========
|
|
|
|
|
@ -17978,67 +17920,61 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -17978,67 +17920,61 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ---------- 2. 改进的 Patch 一致性传播 ----------
|
|
|
|
|
const int PROPAGATION_ITER = 6; |
|
|
|
|
for (int iter = 0; iter < PROPAGATION_ITER; ++iter) { |
|
|
|
|
std::vector<IIndex> newFaceToView = faceToView; |
|
|
|
|
std::vector<float> newFaceScores = faceScores; |
|
|
|
|
|
|
|
|
|
const int PROPAGATION_ITER = 6; // 从2改到6
|
|
|
|
|
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; |
|
|
|
|
if (fid >= (FIndex)scene.mesh.faceFaces.size()) continue; |
|
|
|
|
|
|
|
|
|
for (FIndex fid = 0; fid < (FIndex)faceToView.size(); ++fid) { |
|
|
|
|
if (faceToView[fid] == NO_ID) continue; |
|
|
|
|
if (fid >= (FIndex)scene.mesh.faceFaces.size()) continue; |
|
|
|
|
const Mesh::FaceFaces& neighbors = scene.mesh.faceFaces[fid]; |
|
|
|
|
float currentScore = faceScores[fid]; |
|
|
|
|
|
|
|
|
|
const Mesh::Face& neighbors = scene.mesh.faceFaces[fid]; |
|
|
|
|
float currentScore = faceScores[fid]; |
|
|
|
|
if (currentScore > 0.95f) continue; |
|
|
|
|
|
|
|
|
|
// ★ 放宽:只锁死 > 0.95 的极高分明面,其余都允许被传播覆盖
|
|
|
|
|
if (currentScore > 0.95f) continue; |
|
|
|
|
|
|
|
|
|
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]]++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
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]]++; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
IIndex majorityView = faceToView[fid]; |
|
|
|
|
int maxVote = 0; |
|
|
|
|
for (const auto& v : vote) { |
|
|
|
|
if (v.second > maxVote) { |
|
|
|
|
maxVote = v.second; |
|
|
|
|
majorityView = v.first; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
IIndex majorityView = faceToView[fid]; |
|
|
|
|
int maxVote = 0; |
|
|
|
|
for (const auto& v : vote) { |
|
|
|
|
if (v.second > maxVote) { |
|
|
|
|
maxVote = v.second; |
|
|
|
|
majorityView = v.first; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ★ 放宽:只要多数票 >= 2 且和我不一样,就考虑切换(原来是 >= 3)
|
|
|
|
|
if (maxVote >= 2 && majorityView != faceToView[fid]) { |
|
|
|
|
// 计算多数view在邻居中的平均评分
|
|
|
|
|
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 (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; |
|
|
|
|
|
|
|
|
|
// ★ 简化:去掉颜色阻断,直接让评分说话
|
|
|
|
|
// 只要多数view的平均分不低于我太多(差 < 0.3),就切换
|
|
|
|
|
if (majorityAvgScore > currentScore - 0.5f) { |
|
|
|
|
newFaceToView[fid] = majorityView; |
|
|
|
|
newFaceScores[fid] = majorityAvgScore; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
faceToView.swap(newFaceToView); |
|
|
|
|
faceScores.swap(newFaceScores); |
|
|
|
|
} |
|
|
|
|
if (majorityAvgScore > currentScore - 0.5f) { |
|
|
|
|
newFaceToView[fid] = majorityView; |
|
|
|
|
newFaceScores[fid] = majorityAvgScore; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
faceToView.swap(newFaceToView); |
|
|
|
|
faceScores.swap(newFaceScores); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// ---------- 3. 写回 ----------
|
|
|
|
|
for (size_t i = 0; i < virtualFaceMap.size(); ++i) { |
|
|
|
|
@ -18054,27 +17990,27 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
@@ -18054,27 +17990,27 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// 在写回循环之后,加这个:
|
|
|
|
|
for (int iter = 0; iter < 3; ++iter) { |
|
|
|
|
auto newFaceToView = faceToView; |
|
|
|
|
for (size_t i = 0; i < faceToView.size(); ++i) { |
|
|
|
|
if (faceToView[i] == NO_ID) continue; |
|
|
|
|
std::map<IIndex, int> vote; |
|
|
|
|
const auto& ff = scene.mesh.faceFaces[i]; |
|
|
|
|
for (int k = 0; k < 3; ++k) { |
|
|
|
|
FIndex nb = ff[k]; |
|
|
|
|
if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) |
|
|
|
|
vote[faceToView[nb]]++; |
|
|
|
|
} |
|
|
|
|
if (vote.size() <= 1) continue; |
|
|
|
|
auto best = std::max_element(vote.begin(), vote.end(), |
|
|
|
|
[](const std::pair<IIndex, int>& a, const std::pair<IIndex, int>& b) { |
|
|
|
|
return a.second < b.second; }); |
|
|
|
|
if (best->first != faceToView[i] && best->second >= 2) |
|
|
|
|
newFaceToView[i] = best->first; |
|
|
|
|
} |
|
|
|
|
faceToView.swap(newFaceToView); |
|
|
|
|
} |
|
|
|
|
// 后处理:再跑几轮简单投票
|
|
|
|
|
for (int iter = 0; iter < 3; ++iter) { |
|
|
|
|
auto newFaceToView = faceToView; |
|
|
|
|
for (size_t i = 0; i < faceToView.size(); ++i) { |
|
|
|
|
if (faceToView[i] == NO_ID) continue; |
|
|
|
|
std::map<IIndex, int> vote; |
|
|
|
|
const auto& ff = scene.mesh.faceFaces[i]; |
|
|
|
|
for (int k = 0; k < 3; ++k) { |
|
|
|
|
FIndex nb = ff[k]; |
|
|
|
|
if (nb < (FIndex)faceToView.size() && faceToView[nb] != NO_ID) |
|
|
|
|
vote[faceToView[nb]]++; |
|
|
|
|
} |
|
|
|
|
if (vote.size() <= 1) continue; |
|
|
|
|
auto best = std::max_element(vote.begin(), vote.end(), |
|
|
|
|
[](const std::pair<IIndex, int>& a, const std::pair<IIndex, int>& b) { |
|
|
|
|
return a.second < b.second; }); |
|
|
|
|
if (best->first != faceToView[i] && best->second >= 2) |
|
|
|
|
newFaceToView[i] = best->first; |
|
|
|
|
} |
|
|
|
|
faceToView.swap(newFaceToView); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
DEBUG_EXTRA("====== Virtual Face View Selection Summary ======"); |
|
|
|
|
DEBUG_EXTRA("Total virtual faces : %zu", virtualFaceMap.size()); |
|
|
|
|
|