From 6e0e0f9cc7ed11a8ea64497d5a63aca00e85cb23 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Fri, 31 Jul 2026 11:01:33 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E8=BF=90=E8=A1=8C=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 297 ++++++++++++++++++-------------------- 1 file changed, 142 insertions(+), 155 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index 8d93b11..84fc378 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -669,6 +669,7 @@ public: const Mesh::TexCoordArr& existingTexcoords, // 添加已有UV参数 const Mesh::TexIndexArr& existingTexindices // 添加已有纹理索引参数 ); + bool RasterizeVirtualFaces( const VirtualFaceMap& virtualFaceMap, const std::vector>& virtualFaceViews, @@ -727,31 +728,56 @@ public: // ======================================================================== // 辅助函数:双线性采样 // ======================================================================== - inline Color BilinearSample(const Image8U3& img, const Point2f& p) { - const int w = img.width(); - const int h = img.height(); - const float fx = p.x - 0.5f; // 像素中心对齐 - const float fy = p.y - 0.5f; - int x0 = (int)std::floor(fx); - int y0 = (int)std::floor(fy); - float dx = fx - x0; - float dy = fy - y0; - - auto get = [&](int x, int y) -> Color { - x = std::max(0, std::min(x, w - 1)); - y = std::max(0, std::min(y, h - 1)); - return img(x, y); - }; + inline Color BilinearSample(const Image8U3& img, const Point2f& p) + { + // ✅ 防御 NaN / Inf + if (!std::isfinite(p.x) || !std::isfinite(p.y)) + return Color(0, 0, 0); + + // ✅ 防御空图 + if (img.empty() || img.cols < 2 || img.rows < 2) + return Color(0, 0, 0); - Color c00 = get(x0, y0); - Color c10 = get(x0 + 1, y0); - Color c01 = get(x0, y0 + 1); - Color c11 = get(x0 + 1, y0 + 1); + const float fx = p.x - 0.5f; + const float fy = p.y - 0.5f; - return c00 * ((1.0f - dx) * (1.0f - dy)) - + c10 * (dx * (1.0f - dy)) - + c01 * ((1.0f - dx) * dy) - + c11 * (dx * dy); + const int x0 = static_cast(std::floor(fx)); + const int y0 = static_cast(std::floor(fy)); + const float dx = fx - x0; + const float dy = fy - y0; + + // ✅ 安全 clamp(OpenMVS 最稳写法) + const int x1 = std::min(x0 + 1, img.cols - 1); + const int y1 = std::min(y0 + 1, img.rows - 1); + const int x0c = std::max(x0, 0); + const int y0c = std::max(y0, 0); + + // ✅ 用 ptr 直接访问(最快、最稳) + const cv::Vec3b* row0 = img.ptr(y0c); + const cv::Vec3b* row1 = img.ptr(y1); + + const cv::Vec3b& c00 = row0[x0c]; + const cv::Vec3b& c10 = row0[x1]; + const cv::Vec3b& c01 = row1[x0c]; + const cv::Vec3b& c11 = row1[x1]; + + // BGR → RGB + return Color( + c00[2] * ((1.0f - dx) * (1.0f - dy)) + + c10[2] * (dx * (1.0f - dy)) + + c01[2] * ((1.0f - dx) * dy) + + c11[2] * (dx * dy), + + c00[1] * ((1.0f - dx) * (1.0f - dy)) + + c10[1] * (dx * (1.0f - dy)) + + c01[1] * ((1.0f - dx) * dy) + + c11[1] * (dx * dy), + + c00[0] * ((1.0f - dx) * (1.0f - dy)) + + c10[0] * (dx * (1.0f - dy)) + + c01[0] * ((1.0f - dx) * dy) + + c11[0] * (dx * dy) + ); } // ======================================================================== @@ -14146,14 +14172,6 @@ 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; - - DEBUG_EXTRA("VirtualFaces: total=%d empty=%d (%.1f%%)", - totalVF, emptyVF, 100.0f*emptyVF/totalVF); - if (virtualFaceMap.empty() || virtualFaceViews.size() != virtualFaceMap.size()) return false; @@ -14164,9 +14182,9 @@ bool MeshTexture::RasterizeVirtualFaces( for (const TexCoord& uv : scene.mesh.faceTexcoords) uvBounds.InsertFull(uv); - float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); + float uvWidth = uvBounds.ptMax.x() - uvBounds.ptMin.x(); float uvHeight = uvBounds.ptMax.y() - uvBounds.ptMin.y(); - if (uvWidth < 0.001f) uvWidth = 1.0f; + if (uvWidth < 0.001f) uvWidth = 1.0f; if (uvHeight < 0.001f) uvHeight = 1.0f; int textureSize = ComputeOptimalTextureSize(uvWidth, uvHeight, nTextureSizeMultiple); @@ -14178,66 +14196,80 @@ bool MeshTexture::RasterizeVirtualFaces( Image8U3& atlas = outTextures.back(); atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r)); - // ✅ 深度缓冲器和面片ID缓冲器 - cv::Mat1f depthBuffer(textureSize, textureSize, -FLT_MAX); - cv::Mat1i faceIDBuffer(textureSize, textureSize, -1); - - // ✅ 颜色缓冲器 + // ✅ 深度缓冲器初始化为正无穷(越小越近) + cv::Mat1f depthBuffer(textureSize, textureSize, FLT_MAX); cv::Mat3f colorBuffer(textureSize, textureSize, cv::Vec3f(0.f, 0.f, 0.f)); cv::Mat1b validBuffer(textureSize, textureSize, (uchar)0); // -------------------------------------------------- - // 3. 光栅化参数 - 使用单采样保证清晰度 - // -------------------------------------------------- - constexpr int SUPER_SAMPLE = 1; // ✅ 关键:禁用MSAA,使用单采样 - constexpr float STEP = 1.f / SUPER_SAMPLE; - - // -------------------------------------------------- - // 4. 正向光栅化主循环 + // 3. 正向光栅化主循环 // -------------------------------------------------- #ifdef _USE_OPENMP #pragma omp parallel for schedule(dynamic) #endif - for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) - { + for (int_t idxVF = 0; idxVF < (int_t)virtualFaceMap.size(); ++idxVF) { if (virtualFaceViews[idxVF].empty()) continue; - const IIndex viewID = virtualFaceViews[idxVF][0]; // 单视图 + const IIndex viewID = virtualFaceViews[idxVF][0]; + if (viewID >= (IIndex)images.size()) continue; + const Image& srcImg = images[viewID]; + if (srcImg.image.empty() || srcImg.image.cols < 2 || srcImg.image.rows < 2) + continue; + const Camera& cam = srcImg.camera; + const int srcW = srcImg.image.cols; + const int srcH = srcImg.image.rows; - for (FIndex faceID : virtualFaceMap[idxVF].faces) - { + for (FIndex faceID : virtualFaceMap[idxVF].faces) { + if (faceID >= (FIndex)scene.mesh.faces.size()) continue; + const Face& face = scene.mesh.faces[faceID]; const TexCoord* uv = &scene.mesh.faceTexcoords[faceID * 3]; - const Vertex* verts[3] = { + + // 验证顶点索引 + bool validIndices = true; + for (int i = 0; i < 3; ++i) { + if (face[i] >= scene.mesh.vertices.size()) { + validIndices = false; + break; + } + } + if (!validIndices) continue; + + const Point3f* verts[3] = { &scene.mesh.vertices[face[0]], &scene.mesh.vertices[face[1]], &scene.mesh.vertices[face[2]] }; - // ✅ 关键修复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); + int minX = std::max(0, (int)floor(minU * textureSize)); + int maxX = std::min(textureSize - 1, (int)ceil(maxU * textureSize)); + int minY = std::max(0, (int)floor(minV * textureSize)); + int maxY = std::min(textureSize - 1, (int)ceil(maxV * textureSize)); if (minX > maxX || minY > maxY) continue; - // ✅ 关键修复2:预计算三角形深度范围 - Point3f triCenter = (*verts[0] + *verts[1] + *verts[2]) / 3.0f; - float triDepth = cam.PointDepth(triCenter); - float depthRange = 0.1f * triDepth; // 10%的深度容差 + // ✅ 预计算顶点深度(用于透视校正) + float vertexDepths[3]; + for (int i = 0; i < 3; ++i) { + vertexDepths[i] = cam.PointDepth(*verts[i]); + if (vertexDepths[i] <= 0.0f) { + validIndices = false; + break; + } + } + if (!validIndices) continue; for (int y = minY; y <= maxY; ++y) { for (int x = minX; x <= maxX; ++x) { - Point2f texCoord( (x + 0.5f) / textureSize, (y + 0.5f) / textureSize @@ -14247,119 +14279,74 @@ bool MeshTexture::RasterizeVirtualFaces( 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; - - // ✅ 关键修复3:简化的深度测试 - float depth = cam.PointDepth(P); + // ✅ 透视校正插值 + // 计算插值后的1/z + float invZ = bary.x / vertexDepths[0] + + bary.y / vertexDepths[1] + + bary.z / vertexDepths[2]; + if (invZ <= 0.0f) continue; - // 放宽深度测试:允许一定范围的深度差异 - if (depth < triDepth - depthRange || depth > triDepth + depthRange) - continue; + float depth = 1.0f / invZ; // 校正后的深度 - // ✅ 关键修复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; - - // 投影到图像 - 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); - } - - // 前后判断 - if (!cam.IsInFront(P) && cam.Distance(P) < -0.01f) - continue; + // ✅ 透视校正的世界坐标 + Point3f P( + (verts[0]->x * bary.x / vertexDepths[0] + + verts[1]->x * bary.y / vertexDepths[1] + + verts[2]->x * bary.z / vertexDepths[2]) * depth, + (verts[0]->y * bary.x / vertexDepths[0] + + verts[1]->y * bary.y / vertexDepths[1] + + verts[2]->y * bary.z / vertexDepths[2]) * depth, + (verts[0]->z * bary.x / vertexDepths[0] + + verts[1]->z * bary.y / vertexDepths[1] + + verts[2]->z * bary.z / vertexDepths[2]) * depth + ); - // ✅ 关键修复6:最近邻采样(最锐利) - int ix = int(imgPt.x + 0.5f); - int iy = int(imgPt.y + 0.5f); + // ✅ 正确的深度测试:只保留更近的像素 + if (depth <= 0.0f) continue; + + Point3d P_double( + (verts[0]->x * bary.x / vertexDepths[0] + + verts[1]->x * bary.y / vertexDepths[1] + + verts[2]->x * bary.z / vertexDepths[2]) / invZ, + (verts[0]->y * bary.x / vertexDepths[0] + + verts[1]->y * bary.y / vertexDepths[1] + + verts[2]->y * bary.z / vertexDepths[2]) / invZ, + (verts[0]->z * bary.x / vertexDepths[0] + + verts[1]->z * bary.y / vertexDepths[1] + + verts[2]->z * bary.z / vertexDepths[2]) / invZ + ); + + Point2f imgPt = cam.ProjectPoint(P_double); // 边界检查 - if (ix < 0 || ix >= srcImg.image.width() || - iy < 0 || iy >= srcImg.image.height()) + if (imgPt.x < 0.5f || imgPt.y < 0.5f || + imgPt.x >= srcW - 0.5f || imgPt.y >= srcH - 0.5f) continue; - - // 获取像素颜色(OpenCV BGR -> openMVS RGB) - const cv::Vec3b& pixel = srcImg.image.at(iy, ix); - + + // ✅ 双线性采样(关键!) + Color color = BilinearSample(srcImg.image, imgPt); + if (color[0] < 0 || color[1] < 0 || color[2] < 0) continue; + + // ✅ 单一临界区:原子更新深度和颜色 #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 (depth < depthBuffer(y, x)) { + depthBuffer(y, x) = depth; + colorBuffer(y, x) = cv::Vec3f(color[0], color[1], color[2]); // BGR->RGB + validBuffer(y, x) = 1; } } } - - if (bestDist < INT_MAX) { - colorBuffer(y, x) = bestColor; - validBuffer(y, x) = 1; - } } } } // -------------------------------------------------- - // 6. 最终写入纹理 + // 4. 最终写入纹理 // -------------------------------------------------- for (int y = 0; y < textureSize; ++y) { for (int x = 0; x < textureSize; ++x) { - if (validBuffer(y, x) == 1) { + if (validBuffer(y, x)) { cv::Vec3f c = colorBuffer(y, x); atlas(y, x) = Pixel8U{ (unsigned char)cv::saturate_cast(c[0]),