Browse Source

优化

ManualUV
hesuicong 3 weeks ago
parent
commit
9665d1226a
  1. 538
      libs/MVS/SceneTexture.cpp

538
libs/MVS/SceneTexture.cpp

@ -760,7 +760,9 @@ public: @@ -760,7 +760,9 @@ public:
const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
);
std::vector<uint32_t> faceToPatchID; // faceID → 新 patchID 映射
void MergeSameViewPatches();
void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize);
bool RasterizeVirtualFaces(
const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews,
@ -768,6 +770,12 @@ public: @@ -768,6 +770,12 @@ public:
unsigned nTextureSizeMultiple,
Pixel8U colEmpty,
Mesh::Image8U3Arr& outTextures);
void BuildStandardSeamDataFromRCPatches(
const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews,
int textureSize);
void GlobalAlignPatches(Image8U3& atlas, int textureSize);
void LocalBlendSeams(Image8U3& atlas, int textureSize);
void FeatherTextureSeams(Image8U3& texture,
const std::vector<IIndex>& texelPatchID,
int textureSize,
@ -775,6 +783,7 @@ public: @@ -775,6 +783,7 @@ public:
void AlignPatchColors(Image8U3& atlas,
const std::vector<IIndex>& texelPatchID,
int textureSize);
void SmoothAtlasPatchBoundaries(Image8U3& atlas, int textureSize);
void GlobalPatchColorAlignment(Image8U3& atlas, int textureSize);
void LocalSeamBlending(Image8U3& atlas, int textureSize);
std::vector<Color> patchAvgColor; // ← 直接声明 vector,不要加括号!
@ -14479,6 +14488,100 @@ void MeshTexture::LocalSeamBlending(Image8U3& atlas, int textureSize) @@ -14479,6 +14488,100 @@ void MeshTexture::LocalSeamBlending(Image8U3& atlas, int textureSize)
DEBUG_EXTRA("Local blending done (%s)", TD_TIMER_GET_FMT().c_str());
}
// ============================================================
// 直接在 atlas 上做 patch 边界颜色扩散
// 不依赖 seamEdges,只用 m_texelPatchID
// ============================================================
void MeshTexture::SmoothAtlasPatchBoundaries(Image8U3& atlas, int textureSize)
{
if (m_texelPatchID.empty()) return;
const int bandWidth = 16; // 边界混合带宽度
// 1. 标记所有 patch 边界像素(4-邻域内 m_texelPatchID 不同的像素)
std::vector<uint8_t> isBoundary(textureSize * textureSize, 0);
for (int y = 1; y < textureSize - 1; ++y) {
for (int x = 1; x < textureSize - 1; ++x) {
size_t idx = y * textureSize + x;
int pid = m_texelPatchID[idx];
if (pid == NO_ID) continue;
// 检查 4-邻域
int nb[4] = {
m_texelPatchID[(y-1)*textureSize + x],
m_texelPatchID[(y+1)*textureSize + x],
m_texelPatchID[y*textureSize + (x-1)],
m_texelPatchID[y*textureSize + (x+1)]
};
for (int n = 0; n < 4; ++n) {
if (nb[n] != NO_ID && nb[n] != pid) {
isBoundary[idx] = 1;
break;
}
}
}
}
// 2. 对每个边界像素,向邻居 patch 的颜色做距离加权混合
// 用多次迭代扩散,让颜色在边界两侧渐变
Image8U3 atlasNew = atlas.clone();
for (int iter = 0; iter < 3; ++iter) {
#pragma omp parallel for
for (int y = 1; y < textureSize - 1; ++y) {
for (int x = 1; x < textureSize - 1; ++x) {
size_t idx = y * textureSize + x;
if (!isBoundary[idx]) continue;
int pid = m_texelPatchID[idx];
if (pid == NO_ID) continue;
// 收集邻居中属于不同 patch 的像素颜色
int count = 0;
cv::Vec3f sumColor(0, 0, 0);
float weightSum = 0;
// 3x3 邻域
for (int dy = -1; dy <= 1; ++dy) {
for (int dx = -1; dx <= 1; ++dx) {
if (dx == 0 && dy == 0) continue;
int nx = x + dx, ny = y + dy;
size_t nidx = ny * textureSize + nx;
int npid = m_texelPatchID[nidx];
if (npid == NO_ID || npid == pid) continue;
const Pixel8U& npx = atlas(ny, nx);
if (npx[0] < 5 && npx[1] < 5 && npx[2] < 5) continue;
float w = 1.0f / (dx*dx + dy*dy + 1);
sumColor[0] += npx[2] * w;
sumColor[1] += npx[1] * w;
sumColor[2] += npx[0] * w;
weightSum += w;
count++;
}
}
if (count > 0 && weightSum > 0) {
// 混合比例:边界像素保留 70%,从邻居吸收 30%
const float blendFactor = 0.3f;
Pixel8U& px = atlasNew(y, x);
const Pixel8U& orig = atlas(y, x);
px[2] = cv::saturate_cast<uchar>(orig[2] * (1 - blendFactor) + (sumColor[0] / weightSum) * blendFactor);
px[1] = cv::saturate_cast<uchar>(orig[1] * (1 - blendFactor) + (sumColor[1] / weightSum) * blendFactor);
px[0] = cv::saturate_cast<uchar>(orig[0] * (1 - blendFactor) + (sumColor[2] / weightSum) * blendFactor);
}
}
}
atlas = atlasNew.clone();
}
DEBUG_EXTRA("SmoothAtlasPatchBoundaries: boundary blending done");
}
// ===== 全局 Patch 颜色对齐 =====
// 在 seam edge 上采样两侧 patch 的颜色,求解每个 patch 的偏移量
void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize)
@ -14689,6 +14792,182 @@ void MeshTexture::FeatherTextureSeams(Image8U3& texture, @@ -14689,6 +14792,182 @@ void MeshTexture::FeatherTextureSeams(Image8U3& texture,
}
}
}
void MeshTexture::MergeSameViewPatches()
{
const size_t numPatches = rcPatches.size();
if (numPatches == 0) return;
const size_t numFaces = scene.mesh.faces.size(); // ★ 提前定义
if (faceFaces.empty()) {
scene.mesh.ListIncidenteFaceFaces();
}
// ---- Union-Find ----
std::vector<size_t> parent(numPatches);
std::iota(parent.begin(), parent.end(), 0);
auto Find = [&](size_t x) -> size_t {
while (parent[x] != x) {
parent[x] = parent[parent[x]];
x = parent[x];
}
return x;
};
auto Union = [&](size_t a, size_t b) {
a = Find(a);
b = Find(b);
if (a != b) parent[b] = a;
};
// ---- 合并同 view 相邻 patch ----
for (size_t i = 0; i < numPatches; ++i) {
const RCPatch& patch = rcPatches[i];
if (patch.faces.empty()) continue;
const FIndex faceID = patch.faces[0];
if (faceID >= faceFaces.size()) continue;
const Mesh::FaceFaces& adjFaces = faceFaces[faceID];
for (int e = 0; e < 3; ++e) {
const FIndex adjFaceID = adjFaces[e];
if (adjFaceID == NO_ID || adjFaceID >= (FIndex)numPatches) continue;
if (rcPatches[adjFaceID].viewID == patch.viewID) {
Union(i, adjFaceID);
}
}
}
// ★ 构建旧 patch ID → 新 patch ID 的映射
std::vector<uint32_t> oldToNew(numPatches, UINT32_MAX);
uint32_t newIdx = 0;
for (size_t i = 0; i < numPatches; ++i) {
if (Find(i) == i) {
oldToNew[i] = newIdx++;
}
}
for (size_t i = 0; i < numPatches; ++i) {
oldToNew[i] = oldToNew[Find(i)];
}
// ---- 收集合并后的 groups,生成新 rcPatches ----
std::map<size_t, std::vector<size_t>> groups;
for (size_t i = 0; i < numPatches; ++i) {
groups[Find(i)].push_back(i);
}
std::vector<RCPatch> newPatches;
newPatches.reserve(groups.size());
for (const auto& [root, indices] : groups) {
RCPatch merged;
merged.viewID = rcPatches[root].viewID;
for (size_t idx : indices) {
merged.faces.insert(merged.faces.end(),
rcPatches[idx].faces.begin(),
rcPatches[idx].faces.end());
}
int minX = INT_MAX, minY = INT_MAX, maxX = 0, maxY = 0;
for (size_t idx : indices) {
const cv::Rect& r = rcPatches[idx].rect;
minX = std::min(minX, r.x);
minY = std::min(minY, r.y);
maxX = std::max(maxX, r.x + r.width);
maxY = std::max(maxY, r.y + r.height);
}
if (minX < maxX && minY < maxY) {
merged.rect = cv::Rect(minX, minY, maxX - minX, maxY - minY);
}
newPatches.push_back(std::move(merged));
}
const size_t newPatchCount = newPatches.size(); // ★ 保存,move 后就拿不到了
DEBUG_EXTRA("MergeSameViewPatches: %zu → %zu patches (%.1f%% reduction)",
numPatches, newPatchCount,
100.0 * (1.0 - (double)newPatchCount / numPatches));
// ★★★ 在 std::move 之前,用 newPatches 构建 faceToPatchID ★★★
faceToPatchID.assign(numFaces, UINT32_MAX);
for (uint32_t newPatchID = 0; newPatchID < (uint32_t)newPatchCount; ++newPatchID) {
for (FIndex faceID : newPatches[newPatchID].faces) {
if (faceID < numFaces)
faceToPatchID[faceID] = newPatchID;
}
}
DEBUG_EXTRA("faceToPatchID built: %zu faces mapped", numFaces);
// ★★★ m_texelPatchID 重映射(用 oldToNew,不依赖 newPatches)★★★
if (!m_texelPatchID.empty()) {
for (uint32_t& pid : m_texelPatchID) {
if (pid != NO_ID && pid < (uint32_t)numPatches) {
pid = oldToNew[pid];
}
}
}
// ★★★ 现在才 move ★★★
rcPatches = std::move(newPatches);
// 从此以后 newPatches 是空的,不要再用了
// ★ 更新 rcSeamEdges(用 oldToNew + rcPatches.size())
if (!rcSeamEdges.empty()) {
for (SeamEdge& edge : rcSeamEdges) {
if (edge.rcPatchID0 != UINT32_MAX && edge.rcPatchID0 < (uint32_t)numPatches)
edge.rcPatchID0 = oldToNew[edge.rcPatchID0];
if (edge.rcPatchID1 != UINT32_MAX && edge.rcPatchID1 < (uint32_t)numPatches)
edge.rcPatchID1 = oldToNew[edge.rcPatchID1];
}
rcSeamEdges.erase(
std::remove_if(rcSeamEdges.begin(), rcSeamEdges.end(),
[](const SeamEdge& e) { return e.rcPatchID0 == e.rcPatchID1; }),
rcSeamEdges.end()
);
DEBUG_EXTRA("rcSeamEdges remapped: %zu edges remaining", rcSeamEdges.size());
}
// ★ 调试断言
#ifdef _DEBUG
const size_t patchCount = rcPatches.size();
for (const SeamEdge& e : rcSeamEdges) {
ASSERT(e.rcPatchID0 < patchCount);
ASSERT(e.rcPatchID1 < patchCount);
}
for (uint32_t pid : m_texelPatchID) {
if (pid != UINT32_MAX) ASSERT(pid < patchCount);
}
#endif
DEBUG_EXTRA("m_texelPatchID remapped, rcPatches now has %zu patches", rcPatches.size());
}
void MeshTexture::RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize)
{
// 保存原始 label
std::vector<IIndex> savedLabels(texturePatches.size());
for (size_t i = 0; i < texturePatches.size(); ++i)
savedLabels[i] = texturePatches[i].label;
// 把 atlas 作为临时图像插入 images
const size_t atlasIdx = images.size();
Image atlasImg;
atlasImg.image = atlas; // cv::Mat 引用,不拷贝
images.push_back(atlasImg);
// 所有 patch 指向 atlas
for (size_t i = 0; i < texturePatches.size() - 1; ++i)
texturePatches[i].label = (IIndex)atlasIdx;
// 执行接缝消除(直接修改 atlas 像素)
GlobalSeamLeveling3();
LocalSeamLeveling3();
// 恢复
for (size_t i = 0; i < savedLabels.size(); ++i)
texturePatches[i].label = savedLabels[i];
images.pop_back();
}
// ============================================================
// 3. RC 风格光栅化主函数(含接缝优化)
// ============================================================
@ -14893,26 +15172,239 @@ bool MeshTexture::RasterizeVirtualFaces( @@ -14893,26 +15172,239 @@ bool MeshTexture::RasterizeVirtualFaces(
AlignPatchColors(atlas, m_texelPatchID, textureSize);
}
// 5/6/7. 接缝
BuildSeamEdgesFromRCPatches();
if (!rcSeamEdges.empty()) {
DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu",
rcSeamEdges.size(), patchAvgColor.size());
GlobalPatchColorAlignment(atlas, textureSize);
LocalSeamBlending(atlas, textureSize);
}
// // 5/6/7. 接缝
// BuildSeamEdgesFromRCPatches();
// if (!rcSeamEdges.empty()) {
// DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu",
// rcSeamEdges.size(), patchAvgColor.size());
// GlobalPatchColorAlignment(atlas, textureSize);
// LocalSeamBlending(atlas, textureSize);
// }
if (!seamEdges.empty()) {
TD_TIMER_START();
SeamBlendingFromOriginalImages(atlas);
DEBUG_EXTRA("Seam blending completed: %zu edges (%s)", seamEdges.size(), TD_TIMER_GET_FMT().c_str());
}
// if (!seamEdges.empty()) {
// TD_TIMER_START();
// SeamBlendingFromOriginalImages(atlas);
// DEBUG_EXTRA("Seam blending completed: %zu edges (%s)", seamEdges.size(), TD_TIMER_GET_FMT().c_str());
// }
// 在 rasterization 循环结束后、SmoothAtlasPatchBoundaries 之前加:
int validPixels = 0;
for (size_t i = 0; i < m_texelPatchID.size(); ++i)
if (m_texelPatchID[i] != NO_ID) validPixels++;
DEBUG_EXTRA("m_texelPatchID: %d valid pixels out of %zu", validPixels, m_texelPatchID.size());
// SmoothAtlasPatchBoundaries(atlas, textureSize);
// AlignPatchColors(atlas, m_texelPatchID, textureSize);
// FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3);
return true;
}
void MeshTexture::GlobalAlignPatches(Image8U3& atlas, int textureSize) {
const size_t NP = rcPatches.size();
if (NP == 0 || rcSeamEdges.empty()) 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));
for (size_t i = 0; i < NP; ++i) {
if (patchAvgColor.size() > i) {
avgColor[i] = patchAvgColor[i]; // 你之前算过的 patchAvgColor
}
}
// 简单迭代求解:让相邻 patch 在接缝处颜色一致
for (int iter = 0; iter < 10; ++iter) {
std::vector<float> newGain(NP, 0.0f);
std::vector<int> count(NP, 0);
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];
for (int c = 0; c < 3; ++c) {
if (ca[c] > 1e-6f && cb[c] > 1e-6f) {
const float ratio = cb[c] / ca[c];
newGain[a] += ratio * gain[b];
count[a]++;
}
}
}
for (size_t i = 0; i < NP; ++i) {
if (count[i] > 0) {
gain[i] = newGain[i] / count[i];
}
}
}
// 应用增益到 atlas:遍历每个 patch 的像素,乘上 gain
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)
continue;
cv::Mat patch = atlas(r);
const float g = std::clamp(gain[i], 0.5f, 2.0f);
patch.convertTo(patch, CV_32FC3);
patch *= g;
patch.convertTo(patch, CV_8UC3);
}
DEBUG_EXTRA("GlobalAlignPatches: %zu patches aligned", NP);
}
void MeshTexture::LocalBlendSeams(Image8U3& atlas, int textureSize) {
// ★ 包装为 cv::Mat
cv::Mat atlasMat(atlas.cols, atlas.rows, CV_8UC3, atlas.data);
const int blendWidth = 4;
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)
);
}
}
}
DEBUG_EXTRA("LocalBlendSeams: %zu edges blended", rcSeamEdges.size());
}
// ============================================================
// 把 rcPatches 体系转换为标准 texturePatches + seamVertices 体系
// 必须在 RasterizeVirtualFaces() 完成后调用
// ============================================================
void MeshTexture::BuildStandardSeamDataFromRCPatches(
const VirtualFaceMap& virtualFaceMap,
const std::vector<std::vector<IIndex>>& virtualFaceViews,
int textureSize)
{
const size_t numFaces = scene.mesh.faces.size();
const size_t NP = rcPatches.size();
// ---- 1. texturePatches(最后一个是哨兵)----
texturePatches.clear();
texturePatches.resize(NP + 1);
for (size_t i = 0; i < NP; ++i) {
TexturePatch& tp = texturePatches[i];
tp.label = rcPatches[i].viewID;
tp.rect = rcPatches[i].rect;
// ★ 用 Insert 逐个拷贝,避免 cList 赋值问题
tp.faces.clear();
for (FIndex fid : rcPatches[i].faces) {
tp.faces.Insert(fid);
}
}
// 哨兵 patch(最后一个)
texturePatches[NP].label = NO_ID;
// ---- 2. mapIdxPatch:每个 face → 所属 patch ----
// ★ 用 faceToPatchID(Merge 时已构建好),不要重新扫描
mapIdxPatch.resize(numFaces);
for (size_t fid = 0; fid < numFaces; ++fid) {
mapIdxPatch[fid] = (faceToPatchID[fid] != UINT32_MAX)
? faceToPatchID[fid]
: (uint32_t)NP; // 未映射 → 指向哨兵
}
// ---- 3. components:与 mapIdxPatch 一致 ----
components.resize(numFaces);
for (size_t fid = 0; fid < numFaces; ++fid) {
components[fid] = mapIdxPatch[fid]; // ★ 不用 NO_ID,直接用 patch ID
}
// ---- 4. seamVertices:从 rcSeamEdges 构建(已有正确的 patch ID)----
seamVertices.clear();
if (!rcSeamEdges.empty()) {
// 按顶点收集相邻 patch(通过网格顶点 → face → patch)
std::vector<std::vector<uint32_t>> vertexPatches(scene.mesh.vertices.size());
for (size_t pid = 0; pid < NP; ++pid) {
for (FIndex fid : rcPatches[pid].faces) {
if (fid >= scene.mesh.faces.size()) continue;
const Mesh::Face& face = scene.mesh.faces[fid];
for (int v = 0; v < 3; ++v) {
vertexPatches[face[v]].push_back((uint32_t)pid);
}
}
}
// 去重
for (auto& vp : vertexPatches) {
std::sort(vp.begin(), vp.end());
vp.erase(std::unique(vp.begin(), vp.end()), vp.end());
}
for (size_t vid = 0; vid < vertexPatches.size(); ++vid) {
if (vertexPatches[vid].size() < 2) continue;
SeamVertex sv;
sv.idxVertex = (VIndex)vid;
for (uint32_t pid : vertexPatches[vid]) {
if (pid >= texturePatches.size()) continue;
const TexturePatch& tp = texturePatches[pid];
if (tp.faces.empty()) continue;
// 找该顶点在 patch 中某个 face 上的 UV
TexCoord uv(0, 0);
bool found = false;
for (FIndex fid : tp.faces) {
if (fid >= scene.mesh.faces.size()) continue;
const Mesh::Face& face = scene.mesh.faces[fid];
for (int v = 0; v < 3; ++v) {
if (face[v] == (VIndex)vid) {
uv = scene.mesh.faceTexcoords[fid * 3 + v];
found = true;
break;
}
}
if (found) break;
}
if (!found) continue;
// ★ 防御:检查 UV 有效性
if (!(uv.x >= 0 && uv.x <= 1 && uv.y >= 0 && uv.y <= 1)) {
DEBUG_EXTRA("Invalid UV at vid=%zu fid=%u: (%f, %f)", vid,
tp.faces[0], uv.x, uv.y);
continue;
}
SeamVertex::Patch sp;
sp.idxPatch = pid;
sp.proj = Point2f(uv.x * (float)textureSize, uv.y * (float)textureSize);
sv.patches.Insert(sp);
}
if (sv.patches.size() >= 2)
seamVertices.push_back(sv);
}
}
DEBUG_EXTRA("BuildStandardSeamData: %zu patches, %zu seamVertices, %zu edges",
NP, seamVertices.size(), rcSeamEdges.size());
}
// ============================================================
// 全局光度校正:估计每视图相对 gain (R/G/B 独立)
// 用点云中被多张图共同可见的点做灰度加权最小二乘,
@ -15849,7 +16341,7 @@ if (!g_avgColorsComputed) { @@ -15849,7 +16341,7 @@ if (!g_avgColorsComputed) {
// ========== 颜色一致性代价 ==========
{
const float lambda = 2.35f;
const float lambda = 0.0f;
// ========== 收集邻居颜色(修复版:1-ring + faceToView)==========
std::vector<cv::Vec3f> neighborColors;
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) {
@ -15990,7 +16482,7 @@ if (!g_avgColorsComputed) { @@ -15990,7 +16482,7 @@ if (!g_avgColorsComputed) {
// ★ 简化:去掉颜色阻断,直接让评分说话
// 只要多数view的平均分不低于我太多(差 < 0.3),就切换
if (majorityAvgScore > currentScore - 0.3f) {
if (majorityAvgScore > currentScore - 0.5f) {
newFaceToView[fid] = majorityView;
newFaceScores[fid] = majorityAvgScore;
}
@ -20358,8 +20850,8 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -20358,8 +20850,8 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
fRatioDataSmoothness, nIgnoreMaskLabel, views))
return false;
texture.SmoothVirtualFaceViews(texture.faceViews, mesh.faceFaces, 6);
// ✅ 4. RC 风格光栅化(Affine + Homography 混合)
Mesh::Image8U3Arr textures;
if (!texture.RasterizeVirtualFaces(
@ -20368,6 +20860,18 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi @@ -20368,6 +20860,18 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
nTextureSizeMultiple, colEmpty, textures))
return false;
texture.MergeSameViewPatches();
DEBUG_EXTRA("=== After MergeSameViewPatches, calling BuildStandardSeamData ===");
// // ★★★ 新增:构建标准接缝数据 + 调用全局/局部接缝消除 ★★★
// texture.BuildStandardSeamDataFromRCPatches(
// virtualFaceMap, texture.faceViews, nTextureSizeMultiple);
// // 直接操作 atlas(不污染 images)
// texture.RunSeamLevelingOnAtlas(textures.back(), nTextureSizeMultiple);
texture.GlobalAlignPatches(textures.back(), nTextureSizeMultiple);
texture.LocalBlendSeams(textures.back(), nTextureSizeMultiple);
mesh.texturesDiffuse = std::move(textures);
DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)",
mesh.faces.size(), TD_TIMER_GET_FMT().c_str());

Loading…
Cancel
Save