多视图混合采样替换patch
This commit is contained in:
1 parent
b28b78ee93
commit
d157312fba
1 file changed
+393
-20
+393
-20
@@ -82,6 +82,31 @@ using namespace MVS;
|
|||||||
#if TEXOPT_INFERENCE == TEXOPT_INFERENCE_LBP
|
#if TEXOPT_INFERENCE == TEXOPT_INFERENCE_LBP
|
||||||
#include "../Math/LBP.h"
|
#include "../Math/LBP.h"
|
||||||
namespace MVS {
|
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;
|
typedef LBPInference::NodeID NodeID;
|
||||||
// Potts model as smoothness function
|
// Potts model as smoothness function
|
||||||
LBPInference::EnergyType STCALL SmoothnessPotts(LBPInference::NodeID, LBPInference::NodeID, LBPInference::LabelID l1, LBPInference::LabelID l2) {
|
LBPInference::EnergyType STCALL SmoothnessPotts(LBPInference::NodeID, LBPInference::NodeID, LBPInference::LabelID l1, LBPInference::LabelID l2) {
|
||||||
@@ -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 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 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;
|
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);
|
static std::string GetFileNameWithoutExtension(std::string& strPath);
|
||||||
float CalculateBrightnessScore(const Image& imageData) const;
|
float CalculateBrightnessScore(const Image& imageData) const;
|
||||||
@@ -504,7 +530,7 @@ public:
|
|||||||
void LocalSeamLeveling();
|
void LocalSeamLeveling();
|
||||||
void LocalSeamLeveling3();
|
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 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
|
// Bruce
|
||||||
//*
|
//*
|
||||||
@@ -605,7 +631,18 @@ public:
|
|||||||
return distances[distances.size() / 2];
|
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标准系数,增加数值稳定性处理
|
// 采用ITU-R BT.601标准系数,增加数值稳定性处理
|
||||||
template <typename PIXEL>
|
template <typename PIXEL>
|
||||||
@@ -6820,7 +6857,7 @@ bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataView
|
|||||||
const Image& imageData = images[index];
|
const Image& imageData = images[index];
|
||||||
std::string strPath = imageData.name;
|
std::string strPath = imageData.name;
|
||||||
std::string strName = MeshTexture::GetFileNameWithoutExtension(strPath);
|
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);
|
auto it_view = mapViewCoverageData.find(index);
|
||||||
if (it_view == mapViewCoverageData.end()) {
|
if (it_view == mapViewCoverageData.end()) {
|
||||||
@@ -7455,6 +7492,121 @@ bool MeshTexture::CreateVirtualFaces65(FaceDataViewArr& facesDatas, FaceDataView
|
|||||||
return true;
|
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 输入图像(支持单通道和三通道)
|
* @param imageData 输入图像(支持单通道和三通道)
|
||||||
@@ -9759,10 +9911,11 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
|
|||||||
// CreateVirtualFaces6(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
|
// CreateVirtualFaces6(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
|
||||||
// CreateVirtualFaces61(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
|
// CreateVirtualFaces61(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
|
||||||
// CreateVirtualFaces62(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();
|
TD_TIMER_STARTD();
|
||||||
// CreateVirtualFaces7(facesDatas, virtualFacesDatas, virtualFaces, isVirtualFace, minCommonCameras);
|
// 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);
|
size_t controlCounter(0);
|
||||||
FOREACH(idxVF, virtualFaces) {
|
FOREACH(idxVF, virtualFaces) {
|
||||||
@@ -10791,7 +10944,59 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
|
|||||||
while (mapIdxPatch.size() <= nComponents)
|
while (mapIdxPatch.size() <= nComponents)
|
||||||
mapIdxPatch.Insert(numPatches);
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -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
|
// Bruce
|
||||||
bGlobalSeamLeveling = false;
|
// bGlobalSeamLeveling = false;
|
||||||
bLocalSeamLeveling = false;
|
// bLocalSeamLeveling = false;
|
||||||
// project patches in the corresponding view and compute texture-coordinates and bounding-box
|
// 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);
|
faceTexcoords.resize(faces.size()*3);
|
||||||
faceTexindices.resize(faces.size());
|
faceTexindices.resize(faces.size());
|
||||||
#ifdef TEXOPT_USE_OPENMP
|
#ifdef TEXOPT_USE_OPENMP
|
||||||
@@ -12824,10 +13032,30 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
// compute relative texture coordinates
|
// compute relative texture coordinates
|
||||||
ASSERT(imageData.image.isInside(Point2f(aabb.ptMin)));
|
ASSERT(imageData.image.isInside(Point2f(aabb.ptMin)));
|
||||||
ASSERT(imageData.image.isInside(Point2f(aabb.ptMax)));
|
ASSERT(imageData.image.isInside(Point2f(aabb.ptMax)));
|
||||||
|
|
||||||
|
if (bOriginFaceview)
|
||||||
|
{
|
||||||
texturePatch.rect.x = FLOOR2INT(aabb.ptMin[0])-border;
|
texturePatch.rect.x = FLOOR2INT(aabb.ptMin[0])-border;
|
||||||
texturePatch.rect.y = FLOOR2INT(aabb.ptMin[1])-border;
|
texturePatch.rect.y = FLOOR2INT(aabb.ptMin[1])-border;
|
||||||
texturePatch.rect.width = CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2;
|
texturePatch.rect.width = CEIL2INT(aabb.ptMax[0]-aabb.ptMin[0])+border*2;
|
||||||
texturePatch.rect.height = CEIL2INT(aabb.ptMax[1]-aabb.ptMin[1])+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.tl()));
|
||||||
ASSERT(imageData.image.isInside(texturePatch.rect.br()));
|
ASSERT(imageData.image.isInside(texturePatch.rect.br()));
|
||||||
const TexCoord offset(texturePatch.rect.tl());
|
const TexCoord offset(texturePatch.rect.tl());
|
||||||
@@ -12851,6 +13079,8 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
|
|
||||||
|
|
||||||
LOG_OUT() << "First loop completed" << std::endl;
|
LOG_OUT() << "First loop completed" << std::endl;
|
||||||
|
|
||||||
|
TD_TIMER_STARTD();
|
||||||
// perform seam leveling
|
// perform seam leveling
|
||||||
if (texturePatches.size() > 2 && (bGlobalSeamLeveling || bLocalSeamLeveling)) {
|
if (texturePatches.size() > 2 && (bGlobalSeamLeveling || bLocalSeamLeveling)) {
|
||||||
// create seam vertices and edges
|
// create seam vertices and edges
|
||||||
@@ -12859,17 +13089,19 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
// perform global seam leveling
|
// perform global seam leveling
|
||||||
if (bGlobalSeamLeveling) {
|
if (bGlobalSeamLeveling) {
|
||||||
TD_TIMER_STARTD();
|
TD_TIMER_STARTD();
|
||||||
GlobalSeamLeveling();
|
GlobalSeamLeveling3();
|
||||||
DEBUG_ULTIMATE("\tglobal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
|
DEBUG_EXTRA("\tglobal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
// perform local seam leveling
|
// perform local seam leveling
|
||||||
if (bLocalSeamLeveling) {
|
if (bLocalSeamLeveling) {
|
||||||
TD_TIMER_STARTD();
|
TD_TIMER_STARTD();
|
||||||
LocalSeamLeveling();
|
// LocalSeamLeveling();
|
||||||
DEBUG_ULTIMATE("\tlocal seam leveling completed (%s)", TD_TIMER_GET_FMT().c_str());
|
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
|
// merge texture patches with overlapping rectangles
|
||||||
for (unsigned i=0; i<texturePatches.size()-1; ++i) {
|
for (unsigned i=0; i<texturePatches.size()-1; ++i) {
|
||||||
@@ -12903,6 +13135,11 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
// const unsigned minPatchSize = 20;
|
// const unsigned minPatchSize = 20;
|
||||||
RectsBinPack::RectWIdxArr unplacedRects(texturePatches.size());
|
RectsBinPack::RectWIdxArr unplacedRects(texturePatches.size());
|
||||||
FOREACH(i, texturePatches) {
|
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;
|
// LOG_OUT() << "Third loop completed" << std::endl;
|
||||||
if (maxTextureSize > 0 && (texturePatches[i].rect.width > maxTextureSize || texturePatches[i].rect.height > maxTextureSize)) {
|
if (maxTextureSize > 0 && (texturePatches[i].rect.width > maxTextureSize || texturePatches[i].rect.height > maxTextureSize)) {
|
||||||
@@ -12917,9 +13154,16 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
// pack patches: one pack per texture file
|
// pack patches: one pack per texture file
|
||||||
CLISTDEF2IDX(RectsBinPack::RectWIdxArr, TexIndex) placedRects; {
|
CLISTDEF2IDX(RectsBinPack::RectWIdxArr, TexIndex) placedRects; {
|
||||||
// increase texture size till all patches fit
|
// increase texture size till all patches fit
|
||||||
const unsigned typeRectsBinPack(nRectPackingHeuristic/100);
|
// Bruce
|
||||||
const unsigned typeSplit((nRectPackingHeuristic-typeRectsBinPack*100)/10);
|
unsigned typeRectsBinPack(nRectPackingHeuristic/100);
|
||||||
const unsigned typeHeuristic(nRectPackingHeuristic%10);
|
unsigned typeSplit((nRectPackingHeuristic-typeRectsBinPack*100)/10);
|
||||||
|
unsigned typeHeuristic(nRectPackingHeuristic%10);
|
||||||
|
if (!bOriginFaceview && false)
|
||||||
|
{
|
||||||
|
typeRectsBinPack = 1;
|
||||||
|
typeSplit = 0;
|
||||||
|
typeHeuristic = 1;
|
||||||
|
}
|
||||||
int textureSize = 0;
|
int textureSize = 0;
|
||||||
while (!unplacedRects.empty()) {
|
while (!unplacedRects.empty()) {
|
||||||
TD_TIMER_STARTD();
|
TD_TIMER_STARTD();
|
||||||
@@ -12951,6 +13195,7 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
if (textureSize == maxTextureSize || unplacedRects.empty()) {
|
if (textureSize == maxTextureSize || unplacedRects.empty()) {
|
||||||
// create texture image
|
// create texture image
|
||||||
placedRects.emplace_back(std::move(newPlacedRects));
|
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));
|
texturesDiffuse.emplace_back(textureSize, textureSize).setTo(cv::Scalar(colEmpty.b, colEmpty.g, colEmpty.r));
|
||||||
textureSize = 0;
|
textureSize = 0;
|
||||||
} else {
|
} else {
|
||||||
@@ -12982,23 +13227,150 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
(rect.height == texturePatch.rect.width && rect.width == texturePatch.rect.height));
|
(rect.height == texturePatch.rect.width && rect.width == texturePatch.rect.height));
|
||||||
int x(0), y(1);
|
int x(0), y(1);
|
||||||
if (texturePatch.label != NO_ID) {
|
if (texturePatch.label != NO_ID) {
|
||||||
const Image& imageData = images[texturePatch.label];
|
// --- 多视图混合采样(参考 RC 逻辑) ---
|
||||||
cv::Mat patch(imageData.image(texturePatch.rect));
|
// 1. 获取目标纹理块
|
||||||
|
cv::Mat dstMat = texturesDiffuse[idxTexture](rect);
|
||||||
|
|
||||||
|
// 2. 如果需要旋转(OpenMVS 的内部优化)
|
||||||
|
cv::Mat patchBuffer; // 用于存放旋转后的中间结果
|
||||||
|
cv::Mat* targetMat = &dstMat;
|
||||||
if (rect.width != texturePatch.rect.width) {
|
if (rect.width != texturePatch.rect.width) {
|
||||||
// flip patch and texture-coordinates
|
patchBuffer.create(rect.height, rect.width, dstMat.type());
|
||||||
patch = patch.t();
|
targetMat = &patchBuffer;
|
||||||
x = 1; y = 0;
|
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
|
else
|
||||||
{
|
{
|
||||||
|
//*
|
||||||
auto it = texturePatch.faces.begin();
|
auto it = texturePatch.faces.begin();
|
||||||
while (it != texturePatch.faces.end())
|
while (it != texturePatch.faces.end())
|
||||||
{
|
{
|
||||||
emptyFaceIndexes.push_back(*it);
|
emptyFaceIndexes.push_back(*it);
|
||||||
++it;
|
++it;
|
||||||
}
|
}
|
||||||
|
//*/
|
||||||
}
|
}
|
||||||
// compute final texture coordinates
|
// compute final texture coordinates
|
||||||
const TexCoord offset(rect.tl());
|
const TexCoord offset(rect.tl());
|
||||||
@@ -13017,6 +13389,7 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
|
|||||||
}
|
}
|
||||||
if (texturesDiffuse.size() == 1)
|
if (texturesDiffuse.size() == 1)
|
||||||
faceTexindices.Release();
|
faceTexindices.Release();
|
||||||
|
|
||||||
// apply some sharpening
|
// apply some sharpening
|
||||||
if (fSharpnessWeight > 0) {
|
if (fSharpnessWeight > 0) {
|
||||||
constexpr double sigma = 1.5;
|
constexpr double sigma = 1.5;
|
||||||
|
|||||||
Reference in new issue
Block a user