优化
This commit is contained in:
1 parent
10accfce73
commit
9665d1226a
1 file changed
+521
-17
+521
-17
@@ -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:
|
||||
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:
|
||||
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)
|
||||
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,
|
||||
}
|
||||
}
|
||||
}
|
||||
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(
|
||||
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) {
|
||||
|
||||
// ========== 颜色一致性代价 ==========
|
||||
{
|
||||
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) {
|
||||
|
||||
// ★ 简化:去掉颜色阻断,直接让评分说话
|
||||
// 只要多数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
|
||||
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
|
||||
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());
|
||||
|
||||
Reference in new issue
Block a user