Browse Source

进一步优化

ManualUV
hesuicong 2 weeks ago
parent
commit
2d1f6a13e5
  1. 330
      libs/MVS/SceneTexture.cpp

330
libs/MVS/SceneTexture.cpp

@ -764,6 +764,7 @@ public:
void GlobalSeamLeveling4(); void GlobalSeamLeveling4();
void LocalSeamLeveling(); void LocalSeamLeveling();
void LocalSeamLeveling3(); void LocalSeamLeveling3();
void LocalSeamLeveling4();
void GlobalSeamLevelingExternalUV(); void GlobalSeamLevelingExternalUV();
void LocalSeamLevelingExternalUV(); void LocalSeamLevelingExternalUV();
@ -10054,7 +10055,277 @@ void MeshTexture::LocalSeamLeveling3()
} }
} }
} }
void MeshTexture::LocalSeamLeveling4()
{
ASSERT(!seamVertices.empty());
const unsigned numPatches(texturePatches.size()-1);
// ★ 统计跳过情况
int skipEmptyRect = 0, skipBadLabel = 0, skipBadProj = 0, skipNoSeam = 0;
#ifdef TEXOPT_USE_OPENMP
#pragma omp parallel for schedule(dynamic)
for (int i=0; i<(int)numPatches; ++i) {
#else
for (unsigned i=0; i<numPatches; ++i) {
#endif
const uint32_t idxPatch((uint32_t)i);
const TexturePatch& texturePatch = texturePatches[idxPatch];
// ============================================================
// ★ 保护 1:检查 label (viewID) 合法性
// ============================================================
if (texturePatch.label < 0 || texturePatch.label >= (int)images.size()) {
#ifdef TEXOPT_USE_OPENMP
#pragma omp atomic
#endif
skipBadLabel++;
continue;
}
// ============================================================
// ★ 保护 2:检查 rect 合法性(最常见 crash 原因)
// ============================================================
if (texturePatch.rect.width <= 0 || texturePatch.rect.height <= 0 ||
texturePatch.rect.x < 0 || texturePatch.rect.y < 0) {
#ifdef TEXOPT_USE_OPENMP
#pragma omp atomic
#endif
skipEmptyRect++;
continue;
}
// 检查 rect 是否超出源图像范围
const Image8U3& image0(images[texturePatch.label].image);
if (image0.empty()) {
#ifdef TEXOPT_USE_OPENMP
#pragma omp atomic
#endif
skipBadLabel++;
continue;
}
cv::Rect safeRect = texturePatch.rect & cv::Rect(0, 0, image0.cols, image0.rows);
if (safeRect.width <= 0 || safeRect.height <= 0) {
#ifdef TEXOPT_USE_OPENMP
#pragma omp atomic
#endif
skipEmptyRect++;
continue;
}
// extract image
Image32F3 image, imageOrg;
#ifdef USE_CUDA
if (MeshTextureCUDA::ConvertToCUDA(image0(safeRect), image, 1.0/255.0)) {}
else {
image0(safeRect).convertTo(image, CV_32FC3, 1.0/255.0);
}
#else
image0(safeRect).convertTo(image, CV_32FC3, 1.0/255.0);
#endif
image.copyTo(imageOrg);
if (image.empty() || image.cols < 2 || image.rows < 2) {
#ifdef TEXOPT_USE_OPENMP
#pragma omp atomic
#endif
skipEmptyRect++;
continue;
}
// render patch coverage
Image8U mask(image.size()); {
mask.memset(0);
struct RasterMesh {
Image8U& image;
inline void operator()(const ImageRef& pt) {
if (image.isInside(pt))
image(pt) = interior;
}
} data{mask};
for (const FIndex idxFace: texturePatch.faces) {
if (idxFace >= faces.size()) continue;
const TexCoord* tri = faceTexcoords.data()+idxFace*3;
ColorMap::RasterizeTriangle(tri[0], tri[1], tri[2], data);
}
}
const Sampler sampler;
const TexCoord offset(texturePatch.rect.tl());
// ============================================================
// ★ 保护 3:遍历接缝前,检查这个 patch 在 seamVertices 里有没有引用
// ============================================================
bool patchHasSeam = false;
for (const SeamVertex& sv : seamVertices) {
for (const SeamVertex::Patch& p : sv.patches) {
if (p.idxPatch == idxPatch) {
patchHasSeam = true;
break;
}
}
if (patchHasSeam) break;
}
if (!patchHasSeam) {
// 这个 patch 不在任何接缝上,不需要局部融合,直接复制原图
#ifdef TEXOPT_USE_OPENMP
#pragma omp atomic
#endif
skipNoSeam++;
// 直接把 imageOrg 写回 imagePatch(不做泊松融合)
cv::Mat imagePatch(image0(safeRect));
for (int r=0; r<image.rows; ++r) {
for (int c=0; c<image.cols; ++c) {
const Color& a = imageOrg(r,c);
Pixel8U& v = imagePatch.at<Pixel8U>(r,c);
for (int p=0; p<3; ++p)
v[p] = (uint8_t)CLAMP(ROUND2INT(a[p]*255.f), 0, 255);
}
}
continue;
}
// render the patch border meeting neighbor patches
for (const SeamVertex& seamVertex0: seamVertices) {
if (seamVertex0.patches.size() < 2)
continue;
const uint32_t idxVertPatch0(seamVertex0.patches.Find(idxPatch));
if (idxVertPatch0 == SeamVertex::Patches::NO_INDEX)
continue;
const SeamVertex::Patch& patch0 = seamVertex0.patches[idxVertPatch0];
const TexCoord p0(patch0.proj-offset);
// ★ 保护:p0 必须在 image 范围内
if (p0.x < 0 || p0.y < 0 || p0.x >= image.cols || p0.y >= image.rows) {
skipBadProj++;
continue;
}
// for each edge of this vertex belonging to this patch...
for (const SeamVertex::Patch::Edge& edge0: patch0.edges) {
if (edge0.idxSeamVertex >= seamVertices.size()) continue;
const SeamVertex& seamVertex1 = seamVertices[edge0.idxSeamVertex];
const uint32_t idxVertPatch0Adj(seamVertex1.patches.Find(idxPatch));
if (idxVertPatch0Adj == SeamVertex::Patches::NO_INDEX) continue;
const SeamVertex::Patch& patch0Adj = seamVertex1.patches[idxVertPatch0Adj];
const TexCoord p0Adj(patch0Adj.proj-offset);
if (p0Adj.x < 0 || p0Adj.y < 0 || p0Adj.x >= image.cols || p0Adj.y >= image.rows) {
skipBadProj++;
continue;
}
// find the other patch sharing the same edge
FOREACH(idxVertPatch1, seamVertex0.patches) {
if (idxVertPatch1 == idxVertPatch0) continue;
const SeamVertex::Patch& patch1 = seamVertex0.patches[idxVertPatch1];
if (patch1.idxPatch >= texturePatches.size()) continue; // ★ 越界保护
const uint32_t idxEdge1(patch1.edges.Find(edge0.idxSeamVertex));
if (idxEdge1 == SeamVertex::Patch::Edges::NO_INDEX) continue;
const TexCoord& p1(patch1.proj);
const uint32_t idxVertPatch1Adj(seamVertex1.patches.Find(patch1.idxPatch));
if (idxVertPatch1Adj == SeamVertex::Patches::NO_INDEX) continue;
const SeamVertex::Patch& patch1Adj = seamVertex1.patches[idxVertPatch1Adj];
const TexCoord& p1Adj(patch1Adj.proj);
// 检查 label
const int label1 = texturePatches[patch1.idxPatch].label;
if (label1 < 0 || label1 >= (int)images.size()) continue;
const Image8U3& image1(images[label1].image);
if (image1.empty()) continue;
struct RasterPatch {
Image32F3& image;
Image8U& mask;
const Image32F3& image0;
const Image8U3& image1;
const TexCoord p0, p0Dir;
const TexCoord p1, p1Dir;
const float length;
const Sampler sampler;
inline RasterPatch(Image32F3& _image, Image8U& _mask, const Image32F3& _image0, const Image8U3& _image1,
const TexCoord& _p0, const TexCoord& _p0Adj, const TexCoord& _p1, const TexCoord& _p1Adj)
: image(_image), mask(_mask), image0(_image0), image1(_image1),
p0(_p0), p0Dir(_p0Adj-_p0), p1(_p1), p1Dir(_p1Adj-_p1), length((float)norm(p0Dir)), sampler() {}
inline void operator()(const ImageRef& pt) {
// ★ 保护:pt 必须在 image 范围内
if (pt.x < 0 || pt.y < 0 || pt.x >= image.cols || pt.y >= image.rows)
return;
const float l((float)norm(TexCoord(pt)-p0)/length);
const TexCoord samplePos0(p0 + p0Dir * l);
const Color color0(image0.sample<Sampler,Color>(sampler, samplePos0));
const TexCoord samplePos1(p1 + p1Dir * l);
const Color color1(image1.sample<Sampler,Color>(sampler, samplePos1)/255.f);
image(pt) = Color((color0 + color1) * 0.5f);
mask(pt) = border;
}
} data(image, mask, imageOrg, image1, p0, p0Adj, p1, p1Adj);
// ★ 保护:DrawLine 前检查端点
if (p0.x >= 0 && p0.y >= 0 && p0Adj.x >= 0 && p0Adj.y >= 0 &&
p0.x < image.cols && p0.y < image.rows &&
p0Adj.x < image.cols && p0Adj.y < image.rows) {
Image32F3::DrawLine(p0, p0Adj, data);
}
break;
}
}
// render vertex
AccumColor accumColor;
for (const SeamVertex::Patch& patch: seamVertex0.patches) {
if (patch.idxPatch >= texturePatches.size()) continue;
int plabel = texturePatches[patch.idxPatch].label;
if (plabel < 0 || plabel >= (int)images.size()) continue;
const Image8U3& img(images[plabel].image);
accumColor.Add(img.sample<Sampler,Color>(sampler, patch.proj)/255.f, 1.f);
}
const ImageRef pt(ROUND2INT(patch0.proj-offset));
if (pt.x >= 0 && pt.y >= 0 && pt.x < image.cols && pt.y < image.rows) {
image(pt) = accumColor.Normalized();
mask(pt) = border;
}
}
// make sure the border is continuous
#ifdef USE_CUDA
if (MeshTextureCUDA::ProcessMaskCUDA(mask, 20)) {}
else
#endif
ProcessMask(mask, 20);
// compute texture patch blending
#ifdef USE_CUDA
if (MeshTextureCUDA::PoissonBlendCUDA(image, imageOrg, mask, 1.0f)) {}
else
#endif
PoissonBlending(imageOrg, image, mask);
// apply color correction to the patch image
cv::Mat imagePatch(image0(safeRect));
#ifdef TEXOPT_USE_OPENMP
#pragma omp parallel for collapse(2)
#endif
for (int r=0; r<image.rows; ++r) {
for (int c=0; c<image.cols; ++c) {
if (mask(r,c) == empty) continue;
const Color& a = image(r,c);
Pixel8U& v = imagePatch.at<Pixel8U>(r,c);
for (int p=0; p<3; ++p)
v[p] = (uint8_t)CLAMP(ROUND2INT(a[p]*255.f), 0, 255);
}
}
}
VERBOSE("LocalSeamLeveling3 stats: skipEmptyRect=%d, skipBadLabel=%d, skipBadProj=%d, skipNoSeam=%d",
skipEmptyRect, skipBadLabel, skipBadProj, skipNoSeam);
}
void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene) void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview, Scene *pScene)
{ {
bool bUseExternalUV = false; bool bUseExternalUV = false;
@ -15587,6 +15858,62 @@ void MeshTexture::ApplyGlobalSeamLevelingOnRCPatches(
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);
// ============================================================
// ★ 桥接补丁:确保 texturePatches[i].label = viewID
// ============================================================
// 你的 rcPatches 里每个 patch 有 viewID,但桥接时可能没设 label。
// 如果桥接代码已经设了,这个循环是幂等的,不会破坏任何东西。
VERBOSE("Syncing texturePatches.label ← rcPatches.viewID...");
for (uint32_t i = 0; i < texturePatches.size(); ++i) {
// texturePatches 索引 i 对应 rcPatches 索引 i(你的桥接是 1:1 的)
if (i < rcPatches.size()) {
texturePatches[i].label = rcPatches[i].viewID;
} else {
// 兜底:如果数量不匹配,用 faceToPatchID 反查
// 这种情况不应该发生,打日志报警
VERBOSE(" WARNING: texturePatch[%u] has no matching rcPatch!", i);
}
}
// 验证一下 label 是否正确
int labelOK = 0, labelBad = 0;
for (uint32_t i = 0; i < texturePatches.size(); ++i) {
if (texturePatches[i].label >= 0 && texturePatches[i].label < (int)images.size()) {
++labelOK;
} else {
++labelBad;
}
}
VERBOSE(" label check: %d OK, %d BAD (out of %u)", labelOK, labelBad, (uint32_t)texturePatches.size());
if (labelBad > 0) {
VERBOSE(" ERROR: %d texturePatches have invalid label (viewID out of range)!", labelBad);
// 不 return,让 LSL3 尽量跑,坏 label 的 patch 会采样到错误图像但不会导致崩溃
}
// ============================================================
// ★ 确认 seamVertices 已构建(你的日志显示已经 90152 个)
// ============================================================
if (seamVertices.empty()) {
VERBOSE("seamVertices is empty! Calling CreateSeamVertices()...");
CreateSeamVertices(); // 如果之前没调过,这里补调
VERBOSE(" seamVertices created: %zu", seamVertices.size());
} else {
VERBOSE("seamVertices already built: %zu vertices, ready for LSL3", seamVertices.size());
}
// ============================================================
// ★ 调用 LocalSeamLeveling4
// ============================================================
VERBOSE("Calling LocalSeamLeveling4 (poisson blending on %zu patches)...", texturePatches.size() - 1);
VERBOSE(" This may take 5~15 minutes for %zu patches...", texturePatches.size() - 1);
const auto lsl3Start = std::chrono::high_resolution_clock::now();
LocalSeamLeveling4();
const auto lsl3End = std::chrono::high_resolution_clock::now();
const double lsl3Sec = std::chrono::duration<double>(lsl3End - lsl3Start).count();
VERBOSE("LocalSeamLeveling4 done in %.1f seconds.", lsl3Sec);
for (int i = 0; i < std::min(3, (int)images.size()); ++i) { for (int i = 0; i < std::min(3, (int)images.size()); ++i) {
char dbgName[256]; char dbgName[256];
sprintf(dbgName, "debug_image_%d_after_gsl4.png", i); sprintf(dbgName, "debug_image_%d_after_gsl4.png", i);
@ -17340,6 +17667,7 @@ int MeshTexture::ComputeOptimalTextureSizeAdaptive(
// 防止过小 // 防止过小
if (textureSize < 1024) textureSize = 1024; if (textureSize < 1024) textureSize = 1024;
textureSize = 8192; // ★ 临时强制,调试完删掉
DEBUG_EXTRA("Adaptive texture size: %d (totalPixels=%.0f, validFaces=%d, max=%u)", DEBUG_EXTRA("Adaptive texture size: %d (totalPixels=%.0f, validFaces=%d, max=%u)",
textureSize, totalPixels, validFaces, nTextureSizeMultiple); textureSize, totalPixels, validFaces, nTextureSizeMultiple);
@ -17569,7 +17897,7 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(
// ========== 颜色一致性代价 ========== // ========== 颜色一致性代价 ==========
{ {
const float lambda = 2.0f; const float lambda = 100.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()) {

Loading…
Cancel
Save