|
|
|
|
@ -6935,6 +6935,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
@@ -6935,6 +6935,7 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
|
|
|
|
|
// printf("FaceViewSelection3 2 scene.mesh.vertices.size=%d\n", scene.mesh.vertices.size());
|
|
|
|
|
|
|
|
|
|
bool bUseVirtualFaces(minCommonCameras > 0); |
|
|
|
|
bUseVirtualFaces = false; |
|
|
|
|
|
|
|
|
|
// list all views for each face
|
|
|
|
|
FaceDataViewArr facesDatas; |
|
|
|
|
@ -7673,263 +7674,49 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
@@ -7673,263 +7674,49 @@ bool MeshTexture::FaceViewSelection3( unsigned minCommonCameras, float fOutlierT
|
|
|
|
|
|
|
|
|
|
if (!bUseVirtualFaces) |
|
|
|
|
{ |
|
|
|
|
// assign the best view to each face
|
|
|
|
|
// 分配最佳视图给每个面片
|
|
|
|
|
labels.resize(faces.size()); |
|
|
|
|
{ |
|
|
|
|
// normalize quality values
|
|
|
|
|
float maxQuality(0); |
|
|
|
|
for (const FaceDataArr& faceDatas: facesDatas) { |
|
|
|
|
for (const FaceData& faceData: faceDatas) |
|
|
|
|
if (maxQuality < faceData.quality) |
|
|
|
|
maxQuality = faceData.quality; |
|
|
|
|
} |
|
|
|
|
Histogram32F hist(std::make_pair(0.f, maxQuality), 1000); |
|
|
|
|
for (const FaceDataArr& faceDatas: facesDatas) { |
|
|
|
|
for (const FaceData& faceData: faceDatas) |
|
|
|
|
hist.Add(faceData.quality); |
|
|
|
|
} |
|
|
|
|
const float normQuality(hist.GetApproximatePermille(0.95f)); |
|
|
|
|
|
|
|
|
|
#if TEXOPT_INFERENCE == TEXOPT_INFERENCE_LBP |
|
|
|
|
// initialize inference structures
|
|
|
|
|
const LBPInference::EnergyType MaxEnergy(fRatioDataSmoothness*(LBPInference::EnergyType)LBPInference::MaxEnergy); |
|
|
|
|
LBPInference inference; { |
|
|
|
|
inference.SetNumNodes(faces.size()); |
|
|
|
|
inference.SetSmoothCost(SmoothnessPotts); |
|
|
|
|
// inference.SetSmoothCost(SmoothnessLinear);
|
|
|
|
|
// inference.SetSmoothCost(NewSmoothness);
|
|
|
|
|
|
|
|
|
|
EdgeOutIter ei, eie; |
|
|
|
|
FOREACH(f, faces) { |
|
|
|
|
for (boost::tie(ei, eie) = boost::out_edges(f, graph); ei != eie; ++ei) { |
|
|
|
|
ASSERT(f == (FIndex)ei->m_source); |
|
|
|
|
const FIndex fAdj((FIndex)ei->m_target); |
|
|
|
|
if (f < fAdj) // add edges only once
|
|
|
|
|
inference.SetNeighbors(f, fAdj); |
|
|
|
|
} |
|
|
|
|
// set costs for label 0 (undefined)
|
|
|
|
|
inference.SetDataCost((Label)0, f, MaxEnergy); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
labelsInvalid.resize(faces.size()); |
|
|
|
|
|
|
|
|
|
//*
|
|
|
|
|
for (const FaceDataArr& faceDatas : facesDatas) { |
|
|
|
|
for (const FaceData& faceData : faceDatas) { |
|
|
|
|
if (faceData.quality > maxQuality) |
|
|
|
|
maxQuality = faceData.quality; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
for (const FaceDataArr& faceDatas : facesDatas) { |
|
|
|
|
for (const FaceData& faceData : faceDatas) |
|
|
|
|
hist.Add(faceData.quality); |
|
|
|
|
FOREACH(l, labelsInvalid) { |
|
|
|
|
labelsInvalid[l] = NO_ID; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
FOREACH(f, facesDatas) { |
|
|
|
|
// if (scene.mesh.invalidFacesRelative.data.contains(f))
|
|
|
|
|
// continue;
|
|
|
|
|
|
|
|
|
|
// 直接为每个面片选择最佳视图(跳过推理)
|
|
|
|
|
FOREACH(f, faces) { |
|
|
|
|
const FaceDataArr& faceDatas = facesDatas[f]; |
|
|
|
|
const size_t numViews = faceDatas.size(); |
|
|
|
|
const unsigned minSingleView = 6; // 与虚拟面模式相同的阈值
|
|
|
|
|
|
|
|
|
|
bool bInvalidFacesRelative = false; |
|
|
|
|
IIndex invalidView; |
|
|
|
|
float invalidQuality; |
|
|
|
|
|
|
|
|
|
// if (numViews <= minSingleView) {
|
|
|
|
|
if (true) { |
|
|
|
|
|
|
|
|
|
std::vector<std::pair<float, IIndex>> sortedViews; |
|
|
|
|
sortedViews.reserve(faceDatas.size()); |
|
|
|
|
for (const FaceData& fd : faceDatas) { |
|
|
|
|
|
|
|
|
|
if (fd.bInvalidFacesRelative) |
|
|
|
|
{ |
|
|
|
|
bInvalidFacesRelative = true; |
|
|
|
|
// sortedViews.emplace_back(fd.quality, fd.idxView);
|
|
|
|
|
invalidView = fd.idxView; |
|
|
|
|
invalidQuality = fd.quality; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
// if (fd.quality<=999.0)
|
|
|
|
|
{ |
|
|
|
|
sortedViews.emplace_back(fd.quality, fd.idxView); |
|
|
|
|
// printf("1fd.quality=%f\n", fd.quality);
|
|
|
|
|
} |
|
|
|
|
// else
|
|
|
|
|
// printf("2fd.quality=%f\n", fd.quality);
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
std::sort(sortedViews.begin(), sortedViews.end(), |
|
|
|
|
[](const auto& a, const auto& b) { return a.first > b.first; }); |
|
|
|
|
// 设置数据成本:最佳视角成本最低,其他按质量排序递增
|
|
|
|
|
const float baseCostScale = 0.1f; // 基础成本缩放系数
|
|
|
|
|
const float costStep = 0.3f; // 相邻视角成本增量
|
|
|
|
|
|
|
|
|
|
for (const auto& image : images) |
|
|
|
|
{ |
|
|
|
|
// printf("image name=%s\n", image.name.c_str());
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if (bInvalidFacesRelative && sortedViews.size() == 0) |
|
|
|
|
{ |
|
|
|
|
// const Label label = (Label)sortedViews[0].second + 1;
|
|
|
|
|
const Label label = (Label)invalidView + 1; |
|
|
|
|
float cost = (1.f - invalidQuality / normQuality) * MaxEnergy; |
|
|
|
|
// float cost = 0;
|
|
|
|
|
inference.SetDataCost(label, f, cost); |
|
|
|
|
if (faceDatas.empty()) { |
|
|
|
|
labels[f] = NO_ID; |
|
|
|
|
continue; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// printf("sortedViews size=%d\n", sortedViews.size());
|
|
|
|
|
for (size_t i = 0; i < sortedViews.size(); ++i) { |
|
|
|
|
const Label label = (Label)sortedViews[i].second + 1; |
|
|
|
|
float cost; |
|
|
|
|
|
|
|
|
|
std::string strPath = images[label-1].name; |
|
|
|
|
size_t lastSlash = strPath.find_last_of("/\\"); |
|
|
|
|
if (lastSlash == std::string::npos) lastSlash = 0; // 若无分隔符,从头开始
|
|
|
|
|
else lastSlash++; // 跳过分隔符
|
|
|
|
|
|
|
|
|
|
// 查找扩展名分隔符 '.' 的位置
|
|
|
|
|
size_t lastDot = strPath.find_last_of('.'); |
|
|
|
|
if (lastDot == std::string::npos) lastDot = strPath.size(); // 若无扩展名,截到末尾
|
|
|
|
|
|
|
|
|
|
// 截取文件名(不含路径和扩展名)
|
|
|
|
|
std::string strName = strPath.substr(lastSlash, lastDot - lastSlash); |
|
|
|
|
|
|
|
|
|
if (i == 0) { |
|
|
|
|
// if (true) {
|
|
|
|
|
// 最佳视角
|
|
|
|
|
// cost = (1.f - sortedViews[i].first / normQuality) * MaxEnergy * baseCostScale;
|
|
|
|
|
cost = (1.f - sortedViews[i].first / normQuality) * MaxEnergy; |
|
|
|
|
// cost = 0;
|
|
|
|
|
inference.SetDataCost(label, f, cost); |
|
|
|
|
} else { |
|
|
|
|
// 其他视角:成本随排名线性增加
|
|
|
|
|
int stepIndex = i; |
|
|
|
|
// if (i > 3)
|
|
|
|
|
// stepIndex = i - 3;
|
|
|
|
|
cost = MaxEnergy * (baseCostScale + costStep * stepIndex); |
|
|
|
|
// 确保成本不超过MaxEnergy
|
|
|
|
|
cost = std::min(cost, MaxEnergy); |
|
|
|
|
// cost = MaxEnergy;
|
|
|
|
|
inference.SetDataCost(label, f, cost); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
float bestQuality = -1.0f; |
|
|
|
|
IIndex bestView = NO_ID; |
|
|
|
|
bool bBestIsInvalid = false; |
|
|
|
|
|
|
|
|
|
} else { |
|
|
|
|
for (const FaceData& fd : faceDatas) { |
|
|
|
|
const Label label = (Label)fd.idxView + 1; |
|
|
|
|
const float normalizedQuality = fd.quality / normQuality; |
|
|
|
|
const float cost = (1.f - normalizedQuality) * MaxEnergy; |
|
|
|
|
inference.SetDataCost(label, f, cost); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
//*/
|
|
|
|
|
|
|
|
|
|
// assign the optimal view (label) to each face
|
|
|
|
|
// (label 0 is reserved as undefined)
|
|
|
|
|
inference.Optimize(); |
|
|
|
|
|
|
|
|
|
// extract resulting labeling
|
|
|
|
|
labels.Memset(0xFF); |
|
|
|
|
FOREACH(l, labels) { |
|
|
|
|
const Label label(inference.GetLabel(l)); |
|
|
|
|
ASSERT(label < images.size()+1); |
|
|
|
|
if (label > 0) |
|
|
|
|
labels[l] = label-1; |
|
|
|
|
// 跳过无效视图(根据你的需求决定是否跳过)
|
|
|
|
|
if (fd.bInvalidFacesRelative) { |
|
|
|
|
// 如果没有有效视图,才考虑无效视图
|
|
|
|
|
if (bestView == NO_ID) { |
|
|
|
|
bestView = fd.idxView; |
|
|
|
|
bestQuality = fd.quality; |
|
|
|
|
bBestIsInvalid = true; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#if TEXOPT_INFERENCE == TEXOPT_INFERENCE_TRWS |
|
|
|
|
// find connected components
|
|
|
|
|
ASSERT((FIndex)boost::num_vertices(graph) == faces.size()); |
|
|
|
|
components.resize(faces.size()); |
|
|
|
|
const FIndex nComponents(boost::connected_components(graph, components.data())); |
|
|
|
|
|
|
|
|
|
// map face ID from global to component space
|
|
|
|
|
typedef cList<NodeID, NodeID, 0, 128, NodeID> NodeIDs; |
|
|
|
|
NodeIDs nodeIDs(faces.size()); |
|
|
|
|
NodeIDs sizes(nComponents); |
|
|
|
|
sizes.Memset(0); |
|
|
|
|
FOREACH(c, components) |
|
|
|
|
nodeIDs[c] = sizes[components[c]]++; |
|
|
|
|
|
|
|
|
|
// initialize inference structures
|
|
|
|
|
const LabelID numLabels(images.size()+1); |
|
|
|
|
CLISTDEFIDX(TRWSInference, FIndex) inferences(nComponents); |
|
|
|
|
FOREACH(s, sizes) { |
|
|
|
|
const NodeID numNodes(sizes[s]); |
|
|
|
|
ASSERT(numNodes > 0); |
|
|
|
|
if (numNodes <= 1) |
|
|
|
|
continue; |
|
|
|
|
TRWSInference& inference = inferences[s]; |
|
|
|
|
inference.Init(numNodes, numLabels); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// set data costs
|
|
|
|
|
{ |
|
|
|
|
// add nodes
|
|
|
|
|
CLISTDEF0(EnergyType) D(numLabels); |
|
|
|
|
FOREACH(f, facesDatas) { |
|
|
|
|
TRWSInference& inference = inferences[components[f]]; |
|
|
|
|
if (inference.IsEmpty()) |
|
|
|
|
continue; |
|
|
|
|
D.MemsetValue(MaxEnergy); |
|
|
|
|
const FaceDataArr& faceDatas = facesDatas[f]; |
|
|
|
|
for (const FaceData& faceData: faceDatas) { |
|
|
|
|
const Label label((Label)faceData.idxView); |
|
|
|
|
const float normalizedQuality(faceData.quality>=normQuality ? 1.f : faceData.quality/normQuality); |
|
|
|
|
const EnergyType dataCost(MaxEnergy*(1.f-normalizedQuality)); |
|
|
|
|
D[label] = dataCost; |
|
|
|
|
} |
|
|
|
|
const NodeID nodeID(nodeIDs[f]); |
|
|
|
|
inference.AddNode(nodeID, D.Begin()); |
|
|
|
|
} |
|
|
|
|
// add edges
|
|
|
|
|
EdgeOutIter ei, eie; |
|
|
|
|
FOREACH(f, faces) { |
|
|
|
|
TRWSInference& inference = inferences[components[f]]; |
|
|
|
|
if (inference.IsEmpty()) |
|
|
|
|
continue; |
|
|
|
|
for (boost::tie(ei, eie) = boost::out_edges(f, graph); ei != eie; ++ei) { |
|
|
|
|
ASSERT(f == (FIndex)ei->m_source); |
|
|
|
|
const FIndex fAdj((FIndex)ei->m_target); |
|
|
|
|
ASSERT(components[f] == components[fAdj]); |
|
|
|
|
if (f < fAdj) // add edges only once
|
|
|
|
|
inference.AddEdge(nodeIDs[f], nodeIDs[fAdj]); |
|
|
|
|
} |
|
|
|
|
if (fd.quality > bestQuality) { |
|
|
|
|
bestQuality = fd.quality; |
|
|
|
|
bestView = fd.idxView; |
|
|
|
|
bBestIsInvalid = false; |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
// assign the optimal view (label) to each face
|
|
|
|
|
#ifdef TEXOPT_USE_OPENMP |
|
|
|
|
#pragma omp parallel for schedule(dynamic) |
|
|
|
|
for (int i=0; i<(int)inferences.size(); ++i) { |
|
|
|
|
#else |
|
|
|
|
FOREACH(i, inferences) { |
|
|
|
|
#endif |
|
|
|
|
TRWSInference& inference = inferences[i]; |
|
|
|
|
if (inference.IsEmpty()) |
|
|
|
|
continue; |
|
|
|
|
inference.Optimize(); |
|
|
|
|
} |
|
|
|
|
// extract resulting labeling
|
|
|
|
|
labels.Memset(0xFF); |
|
|
|
|
FOREACH(l, labels) { |
|
|
|
|
TRWSInference& inference = inferences[components[l]]; |
|
|
|
|
if (inference.IsEmpty()) |
|
|
|
|
continue; |
|
|
|
|
const Label label(inference.GetLabel(nodeIDs[l])); |
|
|
|
|
ASSERT(label >= 0 && label < numLabels); |
|
|
|
|
if (label < images.size()) |
|
|
|
|
labels[l] = label; |
|
|
|
|
labels[f] = bestView; |
|
|
|
|
if (bBestIsInvalid) { |
|
|
|
|
labelsInvalid[f] = bestView; |
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
|