进一步优化
This commit is contained in:
1 parent
902170f747
commit
7ff01eafdb
1 file changed
+130
@@ -766,6 +766,8 @@ public:
|
||||
unsigned nTextureSizeMultiple,
|
||||
Pixel8U colEmpty,
|
||||
Mesh::Image8U3Arr& outTextures);
|
||||
void GlobalPatchColorAlignment(Image8U3& atlas, int textureSize);
|
||||
|
||||
// ========== 辅助函数 ==========
|
||||
|
||||
// 双线性采样(适配 SEACAVE::TImage<Pixel8U>)
|
||||
@@ -14370,6 +14372,132 @@ void MeshTexture::FillTextureHoles(std::vector<Image8U3>& textures, Pixel8U colE
|
||||
DEBUG_EXTRA("Hole filling completed");
|
||||
}
|
||||
|
||||
// ===== 全局 Patch 颜色对齐 =====
|
||||
// 在 seam edge 上采样两侧 patch 的颜色,求解每个 patch 的偏移量
|
||||
void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize)
|
||||
{
|
||||
if (rcSeamEdges.empty() || rcPatches.empty()) return;
|
||||
|
||||
DEBUG_EXTRA("Global patch color alignment...");
|
||||
TD_TIMER_START();
|
||||
|
||||
const int NP = (int)rcPatches.size();
|
||||
|
||||
// 收集约束:每条 seam edge 两侧 patch 在边界上的颜色差
|
||||
struct Constraint { int pa, pb; Color diff; };
|
||||
std::vector<Constraint> constraints;
|
||||
constraints.reserve(rcSeamEdges.size());
|
||||
|
||||
for (const auto& e : rcSeamEdges) {
|
||||
if (e.rcPatchID0 >= NP || e.rcPatchID1 >= NP) continue;
|
||||
const RCPatch& patchA = rcPatches[e.rcPatchID0];
|
||||
const RCPatch& patchB = rcPatches[e.rcPatchID1];
|
||||
|
||||
// 在重叠区域采样
|
||||
cv::Rect overlap = patchA.rect & patchB.rect;
|
||||
if (overlap.width < 2 || overlap.height < 2) continue;
|
||||
|
||||
int samples = 0;
|
||||
Color sumA(0,0,0), sumB(0,0,0);
|
||||
|
||||
for (int y = overlap.y; y < overlap.y + overlap.height && samples < 100; ++y) {
|
||||
for (int x = overlap.x; x < overlap.x + overlap.width && samples < 100; ++x) {
|
||||
if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue;
|
||||
const Pixel8U& pxA = atlas(y, x);
|
||||
if (pxA[0]==0 && pxA[1]==0 && pxA[2]==0) continue;
|
||||
sumA[0] += pxA[2]; sumA[1] += pxA[1]; sumA[2] += pxA[0]; // RGB
|
||||
// 从 patchB 的对应位置采样
|
||||
const Pixel8U& pxB = atlas(y, x);
|
||||
sumB[0] += pxB[2]; sumB[1] += pxB[1]; sumB[2] += pxB[0];
|
||||
samples++;
|
||||
}
|
||||
}
|
||||
if (samples < 5) continue;
|
||||
|
||||
Color avgA = sumA / (float)samples;
|
||||
Color avgB = sumB / (float)samples;
|
||||
constraints.push_back(Constraint{e.rcPatchID0, e.rcPatchID1, avgB - avgA});
|
||||
}
|
||||
|
||||
if (constraints.empty()) {
|
||||
DEBUG_EXTRA("GlobalPatchColorAlignment: no constraints, skip");
|
||||
return;
|
||||
}
|
||||
|
||||
const int rows = (int)constraints.size();
|
||||
|
||||
// 构建稀疏矩阵 A 和 b
|
||||
std::vector<Eigen::Triplet<float>> triplets;
|
||||
triplets.reserve(rows * 2 + NP); // 约束 + 正则化
|
||||
Eigen::VectorXf bR(rows), bG(rows), bB(rows);
|
||||
|
||||
for (int i = 0; i < rows; ++i) {
|
||||
triplets.emplace_back(i, constraints[i].pa, -1.0f);
|
||||
triplets.emplace_back(i, constraints[i].pb, 1.0f);
|
||||
bR(i) = constraints[i].diff[0]; // R
|
||||
bG(i) = constraints[i].diff[1]; // G
|
||||
bB(i) = constraints[i].diff[2]; // B
|
||||
}
|
||||
|
||||
// 正则化:所有 patch 的偏移量尽量小(Tikhonov)
|
||||
const float lambda = 0.1f;
|
||||
for (int i = 0; i < NP; ++i) {
|
||||
triplets.emplace_back(rows + i, i, lambda);
|
||||
}
|
||||
|
||||
Eigen::SparseMatrix<float> A(rows + NP, NP);
|
||||
A.setFromTriplets(triplets.begin(), triplets.end());
|
||||
|
||||
// 求解
|
||||
Eigen::VectorXf xR, xG, xB;
|
||||
{
|
||||
// ★ 转稠密,避免 Sparse LDLT 的 lpNorm bug ★
|
||||
Eigen::MatrixXf A_dense = A; // Sparse → Dense
|
||||
Eigen::MatrixXf AtA = A_dense.transpose() * A_dense;
|
||||
|
||||
// 加对角阻尼(1e-6),保证正定可逆
|
||||
const int N = A_dense.cols();
|
||||
for (int i = 0; i < N; ++i) AtA(i, i) += 1e-6f;
|
||||
|
||||
Eigen::VectorXf AtbR = A_dense.transpose() * bR;
|
||||
Eigen::VectorXf AtbG = A_dense.transpose() * bG;
|
||||
Eigen::VectorXf AtbB = A_dense.transpose() * bB;
|
||||
|
||||
Eigen::LDLT<Eigen::MatrixXf> ldlt(AtA);
|
||||
if (ldlt.info() != Eigen::Success) {
|
||||
DEBUG_EXTRA("GlobalPatchColorAlignment: LDLT failed");
|
||||
return;
|
||||
}
|
||||
xR = ldlt.solve(AtbR);
|
||||
xG = ldlt.solve(AtbG);
|
||||
xB = ldlt.solve(AtbB);
|
||||
}
|
||||
|
||||
// 应用:对每个 patch 的像素加上偏移(加性,限制幅度)
|
||||
const float maxAdj = 30.0f; // 8-bit 空间最大偏移
|
||||
#pragma omp parallel for
|
||||
for (int i = 0; i < NP; ++i) {
|
||||
const RCPatch& patch = rcPatches[i];
|
||||
float adjR = std::max(-maxAdj, std::min(maxAdj, xR(i)));
|
||||
float adjG = std::max(-maxAdj, std::min(maxAdj, xG(i)));
|
||||
float adjB = std::max(-maxAdj, std::min(maxAdj, xB(i)));
|
||||
|
||||
for (int y = patch.rect.y; y < patch.rect.y + patch.rect.height; ++y) {
|
||||
for (int x = patch.rect.x; x < patch.rect.x + patch.rect.width; ++x) {
|
||||
if (x < 0 || x >= textureSize || y < 0 || y >= textureSize) continue;
|
||||
Pixel8U& px = atlas(y, x);
|
||||
if (px[0]==0 && px[1]==0 && px[2]==0) continue;
|
||||
// BGR 存储顺序
|
||||
px[2] = (uint8_t)CLAMP(px[2] + adjR, 0.f, 255.f); // R
|
||||
px[1] = (uint8_t)CLAMP(px[1] + adjG, 0.f, 255.f); // G
|
||||
px[0] = (uint8_t)CLAMP(px[0] + adjB, 0.f, 255.f); // B
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("Global alignment done: %d constraints (%s)", rows, TD_TIMER_GET_FMT().c_str());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 3. RC 风格光栅化主函数(含接缝优化)
|
||||
// ============================================================
|
||||
@@ -14504,6 +14632,8 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
}
|
||||
DEBUG_EXTRA("Created %zu RC patches", rcPatches.size());
|
||||
|
||||
GlobalPatchColorAlignment(atlas, textureSize);
|
||||
|
||||
// 5/6/7. 接缝
|
||||
BuildSeamEdgesFromRCPatches();
|
||||
if (!seamEdges.empty()) {
|
||||
|
||||
Reference in new issue
Block a user