From 351a0a274fe9d3e0141dbbe9ad8c36a4f0453186 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Fri, 16 Oct 2026 18:13:21 +0800 Subject: [PATCH] =?UTF-8?q?=E8=A7=A3=E5=86=B3=E7=9F=A9=E5=BD=A2=E6=9D=82?= =?UTF-8?q?=E8=89=B2=E5=92=8C=E8=89=B2=E5=B7=AE=E9=97=AE=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 93 +++++++++++++++++++++++++++++++++------ 1 file changed, 80 insertions(+), 13 deletions(-) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index d16be29..f015ca2 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -16503,7 +16503,7 @@ bool MeshTexture::RasterizeVirtualFaces( continue; } -// ========== 用重心坐标 + 3D 投影替代 homography ========== + // ========== 用重心坐标 + 3D 投影替代 homography ========== cv::Mat mapX(patchH, patchW, CV_32FC1), mapY(patchH, patchW, CV_32FC1); mapX.setTo(-1); mapY.setTo(-1); @@ -16513,6 +16513,14 @@ bool MeshTexture::RasterizeVirtualFaces( FIndex faceIdx = vf.faces[0]; const Face& face = scene.mesh.faces[faceIdx]; + // 提取 3D 顶点和 UV + Point3f v[3]; + TexCoord uv[3]; + for (int j = 0; j < 3; ++j) { + v[j] = scene.mesh.vertices[face[j]]; + uv[j] = scene.mesh.faceTexcoords[faceIdx * 3 + j]; // 注意这里的索引方式 + } + // 获取相机参数 const Camera& cam = images[mainView].camera; bool camValid = (cam.K(0,0) > 0 && cam.K(1,1) > 0); @@ -16529,14 +16537,6 @@ bool MeshTexture::RasterizeVirtualFaces( } } } else { - // 提取 3D 顶点和 UV - Point3f v[3]; - TexCoord uv[3]; - for (int j = 0; j < 3; ++j) { - v[j] = scene.mesh.vertices[face[j]]; - uv[j] = scene.mesh.faceTexcoords[faceIdx * 3 + j]; // 注意这里的索引方式 - } - // 对 patch 内每个像素做重心坐标投影 for (int py = minY; py <= maxY; ++py) { for (int px = minX; px <= maxX; ++px) { @@ -16570,16 +16570,68 @@ bool MeshTexture::RasterizeVirtualFaces( cv::Mat mainPatch; cv::remap(images[mainView].image, mainPatch, mapX, mapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); - // ========== 从辅助视图采样(全部用主视图的映射)========== + // ========== 从辅助视图采样(每个视图用自己的 3D 投影)========== cv::Mat lowFreqPatch(patchH, patchW, CV_32FC3, cv::Scalar(0,0,0)); if (hasLowFreqViews) { float totalWeight = 0; for (const auto& lfv : lowFreqViews) { if (lfv.viewId >= (IIndex)images.size() || images[lfv.viewId].image.empty()) continue; - // ★★★ 所有视图都用主视图的 mapX/mapY,不再各自构建 homography ★★★ + // ★★★ 每个辅助视图用自己的相机做 3D 投影 ★★★ + const Camera& auxCam = images[lfv.viewId].camera; + bool auxCamValid = (auxCam.K(0,0) > 0 && auxCam.K(1,1) > 0); + + cv::Mat auxMapX(patchH, patchW, CV_32FC1), auxMapY(patchH, patchW, CV_32FC1); + auxMapX.setTo(-1); + auxMapY.setTo(-1); + + if (!auxCamValid) { + // fallback 到该视图的 homography + const auto& geom = m_virtualFaceGeometries[i]; + auto it = geom.viewHomographies.find(lfv.viewId); + if (it == geom.viewHomographies.end()) continue; + const float* H = it->second.ptr(); + for (int y = minY; y <= maxY; ++y) { + for (int x = minX; x <= maxX; ++x) { + float u = (float)x / (float)textureSize, v = (float)y / (float)textureSize; + float w = H[6]*u + H[7]*v + H[8]; + if (std::abs(w) < 1e-12f) { + auxMapX.at(y-minY,x-minX) = -1; + auxMapY.at(y-minY,x-minX) = -1; + continue; + } + auxMapX.at(y-minY,x-minX) = (H[0]*u+H[1]*v+H[2])/w; + auxMapY.at(y-minY,x-minX) = (H[3]*u+H[4]*v+H[5])/w; + } + } + } else { + // 用辅助视图自己的相机做 3D 投影 + for (int py = minY; py <= maxY; ++py) { + for (int px = minX; px <= maxX; ++px) { + float u = (float)px / (float)textureSize; + float v_coord = (float)py / (float)textureSize; + + float b0, b1, b2; + if (!ComputeBarycentric(u, v_coord, uv[0], uv[1], uv[2], b0, b1, b2)) { + continue; + } + + Point3f P = v[0] * b0 + v[1] * b1 + v[2] * b2; + + Point2f imgPt = auxCam.ProjectPointP(P); + if (imgPt.x < 0 || imgPt.x >= images[lfv.viewId].image.cols || + imgPt.y < 0 || imgPt.y >= images[lfv.viewId].image.rows) { + continue; + } + + auxMapX.at(py - minY, px - minX) = imgPt.x; + auxMapY.at(py - minY, px - minX) = imgPt.y; + } + } + } + cv::Mat auxPatch; - cv::remap(images[lfv.viewId].image, auxPatch, mapX, mapY, + cv::remap(images[lfv.viewId].image, auxPatch, auxMapX, auxMapY, cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0)); cv::Mat auxFloat; @@ -16649,10 +16701,25 @@ bool MeshTexture::RasterizeVirtualFaces( // cv::Mat finalPatch = highFreq.clone(); // finalPatch = cv::Mat::zeros(finalPatch.size(), finalPatch.type()); - cv::Mat finalPatch = mainFloat; // 只用主视图 + // cv::Mat finalPatch = mainFloat; // 只用主视图 // cv::Mat finalPatch = lowFreqPatch * 0.3f + highFreq * 0.7f; // 低频只做主视图,不做多视图平均(避免发灰) // cv::Mat finalPatch = mainFloat * 0.7f + highFreq * 0.3f; + // cv::Mat finalPatch = lowFreqPatch; + + // 先填空洞:lowFreqPatch 中黑色空洞用 mainFloat 填充 + cv::Mat filledLowFreq = lowFreqPatch.clone(); + for (int y = 0; y < patchH; ++y) { + for (int x = 0; x < patchW; ++x) { + cv::Vec3f color = filledLowFreq.at(y, x); + if (color[0] == 0.0f && color[1] == 0.0f && color[2] == 0.0f) { + filledLowFreq.at(y, x) = mainFloat.at(y, x); + } + } + } + + // 再与 mainFloat 混合(低频占主导,主视图补充细节) + cv::Mat finalPatch = filledLowFreq * 0.7f + mainFloat * 0.3f; // 钳制到 [0, 1] cv::Mat clampedPatch;