Browse Source

多视图混合采样替换patch

master
hesuicong 1 month ago
parent
commit
d157312fba
  1. 413
      libs/MVS/SceneTexture.cpp

413
libs/MVS/SceneTexture.cpp

@ -82,6 +82,31 @@ using namespace MVS; @@ -82,6 +82,31 @@ using namespace MVS;
#if TEXOPT_INFERENCE == TEXOPT_INFERENCE_LBP
#include "../Math/LBP.h"
namespace MVS {
// 2. 重心坐标计算(就地实现)
inline bool ComputeBarycentricCoords(const Point2f& p,
const Point2f& a,
const Point2f& b,
const Point2f& c,
Point2f& uv) {
const Point2f v0 = b - a;
const Point2f v1 = c - a;
const Point2f v2 = p - a;
const float d00 = v0.dot(v0);
const float d01 = v0.dot(v1);
const float d11 = v1.dot(v1);
const float d20 = v2.dot(v0);
const float d21 = v2.dot(v1);
const float denom = d00 * d11 - d01 * d01;
if (fabs(denom) < 1e-10f) return false;
const float v = (d11 * d20 - d01 * d21) / denom;
const float w = (d00 * d21 - d01 * d20) / denom;
uv.x = 1.0f - v - w; // u
uv.y = v; // v
return true;
}
typedef LBPInference::NodeID NodeID;
// Potts model as smoothness function
LBPInference::EnergyType STCALL SmoothnessPotts(LBPInference::NodeID, LBPInference::NodeID, LBPInference::LabelID l1, LBPInference::LabelID l2) {
@ -472,6 +497,7 @@ public: @@ -472,6 +497,7 @@ public:
bool CreateVirtualFaces63(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
bool CreateVirtualFaces64(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
bool CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, std::vector<bool>& isVirtualFace, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
void CreateVirtualFaces8(const FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
static std::string GetFileNameWithoutExtension(std::string& strPath);
float CalculateBrightnessScore(const Image& imageData) const;
@ -504,7 +530,7 @@ public: @@ -504,7 +530,7 @@ public:
void LocalSeamLeveling();
void LocalSeamLeveling3();
void GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview);
void GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName);
void GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& baseFileName, bool bOriginFaceview);
// Bruce
//*
@ -605,7 +631,18 @@ public: @@ -605,7 +631,18 @@ public:
return distances[distances.size() / 2];
}
// 在 MeshTexture 类的 public 或 private 区域添加:
struct FaceCandidates {
IIndex primaryView = NO_ID;
std::vector<IIndex> candidateViews; // Top-N 视图索引
std::vector<float> candidateWeights; // 对应的权重
};
// 成员变量
cList<FaceCandidates, FIndex> faceCandidates;
//*/
/*
// 采用ITU-R BT.601标准系数,增加数值稳定性处理
template <typename PIXEL>
@ -6820,7 +6857,7 @@ bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataView @@ -6820,7 +6857,7 @@ bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataView
const Image& imageData = images[index];
std::string strPath = imageData.name;
std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath);
printf("Top-K index=%d, strName=%s, score=%f\n", index, strName.c_str(), score);
// printf("Top-K index=%d, strName=%s, score=%f\n", index, strName.c_str(), score);
auto it_view = mapViewCoverageData.find(index);
if (it_view == mapViewCoverageData.end()) {
@ -7455,6 +7492,121 @@ bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataView @@ -7455,6 +7492,121 @@ bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataView
return true;
}
// build virtual faces with:
// - similar normal
// - high percentage of common images that see them
void MeshTexture::CreateVirtualFaces8(const FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, unsigned minCommonCameras, float thMaxNormalDeviation) const
{
const float ratioAngleToQuality(0.67f);
const float cosMaxNormalDeviation(COS(FD2R(thMaxNormalDeviation)));
Mesh::FaceIdxArr remainingFaces(faces.size());
std::iota(remainingFaces.begin(), remainingFaces.end(), 0);
std::vector<bool> selectedFaces(faces.size(), false);
cQueue<FIndex, FIndex, 0> currentVirtualFaceQueue;
std::unordered_set<FIndex> queuedFaces;
do {
const FIndex startPos = RAND() % remainingFaces.size();
const FIndex virtualFaceCenterFaceID = remainingFaces[startPos];
ASSERT(currentVirtualFaceQueue.IsEmpty());
const Normal& normalCenter = scene.mesh.faceNormals[virtualFaceCenterFaceID];
const FaceDataArr& centerFaceDatas = facesDatas[virtualFaceCenterFaceID];
// select the common cameras
Mesh::FaceIdxArr virtualFace;
FaceDataArr virtualFaceDatas;
if (centerFaceDatas.empty()) {
virtualFace.emplace_back(virtualFaceCenterFaceID);
selectedFaces[virtualFaceCenterFaceID] = true;
const auto posToErase = remainingFaces.FindFirst(virtualFaceCenterFaceID);
ASSERT(posToErase != Mesh::FaceIdxArr::NO_INDEX);
remainingFaces.RemoveAtMove(posToErase);
} else {
const IIndexArr selectedCams = SelectBestViews(centerFaceDatas, virtualFaceCenterFaceID, minCommonCameras, ratioAngleToQuality);
currentVirtualFaceQueue.AddTail(virtualFaceCenterFaceID);
queuedFaces.clear();
do {
const FIndex currentFaceId = currentVirtualFaceQueue.GetHead();
currentVirtualFaceQueue.PopHead();
// check for condition to add in current virtual face
// normal angle smaller than thMaxNormalDeviation degrees
const Normal& faceNormal = scene.mesh.faceNormals[currentFaceId];
const float cosFaceToCenter(ComputeAngleN(normalCenter.ptr(), faceNormal.ptr()));
if (cosFaceToCenter < cosMaxNormalDeviation)
continue;
// check if current face is seen by all cameras in selectedCams
ASSERT(!selectedCams.empty());
if (!IsFaceVisible(facesDatas[currentFaceId], selectedCams))
continue;
// remove it from remaining faces and add it to the virtual face
{
const auto posToErase = remainingFaces.FindFirst(currentFaceId);
ASSERT(posToErase != Mesh::FaceIdxArr::NO_INDEX);
remainingFaces.RemoveAtMove(posToErase);
selectedFaces[currentFaceId] = true;
virtualFace.push_back(currentFaceId);
}
// add all new neighbors to the queue
const Mesh::FaceFaces& ffaces = faceFaces[currentFaceId];
for (int i = 0; i < 3; ++i) {
const FIndex fIdx = ffaces[i];
if (fIdx == NO_ID)
continue;
if (!selectedFaces[fIdx] && queuedFaces.find(fIdx) == queuedFaces.end()) {
currentVirtualFaceQueue.AddTail(fIdx);
queuedFaces.emplace(fIdx);
}
}
} while (!currentVirtualFaceQueue.IsEmpty());
// compute virtual face quality and create virtual face
for (IIndex idxView: selectedCams) {
FaceData& virtualFaceData = virtualFaceDatas.emplace_back();
virtualFaceData.quality = 0;
virtualFaceData.idxView = idxView;
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
virtualFaceData.color = Point3f::ZERO;
#endif
unsigned processedFaces(0);
for (FIndex fid : virtualFace) {
const FaceDataArr& faceDatas = facesDatas[fid];
for (FaceData& faceData: faceDatas) {
if (faceData.idxView == idxView) {
virtualFaceData.quality += faceData.quality;
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
virtualFaceData.color += faceData.color;
#endif
++processedFaces;
break;
}
}
}
ASSERT(processedFaces > 0);
// 修改方案 B:使用第 20% 分位(更鲁棒)
// 收集所有 quality,排序,取较低的那个
std::vector<float> qualities;
for (FIndex fid : virtualFace) {
const FaceDataArr& faceDatas = facesDatas[fid];
for (FaceData& faceData: faceDatas) {
if (faceData.idxView == idxView) {
qualities.push_back(faceData.quality);
break;
}
}
}
std::sort(qualities.begin(), qualities.end());
virtualFaceData.quality = qualities[qualities.size() * 0.2]; // 取第20%
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
virtualFaceData.color /= processedFaces;
#endif
}
ASSERT(!virtualFaceDatas.empty());
}
virtualFacesDatas.emplace_back(std::move(virtualFaceDatas));
virtualFaces.emplace_back(std::move(virtualFace));
} while (!remainingFaces.empty());
}
/**
*
* @param imageData
@ -9759,10 +9911,11 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT @@ -9759,10 +9911,11 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
// CreateVirtualFaces6(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
// CreateVirtualFaces61(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
// CreateVirtualFaces62(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
CreateVirtualFaces65(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
// CreateVirtualFaces65(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
CreateVirtualFaces8(facesDatas, virtualFacesDatas, virtualFaces, minCommonCameras);
TD_TIMER_STARTD();
// CreateVirtualFaces7(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
DEBUG_EXTRA("CreateVirtualFaces completed: %s", TD_TIMER_GET_FMT().c_str());
// DEBUG_EXTRA("CreateVirtualFaces completed: %s", TD_TIMER_GET_FMT().c_str());
size_t controlCounter(0);
FOREACH(idxVF, virtualFaces) {
@ -10791,7 +10944,59 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT @@ -10791,7 +10944,59 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
while (mapIdxPatch.size() <= nComponents)
mapIdxPatch.Insert(numPatches);
}
// --- 新增:构建每个面的候选视图列表 ---
faceCandidates.resize(faces.size());
const int TOP_N = 3; // 取前3个最佳视图
FOREACH(f, faces) {
faceCandidates[f].primaryView = labels[f];
if (labels[f] == NO_ID || f >= facesDatas.size() || facesDatas[f].empty())
continue;
// 1. 收集所有可见视图,并按质量排序
std::vector<std::pair<float, IIndex>> viewQualities;
viewQualities.reserve(facesDatas[f].size());
for (const FaceData& fd : facesDatas[f]) {
if (!fd.bInvalidFacesRelative) {
viewQualities.emplace_back(fd.quality, fd.idxView);
}
}
if (viewQualities.empty())
continue;
// 按质量降序排序
std::sort(viewQualities.begin(), viewQualities.end(),
[](const auto& a, const auto& b) { return a.first > b.first; });
// 2. 选取 Top-N,使用 Softmax 计算权重(关键:避免模糊!)
const size_t n = std::min(TOP_N, (int)viewQualities.size());
faceCandidates[f].candidateViews.resize(n);
faceCandidates[f].candidateWeights.resize(n);
// Softmax 权重计算(temperature 控制锐度)
const float temperature = 0.05f; // 越小越锐利,0.05f 是个不错的起点
float max_q = viewQualities[0].first;
float sum = 0.0f;
for (size_t i = 0; i < n; ++i) {
faceCandidates[f].candidateViews[i] = viewQualities[i].second;
float w = exp((viewQualities[i].first - max_q) / temperature);
faceCandidates[f].candidateWeights[i] = w;
sum += w;
}
// 归一化
if (sum > 0) {
for (size_t i = 0; i < n; ++i) {
faceCandidates[f].candidateWeights[i] /= sum;
}
}
}
}
return true;
}
@ -12791,13 +12996,16 @@ void MeshTexture::LocalSeamLeveling() @@ -12791,13 +12996,16 @@ void MeshTexture::LocalSeamLeveling()
}
}
void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename)
void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeveling, unsigned nTextureSizeMultiple, unsigned nRectPackingHeuristic, Pixel8U colEmpty, float fSharpnessWeight, int maxTextureSize, const SEACAVE::String& basename, bool bOriginFaceview)
{
// Bruce
bGlobalSeamLeveling = false;
bLocalSeamLeveling = false;
// bGlobalSeamLeveling = false;
// bLocalSeamLeveling = false;
// project patches in the corresponding view and compute texture-coordinates and bounding-box
const int border(2);
// Bruce
int border = 2;
if (!bOriginFaceview)
border = 4;
faceTexcoords.resize(faces.size()*3);
faceTexindices.resize(faces.size());
#ifdef TEXOPT_USE_OPENMP
@ -12824,10 +13032,30 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -12824,10 +13032,30 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
// compute relative texture coordinates
ASSERT(imageData.image.isInside(Point2f(aabb.ptMin)));
ASSERT(imageData.image.isInside(Point2f(aabb.ptMax)));
if (bOriginFaceview)
{
texturePatch.rect.x = FLOOR2INT(aabb.ptMin[0])-border;
texturePatch.rect.y = FLOOR2INT(aabb.ptMin[1])-border;
texturePatch.rect.width = CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2;
texturePatch.rect.height = CEIL2INT(aabb.ptMax[1]-aabb.ptMin[1])+border*2;
}
else
{
texturePatch.rect.x = std::max(0, FLOOR2INT(aabb.ptMin[0])-border);
texturePatch.rect.y = std::max(0, FLOOR2INT(aabb.ptMin[1])-border);
// 限制尺寸不超过图像实际范围
const cv::Mat& img = images[texturePatch.label].image;
texturePatch.rect.width = std::min(
CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2,
img.cols - texturePatch.rect.x
);
texturePatch.rect.height = std::min(
CEIL2INT(aabb.ptMax[1]-aabb.ptMin[1])+border*2,
img.rows - texturePatch.rect.y
);
}
ASSERT(imageData.image.isInside(texturePatch.rect.tl()));
ASSERT(imageData.image.isInside(texturePatch.rect.br()));
const TexCoord offset(texturePatch.rect.tl());
@ -12851,6 +13079,8 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -12851,6 +13079,8 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
LOG_OUT() << "First loop completed" << std::endl;
TD_TIMER_STARTD();
// perform seam leveling
if (texturePatches.size() > 2 && (bGlobalSeamLeveling || bLocalSeamLeveling)) {
// create seam vertices and edges
@ -12859,17 +13089,19 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -12859,17 +13089,19 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
// perform global seam leveling
if (bGlobalSeamLeveling) {
TD_TIMER_STARTD();
GlobalSeamLeveling();
DEBUG_ULTIMATE("\tglobal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
GlobalSeamLeveling3();
DEBUG_EXTRA("\tglobal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
}
// perform local seam leveling
if (bLocalSeamLeveling) {
TD_TIMER_STARTD();
LocalSeamLeveling();
DEBUG_ULTIMATE("\tlocal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
// LocalSeamLeveling();
LocalSeamLeveling3();
DEBUG_EXTRA("\tlocal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
}
}
DEBUG_EXTRA("seam (%s)", TD_TIMER_GET_FMT().c_str());
// merge texture patches with overlapping rectangles
for (unsigned i=0; i<texturePatches.size()-1; ++i) {
@ -12903,6 +13135,11 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -12903,6 +13135,11 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
// const unsigned minPatchSize = 20;
RectsBinPack::RectWIdxArr unplacedRects(texturePatches.size());
FOREACH(i, texturePatches) {
if (texturePatches[i].label == NO_ID) {
// 将无效面片区域填充为绿色
// texturesDiffuse[i](texturePatches[i].rect).setTo(cv::Scalar(0, 255, 0)); // BGR格式,绿色
// continue;
}
// LOG_OUT() << "Third loop completed" << std::endl;
if (maxTextureSize > 0 && (texturePatches[i].rect.width > maxTextureSize || texturePatches[i].rect.height > maxTextureSize)) {
@ -12917,9 +13154,16 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -12917,9 +13154,16 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
// pack patches: one pack per texture file
CLISTDEF2IDX(RectsBinPack::RectWIdxArr, TexIndex) placedRects; {
// increase texture size till all patches fit
const unsigned typeRectsBinPack(nRectPackingHeuristic/100);
const unsigned typeSplit((nRectPackingHeuristic-typeRectsBinPack*100)/10);
const unsigned typeHeuristic(nRectPackingHeuristic%10);
// Bruce
unsigned typeRectsBinPack(nRectPackingHeuristic/100);
unsigned typeSplit((nRectPackingHeuristic-typeRectsBinPack*100)/10);
unsigned typeHeuristic(nRectPackingHeuristic%10);
if (!bOriginFaceview && false)
{
typeRectsBinPack = 1;
typeSplit = 0;
typeHeuristic = 1;
}
int textureSize = 0;
while (!unplacedRects.empty()) {
TD_TIMER_STARTD();
@ -12951,6 +13195,7 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -12951,6 +13195,7 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
if (textureSize == maxTextureSize || unplacedRects.empty()) {
// create texture image
placedRects.emplace_back(std::move(newPlacedRects));
// Pixel8U colEmpty2=Pixel8U(0,0,255);
texturesDiffuse.emplace_back(textureSize, textureSize).setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
textureSize = 0;
} else {
@ -12982,23 +13227,150 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -12982,23 +13227,150 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
(rect.height == texturePatch.rect.width && rect.width == texturePatch.rect.height));
int x(0), y(1);
if (texturePatch.label != NO_ID) {
const Image& imageData = images[texturePatch.label];
cv::Mat patch(imageData.image(texturePatch.rect));
// --- 多视图混合采样(参考 RC 逻辑) ---
// 1. 获取目标纹理块
cv::Mat dstMat = texturesDiffuse[idxTexture](rect);
// 2. 如果需要旋转(OpenMVS 的内部优化)
cv::Mat patchBuffer; // 用于存放旋转后的中间结果
cv::Mat* targetMat = &dstMat;
if (rect.width != texturePatch.rect.width) {
// flip patch and texture-coordinates
patch = patch.t();
patchBuffer.create(rect.height, rect.width, dstMat.type());
targetMat = &patchBuffer;
x = 1; y = 0;
} else {
patchBuffer = dstMat; // 指向同一块内存
}
// 遍历 Patch 中的每一个三角形进行光栅化
for (const FIndex idxFace : texturePatch.faces) {
if (idxFace >= this->faceCandidates.size() ||
this->faceCandidates[idxFace].candidateViews.empty())
continue;
const Face& face = faces[idxFace];
const TexCoord* texcoords = faceTexcoords.data() + idxFace * 3;
// 手动计算三角形包围盒(不使用 AABB2i)
int minX = INT_MAX, minY = INT_MAX, maxX = INT_MIN, maxY = INT_MIN;
for (int i = 0; i < 3; ++i) {
Point2f pt(texcoords[i][0], texcoords[i][1]);
int x = FLOOR2INT(pt.x);
int y = FLOOR2INT(pt.y);
minX = std::min(minX, x);
minY = std::min(minY, y);
maxX = std::max(maxX, x);
maxY = std::max(maxY, y);
}
// 裁剪到 Patch 边界
minX = std::max(minX, 0);
minY = std::max(minY, 0);
maxX = std::min(maxX, targetMat->cols - 1);
maxY = std::min(maxY, targetMat->rows - 1);
if (minX > maxX || minY > maxY) continue;
// 预存 UV
Point2f uvTri[3];
for (int i = 0; i < 3; ++i)
uvTri[i] = Point2f(texcoords[i][0], texcoords[i][1]);
// 光栅化
for (int py = minY; py <= maxY; ++py) {
for (int px = minX; px <= maxX; ++px) {
Point2f P(px + 0.5f, py + 0.5f);
Point2f bary;
if (!ComputeBarycentricCoords(P, uvTri[0], uvTri[1], uvTri[2], bary))
continue;
float u = bary.x;
float v = bary.y;
float w = 1.0f - u - v;
if (u < 0 || v < 0 || w < 0) continue;
const auto& candidates = this->faceCandidates[idxFace];
// 多视图加权采样(完全自包含,不依赖任何外部类型)
float acc_r = 0.0f, acc_g = 0.0f, acc_b = 0.0f, acc_w = 0.0f;
for (size_t k = 0; k < candidates.candidateViews.size(); ++k) {
const IIndex viewID = candidates.candidateViews[k];
float weight = candidates.candidateWeights[k];
const Image& candImage = images[viewID];
// 插值 3D 点
Point3f pt3D =
vertices[face[0]] * w +
vertices[face[1]] * u +
vertices[face[2]] * v;
Point2f uv_cand = candImage.camera.ProjectPointP(pt3D);
if (!candImage.image.isInsideWithBorder(uv_cand, border))
continue;
// 双线性插值(直接使用 cv::Vec3f,避免 Color 类型)
const cv::Mat& src = candImage.image;
const int ix = cvFloor(uv_cand.x);
const int iy = cvFloor(uv_cand.y);
const float dx = uv_cand.x - ix;
const float dy = uv_cand.y - iy;
// 边界保护
const int ix1 = std::min(ix + 1, src.cols - 1);
const int iy1 = std::min(iy + 1, src.rows - 1);
// 读取四个像素(使用 cv::Vec3f,OpenMVS 的图像通常是 CV_32FC3)
const cv::Vec3f& c00 = src.at<cv::Vec3f>(iy, ix);
const cv::Vec3f& c01 = src.at<cv::Vec3f>(iy, ix1);
const cv::Vec3f& c10 = src.at<cv::Vec3f>(iy1, ix);
const cv::Vec3f& c11 = src.at<cv::Vec3f>(iy1, ix1);
// 双线性插值公式
const float inv_a = (1.0f - dx) * (1.0f - dy);
const float inv_b = dx * (1.0f - dy);
const float inv_c = (1.0f - dx) * dy;
const float inv_d = dx * dy;
const float r = c00[0] * inv_a + c01[0] * inv_b + c10[0] * inv_c + c11[0] * inv_d;
const float g = c00[1] * inv_a + c01[1] * inv_b + c10[1] * inv_c + c11[1] * inv_d;
const float b = c00[2] * inv_a + c01[2] * inv_b + c10[2] * inv_c + c11[2] * inv_d;
acc_r += r * weight;
acc_g += g * weight;
acc_b += b * weight;
acc_w += weight;
}
// 写入最终颜色
if (acc_w > 0.0f) {
const float inv_w = 1.0f / acc_w;
Pixel8U finalColor8u;
finalColor8u.r = cv::saturate_cast<uchar>(acc_r * inv_w * 255.f);
finalColor8u.g = cv::saturate_cast<uchar>(acc_g * inv_w * 255.f);
finalColor8u.b = cv::saturate_cast<uchar>(acc_b * inv_w * 255.f);
targetMat->at<Pixel8U>(py, px) = finalColor8u;
}
}
}
}
// 7. 如果之前发生了旋转,转置回去
if (rect.width != texturePatch.rect.width) {
cv::transpose(patchBuffer, dstMat);
}
patch.copyTo(texturesDiffuse[idxTexture](rect));
}
else
{
//*
auto it = texturePatch.faces.begin();
while (it != texturePatch.faces.end())
{
emptyFaceIndexes.push_back(*it);
++it;
}
//*/
}
// compute final texture coordinates
const TexCoord offset(rect.tl());
@ -13017,6 +13389,7 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve @@ -13017,6 +13389,7 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
}
if (texturesDiffuse.size() == 1)
faceTexindices.Release();
// apply some sharpening
if (fSharpnessWeight > 0) {
constexpr double sigma = 1.5;

Loading…
Cancel
Save