|
|
|
|
@ -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()); |
|
|
|
|
|