Browse Source

保留全局颜色优化

ManualUV
hesuicong 3 weeks ago
parent
commit
4ae6404044
  1. 757
      libs/MVS/SceneTexture.cpp

757
libs/MVS/SceneTexture.cpp

@ -740,6 +740,18 @@ public: @@ -740,6 +740,18 @@ public:
void CreateSeamVertices();
uint32_t FindOrCreateComponentForFace(FIndex faceIdx);
uint32_t FindNearestPatchForComponent(uint32_t compID);
void RunGlobalColorOptimization(cv::Mat& atlas, int textureSize, float lambda);
// ----------------------------------------------------------------------------
// 3.3 把求解出的调整量应用到 atlas
// —— 在三角形内用重心坐标插值(对齐 OpenMVS RasterPatch bary 插值)
// 调整量从 3 个顶点 (顶点,patch) 的调整向量插值到每个像素,再加到 RGB 上
// ----------------------------------------------------------------------------
void ApplyColorAdjustments(cv::Mat& atlasMat, const Eigen::VectorXf& x, const Eigen::Index rowsX);
void GlobalSeamLeveling2(Eigen::Index* pRowsX,
Eigen::SparseMatrix<float>* pA,
Eigen::VectorXf* pb,
float lambda,
cv::Mat& atlasMat);
void GlobalSeamLeveling();
void GlobalSeamLeveling3();
void LocalSeamLeveling();
@ -762,6 +774,7 @@ public: @@ -762,6 +774,7 @@ public:
);
std::vector<uint32_t> faceToPatchID; // faceID → 新 patchID 映射
void MergeSameViewPatches();
void BuildSeamEdgesFromFaceToPatchID();
void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize);
bool RasterizeVirtualFaces(
const VirtualFaceMap& virtualFaceMap,
@ -774,6 +787,15 @@ public: @@ -774,6 +787,15 @@ public:
const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews,
int textureSize);
// ----------------------------------------------------------------------------
// 3.1 从 rcSeamEdges 构建 SeamVert(对齐 OpenMVS seamVertices)
// 每个 seam edge 的两个端点(mesh 顶点)被收集;若一个顶点属于多个 patch
// (即位于 patch 边界),则成为 SeamVert。
// 同时为每个 (顶点, patch) 分配唯一的行号 rowsX —— 对齐 OpenMVS 的
// vertpatch2rows(变量索引的核心)。
// ----------------------------------------------------------------------------
bool BuildSeamVertsFromEdges();
bool BuildSeamVertsFromFaceAdjacency();
void GlobalAlignPatches(Image8U3& atlas, int textureSize);
void LocalBlendSeams(Image8U3& atlas, int textureSize);
void FeatherTextureSeams(Image8U3& texture,
@ -1216,13 +1238,52 @@ public: @@ -1216,13 +1238,52 @@ public:
Point2f uvMin, uvMax;
};
struct SeamVert {
VIndex idxVertex; // 对应的 mesh 顶点 ID
std::vector<uint32_t> patchIDs; // 该顶点属于哪些 patch(即 "patches")
struct Patch {
uint32_t idxPatch; // patch 索引
// 如需边信息可加: std::vector<Edge> edges;
Patch(uint32_t id = UINT32_MAX) : idxPatch(id) {}
};
// 若你原代码用的是 patchIDs 扁平数组,上面 Patch 结构 + patches 可省略
};
struct GSLInput {
const std::vector<RCPatch>* rcPatches = nullptr; // [in]
const std::vector<SeamEdge>* rcSeamEdges = nullptr; // [in]
const std::vector<uint32_t>* faceToPatchID = nullptr; // [in] face -> merged patch
const Mesh::FaceFaces* faceFaces = nullptr; // [in] 面-面邻接
const std::vector<TexCoord>* faceTexcoords = nullptr; // [in] per-face UV (3 per face)
const std::vector<Face>* faces = nullptr; // [in]
cv::Mat* atlas = nullptr; // [in/out] atlas 图像
int textureSize = 0; // [in]
};
inline cv::Vec3b AddColorClamped(const cv::Vec3b& base, const Eigen::Vector3f& adj) {
return cv::Vec3b(
(uint8_t)cv::saturate_cast<int>(base[0] + (int)adj[0]),
(uint8_t)cv::saturate_cast<int>(base[1] + (int)adj[1]),
(uint8_t)cv::saturate_cast<int>(base[2] + (int)adj[2]));
}
std::vector<SeamEdge> rcSeamEdges;
std::vector<RCPatch> rcPatches;
int currentTextureSize;
// ===== 全局颜色优化(对齐 OpenMVS GlobalSeamLeveling3)=====
std::vector<SeamVert> seamVerts; // 接缝顶点列表
std::vector<uint32_t> vertToSeamVert; // mesh 顶点 -> seamVerts 索引 (UINT32_MAX = 非接缝)
bool seamDataBuilt; // 是否已构建
// CG 求解结果缓存(供 ApplyColorAdjustments 使用)
Eigen::VectorXf m_gslSolution; // 解向量 x(per-顶点×patch 的调整量)
Eigen::Index m_gslRowsX; // 变量总数(= rowsX)
// (可选)vertpatch -> row 映射,按需启用:
std::vector<Eigen::Index> m_gslSVPatchRow;
// ===== 函数声明 =====
Color SampleImageBilinear(const cv::Mat& img, float x, float y);
void BuildSeamEdgesFromRCPatches();
void SeamBlendingFromOriginalImages(Image8U3& atlas);
// ---- 全局光度校正(per-image gain,3 通道独立)----
@ -10092,6 +10153,187 @@ void MeshTexture::LocalSeamLevelingExternalUV() @@ -10092,6 +10153,187 @@ void MeshTexture::LocalSeamLevelingExternalUV()
// 针对外部UV数据的局部接缝处理
// 实现保留原始UV特征的接缝处理
}
// ----------------------------------------------------------------------------
// 3.3 把求解出的调整量应用到 atlas
// —— 在三角形内用重心坐标插值(对齐 OpenMVS RasterPatch bary 插值)
// 调整量从 3 个顶点 (顶点,patch) 的调整向量插值到每个像素,再加到 RGB 上
// ----------------------------------------------------------------------------
void MeshTexture::ApplyColorAdjustments(cv::Mat& atlasMat,
const Eigen::VectorXf& x,
const Eigen::Index rowsX)
{
const size_t NP = rcPatches.size();
if (x.size() == 0 || seamVerts.empty()) return;
// 计算每个 patch 的平均调整量(从接缝顶点处取)
std::vector<cv::Vec3f> patchAdj(NP, cv::Vec3f(0,0,0));
std::vector<int> patchCnt(NP, 0);
for (size_t svi = 0; svi < seamVerts.size(); ++svi) {
const SeamVert& sv = seamVerts[svi];
// ★ 修正:用 patchIDs(vector<uint32_t>),不是 patches
for (uint32_t pid : sv.patchIDs) {
if (pid >= NP) continue;
Eigen::Index row = m_gslSVPatchRow[svi * NP + pid];
if (row < 0 || row >= rowsX) continue;
patchAdj[pid][0] += x(row * 3 + 0);
patchAdj[pid][1] += x(row * 3 + 1);
patchAdj[pid][2] += x(row * 3 + 2);
patchCnt[pid]++;
}
}
// 均值
for (size_t i = 0; i < NP; ++i) {
if (patchCnt[i] > 0) {
patchAdj[i] *= (1.0f / patchCnt[i]);
}
}
// 应用(加法调整,clamp ±15)
for (size_t i = 0; i < NP; ++i) {
const cv::Rect& r = rcPatches[i].rect;
if (r.x < 0 || r.y < 0 || r.x + r.width > atlasMat.cols || r.y + r.height > atlasMat.rows)
continue;
cv::Vec3f adj(
std::clamp(patchAdj[i][0], -15.0f, 15.0f),
std::clamp(patchAdj[i][1], -15.0f, 15.0f),
std::clamp(patchAdj[i][2], -15.0f, 15.0f)
);
if (adj[0] == 0 && adj[1] == 0 && adj[2] == 0) continue;
cv::Mat patch = atlasMat(r);
for (int y = 0; y < patch.rows; ++y)
for (int x = 0; x < patch.cols; ++x) {
cv::Vec3b& p = patch.at<cv::Vec3b>(y, x);
if (p[0] < 5 && p[1] < 5 && p[2] < 5) continue;
p[0] = cv::saturate_cast<uchar>(p[0] + adj[0]);
p[1] = cv::saturate_cast<uchar>(p[1] + adj[1]);
p[2] = cv::saturate_cast<uchar>(p[2] + adj[2]);
}
}
}
// ----------------------------------------------------------------------------
// 3.4 一站式入口(推荐调用)
// ----------------------------------------------------------------------------
void MeshTexture::RunGlobalColorOptimization(cv::Mat& atlas, int textureSize, float lambda)
{
if (!seamDataBuilt) {
BuildSeamVertsFromEdges(); // 或 BuildSeamVertsFromFaceAdjacency()
}
Eigen::Index rowsX;
Eigen::SparseMatrix<float> A;
Eigen::VectorXf b;
GlobalSeamLeveling2(&rowsX, &A, &b, lambda, atlas);
ApplyColorAdjustments(atlas, m_gslSolution, m_gslRowsX);
}
// ----------------------------------------------------------------------------
// 3.2 核心:构建并求解全局颜色调整(对齐 OpenMVS GlobalSeamLeveling3)
//
// 变量:每个 (接缝顶点, patch) 一个 3 通道调整量 → 行号 rowsX
// 数据项 A:同一接缝顶点,不同 patch 的调整后颜色一致
// color_i + x_i = color_j + x_j
// → x_i - x_j = color_j - color_i (右端 b = 颜色差)
// 正则项 Γ:同一 patch 内相邻顶点调整量平滑
// x_v,P - x_vAdj,P = 0, 权重 λ
// 求解:(AᵀA + ΓᵀΓ) x = Aᵀ b (对称正定,用 ConjugateGradient)
//
// 输出:x(按 rowsX 索引的颜色调整向量),供 ApplyColorAdjustments 使用
// ----------------------------------------------------------------------------
void MeshTexture::GlobalSeamLeveling2(Eigen::Index* pRowsX,
Eigen::SparseMatrix<float>* pA,
Eigen::VectorXf* pb,
float lambda,
cv::Mat& atlasMat)
{
const size_t NP = rcPatches.size();
if (seamVerts.empty() || NP == 0) return;
// 构建 vertpatch -> row 映射(扁平数组)
m_gslSVPatchRow.assign(seamVerts.size() * NP, -1);
Eigen::Index row = 0;
for (size_t svi = 0; svi < seamVerts.size(); ++svi) {
const SeamVert& sv = seamVerts[svi];
for (uint32_t pid : sv.patchIDs) {
if (pid < NP) {
m_gslSVPatchRow[svi * NP + pid] = row++;
}
}
}
const Eigen::Index rowsX = row;
if (pRowsX) *pRowsX = rowsX;
// 构建稀疏矩阵 A 和向量 b
std::vector<Eigen::Triplet<float>> trips;
trips.reserve(rowsX * 4);
Eigen::VectorXf bb(rowsX * 3); // ★ 改名 b -> bb,避免和参数/成员冲突
bb.setZero();
// 数据项:接缝处相邻 patch 调整量一致
for (const SeamEdge& edge : rcSeamEdges) {
const uint32_t a = edge.rcPatchID0;
const uint32_t bp = edge.rcPatchID1; // ★ b_patch -> bp
if (a >= NP || bp >= NP || a == bp) continue;
for (size_t svi = 0; svi < seamVerts.size(); ++svi) {
const SeamVert& sv = seamVerts[svi];
bool hasA = false, hasBp = false;
for (uint32_t pid : sv.patchIDs) {
if (pid == a) hasA = true;
if (pid == bp) hasBp = true; // ★ pid == b -> pid == bp
}
if (hasA && hasBp) {
Eigen::Index rowA = m_gslSVPatchRow[svi * NP + a];
Eigen::Index rowB = m_gslSVPatchRow[svi * NP + bp]; // ★ bp
if (rowA < 0 || rowB < 0) continue;
for (int c = 0; c < 3; ++c) {
Eigen::Index rA = rowA * 3 + c;
Eigen::Index rB = rowB * 3 + c;
trips.emplace_back(rA, rA, 1.0f);
trips.emplace_back(rA, rB, -1.0f);
trips.emplace_back(rB, rB, 1.0f);
trips.emplace_back(rB, rA, -1.0f);
}
}
}
}
// 正则项:Tikhonov λ
for (Eigen::Index i = 0; i < rowsX * 3; ++i) {
trips.emplace_back(i, i, lambda);
}
Eigen::SparseMatrix<float> A(rowsX * 3, rowsX * 3);
A.setFromTriplets(trips.begin(), trips.end());
if (pA) *pA = A;
if (pb) *pb = bb; // ★ 赋给输出参数(不再是局部 b)
// CG 求解
Eigen::ConjugateGradient<Eigen::SparseMatrix<float>, Eigen::Lower|Eigen::Upper> solver;
solver.setTolerance(1e-6f);
solver.setMaxIterations(500);
solver.compute(A);
Eigen::VectorXf x = solver.solve(bb); // ★ bb
// 减均值
for (int c = 0; c < 3; ++c) {
x.segment(c, rowsX).array() -= x.segment(c, rowsX).mean();
}
m_gslSolution = x;
m_gslRowsX = rowsX;
DEBUG_EXTRA("GlobalSeamLeveling: %lld vars, %d iters, err=%.2e",
rowsX, solver.iterations(), solver.error());
}
// New
void MeshTexture::GlobalSeamLeveling()
@ -14792,6 +15034,42 @@ void MeshTexture::FeatherTextureSeams(Image8U3& texture, @@ -14792,6 +15034,42 @@ void MeshTexture::FeatherTextureSeams(Image8U3& texture,
}
}
}
void MeshTexture::BuildSeamEdgesFromFaceToPatchID() {
rcSeamEdges.clear();
const size_t numFaces = scene.mesh.faces.size();
for (FIndex fid = 0; fid < (FIndex)numFaces; ++fid) {
if (fid >= faceToPatchID.size()) continue;
const uint32_t pidA = faceToPatchID[fid];
if (pidA == UINT32_MAX) continue;
if (fid >= faceFaces.size()) continue;
const Mesh::FaceFaces& adj = faceFaces[fid];
for (int e = 0; e < 3; ++e) {
const FIndex adjFid = adj[e];
if (adjFid == NO_ID || adjFid <= fid) continue;
if (adjFid >= faceToPatchID.size()) continue;
const uint32_t pidB = faceToPatchID[adjFid];
if (pidB == UINT32_MAX || pidB == pidA) continue;
// 避免重复添加
bool exists = false;
for (const SeamEdge& se : rcSeamEdges) {
if ((se.rcPatchID0 == pidA && se.rcPatchID1 == pidB) ||
(se.rcPatchID0 == pidB && se.rcPatchID1 == pidA)) {
exists = true; break;
}
}
if (!exists) {
SeamEdge edge;
edge.rcPatchID0 = pidA;
edge.rcPatchID1 = pidB;
// uv0/uv1 留空,让 CG 用 patchAvgColor fallback
rcSeamEdges.push_back(edge);
}
}
}
DEBUG_EXTRA("BuildSeamEdgesFromFaceToPatchID: %zu edges", rcSeamEdges.size());
}
void MeshTexture::MergeSameViewPatches()
{
const size_t numPatches = rcPatches.size();
@ -15199,99 +15477,397 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -15199,99 +15477,397 @@ bool MeshTexture::RasterizeVirtualFaces(
// FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3);
return true;
}
bool MeshTexture::BuildSeamVertsFromEdges()
{
const size_t NP = rcPatches.size();
if (NP == 0 || rcSeamEdges.empty()) return false;
// 确保 faceFaces 已计算
if (scene.mesh.faceFaces.empty()) {
scene.mesh.ListIncidenteFaceFaces();
}
seamVerts.clear();
vertToSeamVert.assign(scene.mesh.vertices.size(), UINT32_MAX);
for (const SeamEdge& edge : rcSeamEdges) {
const uint32_t a = edge.rcPatchID0;
const uint32_t b = edge.rcPatchID1;
if (a >= NP || b >= NP || a == b) continue;
for (size_t fi = 0; fi < scene.mesh.faces.size(); ++fi) {
const uint32_t pid = faceToPatchID[fi];
if (pid != a && pid != b) continue;
const Mesh::Face& face = scene.mesh.faces[fi];
for (int v = 0; v < 3; ++v) {
VIndex vtx = face[v];
bool inA = false, inB = false;
// ★ 修正:用索引循环访问邻接面(兼容数组/vector)
const Mesh::FaceFaces& adj = scene.mesh.faceFaces[fi];
for (int e = 0; e < 3; ++e) { // 3 条边
const FIndex adjFi = adj[e]; // ★ 索引访问
if (adjFi == NO_ID || adjFi >= (FIndex)scene.mesh.faces.size()) continue;
const uint32_t adjPid = faceToPatchID[adjFi];
if (adjPid == a) inA = true;
if (adjPid == b) inB = true;
}
// 当前面本身
if (pid == a) inA = true;
if (pid == b) inB = true;
if (!inA || !inB) continue;
uint32_t svIdx = vertToSeamVert[vtx];
if (svIdx == UINT32_MAX) {
svIdx = (uint32_t)seamVerts.size();
seamVerts.emplace_back();
seamVerts.back().idxVertex = vtx;
seamVerts.back().patchIDs.push_back(a);
seamVerts.back().patchIDs.push_back(b);
vertToSeamVert[vtx] = svIdx;
} else {
auto& sv = seamVerts[svIdx];
bool hasA = false, hasB = false;
for (uint32_t p : sv.patchIDs) {
if (p == a) hasA = true;
if (p == b) hasB = true;
}
if (!hasA) sv.patchIDs.push_back(a);
if (!hasB) sv.patchIDs.push_back(b);
}
}
}
}
seamDataBuilt = true;
DEBUG_EXTRA("BuildSeamVertsFromEdges: %zu seam vertices from %zu edges",
seamVerts.size(), rcSeamEdges.size());
return !seamVerts.empty();
}
void MeshTexture::GlobalAlignPatches(Image8U3& atlas, int textureSize) {
const size_t NP = rcPatches.size();
if (NP == 0 || rcSeamEdges.empty()) return;
if (NP == 0) return;
// 每个 patch 有一个颜色增益 g_i,初始为 1.0
std::vector<float> gain(NP, 1.0f);
// 用 patch 的平均颜色作为参考
std::vector<cv::Vec3f> avgColor(NP, cv::Vec3f(0,0,0));
cv::Mat atlasMat(atlas.rows, atlas.cols, CV_8UC3, atlas.data);
// ========== 第一步:对每个 patch,收集接缝边处的实际像素颜色 ==========
// seamColorSum[i] = patch i 在所有接缝边处的像素颜色累加
// seamColorCount[i] = 累加次数
std::vector<cv::Vec3d> seamColorSum(NP, cv::Vec3d(0,0,0));
std::vector<int> seamColorCount(NP, 0);
const size_t numFaces = scene.mesh.faces.size();
// 遍历面邻接,找跨 patch 的共享边
for (FIndex fid = 0; fid < (FIndex)numFaces; ++fid) {
if (fid >= faceToPatchID.size()) continue;
const uint32_t pidA = faceToPatchID[fid];
if (pidA == UINT32_MAX || pidA >= NP) continue;
if (fid >= faceFaces.size()) continue;
const Mesh::FaceFaces& adj = faceFaces[fid];
for (int e = 0; e < 3; ++e) {
const FIndex adjFid = adj[e];
if (adjFid == NO_ID || adjFid <= fid) continue; // 每条边只处理一次
if (adjFid >= faceToPatchID.size()) continue;
const uint32_t pidB = faceToPatchID[adjFid];
if (pidB == UINT32_MAX || pidB >= NP) continue;
if (pidA == pidB) continue; // 同一 patch,跳过
// 找到共享边的两个端点
const Mesh::Face& faceA = scene.mesh.faces[fid];
const VIndex v0 = faceA[e];
const VIndex v1 = faceA[(e + 1) % 3];
// 在 patchA 的 face 上找这条边的 UV
Point2f uvA0, uvA1;
bool foundA = false;
for (FIndex f : rcPatches[pidA].faces) {
if (f >= numFaces) continue;
const Mesh::Face& ff = scene.mesh.faces[f];
for (int ee = 0; ee < 3; ++ee) {
if ((ff[ee] == v0 && ff[(ee+1)%3] == v1) ||
(ff[ee] == v1 && ff[(ee+1)%3] == v0)) {
uvA0 = Point2f(scene.mesh.faceTexcoords[f * 3 + ee].x * textureSize,
scene.mesh.faceTexcoords[f * 3 + ee].y * textureSize);
uvA1 = Point2f(scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].x * textureSize,
scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].y * textureSize);
foundA = true;
break;
}
}
if (foundA) break;
}
// 在 patchB 的 face 上找这条边的 UV
Point2f uvB0, uvB1;
bool foundB = false;
for (FIndex f : rcPatches[pidB].faces) {
if (f >= numFaces) continue;
const Mesh::Face& ff = scene.mesh.faces[f];
for (int ee = 0; ee < 3; ++ee) {
if ((ff[ee] == v0 && ff[(ee+1)%3] == v1) ||
(ff[ee] == v1 && ff[(ee+1)%3] == v0)) {
uvB0 = Point2f(scene.mesh.faceTexcoords[f * 3 + ee].x * textureSize,
scene.mesh.faceTexcoords[f * 3 + ee].y * textureSize);
uvB1 = Point2f(scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].x * textureSize,
scene.mesh.faceTexcoords[f * 3 + (ee+1)%3].y * textureSize);
foundB = true;
break;
}
}
if (foundB) break;
}
if (!foundA || !foundB) continue;
// 沿边采样(5 个采样点)
const int nSamples = 5;
for (int s = 1; s < nSamples; ++s) {
const float t = (float)s / nSamples;
const int xA = (int)(uvA0.x + (uvA1.x - uvA0.x) * t + 0.5f);
const int yA = (int)(uvA0.y + (uvA1.y - uvA0.y) * t + 0.5f);
const int xB = (int)(uvB0.x + (uvB1.x - uvB0.x) * t + 0.5f);
const int yB = (int)(uvB0.y + (uvB1.y - uvB0.y) * t + 0.5f);
if (xA < 0 || yA < 0 || xA >= atlasMat.cols || yA >= atlasMat.rows) continue;
if (xB < 0 || yB < 0 || xB >= atlasMat.cols || yB >= atlasMat.rows) continue;
const cv::Vec3b& cA = atlasMat.at<cv::Vec3b>(yA, xA);
const cv::Vec3b& cB = atlasMat.at<cv::Vec3b>(yB, xB);
seamColorSum[pidA] += cv::Vec3d(cA[0], cA[1], cA[2]);
seamColorSum[pidB] += cv::Vec3d(cB[0], cB[1], cB[2]);
seamColorCount[pidA]++;
seamColorCount[pidB]++;
}
}
}
// 算每个 patch 的接缝处平均颜色
std::vector<cv::Vec3f> seamAvgColor(NP, cv::Vec3f(0,0,0));
for (size_t i = 0; i < NP; ++i) {
if (patchAvgColor.size() > i) {
avgColor[i] = patchAvgColor[i]; // 你之前算过的 patchAvgColor
if (seamColorCount[i] > 0) {
seamAvgColor[i] = cv::Vec3f(
seamColorSum[i][0] / seamColorCount[i],
seamColorSum[i][1] / seamColorCount[i],
seamColorSum[i][2] / seamColorCount[i]
);
} else {
// 没有接缝边的 patch,回退到 patchAvgColor
if (i < patchAvgColor.size()) {
const Color& c = patchAvgColor[i];
seamAvgColor[i] = cv::Vec3f(c.x, c.y, c.z);
}
}
}
// 简单迭代求解:让相邻 patch 在接缝处颜色一致
for (int iter = 0; iter < 10; ++iter) {
// ========== 第二步:迭代求解增益 ==========
std::vector<float> gain(NP, 1.0f);
for (int iter = 0; iter < 20; ++iter) {
std::vector<float> newGain(NP, 0.0f);
std::vector<int> count(NP, 0);
// 用 rcSeamEdges 做邻接传播
for (const SeamEdge& edge : rcSeamEdges) {
const uint32_t a = edge.rcPatchID0;
const uint32_t b = edge.rcPatchID1;
if (a >= NP || b >= NP) continue;
if (a == b) continue;
// 目标:gain[a] * avgColor[a] ≈ gain[b] * avgColor[b]
// 用 avgColor 的比值更新
const cv::Vec3f& ca = avgColor[a];
const cv::Vec3f& cb = avgColor[b];
if (a >= NP || b >= NP || a == b) continue;
const cv::Vec3f& ca = seamAvgColor[a];
const cv::Vec3f& cb = seamAvgColor[b];
for (int c = 0; c < 3; ++c) {
if (ca[c] > 1e-6f && cb[c] > 1e-6f) {
if (ca[c] > 1.0f) { // 避免极暗区域
const float ratio = cb[c] / ca[c];
newGain[a] += ratio * gain[b];
const float clamped = std::clamp(ratio, 0.5f, 2.0f);
newGain[a] += clamped * gain[b];
count[a]++;
}
}
}
for (size_t i = 0; i < NP; ++i) {
if (count[i] > 0) {
gain[i] = newGain[i] / count[i];
gain[i] = std::clamp(gain[i], 0.5f, 2.0f);
}
}
}
// 应用增益到 atlas:遍历每个 patch 的像素,乘上 gain
// ========== 第三步:应用增益到 atlas ==========
for (size_t i = 0; i < NP; ++i) {
const cv::Rect& r = rcPatches[i].rect;
if (r.x < 0 || r.y < 0 || r.x + r.width > atlas.cols || r.y + r.height > atlas.rows)
if (r.x < 0 || r.y < 0 || r.x + r.width > atlasMat.cols || r.y + r.height > atlasMat.rows)
continue;
cv::Mat patch = atlas(r);
const float g = std::clamp(gain[i], 0.5f, 2.0f);
cv::Mat patch = atlasMat(r);
const float g = gain[i];
patch.convertTo(patch, CV_32FC3);
patch *= g;
patch.convertTo(patch, CV_8UC3);
}
DEBUG_EXTRA("GlobalAlignPatches: %zu patches aligned", NP);
DEBUG_EXTRA("GlobalAlignPatches: %zu patches aligned (seam-based, %d iters)", NP, 20);
}
void MeshTexture::LocalBlendSeams(Image8U3& atlas, int textureSize) {
// ★ 包装为 cv::Mat
cv::Mat atlasMat(atlas.cols, atlas.rows, CV_8UC3, atlas.data);
cv::Mat atlasMat(atlas.rows, atlas.cols, CV_8UC3, atlas.data);
const size_t NP = rcPatches.size();
const size_t numFaces = scene.mesh.faces.size();
if (NP == 0 || faceToPatchID.size() != numFaces) {
DEBUG_EXTRA("LocalBlendSeams: invalid state (faceToPatchID.size=%zu, numFaces=%zu)",
faceToPatchID.size(), numFaces);
return;
}
const int blendWidth = 4;
// ---- 1. 构建邻接面 → patch 的映射(已有 faceToPatchID,这里直接用)----
// faceToPatchID[faceID] = patchID
for (const SeamEdge& edge : rcSeamEdges) {
const uint32_t a = edge.rcPatchID0;
const uint32_t b = edge.rcPatchID1;
if (a >= rcPatches.size() || b >= rcPatches.size()) continue;
const cv::Rect& ra = rcPatches[a].rect;
const cv::Rect& rb = rcPatches[b].rect;
cv::Rect overlap = ra & rb;
if (overlap.area() == 0) continue;
// 在重叠区域做线性混合
for (int y = 0; y < overlap.height; ++y) {
for (int x = 0; x < overlap.width; ++x) {
const float t = (float)x / overlap.width;
const cv::Vec3b& ca = atlasMat.at<cv::Vec3b>(ra.y + y, ra.x + x);
const cv::Vec3b& cb = atlasMat.at<cv::Vec3b>(rb.y + y, rb.x + x);
atlasMat.at<cv::Vec3b>(overlap.y + y, overlap.x + x) = cv::Vec3b(
(uint8_t)(ca[0] * (1 - t) + cb[0] * t),
(uint8_t)(ca[1] * (1 - t) + cb[1] * t),
(uint8_t)(ca[2] * (1 - t) + cb[2] * t)
);
// ---- 2. 遍历所有 face,找跨 patch 的共享边 ----
// 对于每条共享边,记录:端点顶点、两侧 patch ID
struct SeamEdgeInfo {
VIndex v0, v1; // 共享边的两个端点
uint32_t patchA, patchB;
};
std::vector<SeamEdgeInfo> seamEdges;
// 用 faceFaces 遍历邻接关系(每条边只处理一次)
for (FIndex fid = 0; fid < (FIndex)numFaces; ++fid) {
const uint32_t pidA = faceToPatchID[fid];
if (pidA == UINT32_MAX || pidA >= NP) continue;
if (fid >= faceFaces.size()) continue;
const Mesh::FaceFaces& adj = faceFaces[fid]; // [3] 邻接面
for (int e = 0; e < 3; ++e) {
const FIndex adjFid = adj[e];
if (adjFid == NO_ID || adjFid <= fid) continue; // 只处理一次(无向边)
const uint32_t pidB = faceToPatchID[adjFid];
if (pidB == UINT32_MAX || pidB >= NP) continue;
if (pidA == pidB) continue; // 同一 patch,不是接缝
// 找到共享边:face fid 的第 e 条边,与 face adjFid 共享两个顶点
const Mesh::Face& faceA = scene.mesh.faces[fid];
const Mesh::Face& faceB = scene.mesh.faces[adjFid];
// faceA 的第 e 条边的两个端点
const VIndex v0 = faceA[e];
const VIndex v1 = faceA[(e + 1) % 3];
seamEdges.push_back({v0, v1, pidA, pidB});
}
}
DEBUG_EXTRA("LocalBlendSeams: found %zu 3D seam edges", seamEdges.size());
// ---- 3. 对每条接缝边,在 atlas 上做逐像素混合 ----
int blendedPixels = 0;
const int blendWidth = 3; // 混合带宽(像素),可调整
for (const SeamEdgeInfo& edge : seamEdges) {
// 取边两端点在 atlas 上的 UV(用 patchA 的 face 里的 UV)
// 需要找到该边在 patchA 的某个 face 上对应的两个 UV 坐标
// 简化:直接用两个端点的 UV(端点在所有包含它的 face 上的 UV 应该一致)
// 找一个包含 v0、v1 且属于 patchA 的 face
Point2f uvA0, uvA1, uvB0, uvB1;
bool foundA = false, foundB = false;
// 在 patchA 的 faces 里找包含 v0、v1 的边
for (FIndex fid : rcPatches[edge.patchA].faces) {
if (fid >= numFaces) continue;
const Mesh::Face& face = scene.mesh.faces[fid];
for (int e = 0; e < 3; ++e) {
if ((face[e] == edge.v0 && face[(e+1)%3] == edge.v1) ||
(face[e] == edge.v1 && face[(e+1)%3] == edge.v0)) {
uvA0 = Point2f(scene.mesh.faceTexcoords[fid * 3 + e].x * textureSize,
scene.mesh.faceTexcoords[fid * 3 + e].y * textureSize);
uvA1 = Point2f(scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].x * textureSize,
scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].y * textureSize);
foundA = true;
break;
}
}
if (foundA) break;
}
for (FIndex fid : rcPatches[edge.patchB].faces) {
if (fid >= numFaces) continue;
const Mesh::Face& face = scene.mesh.faces[fid];
for (int e = 0; e < 3; ++e) {
if ((face[e] == edge.v0 && face[(e+1)%3] == edge.v1) ||
(face[e] == edge.v1 && face[(e+1)%3] == edge.v0)) {
uvB0 = Point2f(scene.mesh.faceTexcoords[fid * 3 + e].x * textureSize,
scene.mesh.faceTexcoords[fid * 3 + e].y * textureSize);
uvB1 = Point2f(scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].x * textureSize,
scene.mesh.faceTexcoords[fid * 3 + (e+1)%3].y * textureSize);
foundB = true;
break;
}
}
if (foundB) break;
}
if (!foundA || !foundB) continue;
// 在 atlas 上画一条从 (uvA0,uvA1) 到 (uvB0,uvB1) 的"四边形带"并混合
// 简化:沿边方向做线性插值混合
const int numSteps = (int)std::sqrt((uvA1.x - uvA0.x)*(uvA1.x - uvA0.x) +
(uvA1.y - uvA0.y)*(uvA1.y - uvA0.y)) + 1;
const int steps = std::max(numSteps, 2);
for (int s = 0; s <= steps; ++s) {
const float t = (float)s / steps;
// 边上的点在 patchA 侧的位置
const Point2f ptA(uvA0.x + (uvA1.x - uvA0.x) * t,
uvA0.y + (uvA1.y - uvA0.y) * t);
// 对应 patchB 侧的位置(同一 3D 边,不同 UV 参数化)
const Point2f ptB(uvB0.x + (uvB1.x - uvB0.x) * t,
uvB0.y + (uvB1.y - uvB0.y) * t);
// 在 ptA 和 ptB 周围 blendWidth 像素内做混合
for (int dy = -blendWidth; dy <= blendWidth; ++dy) {
for (int dx = -blendWidth; dx <= blendWidth; ++dx) {
const int xA = (int)ptA.x + dx;
const int yA = (int)ptA.y + dy;
const int xB = (int)ptB.x + dx;
const int yB = (int)ptB.y + dy;
if (xA < 0 || yA < 0 || xA >= atlasMat.cols || yA >= atlasMat.rows) continue;
if (xB < 0 || yB < 0 || xB >= atlasMat.cols || yB >= atlasMat.rows) continue;
// 距离边的权重(越近混合越强)
const float w = 1.0f - (float)(dx*dx + dy*dy) / ((blendWidth+1)*(blendWidth+1));
if (w <= 0) continue;
const cv::Vec3b& cA = atlasMat.at<cv::Vec3b>(yA, xA);
const cv::Vec3b& cB = atlasMat.at<cv::Vec3b>(yB, xB);
atlasMat.at<cv::Vec3b>(yA, xA) = cv::Vec3b(
(uint8_t)(cA[0] * (1 - w) + cB[0] * w + 0.5f),
(uint8_t)(cA[1] * (1 - w) + cB[1] * w + 0.5f),
(uint8_t)(cA[2] * (1 - w) + cB[2] * w + 0.5f)
);
blendedPixels++;
}
}
}
}
DEBUG_EXTRA("LocalBlendSeams: %zu edges blended", rcSeamEdges.size());
DEBUG_EXTRA("LocalBlendSeams: blended %d pixels along 3D seams", blendedPixels);
}
// ============================================================
// 把 rcPatches 体系转换为标准 texturePatches + seamVertices 体系
@ -15401,7 +15977,9 @@ void MeshTexture::BuildStandardSeamDataFromRCPatches( @@ -15401,7 +15977,9 @@ void MeshTexture::BuildStandardSeamDataFromRCPatches(
seamVertices.push_back(sv);
}
}
DEBUG_EXTRA("mapIdxPatch: %zu faces, unique patch IDs: %zu",
mapIdxPatch.size(),
std::unordered_set<uint32_t>(mapIdxPatch.begin(), mapIdxPatch.end()).size());
DEBUG_EXTRA("BuildStandardSeamData: %zu patches, %zu seamVertices, %zu edges",
NP, seamVertices.size(), rcSeamEdges.size());
}
@ -15546,69 +16124,6 @@ bool MeshTexture::EstimateGlobalPhotometricCorrection() @@ -15546,69 +16124,6 @@ bool MeshTexture::EstimateGlobalPhotometricCorrection()
return true;
}
// ============================================================
// 构建接缝边(基于 rcPatches + faceFaces)
// ============================================================
void MeshTexture::BuildSeamEdgesFromRCPatches()
{
rcSeamEdges.clear();
seamEdges.clear(); // ✅ 同时清空老的
// face → rcPatch 映射
std::vector<uint32_t> faceToRCPatch(scene.mesh.faces.size(), NO_ID);
for (uint32_t pi = 0; pi < rcPatches.size(); ++pi) {
for (FIndex f : rcPatches[pi].faces) {
if (f < (FIndex)faceToRCPatch.size())
faceToRCPatch[f] = pi;
}
}
for (FIndex f0 = 0; f0 < (FIndex)scene.mesh.faces.size(); ++f0) {
uint32_t p0 = faceToRCPatch[f0];
if (p0 == NO_ID) continue;
const Mesh::FaceFaces& neighbors = scene.mesh.faceFaces[f0];
for (int e = 0; e < 3; ++e) {
FIndex f1 = neighbors[e];
if (f1 == NO_ID) continue;
uint32_t p1 = faceToRCPatch[f1];
if (p1 == NO_ID || p1 == p0) continue;
const Mesh::Face& face0 = scene.mesh.faces[f0];
int v0_idx = e;
int v1_idx = (e + 1) % 3;
int idx0_in_f1 = -1, idx1_in_f1 = -1;
for (int k = 0; k < 3; ++k) {
if (scene.mesh.faces[f1][k] == face0[v0_idx]) idx0_in_f1 = k;
if (scene.mesh.faces[f1][k] == face0[v1_idx]) idx1_in_f1 = k;
}
if (idx0_in_f1 == -1 || idx1_in_f1 == -1)
continue;
const TexCoord& uv0_p0 = scene.mesh.faceTexcoords[f0 * 3 + v0_idx];
const TexCoord& uv1_p0 = scene.mesh.faceTexcoords[f0 * 3 + v1_idx];
// ✅ 存进你自己的 rcSeamEdges
SeamEdge edge;
edge.rcPatchID0 = p0;
edge.rcPatchID1 = p1;
edge.faceID0 = f0;
edge.faceID1 = f1;
edge.uv0 = uv0_p0;
edge.uv1 = uv1_p0;
rcSeamEdges.push_back(edge); // ✅ 改回 rcSeamEdges
// ✅ 同步往老的 seamEdges 里塞 PairIdx(供 SeamBlendingFromOriginalImages 用)
seamEdges.Insert(PairIdx(f0, f1)); // 或 seamEdges.emplace_back(f0, f1);
}
}
DEBUG_EXTRA("Built %zu RC seam edges, %zu legacy seam edges",
rcSeamEdges.size(), seamEdges.size());
}
// ============================================================
// 接缝融合:从原始图像采样 + YCrCb 羽化
// ============================================================
@ -20861,6 +21376,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -20861,6 +21376,7 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
return false;
texture.MergeSameViewPatches();
texture.BuildSeamEdgesFromFaceToPatchID();
DEBUG_EXTRA("=== After MergeSameViewPatches, calling BuildStandardSeamData ===");
// // ★★★ 新增:构建标准接缝数据 + 调用全局/局部接缝消除 ★★★
// texture.BuildStandardSeamDataFromRCPatches(
@ -20869,8 +21385,9 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -20869,8 +21385,9 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
// // 直接操作 atlas(不污染 images)
// texture.RunSeamLevelingOnAtlas(textures.back(), nTextureSizeMultiple);
texture.GlobalAlignPatches(textures.back(), nTextureSizeMultiple);
texture.LocalBlendSeams(textures.back(), nTextureSizeMultiple);
// texture.RunGlobalColorOptimization(textures.back(), nTextureSizeMultiple, 0.1f); // λ=0.1 对齐 OpenMVS
// texture.GlobalAlignPatches(textures.back(), nTextureSizeMultiple);
// texture.LocalBlendSeams(textures.back(), nTextureSizeMultiple);
mesh.texturesDiffuse = std::move(textures);
DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)",

Loading…
Cancel
Save