多视图混合采样替换patch
This commit is contained in:
1 parent
b28b78ee93
commit
d157312fba
1 file changed
+398
-25
+398
-25
@@ -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:
|
||||
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:
|
||||
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:
|
||||
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
|
||||
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
|
||||
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
|
||||
// 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
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
// 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
|
||||
|
||||
|
||||
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
|
||||
// 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
|
||||
// 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
|
||||
// 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
|
||||
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
|
||||
(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
|
||||
}
|
||||
if (texturesDiffuse.size() == 1)
|
||||
faceTexindices.Release();
|
||||
|
||||
// apply some sharpening
|
||||
if (fSharpnessWeight > 0) {
|
||||
constexpr double sigma = 1.5;
|
||||
|
||||
Reference in new issue
Block a user