色差进一步优化

This commit is contained in:
hesuicong committed 2026-09-27 15:39:32 +08:00
1 parent 990fff69c5
commit e68ecd01db
1 file changed
+205 -225
+205 -225
View File
@@ -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;