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