|
|
|
@ -8911,50 +8911,50 @@ void MeshTexture::CreateSeamVertices() |
|
|
|
DEBUG_EXTRA(" patch stats: single(patches<2)=%zu, multi(>=2)=%zu, maxPatches=%zu", |
|
|
|
DEBUG_EXTRA(" patch stats: single(patches<2)=%zu, multi(>=2)=%zu, maxPatches=%zu", |
|
|
|
svSingle, svMulti, svMaxPatches); |
|
|
|
svSingle, svMulti, svMaxPatches); |
|
|
|
|
|
|
|
|
|
|
|
// ★★★ 截断 seamVertices 到合理规模(原版 OpenMVS 通常 < 50000)★★★
|
|
|
|
// // ★★★ 截断 seamVertices 到合理规模(原版 OpenMVS 通常 < 50000)★★★
|
|
|
|
// 按 patches 数量排序,保留最重要的(patches 多的 = 色差严重的接缝)
|
|
|
|
// // 按 patches 数量排序,保留最重要的(patches 多的 = 色差严重的接缝)
|
|
|
|
const size_t MAX_SEAM_VERTICES = 40000; // ← 可调,先试 40000
|
|
|
|
// const size_t MAX_SEAM_VERTICES = 40000; // ← 可调,先试 40000
|
|
|
|
if (seamVertices.GetSize() > MAX_SEAM_VERTICES) { |
|
|
|
// if (seamVertices.GetSize() > MAX_SEAM_VERTICES) {
|
|
|
|
DEBUG_EXTRA(" Truncating seamVertices: %u -> %zu (keeping highest patch-count)", |
|
|
|
// DEBUG_EXTRA(" Truncating seamVertices: %u -> %zu (keeping highest patch-count)",
|
|
|
|
seamVertices.GetSize(), MAX_SEAM_VERTICES); |
|
|
|
// seamVertices.GetSize(), MAX_SEAM_VERTICES);
|
|
|
|
|
|
|
|
|
|
|
|
// 建 (索引, patch数量) 对,按 patch 数量降序
|
|
|
|
// // 建 (索引, patch数量) 对,按 patch 数量降序
|
|
|
|
std::vector<std::pair<uint32_t, size_t>> idxAndCount(seamVertices.GetSize()); |
|
|
|
// std::vector<std::pair<uint32_t, size_t>> idxAndCount(seamVertices.GetSize());
|
|
|
|
for (uint32_t i = 0; i < seamVertices.GetSize(); ++i) |
|
|
|
// for (uint32_t i = 0; i < seamVertices.GetSize(); ++i)
|
|
|
|
idxAndCount[i] = {i, seamVertices[i].patches.size()}; |
|
|
|
// idxAndCount[i] = {i, seamVertices[i].patches.size()};
|
|
|
|
std::sort(idxAndCount.begin(), idxAndCount.end(), |
|
|
|
// std::sort(idxAndCount.begin(), idxAndCount.end(),
|
|
|
|
[](const auto& a, const auto& b) { return a.second > b.second; }); |
|
|
|
// [](const auto& a, const auto& b) { return a.second > b.second; });
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 选前 MAX_SEAM_VERTICES 个索引,排序回原始顺序以保持稳定性
|
|
|
|
|
|
|
|
// std::vector<uint32_t> keep(idxAndCount.size());
|
|
|
|
|
|
|
|
// for (size_t i = 0; i < MAX_SEAM_VERTICES; ++i) keep[i] = idxAndCount[i].first;
|
|
|
|
|
|
|
|
// std::sort(keep.begin(), keep.begin() + MAX_SEAM_VERTICES);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// // 重建 seamVertices(只保留选中的)
|
|
|
|
|
|
|
|
// decltype(seamVertices) newSV;
|
|
|
|
|
|
|
|
// newSV.Reserve(MAX_SEAM_VERTICES);
|
|
|
|
|
|
|
|
// std::unordered_map<uint32_t, uint32_t> old2new; // 旧索引 → 新索引
|
|
|
|
|
|
|
|
// for (size_t i = 0; i < MAX_SEAM_VERTICES; ++i) {
|
|
|
|
|
|
|
|
// const uint32_t oldIdx = keep[i];
|
|
|
|
|
|
|
|
// old2new[oldIdx] = (uint32_t)newSV.GetSize();
|
|
|
|
|
|
|
|
// newSV.push_back(std::move(seamVertices[oldIdx]));
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// 选前 MAX_SEAM_VERTICES 个索引,排序回原始顺序以保持稳定性
|
|
|
|
// // ★★★ 关键:更新所有 Patch::edges 里的 idxSeamVertex 引用 ★★★
|
|
|
|
std::vector<uint32_t> keep(idxAndCount.size()); |
|
|
|
// for (uint32_t i = 0; i < newSV.GetSize(); ++i) {
|
|
|
|
for (size_t i = 0; i < MAX_SEAM_VERTICES; ++i) keep[i] = idxAndCount[i].first; |
|
|
|
// for (auto& patch : newSV[i].patches) {
|
|
|
|
std::sort(keep.begin(), keep.begin() + MAX_SEAM_VERTICES); |
|
|
|
// for (auto& edge : patch.edges) {
|
|
|
|
|
|
|
|
// auto it = old2new.find(edge.idxSeamVertex);
|
|
|
|
// 重建 seamVertices(只保留选中的)
|
|
|
|
// if (it != old2new.end())
|
|
|
|
decltype(seamVertices) newSV; |
|
|
|
// edge.idxSeamVertex = it->second;
|
|
|
|
newSV.Reserve(MAX_SEAM_VERTICES); |
|
|
|
// else
|
|
|
|
std::unordered_map<uint32_t, uint32_t> old2new; // 旧索引 → 新索引
|
|
|
|
// edge.idxSeamVertex = i; // 指向自己(会被忽略)
|
|
|
|
for (size_t i = 0; i < MAX_SEAM_VERTICES; ++i) { |
|
|
|
// }
|
|
|
|
const uint32_t oldIdx = keep[i]; |
|
|
|
// }
|
|
|
|
old2new[oldIdx] = (uint32_t)newSV.GetSize(); |
|
|
|
// }
|
|
|
|
newSV.push_back(std::move(seamVertices[oldIdx])); |
|
|
|
// seamVertices = std::move(newSV);
|
|
|
|
} |
|
|
|
// DEBUG_EXTRA(" Truncated seamVertices: %u final", seamVertices.GetSize());
|
|
|
|
|
|
|
|
// }
|
|
|
|
// ★★★ 关键:更新所有 Patch::edges 里的 idxSeamVertex 引用 ★★★
|
|
|
|
|
|
|
|
for (uint32_t i = 0; i < newSV.GetSize(); ++i) { |
|
|
|
|
|
|
|
for (auto& patch : newSV[i].patches) { |
|
|
|
|
|
|
|
for (auto& edge : patch.edges) { |
|
|
|
|
|
|
|
auto it = old2new.find(edge.idxSeamVertex); |
|
|
|
|
|
|
|
if (it != old2new.end()) |
|
|
|
|
|
|
|
edge.idxSeamVertex = it->second; |
|
|
|
|
|
|
|
else |
|
|
|
|
|
|
|
edge.idxSeamVertex = i; // 指向自己(会被忽略)
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
seamVertices = std::move(newSV); |
|
|
|
|
|
|
|
DEBUG_EXTRA(" Truncated seamVertices: %u final", seamVertices.GetSize()); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
// Native
|
|
|
|
// Native
|
|
|
|
@ -9462,7 +9462,8 @@ void MeshTexture::GlobalSeamLeveling4() |
|
|
|
const float r = x(v * 3 + 0); |
|
|
|
const float r = x(v * 3 + 0); |
|
|
|
const float g = x(v * 3 + 1); |
|
|
|
const float g = x(v * 3 + 1); |
|
|
|
const float b_ch = x(v * 3 + 2); |
|
|
|
const float b_ch = x(v * 3 + 2); |
|
|
|
const float mean = (r + g + b_ch) / 3.0f; // 去均值
|
|
|
|
const float mean = (r + g + b_ch) / 3.0f; |
|
|
|
|
|
|
|
// colorAdjustments[v] = Color(r, g, b_ch);
|
|
|
|
colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean); |
|
|
|
colorAdjustments[v] = Color(r - mean, g - mean, b_ch - mean); |
|
|
|
} |
|
|
|
} |
|
|
|
} else { |
|
|
|
} else { |
|
|
|
@ -9513,22 +9514,28 @@ void MeshTexture::GlobalSeamLeveling4() |
|
|
|
if (idxFace >= faces.GetSize()) continue; |
|
|
|
if (idxFace >= faces.GetSize()) continue; |
|
|
|
const Face& face = faces[idxFace]; |
|
|
|
const Face& face = faces[idxFace]; |
|
|
|
|
|
|
|
|
|
|
|
// ★ 检查 faceTexcoords 访问
|
|
|
|
|
|
|
|
ASSERT(idxFace * 3 + 2 < faceTexcoords.GetSize()); |
|
|
|
ASSERT(idxFace * 3 + 2 < faceTexcoords.GetSize()); |
|
|
|
|
|
|
|
|
|
|
|
// ★ 检查 face[v] 不越界
|
|
|
|
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
ASSERT(face[v] < vertices.size()); |
|
|
|
ASSERT(face[v] < vertices.size()); |
|
|
|
ASSERT(face[v] < vertpatch2rows.size()); |
|
|
|
ASSERT(face[v] < vertpatch2rows.size()); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
data.tri = faceTexcoords.Begin() + idxFace * 3; |
|
|
|
// ★ UV [0,1] → patch 局部像素坐标
|
|
|
|
|
|
|
|
const cv::Size patchSize = texturePatch.rect.size(); |
|
|
|
|
|
|
|
TexCoord localTri[3]; |
|
|
|
|
|
|
|
const TexCoord* uvTri = faceTexcoords.Begin() + idxFace * 3; |
|
|
|
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
|
|
|
|
localTri[v] = TexCoord(uvTri[v].x * patchSize.width, |
|
|
|
|
|
|
|
uvTri[v].y * patchSize.height); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
data.tri = localTri; |
|
|
|
|
|
|
|
|
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
for (int v = 0; v < 3; ++v) { |
|
|
|
ASSERT(face[v] < vertices.size()); // ★ 加这行
|
|
|
|
ASSERT(face[v] < vertices.size()); |
|
|
|
auto search = vertpatch2rows[face[v]].find(idxPatch); |
|
|
|
auto search = vertpatch2rows[face[v]].find(idxPatch); |
|
|
|
if (search != vertpatch2rows[face[v]].end()) |
|
|
|
if (search != vertpatch2rows[face[v]].end()) |
|
|
|
{ |
|
|
|
{ |
|
|
|
ASSERT(search->second < (Eigen::Index)colorAdjustments.size()); // ★ 关键
|
|
|
|
ASSERT(search->second < (Eigen::Index)colorAdjustments.size()); |
|
|
|
data.colors[v] = colorAdjustments[search->second]; |
|
|
|
data.colors[v] = colorAdjustments[search->second]; |
|
|
|
} |
|
|
|
} |
|
|
|
else |
|
|
|
else |
|
|
|
@ -9536,8 +9543,7 @@ void MeshTexture::GlobalSeamLeveling4() |
|
|
|
} |
|
|
|
} |
|
|
|
ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data); |
|
|
|
ColorMap::RasterizeTriangleBary(data.tri[0], data.tri[1], data.tri[2], data); |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
imageAdj.DilateMean<5>(imageAdj, Color::ZERO); |
|
|
|
imageAdj.DilateMean<1>(imageAdj, Color::ZERO); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// ★ 把修正叠加到 outPatch(从 srcROI 读原始颜色)
|
|
|
|
// ★ 把修正叠加到 outPatch(从 srcROI 读原始颜色)
|
|
|
|
for (int r = 0; r < srcROI.rows; ++r) { |
|
|
|
for (int r = 0; r < srcROI.rows; ++r) { |
|
|
|
@ -15568,6 +15574,14 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( |
|
|
|
(int)(atlas.rows * atlas.step)); |
|
|
|
(int)(atlas.rows * atlas.step)); |
|
|
|
DEBUG_EXTRA("expected: %d x %d x 3 = %d bytes", |
|
|
|
DEBUG_EXTRA("expected: %d x %d x 3 = %d bytes", |
|
|
|
textureSize, textureSize, textureSize * textureSize * 3); |
|
|
|
textureSize, textureSize, textureSize * textureSize * 3); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (int i = 0; i < std::min(3, (int)images.size()); ++i) { |
|
|
|
|
|
|
|
char dbgName[256]; |
|
|
|
|
|
|
|
sprintf(dbgName, "debug_image_%d_after_gsl4.png", i); |
|
|
|
|
|
|
|
cv::imwrite(dbgName, images[i].image); // ← 用 .image
|
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
DEBUG_EXTRA("Exported debug images after GSL4"); |
|
|
|
|
|
|
|
|
|
|
|
// ========== 3. 重光栅化(从已被 GSL4 修正的 images 读)==========
|
|
|
|
// ========== 3. 重光栅化(从已被 GSL4 修正的 images 读)==========
|
|
|
|
ASSERT(atlas.rows == textureSize && atlas.cols == textureSize); |
|
|
|
ASSERT(atlas.rows == textureSize && atlas.cols == textureSize); |
|
|
|
for (int pi = 0; pi < (int)rcPatches.size(); ++pi) { |
|
|
|
for (int pi = 0; pi < (int)rcPatches.size(); ++pi) { |
|
|
|
@ -15583,6 +15597,20 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( |
|
|
|
// cv::Mat correctedPatch = srcImg.image(tp.rect).clone();
|
|
|
|
// cv::Mat correctedPatch = srcImg.image(tp.rect).clone();
|
|
|
|
cv::Mat correctedPatch = correctedPatchImages[rcToTexPatch[pi]].clone(); // 局部拷贝
|
|
|
|
cv::Mat correctedPatch = correctedPatchImages[rcToTexPatch[pi]].clone(); // 局部拷贝
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 加上严格检查
|
|
|
|
|
|
|
|
static int cnt = 0; |
|
|
|
|
|
|
|
if (cnt < 5) { |
|
|
|
|
|
|
|
DEBUG_EXTRA("Patch %d: rcToTexPatch=%d, correctedPatch empty=%d, size=(%dx%d), type=%d", |
|
|
|
|
|
|
|
cnt, rcToTexPatch[pi], correctedPatch.empty(), |
|
|
|
|
|
|
|
correctedPatch.cols, correctedPatch.rows, correctedPatch.type()); |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (!correctedPatch.empty()) { |
|
|
|
|
|
|
|
char nm[256]; sprintf(nm, "debug_corrected_patch_%d.png", cnt); |
|
|
|
|
|
|
|
cv::imwrite(nm, correctedPatch); |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
cnt++; |
|
|
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
if (correctedPatch.empty()) continue; |
|
|
|
if (correctedPatch.empty()) continue; |
|
|
|
|
|
|
|
|
|
|
|
const RCPatch& rp = rcPatches[pi]; |
|
|
|
const RCPatch& rp = rcPatches[pi]; |
|
|
|
@ -15616,6 +15644,7 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( |
|
|
|
cv::remap(correctedPatch, remapped, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); |
|
|
|
cv::remap(correctedPatch, remapped, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); |
|
|
|
|
|
|
|
|
|
|
|
for (int y = 0; y < patchH; ++y) |
|
|
|
for (int y = 0; y < patchH; ++y) |
|
|
|
|
|
|
|
{ |
|
|
|
for (int x = 0; x < patchW; ++x) { |
|
|
|
for (int x = 0; x < patchW; ++x) { |
|
|
|
cv::Vec3b color = remapped.at<cv::Vec3b>(y, x); |
|
|
|
cv::Vec3b color = remapped.at<cv::Vec3b>(y, x); |
|
|
|
if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue; |
|
|
|
if (color[0] < 5 && color[1] < 5 && color[2] < 5) continue; |
|
|
|
@ -15629,9 +15658,16 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches( |
|
|
|
pixel[1] = color[1]; |
|
|
|
pixel[1] = color[1]; |
|
|
|
pixel[2] = color[2]; |
|
|
|
pixel[2] = color[2]; |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
|
|
|
|
|
|
|
|
atlas = localAtlas; |
|
|
|
atlas = localAtlas; |
|
|
|
|
|
|
|
// // 调试用:导出调整后的 atlas
|
|
|
|
|
|
|
|
// static int dbgCount = 0;
|
|
|
|
|
|
|
|
// char dbgName[256];
|
|
|
|
|
|
|
|
// sprintf(dbgName, "debug_atlas_after_gsl4_%d.png", dbgCount++);
|
|
|
|
|
|
|
|
// cv::imwrite(dbgName, atlas);
|
|
|
|
|
|
|
|
// DEBUG_EXTRA("Exported %s", dbgName);
|
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
} |
|
|
|
DEBUG_EXTRA("Re-rasterization done."); |
|
|
|
DEBUG_EXTRA("Re-rasterization done."); |
|
|
|
|