From 56f3c93216e1712628afd804938deb644c4e9005 Mon Sep 17 00:00:00 2001 From: hesuicong Date: Tue, 18 Aug 2026 11:02:48 +0800 Subject: [PATCH] =?UTF-8?q?=E6=8E=A5=E7=BC=9D=E4=BC=98=E5=8C=96?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- libs/MVS/SceneTexture.cpp | 159 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 159 insertions(+) diff --git a/libs/MVS/SceneTexture.cpp b/libs/MVS/SceneTexture.cpp index fb302f0..7a514eb 100644 --- a/libs/MVS/SceneTexture.cpp +++ b/libs/MVS/SceneTexture.cpp @@ -763,6 +763,52 @@ public: unsigned nTextureSizeMultiple, Pixel8U colEmpty, Mesh::Image8U3Arr& outTextures); + // ========== 辅助函数 ========== + + // 双线性采样(适配 SEACAVE::TImage) + static inline Pixel8U SampleBilinear(const Image8U3& img, const Point2f& pt) { + float x = pt.x * img.width() - 0.5f; + float y = pt.y * img.height() - 0.5f; + int x0 = (int)floorf(x), y0 = (int)floorf(y); + int x1 = x0 + 1, y1 = y0 + 1; + float wx = x - x0, wy = y - y0; + + // 边界裁剪 + x0 = std::max(0, std::min(x0, (int)img.width() - 1)); + x1 = std::max(0, std::min(x1, (int)img.width() - 1)); + y0 = std::max(0, std::min(y0, (int)img.height() - 1)); + y1 = std::max(0, std::min(y1, (int)img.height() - 1)); + + const Pixel8U& p00 = img(y0, x0); + const Pixel8U& p01 = img(y0, x1); + const Pixel8U& p10 = img(y1, x0); + const Pixel8U& p11 = img(y1, x1); + + auto lerp = [](uint8_t a, uint8_t b, float t) -> uint8_t { + return (uint8_t)(a * (1 - t) + b * t); + }; + uint8_t r = lerp(lerp(p00.r, p01.r, wx), lerp(p10.r, p11.r, wx), wy); + uint8_t g = lerp(lerp(p00.g, p01.g, wx), lerp(p10.g, p11.g, wx), wy); + uint8_t b = lerp(lerp(p00.b, p01.b, wx), lerp(p10.b, p11.b, wx), wy); + return Pixel8U(b, g, r); // BGR 存储 + } + + // 重心坐标计算 + static inline Point3f Barycentric(const TexCoord& p, const TexCoord& a, + const TexCoord& b, const TexCoord& c) { + float den = (b.y - c.y) * (a.x - c.x) + (c.x - b.x) * (a.y - c.y); + if (fabsf(den) < 1e-10f) return Point3f(-1, -1, -1); + float v = ((b.y - c.y) * (p.x - c.x) + (c.x - b.x) * (p.y - c.y)) / den; + float w = ((c.y - a.y) * (p.x - c.x) + (a.x - c.x) * (p.y - c.y)) / den; + float u = 1.0f - v - w; + return Point3f(u, v, w); + } + + // 从重心坐标恢复 3D 点 + static inline Point3f BaryTo3D(const Point3f& bary, const Point3f& v0, + const Point3f& v1, const Point3f& v2) { + return v0 * bary.x + v1 * bary.y + v2 * bary.z; + } struct TexelScore { float score = -1.0f; @@ -14409,6 +14455,119 @@ bool MeshTexture::RasterizeVirtualFaces( } } } + + // ========== 接缝区域多视图加权融合 ========== + // 收集需要融合的面(出现在 seamEdges 中的面) + std::set seamFaceIndices; + for (const auto& edge : seamEdges) { + seamFaceIndices.insert((int)edge.i); + seamFaceIndices.insert((int)edge.j); + } + + if (!seamFaceIndices.empty()) { + DEBUG_EXTRA("[Blend] Fusing %zu seam faces...", seamFaceIndices.size()); + + for (int i : seamFaceIndices) { + if (i < 0 || i >= (int)virtualFaceMap.size()) continue; + const VirtualFace& vf = virtualFaceMap[i]; + if (vf.faces.empty() || virtualFaceViews[i].empty()) continue; + + IIndex refView = virtualFaceViews[i][0]; + const auto& candidateViews = virtualFaceViews[i]; + const auto& candidateWeights = virtualFaceViewWeights[i]; + + // 获取该面的 UV 包围盒 + const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i]; + if (!geom.isValid) continue; + + int minX = std::max(0, (int)floor(geom.uvBounds.ptMin.x() * textureSize)); + int maxX = std::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.x() * textureSize)); + int minY = std::max(0, (int)floor(geom.uvBounds.ptMin.y() * textureSize)); + int maxY = std::max(0, (int)ceil(geom.uvBounds.ptMax.y() * textureSize)); + if (minX > maxX || minY > maxY) continue; + + // 逐像素多视图融合 + for (int y = minY; y <= maxY; ++y) { + for (int x = minX; x <= maxX; ++x) { + size_t idx = y * textureSize + x; + if (m_texelScores[idx].viewID < 0) continue; + + // 只处理当前面贡献的像素(避免覆盖其他面) + // 用 score 判断:如果当前面不是胜者,跳过 + float myScore = candidateWeights.empty() ? 0.5f : candidateWeights[0]; + if (m_texelScores[idx].score > myScore + 0.01f) continue; + + // 计算 3D 点(从重心坐标) + // 简化:用该面第一个三角形 + FIndex fid = vf.faces[0]; + const Face& face = scene.mesh.faces[fid]; + const TexCoord* uvs = &scene.mesh.faceTexcoords[fid * 3]; + Point3f bary = Barycentric( + TexCoord((float)x / textureSize, (float)y / textureSize), + uvs[0], uvs[1], uvs[2]); + if (bary.x < 0 || bary.y < 0 || bary.z < 0) continue; + + Point3f pt3D = BaryTo3D(bary, + scene.mesh.vertices[face[0]], + scene.mesh.vertices[face[1]], + scene.mesh.vertices[face[2]]); + + // 多视图采样 + cv::Vec3f blended(0, 0, 0); + float totalW = 0.0f; + + for (size_t k = 0; k < candidateViews.size(); ++k) { + IIndex vid = candidateViews[k]; + float w = candidateWeights[k]; + if (w < 0.01f) continue; + + const Image& img = images[vid]; + Point2f pt2D = img.camera.ProjectPoint(Point3d(pt3D)); + if (pt2D.x < 0 || pt2D.y < 0 || + pt2D.x >= img.image.width() - 1 || + pt2D.y >= img.image.height() - 1) continue; + + Pixel8U c = SampleBilinear(img.image, pt2D); + cv::Vec3f color(c.r, c.g, c.b); + + // 局部增益:以 refView 为参考 + if (vid != refView) { + const Image& refImg = images[refView]; + Point2f refPt = refImg.camera.ProjectPoint(Point3d(pt3D)); + if (refPt.x >= 0 && refPt.y >= 0 && + refPt.x < refImg.image.width() - 1 && + refPt.y < refImg.image.height() - 1) { + Pixel8U rc = SampleBilinear(refImg.image, refPt); + cv::Vec3f refColor(rc.r, rc.g, rc.b); + // 加 1 防除零 + float gr = (refColor[0] + 1.0f) / (color[0] + 1.0f); + float gg = (refColor[1] + 1.0f) / (color[1] + 1.0f); + float gb = (refColor[2] + 1.0f) / (color[2] + 1.0f); + gr = std::max(0.5f, std::min(2.0f, gr)); + gg = std::max(0.5f, std::min(2.0f, gg)); + gb = std::max(0.5f, std::min(2.0f, gb)); + color = cv::Vec3f(color[0]*gr, color[1]*gg, color[2]*gb); + } + } + + blended += color * w; + totalW += w; + } + + if (totalW > 0.01f) { + blended /= totalW; + atlas(y, x) = Pixel8U( + (uint8_t)std::min(255.0f, blended[2]), // B + (uint8_t)std::min(255.0f, blended[1]), // G + (uint8_t)std::min(255.0f, blended[0]) // R + ); + } + } + } + } + DEBUG_EXTRA("[Blend] Multi-view seam fusion done"); + } + // ========== 接缝融合 END ========== m_texelScores.clear(); DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());