From e8e363234164299d03b93767cfca839a5c127575 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Wed, 22 Jul 2026 16:55:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B8=85=E6=99=B0=E5=92=8C=E7=A9=BA=E7=99=BD?= =?UTF-8?q?=E7=9A=84=E4=B8=AD=E9=97=B4=E7=89=88=E6=9C=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 213 +++++++++++++++++++++++++------------- 1 file changed, 142 insertions(+), 71 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 9525154..89c81f2 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -14073,13 +14073,13 @@ bool MeshTexture::RasterizeVirtualFaces( DEBUG_EXTRA("Forward Rasterization Engine: Starting..."); TD_TIMER_START(); - int totalVF = virtualFaceMap.size(); - int emptyVF = 0; - for (auto& vf : virtualFaceViews) - if (vf.empty()) ++emptyVF; + int totalVF = virtualFaceMap.size(); + int emptyVF = 0; + for (auto& vf : virtualFaceViews) + if (vf.empty()) ++emptyVF; - DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)", - totalVF, emptyVF, 100.0f*emptyVF/totalVF); + DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)", + totalVF, emptyVF, 100.0f*emptyVF/totalVF); if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) return false; @@ -14099,21 +14099,25 @@ bool MeshTexture::RasterizeVirtualFaces( int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); // -------------------------------------------------- - // 2. 创建纹理与累积缓冲区 + // 2. 创建纹理与缓冲器 // -------------------------------------------------- outTextures.emplace_back(textureSize, textureSize); Image8U3& atlas = outTextures.back(); atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); - cv::Mat1f weightAccum(textureSize, textureSize, 0.f); - cv::Mat3f colorAccum(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); + // ✅ 深度缓冲器和面片ID缓冲器 + cv::Mat1f depthBuffer(textureSize, textureSize, -FLT_MAX); + cv::Mat1i faceIDBuffer(textureSize, textureSize, -1); + + // ✅ 颜色缓冲器 + cv::Mat3f colorBuffer(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); + cv::Mat1b validBuffer(textureSize, textureSize, (uchar)0); // -------------------------------------------------- - // 3. 光栅化参数 + // 3. 光栅化参数 - 使用单采样保证清晰度 // -------------------------------------------------- - constexpr int SUPER_SAMPLE = 2; + constexpr int SUPER_SAMPLE = 1; // ✅ 关键:禁用MSAA,使用单采样 constexpr float STEP = 1.f / SUPER_SAMPLE; - const float MAX_DIST_SQ = 25.f / (textureSize * textureSize); // 距离阈值 // -------------------------------------------------- // 4. 正向光栅化主循环 @@ -14140,83 +14144,150 @@ bool MeshTexture::RasterizeVirtualFaces( &scene.mesh.vertices[face[2]] }; - // 纹理空间包围盒 - cv::Rect bbox; - for (int i = 0; i < 3; ++i) { - int px = int(uv[i].x * textureSize); - int py = int(uv[i].y * textureSize); - if (bbox.empty()) - bbox = cv::Rect(px, py, 1, 1); - else - bbox |= cv::Rect(px, py, 1, 1); - } - // 与纹理边界取交集 - bbox &= cv::Rect(0, 0, textureSize, textureSize); + // ✅ 关键修复1:扩大包围盒,确保覆盖所有像素 + float minU = std::min({uv[0].x, uv[1].x, uv[2].x}); + float maxU = std::max({uv[0].x, uv[1].x, uv[2].x}); + float minV = std::min({uv[0].y, uv[1].y, uv[2].y}); + float maxV = std::max({uv[0].y, uv[1].y, uv[2].y}); + + int minX = std::max(0, (int)floor(minU * textureSize) - 1); + int maxX = std::min(textureSize - 1, (int)ceil(maxU * textureSize) + 1); + int minY = std::max(0, (int)floor(minV * textureSize) - 1); + int maxY = std::min(textureSize - 1, (int)ceil(maxV * textureSize) + 1); + + if (minX > maxX || minY > maxY) continue; - // MSAA 采样 - for (int y = bbox.y; y < bbox.y + bbox.height; ++y) { - for (int x = bbox.x; x < bbox.x + bbox.width; ++x) { + // ✅ 关键修复2:预计算三角形深度范围 + Point3f triCenter = (*verts[0] + *verts[1] + *verts[2]) / 3.0f; + float triDepth = cam.PointDepth(triCenter); + float depthRange = 0.1f * triDepth; // 10%的深度容差 - for (int sy = 0; sy < SUPER_SAMPLE; ++sy) { - for (int sx = 0; sx < SUPER_SAMPLE; ++sx) { + for (int y = minY; y <= maxY; ++y) { + for (int x = minX; x <= maxX; ++x) { - Point2f texCoord( - (x + (sx + 0.5f) * STEP) / textureSize, - (y + (sy + 0.5f) * STEP) / textureSize - ); + Point2f texCoord( + (x + 0.5f) / textureSize, + (y + 0.5f) / textureSize + ); - Point3f bary; - if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) - continue; + Point3f bary; + if (!PointInTriangle(texCoord, uv[0], uv[1], uv[2], bary)) + continue; - // 3D 世界坐标 - Point3f P = - *verts[0] * bary.x + - *verts[1] * bary.y + - *verts[2] * bary.z; + // 3D 世界坐标 + Point3f P = + *verts[0] * bary.x + + *verts[1] * bary.y + + *verts[2] * bary.z; - // 投影到图像 - Point2f imgPt = ProjectPointWithAutoCorrection(cam, P, srcImg); - // 允许少量越界 - if (!srcImg.image.isInside(imgPt)) { - imgPt.x = std::clamp(imgPt.x, 0.f, srcImg.image.width() - 1.1f); - imgPt.y = std::clamp(imgPt.y, 0.f, srcImg.image.height() - 1.1f); - } + // ✅ 关键修复3:简化的深度测试 + float depth = cam.PointDepth(P); + + // 放宽深度测试:允许一定范围的深度差异 + if (depth < triDepth - depthRange || depth > triDepth + depthRange) + continue; + + // ✅ 关键修复4:严格的面片ID检查 + bool accept = false; + #pragma omp critical + { + // 如果像素未被占用,或者当前面片ID相同,或者深度更近 + if (faceIDBuffer(y, x) == -1 || + faceIDBuffer(y, x) == (int)faceID || + depth > depthBuffer(y, x)) { + + // 更新深度和面片ID + depthBuffer(y, x) = depth; + faceIDBuffer(y, x) = (int)faceID; + accept = true; + } + } + + if (!accept) continue; - // 放宽前后判断(允许微小负值) - if (!cam.IsInFront(P) && cam.Distance(P) < -0.01f) - continue; + // 投影到图像 + Point2f imgPt = ProjectPointWithAutoCorrection(cam, P, srcImg); + + // ✅ 关键修复5:改进的边界处理 + if (!srcImg.image.isInside(imgPt)) { + // 尝试在图像边界内寻找最近的有效点 + imgPt.x = std::clamp(imgPt.x, 0.f, srcImg.image.width() - 1.1f); + imgPt.y = std::clamp(imgPt.y, 0.f, srcImg.image.height() - 1.1f); + } - // 双三次采样 - Sampler sampler; - Color c = srcImg.image.sample(sampler, imgPt); + // 前后判断 + if (!cam.IsInFront(P) && cam.Distance(P) < -0.01f) + continue; - // 累加到原子操作区域 -#ifdef _USE_OPENMP -#pragma omp critical -#endif - { - cv::Vec3f& acc = colorAccum(y, x); - acc[0] += c[2]; - acc[1] += c[1]; - acc[2] += c[0]; - weightAccum(y, x) += 1.f; + // ✅ 关键修复6:最近邻采样(最锐利) + int ix = int(imgPt.x + 0.5f); + int iy = int(imgPt.y + 0.5f); + + // 边界检查 + if (ix < 0 || ix >= srcImg.image.width() || + iy < 0 || iy >= srcImg.image.height()) + continue; + + // 获取像素颜色(OpenCV BGR -> openMVS RGB) + const cv::Vec3b& pixel = srcImg.image.at(iy, ix); + + #pragma omp critical + { + // ✅ 直接写入,不混合 + colorBuffer(y, x) = cv::Vec3f(pixel[2], pixel[1], pixel[0]); + validBuffer(y, x) = 1; + } + } + } + } + } + + // -------------------------------------------------- + // 5. 空洞填充(修复零散空白点) + // -------------------------------------------------- + DEBUG_EXTRA("Filling holes in rasterized texture..."); + + // 使用膨胀操作填充小的空洞 + cv::Mat1b dilatedValid = validBuffer.clone(); + cv::dilate(dilatedValid, dilatedValid, cv::Mat(), cv::Point(-1,-1), 2); + + for (int y = 0; y < textureSize; ++y) { + for (int x = 0; x < textureSize; ++x) { + if (validBuffer(y, x) == 0 && dilatedValid(y, x) == 1) { + // 找到最近的有效像素 + int bestDist = INT_MAX; + cv::Vec3f bestColor(0,0,0); + + for (int dy = -3; dy <= 3; ++dy) { + for (int dx = -3; dx <= 3; ++dx) { + int ny = y + dy; + int nx = x + dx; + if (ny >= 0 && ny < textureSize && nx >= 0 && nx < textureSize && + validBuffer(ny, nx) == 1) { + int dist = dy*dy + dx*dx; + if (dist < bestDist) { + bestDist = dist; + bestColor = colorBuffer(ny, nx); } } } } + + if (bestDist < INT_MAX) { + colorBuffer(y, x) = bestColor; + validBuffer(y, x) = 1; + } } } } // -------------------------------------------------- - // 5. 权重归一化 + // 6. 最终写入纹理 // -------------------------------------------------- for (int y = 0; y < textureSize; ++y) { for (int x = 0; x < textureSize; ++x) { - float w = weightAccum(y, x); - if (w > 0.f) { - cv::Vec3f c = colorAccum(y, x) / w; + if (validBuffer(y, x) == 1) { + cv::Vec3f c = colorBuffer(y, x); atlas(y, x) = Pixel8U{ (unsigned char)cv::saturate_cast(c[0]), (unsigned char)cv::saturate_cast(c[1]), @@ -14556,7 +14627,7 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, } if (faceNeighbors[faceID2].empty()) { - DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2); + // DEBUG_EXTRA("FATAL: faceNeighbors[%u] is empty! FaceViewSelection mode error.", faceID2); } //------------------------------------------------------------------ @@ -14601,8 +14672,8 @@ bool MeshTexture::SelectBestViewsForVirtualFaces(VirtualFaceMap& virtualFaceMap, ++fallbackByCenterFace; // 降低日志级别,避免刷屏,用 DEBUG 而非 EXTRA - DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)", - i, forcedView); + // DEBUG("VF[%zu] EMPTY -> FORCED view %d (ignoring physical occlusion)", + // i, forcedView); continue; }