|
|
|
|
@ -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))); |
|
|
|
|
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; |
|
|
|
|
|
|
|
|
|
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,10 +13154,17 @@ void MeshTexture::GenerateTexture2(bool bGlobalSeamLeveling, bool bLocalSeamLeve
@@ -12917,10 +13154,17 @@ 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); |
|
|
|
|
int textureSize = 0; |
|
|
|
|
// 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(); |
|
|
|
|
if (textureSize == 0) { |
|
|
|
|
@ -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; |
|
|
|
|
|