全局优化
This commit is contained in:
1 parent
04495f3426
commit
b89bf91ccf
1 file changed
+300
-144
+300
-144
@@ -1091,6 +1091,34 @@ public:
|
||||
void ProjectFaceToTexture(FIndex faceID, IIndex viewID, const TexCoord* uv, Image8U3& texture);
|
||||
bool PointInTriangle(const Point2f& p, const Point2f& a, const Point2f& b, const Point2f& c, Point3f& bary);
|
||||
int ComputeOptimalTextureSize(float uvWidth, float uvHeight, unsigned multiple);
|
||||
std::vector<std::vector<float>> m_virtualFaceViewWeights;
|
||||
// ===== Seam 相关结构 =====
|
||||
struct SeamEdge {
|
||||
uint32_t rcPatchID0;
|
||||
uint32_t rcPatchID1;
|
||||
FIndex faceID0; // 对应第一个面的VirtualFaceGeometry索引
|
||||
FIndex faceID1; // 对应第二个面的VirtualFaceGeometry索引
|
||||
Point2f uv0; // atlas上边的起点UV
|
||||
Point2f uv1; // atlas上边的终点UV
|
||||
};
|
||||
|
||||
// ===== RCPatch(你已有的,确认包含以下字段)=====
|
||||
struct RCPatch {
|
||||
IIndex viewID;
|
||||
cv::Rect rect;
|
||||
std::vector<FIndex> faces;
|
||||
Point2f uvMin, uvMax;
|
||||
};
|
||||
|
||||
std::vector<SeamEdge> rcSeamEdges;
|
||||
std::vector<RCPatch> rcPatches;
|
||||
int currentTextureSize;
|
||||
|
||||
// ===== 函数声明 =====
|
||||
Color SampleImageBilinear(const cv::Mat& img, float x, float y);
|
||||
void BuildSeamEdgesFromRCPatches();
|
||||
void SeamBlendingFromOriginalImages(Image8U3& atlas);
|
||||
|
||||
// Bruce
|
||||
//*
|
||||
template <typename PIXEL>
|
||||
@@ -14254,7 +14282,7 @@ void MeshTexture::FillTextureHoles(std::vector<Image8U3>& textures, Pixel8U colE
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 3. RC 风格光栅化主函数
|
||||
// 3. RC 风格光栅化主函数(含接缝优化)
|
||||
// ============================================================
|
||||
bool MeshTexture::RasterizeVirtualFaces(
|
||||
const VirtualFaceMap& virtualFaceMap,
|
||||
@@ -14277,17 +14305,14 @@ 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);
|
||||
int textureSize = ComputeOptimalTextureSizeAdaptive(
|
||||
int textureSize = ComputeOptimalTextureSizeAdaptive(
|
||||
virtualFaceMap, virtualFaceViews, nTextureSizeMultiple);
|
||||
|
||||
// 兜底
|
||||
if (textureSize < 1024) textureSize = 1024;
|
||||
if (textureSize < 1024) textureSize = 1024;
|
||||
if (textureSize > 16384) textureSize = 16384;
|
||||
|
||||
// --------------------------------------------------
|
||||
@@ -14297,17 +14322,17 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
Image8U3& atlas = outTextures.back();
|
||||
atlas.setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
|
||||
|
||||
// ✅ 初始化评分缓冲
|
||||
m_texelScores.assign(textureSize * textureSize, TexelScore{});
|
||||
m_texelScores.assign(textureSize * textureSize, TexelScore{});
|
||||
|
||||
// ✅ 计算所有虚拟面的几何和映射矩阵
|
||||
if (!ComputeVirtualFaceGeometry(virtualFaceMap)) {
|
||||
DEBUG_EXTRA("Failed to compute virtual face geometries");
|
||||
return false;
|
||||
}
|
||||
|
||||
currentTextureSize = textureSize;
|
||||
|
||||
// --------------------------------------------------
|
||||
// 3. RC 风格光栅化:按虚拟面批量处理
|
||||
// 3. RC 风格光栅化
|
||||
// --------------------------------------------------
|
||||
#ifdef _USE_OPENMP
|
||||
#pragma omp parallel for schedule(dynamic)
|
||||
@@ -14326,129 +14351,277 @@ bool MeshTexture::RasterizeVirtualFaces(
|
||||
if (srcImg.image.empty() || srcImg.image.cols < 2 || srcImg.image.rows < 2)
|
||||
continue;
|
||||
|
||||
// ✅ UV 包围盒 → 纹理像素范围
|
||||
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::min(textureSize - 1, (int)ceil(geom.uvBounds.ptMax.y() * textureSize));
|
||||
|
||||
if (minX > maxX || minY > maxY) continue;
|
||||
|
||||
int patchW = maxX - minX + 1;
|
||||
int patchH = maxY - minY + 1;
|
||||
|
||||
// ✅ 映射矩阵
|
||||
cv::Mat mapX(patchH, patchW, CV_32FC1);
|
||||
cv::Mat mapY(patchH, patchW, CV_32FC1);
|
||||
|
||||
// ✅ 直接展开 H 系数(无临时 Mat,RC 标准写法)
|
||||
const float* H = geom.homography.ptr<float>();
|
||||
|
||||
for (int y = minY; y <= maxY; ++y) {
|
||||
for (int x = minX; x <= maxX; ++x) {
|
||||
float u = (float)x / (float)textureSize;
|
||||
float v = (float)y / (float)textureSize;
|
||||
|
||||
float w = H[6] * u + H[7] * v + H[8];
|
||||
|
||||
// ✅ 数值保护(防止除零)
|
||||
float w = H[6]*u + H[7]*v + H[8];
|
||||
if (std::abs(w) < 1e-12f) {
|
||||
mapX.at<float>(y - minY, x - minX) = -1.0f;
|
||||
mapY.at<float>(y - minY, x - minX) = -1.0f;
|
||||
mapX.at<float>(y-minY, x-minX) = -1.0f;
|
||||
mapY.at<float>(y-minY, x-minX) = -1.0f;
|
||||
continue;
|
||||
}
|
||||
|
||||
float imgX = (H[0] * u + H[1] * v + H[2]) / w;
|
||||
float imgY = (H[3] * u + H[4] * v + H[5]) / w;
|
||||
|
||||
mapX.at<float>(y - minY, x - minX) = imgX;
|
||||
mapY.at<float>(y - minY, x - minX) = imgY;
|
||||
mapX.at<float>(y-minY, x-minX) = (H[0]*u + H[1]*v + H[2]) / w;
|
||||
mapY.at<float>(y-minY, x-minX) = (H[3]*u + H[4]*v + H[5]) / w;
|
||||
}
|
||||
}
|
||||
|
||||
// ✅ 一次性 remap 整个 patch
|
||||
cv::Mat patch;
|
||||
cv::remap(srcImg.image, patch, mapX, mapY,
|
||||
cv::INTER_LINEAR, cv::BORDER_CONSTANT,
|
||||
cv::Scalar(0, 0, 0));
|
||||
cv::INTER_LINEAR, cv::BORDER_CONSTANT, cv::Scalar(0,0,0));
|
||||
|
||||
// ✅ 拷贝到 atlas(OpenMP critical 区)
|
||||
#pragma omp critical
|
||||
{
|
||||
for (int y = 0; y < patchH; ++y) {
|
||||
for (int x = 0; x < patchW; ++x) {
|
||||
// 在 RasterizeVirtualFaces 的像素写入循环中
|
||||
// ✅ 只保留最核心的逻辑,不做任何衰减
|
||||
#pragma omp critical
|
||||
{
|
||||
for (int y = 0; y < patchH; ++y) {
|
||||
for (int x = 0; x < patchW; ++x) {
|
||||
cv::Vec3b color = patch.at<cv::Vec3b>(y, x);
|
||||
if (color[0] == 0 && color[1] == 0 && color[2] == 0) continue;
|
||||
|
||||
cv::Vec3b color = patch.at<cv::Vec3b>(y, x);
|
||||
if (color[0] == 0 && color[1] == 0 && color[2] == 0)
|
||||
continue;
|
||||
int atlasX = x + minX;
|
||||
int atlasY = y + minY;
|
||||
if (atlasX < 0 || atlasX >= textureSize ||
|
||||
atlasY < 0 || atlasY >= textureSize) continue;
|
||||
|
||||
int atlasX = x + minX;
|
||||
int atlasY = y + minY;
|
||||
if (atlasX < 0 || atlasX >= textureSize ||
|
||||
atlasY < 0 || atlasY >= textureSize)
|
||||
continue;
|
||||
size_t idx = atlasY * textureSize + atlasX;
|
||||
TexelScore& ts = m_texelScores[idx];
|
||||
float currentScore = virtualFaceViewWeights[i].empty()
|
||||
? -1.0f : virtualFaceViewWeights[i][0];
|
||||
|
||||
size_t idx = atlasY * textureSize + atlasX;
|
||||
TexelScore& ts = m_texelScores[idx];
|
||||
|
||||
float currentScore = virtualFaceViewWeights[i].empty()
|
||||
? -1.0f
|
||||
: virtualFaceViewWeights[i][0];
|
||||
|
||||
// ✅ 正常写入,不衰减,不修改
|
||||
if (currentScore > ts.score) {
|
||||
atlas(atlasY, atlasX) =
|
||||
Pixel8U{color[2], color[1], color[0]};
|
||||
ts.score = currentScore;
|
||||
ts.viewID = viewID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
m_texelScores.clear();
|
||||
|
||||
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());
|
||||
|
||||
// ✅ 新增:接缝融合后处理
|
||||
if (true) { // 可配置开关
|
||||
TD_TIMER_START();
|
||||
|
||||
// 1. 检测需要融合的接缝
|
||||
std::vector<SeamInfo> seams;
|
||||
if (!DetectSeamsForBlending(virtualFaceMap, virtualFaceViews, seams)) {
|
||||
DEBUG_EXTRA("Seam detection failed");
|
||||
return true; // 不影响主流程
|
||||
}
|
||||
// // 2. 方向性高斯羽化
|
||||
// FeatherSeams(seams, textureSize, atlas);
|
||||
|
||||
// 2. 对每个接缝进行融合
|
||||
for (const SeamInfo& seam : seams) {
|
||||
const VirtualFaceGeometry& geomA = m_virtualFaceGeometries[seam.faceA];
|
||||
const VirtualFaceGeometry& geomB = m_virtualFaceGeometries[seam.faceB];
|
||||
|
||||
// 提取接缝两侧像素
|
||||
std::vector<Point2i> pixelsA, pixelsB;
|
||||
ExtractSeamPixels(seam, geomA, geomB, textureSize, pixelsA, pixelsB);
|
||||
|
||||
if (!pixelsA.empty() || !pixelsB.empty()) {
|
||||
// 执行融合
|
||||
BlendSeamPixels(seam, pixelsA, pixelsB, geomA, geomB, textureSize, atlas);
|
||||
if (currentScore > ts.score) {
|
||||
atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]};
|
||||
ts.score = currentScore;
|
||||
ts.viewID = viewID;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("Seam blending completed: %zu seams processed (%s)",
|
||||
seams.size(), TD_TIMER_GET_FMT().c_str());
|
||||
}
|
||||
|
||||
m_texelScores.clear();
|
||||
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());
|
||||
|
||||
m_virtualFaceViewWeights = virtualFaceViewWeights;
|
||||
|
||||
// --------------------------------------------------
|
||||
// 4. 构建 RCPatch
|
||||
// --------------------------------------------------
|
||||
rcPatches.clear();
|
||||
for (size_t i = 0; i < virtualFaceMap.size(); ++i) {
|
||||
if (virtualFaceViews[i].empty()) continue;
|
||||
const VirtualFaceGeometry& geom = m_virtualFaceGeometries[i];
|
||||
if (!geom.isValid) continue;
|
||||
|
||||
RCPatch patch;
|
||||
patch.viewID = virtualFaceViews[i][0];
|
||||
patch.faces = { static_cast<FIndex>(i) };
|
||||
patch.uvMin = geom.uvBounds.ptMin;
|
||||
patch.uvMax = geom.uvBounds.ptMax;
|
||||
patch.rect = cv::Rect(
|
||||
(int)(geom.uvBounds.ptMin.x() * textureSize),
|
||||
(int)(geom.uvBounds.ptMin.y() * textureSize),
|
||||
(int)((geom.uvBounds.ptMax.x() - geom.uvBounds.ptMin.x()) * textureSize) + 1,
|
||||
(int)((geom.uvBounds.ptMax.y() - geom.uvBounds.ptMin.y()) * textureSize) + 1
|
||||
);
|
||||
patch.rect &= cv::Rect(0, 0, textureSize, textureSize);
|
||||
if (patch.rect.width > 0 && patch.rect.height > 0)
|
||||
rcPatches.push_back(patch);
|
||||
}
|
||||
DEBUG_EXTRA("Created %zu RC patches", rcPatches.size());
|
||||
|
||||
// --------------------------------------------------
|
||||
// 5. 构建接缝边
|
||||
// --------------------------------------------------
|
||||
BuildSeamEdgesFromRCPatches();
|
||||
|
||||
// --------------------------------------------------
|
||||
// 6. 接缝融合(从原始图像采样)
|
||||
// --------------------------------------------------
|
||||
if (!seamEdges.empty()) {
|
||||
TD_TIMER_START();
|
||||
SeamBlendingFromOriginalImages(atlas);
|
||||
DEBUG_EXTRA("Seam blending completed: %zu edges (%s)",
|
||||
seamEdges.size(), TD_TIMER_GET_FMT().c_str());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 构建接缝边(基于 rcPatches + faceFaces)
|
||||
// ============================================================
|
||||
void MeshTexture::BuildSeamEdgesFromRCPatches()
|
||||
{
|
||||
rcSeamEdges.clear();
|
||||
|
||||
// face → rcPatch 映射
|
||||
std::vector<uint32_t> faceToRCPatch(scene.mesh.faces.size(), NO_ID);
|
||||
for (uint32_t pi = 0; pi < rcPatches.size(); ++pi) {
|
||||
for (FIndex f : rcPatches[pi].faces) {
|
||||
if (f < (FIndex)faceToRCPatch.size())
|
||||
faceToRCPatch[f] = pi;
|
||||
}
|
||||
}
|
||||
|
||||
for (FIndex f0 = 0; f0 < (FIndex)scene.mesh.faces.size(); ++f0) {
|
||||
uint32_t p0 = faceToRCPatch[f0];
|
||||
if (p0 == NO_ID) continue;
|
||||
|
||||
const Mesh::FaceFaces& neighbors = scene.mesh.faceFaces[f0];
|
||||
for (int e = 0; e < 3; ++e) {
|
||||
FIndex f1 = neighbors[e];
|
||||
if (f1 == NO_ID) continue;
|
||||
|
||||
uint32_t p1 = faceToRCPatch[f1];
|
||||
if (p1 == NO_ID || p1 == p0) continue;
|
||||
|
||||
// ---------- 找共享边的两个顶点 ----------
|
||||
const Mesh::Face& face0 = scene.mesh.faces[f0];
|
||||
int v0_idx = e;
|
||||
int v1_idx = (e + 1) % 3;
|
||||
|
||||
// 在 f1 中找对应顶点
|
||||
int idx0_in_f1 = -1, idx1_in_f1 = -1;
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
if (scene.mesh.faces[f1][k] == face0[v0_idx]) idx0_in_f1 = k;
|
||||
if (scene.mesh.faces[f1][k] == face0[v1_idx]) idx1_in_f1 = k;
|
||||
}
|
||||
if (idx0_in_f1 == -1 || idx1_in_f1 == -1)
|
||||
continue;
|
||||
|
||||
// ---------- ✅ UV 坐标(关键) ----------
|
||||
const TexCoord& uv0_p0 = scene.mesh.faceTexcoords[f0 * 3 + v0_idx];
|
||||
const TexCoord& uv1_p0 = scene.mesh.faceTexcoords[f0 * 3 + v1_idx];
|
||||
const TexCoord& uv0_p1 = scene.mesh.faceTexcoords[f1 * 3 + idx0_in_f1];
|
||||
const TexCoord& uv1_p1 = scene.mesh.faceTexcoords[f1 * 3 + idx1_in_f1];
|
||||
|
||||
// ---------- 构建接缝边 ----------
|
||||
SeamEdge edge;
|
||||
edge.rcPatchID0 = p0;
|
||||
edge.rcPatchID1 = p1;
|
||||
edge.faceID0 = f0;
|
||||
edge.faceID1 = f1;
|
||||
edge.uv0 = (uv0_p0 + uv1_p0) * 0.5f; // patch0 边中点
|
||||
edge.uv1 = (uv0_p1 + uv1_p1) * 0.5f; // patch1 边中点
|
||||
|
||||
rcSeamEdges.push_back(edge);
|
||||
}
|
||||
}
|
||||
|
||||
DEBUG_EXTRA("Built %zu RC seam edges", rcSeamEdges.size());
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 接缝融合:从原始图像采样 + YCrCb 羽化
|
||||
// ============================================================
|
||||
void MeshTexture::SeamBlendingFromOriginalImages(Image8U3& atlas)
|
||||
{
|
||||
const int R = 6; // 过渡半径(像素),头发区域建议≥6
|
||||
|
||||
for (const SeamEdge& edge : rcSeamEdges) {
|
||||
if (edge.rcPatchID0 >= rcPatches.size() || edge.rcPatchID1 >= rcPatches.size())
|
||||
continue;
|
||||
if (edge.faceID0 >= (FIndex)m_virtualFaceGeometries.size() ||
|
||||
edge.faceID1 >= (FIndex)m_virtualFaceGeometries.size())
|
||||
continue;
|
||||
|
||||
const RCPatch& patch0 = rcPatches[edge.rcPatchID0];
|
||||
const RCPatch& patch1 = rcPatches[edge.rcPatchID1];
|
||||
if (patch0.viewID >= (IIndex)images.size() || patch1.viewID >= (IIndex)images.size())
|
||||
continue;
|
||||
|
||||
const cv::Mat& img0 = images[patch0.viewID].image;
|
||||
const cv::Mat& img1 = images[patch1.viewID].image;
|
||||
if (img0.empty() || img1.empty()) continue;
|
||||
|
||||
// 1. 取两个面对应的单应矩阵(光栅化时用的同一个!)
|
||||
const VirtualFaceGeometry& geom0 = m_virtualFaceGeometries[edge.faceID0];
|
||||
const VirtualFaceGeometry& geom1 = m_virtualFaceGeometries[edge.faceID1];
|
||||
if (!geom0.isValid || !geom1.isValid) continue;
|
||||
|
||||
// 2. 计算边的方向和垂直法向(atlas UV空间)
|
||||
Point2f edgeDir = edge.uv1 - edge.uv0;
|
||||
float edgeLenUV = std::sqrt(edgeDir.x*edgeDir.x + edgeDir.y*edgeDir.y);
|
||||
if (edgeLenUV < 1e-6f) continue;
|
||||
Point2f edgeDirNorm = edgeDir / edgeLenUV;
|
||||
Point2f perpDir(-edgeDirNorm.y, edgeDirNorm.x); // 垂直于边的法向
|
||||
|
||||
// 3. 沿边多采样(长边必须多采,避免漏采高频细节)
|
||||
int numEdgeSamples = std::max(1, (int)(edgeLenUV * currentTextureSize));
|
||||
for (int s = 0; s < numEdgeSamples; ++s) {
|
||||
float tEdge = (float)s / (float)numEdgeSamples;
|
||||
Point2f edgeUV = edge.uv0 * (1.0f - tEdge) + edge.uv1 * tEdge; // atlas上的边点UV
|
||||
|
||||
// 4. 沿垂直边的方向扩展过渡带(核心!)
|
||||
for (int r = -R; r <= R; ++r) {
|
||||
float tBlend = (float)(r + R) / (2.0f * R); // 混合权重:r<0偏patch0,r>0偏patch1
|
||||
float blendW = 1.0f - std::abs(tBlend - 0.5f) * 2.0f; // 中心权重高
|
||||
|
||||
// atlas UV偏移(垂直于边的方向)
|
||||
float offsetUV = (float)r / (float)currentTextureSize;
|
||||
Point2f curUV = edgeUV + perpDir * offsetUV;
|
||||
|
||||
// 5. 用单应矩阵把atlas UV映射到两个视图的图像坐标(和光栅化逻辑完全一致!)
|
||||
// 映射patch0的视图
|
||||
float w0 = geom0.homography.at<float>(2,0)*curUV.x + geom0.homography.at<float>(2,1)*curUV.y + geom0.homography.at<float>(2,2);
|
||||
if (std::abs(w0) < 1e-12f) continue;
|
||||
float imgX0 = (geom0.homography.at<float>(0,0)*curUV.x + geom0.homography.at<float>(0,1)*curUV.y + geom0.homography.at<float>(0,2)) / w0;
|
||||
float imgY0 = (geom0.homography.at<float>(1,0)*curUV.x + geom0.homography.at<float>(1,1)*curUV.y + geom0.homography.at<float>(1,2)) / w0;
|
||||
|
||||
// 映射patch1的视图
|
||||
float w1 = geom1.homography.at<float>(2,0)*curUV.x + geom1.homography.at<float>(2,1)*curUV.y + geom1.homography.at<float>(2,2);
|
||||
if (std::abs(w1) < 1e-12f) continue;
|
||||
float imgX1 = (geom1.homography.at<float>(0,0)*curUV.x + geom1.homography.at<float>(0,1)*curUV.y + geom1.homography.at<float>(0,2)) / w1;
|
||||
float imgY1 = (geom1.homography.at<float>(1,0)*curUV.x + geom1.homography.at<float>(1,1)*curUV.y + geom1.homography.at<float>(1,2)) / w1;
|
||||
|
||||
// 6. 从两个原始视图采样同一个三维点的颜色
|
||||
Color col0 = RGB2YCBCR(SampleImageBilinear(img0, imgX0, imgY0));
|
||||
Color col1 = RGB2YCBCR(SampleImageBilinear(img1, imgX1, imgY1));
|
||||
|
||||
// 7. 转换到atlas像素坐标
|
||||
int px = (int)(curUV.x * currentTextureSize);
|
||||
int py = (int)(curUV.y * currentTextureSize);
|
||||
if (px < 0 || px >= currentTextureSize || py < 0 || py >= currentTextureSize)
|
||||
continue;
|
||||
|
||||
// 8. 结合评分系统:只覆盖评分更低的像素(避免破坏最优视图)
|
||||
size_t idx = py * currentTextureSize + px;
|
||||
if (idx >= m_texelScores.size()) continue;
|
||||
float currentScore = m_texelScores[idx].score;
|
||||
float edgeScore0 = m_virtualFaceViewWeights[edge.faceID0].empty() ? -1 : m_virtualFaceViewWeights[edge.faceID0][0];
|
||||
float edgeScore1 = m_virtualFaceViewWeights[edge.faceID1].empty() ? -1 : m_virtualFaceViewWeights[edge.faceID1][0];
|
||||
// 只有当混合后的权重对应的视图评分更高时才覆盖
|
||||
if (tBlend < 0.5f && edgeScore0 <= currentScore) continue;
|
||||
if (tBlend >= 0.5f && edgeScore1 <= currentScore) continue;
|
||||
|
||||
// 9. 混合颜色(YCrCb空间,避免亮度偏移)
|
||||
Color mixedYCbCr = col0 * (1.0f - tBlend) + col1 * tBlend;
|
||||
Color mixedRGB = YCBCR2RGB(mixedYCbCr);
|
||||
|
||||
// 10. 写入atlas
|
||||
Pixel8U& p = atlas(py, px);
|
||||
p = Pixel8U(
|
||||
CLAMP((int)roundf(mixedRGB.x), 0, 255),
|
||||
CLAMP((int)roundf(mixedRGB.y), 0, 255),
|
||||
CLAMP((int)roundf(mixedRGB.z), 0, 255)
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
float MeshTexture::EstimatePixelSize(const Point3f& faceCenter, const Normal& faceNormal,
|
||||
const Image& image) {
|
||||
const Camera& cam = image.camera;
|
||||
@@ -18193,52 +18366,6 @@ bool PointInTriangle(const Point2f& p, const Point2f& a, const Point2f& b, const
|
||||
barycentric.y >= 0 && barycentric.y <= 1 &&
|
||||
barycentric.z >= 0 && barycentric.z <= 1);
|
||||
}
|
||||
// 辅助函数:从图像中双线性插值采样颜色
|
||||
// 修正颜色顺序和边界处理
|
||||
Pixel8U SampleImageBilinear(const Image8U3& image, const Point2f& point) {
|
||||
// 边界检查,防止越界
|
||||
float x = CLAMP(point.x, 0.0f, (float)(image.cols - 1));
|
||||
float y = CLAMP(point.y, 0.0f, (float)(image.rows - 1));
|
||||
|
||||
int x0 = (int)floor(x);
|
||||
int y0 = (int)floor(y);
|
||||
int x1 = std::min(x0 + 1, image.cols - 1);
|
||||
int y1 = std::min(y0 + 1, image.rows - 1);
|
||||
|
||||
// 确保x0,y0不会超出下界
|
||||
x0 = std::max(0, x0);
|
||||
y0 = std::max(0, y0);
|
||||
|
||||
float dx = x - x0;
|
||||
float dy = y - y0;
|
||||
float dx1 = 1.0f - dx;
|
||||
float dy1 = 1.0f - dy;
|
||||
|
||||
// 获取四个角点的像素
|
||||
const Pixel8U& p00 = image(y0, x0);
|
||||
const Pixel8U& p01 = image(y0, x1);
|
||||
const Pixel8U& p10 = image(y1, x0);
|
||||
const Pixel8U& p11 = image(y1, x1);
|
||||
|
||||
// 方法1:使用结构体成员访问(推荐)
|
||||
float b = p00.b * dx1 * dy1 + p01.b * dx * dy1 + p10.b * dx1 * dy + p11.b * dx * dy;
|
||||
float g = p00.g * dx1 * dy1 + p01.g * dx * dy1 + p10.g * dx1 * dy + p11.g * dx * dy;
|
||||
float r = p00.r * dx1 * dy1 + p01.r * dx * dy1 + p10.r * dx1 * dy + p11.r * dx * dy;
|
||||
|
||||
/*
|
||||
// 方法2:如果Pixel8U支持[]操作符
|
||||
// 注意:OpenMVS的Pixel8U可能是BGR或RGB顺序,需要根据实际情况调整
|
||||
float b = p00[0] * dx1 * dy1 + p01[0] * dx * dy1 + p10[0] * dx1 * dy + p11[0] * dx * dy;
|
||||
float g = p00[1] * dx1 * dy1 + p01[1] * dx * dy1 + p10[1] * dx1 * dy + p11[1] * dx * dy;
|
||||
float r = p00[2] * dx1 * dy1 + p01[2] * dx * dy1 + p10[2] * dx1 * dy + p11[2] * dx * dy;
|
||||
*/
|
||||
|
||||
return Pixel8U(
|
||||
(unsigned char)CLAMP(b, 0.0f, 255.0f),
|
||||
(unsigned char)CLAMP(g, 0.0f, 255.0f),
|
||||
(unsigned char)CLAMP(r, 0.0f, 255.0f)
|
||||
);
|
||||
}
|
||||
|
||||
void FillTextureGaps2(Image8U3& textureAtlas, const Mesh::TexCoordArr& faceTexcoords,
|
||||
FIndex nFaces, const MeshTexture::LabelArr& faceLabels,
|
||||
@@ -18721,6 +18848,35 @@ bool MeshTexture::ValidateProjection(const Vertex& worldPoint,
|
||||
return true;
|
||||
}
|
||||
|
||||
// ============================================================
|
||||
// 双线性采样(从原始图像按 UV 采样)
|
||||
// ============================================================
|
||||
MeshTexture::Color MeshTexture::SampleImageBilinear(const cv::Mat& img, float x, float y)
|
||||
{
|
||||
if (img.empty() || x < 0 || x >= img.cols-1 || y < 0 || y >= img.rows-1)
|
||||
return Color(0,0,0);
|
||||
|
||||
int xi = (int)x;
|
||||
int yi = (int)y;
|
||||
float dx = x - xi;
|
||||
float dy = y - yi;
|
||||
|
||||
cv::Vec3b p00 = img.at<cv::Vec3b>(yi, xi);
|
||||
cv::Vec3b p01 = img.at<cv::Vec3b>(yi+1, xi);
|
||||
cv::Vec3b p10 = img.at<cv::Vec3b>(yi, xi+1);
|
||||
cv::Vec3b p11 = img.at<cv::Vec3b>(yi+1, xi+1);
|
||||
|
||||
Color c;
|
||||
for (int k = 0; k < 3; ++k) {
|
||||
float v00 = (k == 0) ? p00[2] : (k == 1) ? p00[1] : p00[0];
|
||||
float v01 = (k == 0) ? p01[2] : (k == 1) ? p01[1] : p01[0];
|
||||
float v10 = (k == 0) ? p10[2] : (k == 1) ? p10[1] : p10[0];
|
||||
float v11 = (k == 0) ? p11[2] : (k == 1) ? p11[1] : p11[0];
|
||||
c[k] = v00*(1-dx)*(1-dy) + v01*(1-dx)*dy + v10*dx*(1-dy) + v11*dx*dy;
|
||||
}
|
||||
return c;
|
||||
}
|
||||
|
||||
Pixel8U MeshTexture::SampleImageBilinear(const Image8U3& image, const Point2f& point) {
|
||||
const int x1 = (int)point.x;
|
||||
const int y1 = (int)point.y;
|
||||
|
||||
Reference in new issue
Block a user