接缝优化
This commit is contained in:
1 parent
b89bf91ccf
commit
56f3c93216
1 file changed
+159
@@ -763,6 +763,52 @@ public:
|
||||
unsigned nTextureSizeMultiple,
|
||||
Pixel8U colEmpty,
|
||||
Mesh::Image8U3Arr& outTextures);
|
||||
// ========== 辅助函数 ==========
|
||||
|
||||
// 双线性采样(适配 SEACAVE::TImage<Pixel8U>)
|
||||
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<int> 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());
|
||||
|
||||
Reference in new issue
Block a user