优化
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::TexCoordArr& existingTexcoords, // 添加已有UV参数
|
||||||
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
|
const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数
|
||||||
);
|
);
|
||||||
|
std::vector<uint32_t> faceToPatchID; // faceID → 新 patchID 映射
|
||||||
|
void MergeSameViewPatches();
|
||||||
|
void RunSeamLevelingOnAtlas(Image8U3& atlas, int textureSize);
|
||||||
bool RasterizeVirtualFaces(
|
bool RasterizeVirtualFaces(
|
||||||
const VirtualFaceMap& virtualFaceMap,
|
const VirtualFaceMap& virtualFaceMap,
|
||||||
const std::vector<std::vector<IIndex>>& virtualFaceViews,
|
const std::vector<std::vector<IIndex>>& virtualFaceViews,
|
||||||
@@ -768,6 +770,12 @@ public:
|
|||||||
unsigned nTextureSizeMultiple,
|
unsigned nTextureSizeMultiple,
|
||||||
Pixel8U colEmpty,
|
Pixel8U colEmpty,
|
||||||
Mesh::Image8U3Arr& outTextures);
|
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,
|
void FeatherTextureSeams(Image8U3& texture,
|
||||||
const std::vector<IIndex>& texelPatchID,
|
const std::vector<IIndex>& texelPatchID,
|
||||||
int textureSize,
|
int textureSize,
|
||||||
@@ -775,6 +783,7 @@ public:
|
|||||||
void AlignPatchColors(Image8U3& atlas,
|
void AlignPatchColors(Image8U3& atlas,
|
||||||
const std::vector<IIndex>& texelPatchID,
|
const std::vector<IIndex>& texelPatchID,
|
||||||
int textureSize);
|
int textureSize);
|
||||||
|
void SmoothAtlasPatchBoundaries(Image8U3& atlas, int textureSize);
|
||||||
void GlobalPatchColorAlignment(Image8U3& atlas, int textureSize);
|
void GlobalPatchColorAlignment(Image8U3& atlas, int textureSize);
|
||||||
void LocalSeamBlending(Image8U3& atlas, int textureSize);
|
void LocalSeamBlending(Image8U3& atlas, int textureSize);
|
||||||
std::vector<Color> patchAvgColor; // ← 直接声明 vector,不要加括号!
|
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());
|
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 颜色对齐 =====
|
// ===== 全局 Patch 颜色对齐 =====
|
||||||
// 在 seam edge 上采样两侧 patch 的颜色,求解每个 patch 的偏移量
|
// 在 seam edge 上采样两侧 patch 的颜色,求解每个 patch 的偏移量
|
||||||
void MeshTexture::GlobalPatchColorAlignment(Image8U3& atlas, int textureSize)
|
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 风格光栅化主函数(含接缝优化)
|
// 3. RC 风格光栅化主函数(含接缝优化)
|
||||||
// ============================================================
|
// ============================================================
|
||||||
@@ -14893,26 +15172,239 @@ bool MeshTexture::RasterizeVirtualFaces(
|
|||||||
AlignPatchColors(atlas, m_texelPatchID, textureSize);
|
AlignPatchColors(atlas, m_texelPatchID, textureSize);
|
||||||
}
|
}
|
||||||
|
|
||||||
// 5/6/7. 接缝
|
// // 5/6/7. 接缝
|
||||||
BuildSeamEdgesFromRCPatches();
|
// BuildSeamEdgesFromRCPatches();
|
||||||
if (!rcSeamEdges.empty()) {
|
// if (!rcSeamEdges.empty()) {
|
||||||
DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu",
|
// DEBUG_EXTRA("Before GlobalAlign: rcSeamEdges=%zu, patchAvgColor=%zu",
|
||||||
rcSeamEdges.size(), patchAvgColor.size());
|
// rcSeamEdges.size(), patchAvgColor.size());
|
||||||
GlobalPatchColorAlignment(atlas, textureSize);
|
// GlobalPatchColorAlignment(atlas, textureSize);
|
||||||
LocalSeamBlending(atlas, textureSize);
|
// LocalSeamBlending(atlas, textureSize);
|
||||||
}
|
// }
|
||||||
|
|
||||||
if (!seamEdges.empty()) {
|
// if (!seamEdges.empty()) {
|
||||||
TD_TIMER_START();
|
// TD_TIMER_START();
|
||||||
SeamBlendingFromOriginalImages(atlas);
|
// SeamBlendingFromOriginalImages(atlas);
|
||||||
DEBUG_EXTRA("Seam blending completed: %zu edges (%s)", seamEdges.size(), TD_TIMER_GET_FMT().c_str());
|
// 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);
|
// AlignPatchColors(atlas, m_texelPatchID, textureSize);
|
||||||
// FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3);
|
// FeatherTextureSeams(atlas, m_texelPatchID, textureSize, 3);
|
||||||
return true;
|
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 独立)
|
// 全局光度校正:估计每视图相对 gain (R/G/B 独立)
|
||||||
// 用点云中被多张图共同可见的点做灰度加权最小二乘,
|
// 用点云中被多张图共同可见的点做灰度加权最小二乘,
|
||||||
@@ -15849,7 +16341,7 @@ if (!g_avgColorsComputed) {
|
|||||||
|
|
||||||
// ========== 颜色一致性代价 ==========
|
// ========== 颜色一致性代价 ==========
|
||||||
{
|
{
|
||||||
const float lambda = 2.35f;
|
const float lambda = 0.0f;
|
||||||
// ========== 收集邻居颜色(修复版:1-ring + faceToView)==========
|
// ========== 收集邻居颜色(修复版:1-ring + faceToView)==========
|
||||||
std::vector<cv::Vec3f> neighborColors;
|
std::vector<cv::Vec3f> neighborColors;
|
||||||
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) {
|
if (!scene.mesh.faceFaces.empty() && faceID < scene.mesh.faceFaces.size()) {
|
||||||
@@ -15990,7 +16482,7 @@ if (!g_avgColorsComputed) {
|
|||||||
|
|
||||||
// ★ 简化:去掉颜色阻断,直接让评分说话
|
// ★ 简化:去掉颜色阻断,直接让评分说话
|
||||||
// 只要多数view的平均分不低于我太多(差 < 0.3),就切换
|
// 只要多数view的平均分不低于我太多(差 < 0.3),就切换
|
||||||
if (majorityAvgScore > currentScore - 0.3f) {
|
if (majorityAvgScore > currentScore - 0.5f) {
|
||||||
newFaceToView[fid] = majorityView;
|
newFaceToView[fid] = majorityView;
|
||||||
newFaceScores[fid] = majorityAvgScore;
|
newFaceScores[fid] = majorityAvgScore;
|
||||||
}
|
}
|
||||||
@@ -20358,8 +20850,8 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
|||||||
fRatioDataSmoothness, nIgnoreMaskLabel, views))
|
fRatioDataSmoothness, nIgnoreMaskLabel, views))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
|
|
||||||
texture.SmoothVirtualFaceViews(texture.faceViews, mesh.faceFaces, 6);
|
texture.SmoothVirtualFaceViews(texture.faceViews, mesh.faceFaces, 6);
|
||||||
|
|
||||||
// ✅ 4. RC 风格光栅化(Affine + Homography 混合)
|
// ✅ 4. RC 风格光栅化(Affine + Homography 混合)
|
||||||
Mesh::Image8U3Arr textures;
|
Mesh::Image8U3Arr textures;
|
||||||
if (!texture.RasterizeVirtualFaces(
|
if (!texture.RasterizeVirtualFaces(
|
||||||
@@ -20368,6 +20860,18 @@ bool Scene::TextureMesh(unsigned nResolutionLevel, unsigned nMinResolution, unsi
|
|||||||
nTextureSizeMultiple, colEmpty, textures))
|
nTextureSizeMultiple, colEmpty, textures))
|
||||||
return false;
|
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);
|
mesh.texturesDiffuse = std::move(textures);
|
||||||
DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)",
|
DEBUG_EXTRA("Existing UV texturing completed: %u faces (%s)",
|
||||||
mesh.faces.size(), TD_TIMER_GET_FMT().c_str());
|
mesh.faces.size(), TD_TIMER_GET_FMT().c_str());
|
||||||
|
|||||||
Reference in new issue
Block a user