Browse Source

全局优化

ManualUV
hesuicong 1 day ago
parent
commit
b89bf91ccf
  1. 434
      libs/MVS/SceneTexture.cpp

434
libs/MVS/SceneTexture.cpp

@ -1091,6 +1091,34 @@ public: @@ -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 @@ -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( @@ -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( @@ -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( @@ -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;
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];
if (currentScore > ts.score) {
atlas(atlasY, atlasX) = Pixel8U{color[2], color[1], color[0]};
ts.score = currentScore;
ts.viewID = viewID;
}
}
}
}
}
cv::Vec3b color = patch.at<cv::Vec3b>(y, x);
if (color[0] == 0 && color[1] == 0 && color[2] == 0)
continue;
m_texelScores.clear();
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());
int atlasX = x + minX;
int atlasY = y + minY;
if (atlasX < 0 || atlasX >= textureSize ||
atlasY < 0 || atlasY >= textureSize)
continue;
m_virtualFaceViewWeights = virtualFaceViewWeights;
size_t idx = atlasY * textureSize + atlasX;
TexelScore& ts = m_texelScores[idx];
// --------------------------------------------------
// 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());
float currentScore = virtualFaceViewWeights[i].empty()
? -1.0f
: virtualFaceViewWeights[i][0];
// --------------------------------------------------
// 5. 构建接缝边
// --------------------------------------------------
BuildSeamEdgesFromRCPatches();
// ✅ 正常写入,不衰减,不修改
if (currentScore > ts.score) {
atlas(atlasY, atlasX) =
Pixel8U{color[2], color[1], color[0]};
ts.score = currentScore;
ts.viewID = viewID;
}
}
}
}
// --------------------------------------------------
// 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());
}
m_texelScores.clear();
return true;
}
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; // 不影响主流程
// ============================================================
// 构建接缝边(基于 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;
}
// // 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);
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("Seam blending completed: %zu seams processed (%s)",
seams.size(), TD_TIMER_GET_FMT().c_str());
}
DEBUG_EXTRA("RC-style Rasterization completed: %s", TD_TIMER_GET_FMT().c_str());
return true;
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 @@ -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, @@ -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;

Loading…
Cancel
Save