Skip to content

Commit eeedab7

Browse files
committed
texture: fix corner case in global seam leveling logic
1 parent 5221549 commit eeedab7

File tree

3 files changed

+45
-113
lines changed

3 files changed

+45
-113
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
CMakeSettings.json
3434
.vs/
3535
.idea/
36+
.venv/
3637
.vscode/
3738
out/
3839
bin*/

libs/Common/Types.inl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2398,7 +2398,7 @@ template <typename TYPE>
23982398
template <typename SAMPLER, typename INTERTYPE>
23992399
INTERTYPE TImage<TYPE>::sample(const SAMPLER& sampler, const TPoint2<typename SAMPLER::Type>& pt) const
24002400
{
2401-
return Sampler::Sample< INTERTYPE, INTERTYPE, TImage<TYPE>, SAMPLER, TPoint2<typename SAMPLER::Type> >(*this, sampler, pt);
2401+
return Sampler::Sample< TYPE, INTERTYPE, TImage<TYPE>, SAMPLER, TPoint2<typename SAMPLER::Type> >(*this, sampler, pt);
24022402
}
24032403

24042404
// convert color image to gray

libs/MVS/SceneTexture.cpp

Lines changed: 43 additions & 112 deletions
Original file line numberDiff line numberDiff line change
@@ -210,39 +210,6 @@ struct MeshTexture {
210210
};
211211
typedef cList<SeamVertex,const SeamVertex&,1,256,uint32_t> SeamVertices;
212212

213-
// used to iterate vertex labels
214-
struct PatchIndex {
215-
bool bIndex;
216-
union {
217-
uint32_t idxPatch;
218-
uint32_t idxSeamVertex;
219-
};
220-
};
221-
typedef CLISTDEF0(PatchIndex) PatchIndices;
222-
struct VertexPatchIterator {
223-
uint32_t idx;
224-
uint32_t idxPatch;
225-
const SeamVertex::Patches* pPatches;
226-
inline VertexPatchIterator(const PatchIndex& patchIndex, const SeamVertices& seamVertices) : idx(NO_ID) {
227-
if (patchIndex.bIndex) {
228-
pPatches = &seamVertices[patchIndex.idxSeamVertex].patches;
229-
} else {
230-
idxPatch = patchIndex.idxPatch;
231-
pPatches = NULL;
232-
}
233-
}
234-
inline operator uint32_t () const {
235-
return idxPatch;
236-
}
237-
inline bool Next() {
238-
if (pPatches == NULL)
239-
return (idx++ == NO_ID);
240-
if (++idx >= pPatches->size())
241-
return false;
242-
idxPatch = (*pPatches)[idx].idxPatch;
243-
return true;
244-
}
245-
};
246213

247214
// used to sample seam edges
248215
typedef TAccumulator<Color> AccumColor;
@@ -289,12 +256,12 @@ struct MeshTexture {
289256
#if TEXOPT_FACEOUTLIER != TEXOPT_FACEOUTLIER_NA
290257
bool FaceOutlierDetection(FaceDataArr& faceDatas, float fOutlierThreshold) const;
291258
#endif
292-
259+
293260
void CreateVirtualFaces(const FaceDataViewArr& facesDatas, FaceDataViewArr& virtualFacesDatas, VirtualFaceIdxsArr& virtualFaces, unsigned minCommonCameras=2, float thMaxNormalDeviation=25.f) const;
294261
IIndexArr SelectBestView(const FaceDataArr& faceDatas, FIndex fid, unsigned minCommonCameras, float ratioAngleToQuality) const;
295262

296263
bool FaceViewSelection(unsigned minCommonCameras, float fOutlierThreshold, float fRatioDataSmoothness, int nIgnoreMaskLabel, const IIndexArr& views);
297-
264+
298265
void CreateSeamVertices();
299266
void GlobalSeamLeveling();
300267
void LocalSeamLeveling();
@@ -623,7 +590,7 @@ IIndexArr MeshTexture::SelectBestView(const FaceDataArr& faceDatas, FIndex fid,
623590
{
624591
ASSERT(!faceDatas.empty());
625592
#if 1
626-
593+
627594
// compute scores based on the view quality and its angle to the face normal
628595
float maxQuality = 0;
629596
for (const FaceData& faceData: faceDatas)
@@ -647,7 +614,7 @@ IIndexArr MeshTexture::SelectBestView(const FaceDataArr& faceDatas, FIndex fid,
647614
});
648615

649616
#else
650-
617+
651618
// sort qualityPodium in relation to faceDatas[index].quality decreasing
652619
IIndexArr qualityPodium(faceDatas.size());
653620
std::iota(qualityPodium.begin(), qualityPodium.end(), 0);
@@ -685,7 +652,7 @@ IIndexArr MeshTexture::SelectBestView(const FaceDataArr& faceDatas, FIndex fid,
685652
scorePodium.Sort([&scores](IIndex i, IIndex j) {
686653
return scores[i] < scores[j];
687654
});
688-
655+
689656
#endif
690657
IIndexArr cameras(MINF(minCommonCameras, faceDatas.size()));
691658
FOREACH(i, cameras)
@@ -700,7 +667,7 @@ static bool IsFaceVisible(const MeshTexture::FaceDataArr& faceDatas, const IInde
700667
for (IIndex camId : cameraList) {
701668
if (cfCam == camId) {
702669
if (++camFoundCounter == cameraList.size())
703-
return true;
670+
return true;
704671
break;
705672
}
706673
}
@@ -1136,7 +1103,7 @@ bool MeshTexture::FaceViewSelection(unsigned minCommonCameras, float fOutlierThr
11361103

11371104
graph.clear();
11381105
}
1139-
1106+
11401107
// create the graph of faces: each vertex is a face and the edges are the edges shared by the faces
11411108
FOREACH(idxFace, faces) {
11421109
MAYBEUNUSED const Mesh::FIndex idx((Mesh::FIndex)boost::add_vertex(graph));
@@ -1364,45 +1331,26 @@ void MeshTexture::GlobalSeamLeveling()
13641331
ASSERT(!seamVertices.empty());
13651332
const unsigned numPatches(texturePatches.size()-1);
13661333

1367-
// find the patch ID for each vertex
1368-
PatchIndices patchIndices(vertices.size());
1369-
patchIndices.Memset(0);
1334+
// assign a row index within the solution vector x to each vertex/patch
1335+
ASSERT(vertices.size() < static_cast<VIndex>(std::numeric_limits<MatIdx>::max()));
1336+
typedef std::unordered_map<uint32_t,MatIdx> VertexPatch2RowMap;
1337+
cList<VertexPatch2RowMap> vertpatch2rows(vertices.size());
1338+
1339+
// find the patch IDs for each vertex
13701340
FOREACH(f, faces) {
13711341
const uint32_t idxPatch(mapIdxPatch[components[f]]);
1342+
if (idxPatch == numPatches)
1343+
continue;
13721344
const Face& face = faces[f];
13731345
for (int v=0; v<3; ++v)
1374-
patchIndices[face[v]].idxPatch = idxPatch;
1375-
}
1376-
FOREACH(i, seamVertices) {
1377-
const SeamVertex& seamVertex = seamVertices[i];
1378-
ASSERT(!seamVertex.patches.empty());
1379-
PatchIndex& patchIndex = patchIndices[seamVertex.idxVertex];
1380-
patchIndex.bIndex = true;
1381-
patchIndex.idxSeamVertex = i;
1346+
vertpatch2rows[face[v]][idxPatch] = 0;
13821347
}
13831348

1384-
// assign a row index within the solution vector x to each vertex/patch
1385-
ASSERT(vertices.size() < static_cast<VIndex>(std::numeric_limits<MatIdx>::max()));
1349+
// assign a row to each vertex/patch
13861350
MatIdx rowsX(0);
1387-
typedef std::unordered_map<uint32_t,MatIdx> VertexPatch2RowMap;
1388-
cList<VertexPatch2RowMap> vertpatch2rows(vertices.size());
1389-
FOREACH(i, vertices) {
1390-
const PatchIndex& patchIndex = patchIndices[i];
1391-
VertexPatch2RowMap& vertpatch2row = vertpatch2rows[i];
1392-
if (patchIndex.bIndex) {
1393-
// vertex is part of multiple patches
1394-
const SeamVertex& seamVertex = seamVertices[patchIndex.idxSeamVertex];
1395-
ASSERT(seamVertex.idxVertex == i);
1396-
for (const SeamVertex::Patch& patch: seamVertex.patches) {
1397-
ASSERT(patch.idxPatch != numPatches);
1398-
vertpatch2row[patch.idxPatch] = rowsX++;
1399-
}
1400-
} else
1401-
if (patchIndex.idxPatch < numPatches) {
1402-
// vertex is part of only one patch
1403-
vertpatch2row[patchIndex.idxPatch] = rowsX++;
1404-
}
1405-
}
1351+
FOREACH(i, vertices)
1352+
for (auto& [idxPatch, row] : vertpatch2rows[i])
1353+
row = rowsX++;
14061354

14071355
// fill Tikhonov's Gamma matrix (regularization constraints)
14081356
const float lambda(0.1f);
@@ -1412,24 +1360,17 @@ void MeshTexture::GlobalSeamLeveling()
14121360
FOREACH(v, vertices) {
14131361
adjVerts.Empty();
14141362
scene.mesh.GetAdjVertices(v, adjVerts);
1415-
VertexPatchIterator itV(patchIndices[v], seamVertices);
1416-
while (itV.Next()) {
1417-
const uint32_t idxPatch(itV);
1418-
if (idxPatch == numPatches)
1419-
continue;
1420-
const MatIdx col(vertpatch2rows[v].at(idxPatch));
1363+
for (const auto& [idxPatch, col] : vertpatch2rows[v]) {
1364+
ASSERT(idxPatch < numPatches);
14211365
for (const VIndex vAdj: adjVerts) {
14221366
if (v >= vAdj)
14231367
continue;
1424-
VertexPatchIterator itVAdj(patchIndices[vAdj], seamVertices);
1425-
while (itVAdj.Next()) {
1426-
const uint32_t idxPatchAdj(itVAdj);
1427-
if (idxPatch == idxPatchAdj) {
1428-
const MatIdx colAdj(vertpatch2rows[vAdj].at(idxPatchAdj));
1429-
rows.emplace_back(rowsGamma, col, lambda);
1430-
rows.emplace_back(rowsGamma, colAdj, -lambda);
1431-
++rowsGamma;
1432-
}
1368+
const auto itVAdj(vertpatch2rows[vAdj].find(idxPatch));
1369+
if (itVAdj != vertpatch2rows[vAdj].end()) {
1370+
const MatIdx colAdj(itVAdj->second);
1371+
rows.emplace_back(rowsGamma, col, lambda);
1372+
rows.emplace_back(rowsGamma, colAdj, -lambda);
1373+
++rowsGamma;
14331374
}
14341375
}
14351376
}
@@ -1899,29 +1840,21 @@ void MeshTexture::LocalSeamLeveling()
18991840
if (idxVertPatch0 == SeamVertex::Patches::NO_INDEX)
19001841
continue;
19011842
const SeamVertex::Patch& patch0 = seamVertex0.patches[idxVertPatch0];
1902-
const TexCoord p0(patch0.proj-offset);
1843+
const TexCoord p0(patch0.proj - offset);
19031844
// for each edge of this vertex belonging to this patch...
19041845
for (const SeamVertex::Patch::Edge& edge0: patch0.edges) {
19051846
// select the same edge leaving from the adjacent vertex
19061847
const SeamVertex& seamVertex1 = seamVertices[edge0.idxSeamVertex];
1907-
const uint32_t idxVertPatch0Adj(seamVertex1.patches.Find(idxPatch));
1908-
ASSERT(idxVertPatch0Adj != SeamVertex::Patches::NO_INDEX);
1909-
const SeamVertex::Patch& patch0Adj = seamVertex1.patches[idxVertPatch0Adj];
1910-
const TexCoord p0Adj(patch0Adj.proj-offset);
1911-
// find the other patch sharing the same edge (edge with same adjacent vertex)
1912-
FOREACH(idxVertPatch1, seamVertex0.patches) {
1913-
if (idxVertPatch1 == idxVertPatch0)
1914-
continue;
1915-
const SeamVertex::Patch& patch1 = seamVertex0.patches[idxVertPatch1];
1916-
const uint32_t idxEdge1(patch1.edges.Find(edge0.idxSeamVertex));
1917-
if (idxEdge1 == SeamVertex::Patch::Edges::NO_INDEX)
1848+
const SeamVertex::Patch& patch0Adj = seamVertex1.patches[seamVertex1.patches.Find(idxPatch)];
1849+
const TexCoord p0Adj(patch0Adj.proj - offset);
1850+
// find the other patch sharing the same edge
1851+
for (const SeamVertex::Patch& patch1: seamVertex0.patches) {
1852+
if (patch1.idxPatch == idxPatch)
19181853
continue;
1919-
const TexCoord& p1(patch1.proj);
1854+
if (patch1.edges.Find(edge0.idxSeamVertex) == SeamVertex::Patch::Edges::NO_INDEX)
1855+
continue; // edge not shared with this patch
19201856
// select the same edge belonging to the second patch leaving from the adjacent vertex
1921-
const uint32_t idxVertPatch1Adj(seamVertex1.patches.Find(patch1.idxPatch));
1922-
ASSERT(idxVertPatch1Adj != SeamVertex::Patches::NO_INDEX);
1923-
const SeamVertex::Patch& patch1Adj = seamVertex1.patches[idxVertPatch1Adj];
1924-
const TexCoord& p1Adj(patch1Adj.proj);
1857+
const SeamVertex::Patch& patch1Adj = seamVertex1.patches[seamVertex1.patches.Find(patch1.idxPatch)];
19251858
// this is an edge separating two (valid) patches;
19261859
// draw it on this patch as the mean color of the two patches
19271860
const Image8U3& image1(images[texturePatches[patch1.idxPatch].label].image);
@@ -1939,17 +1872,15 @@ void MeshTexture::LocalSeamLeveling()
19391872
: image(_image), mask(_mask), image0(_image0), image1(_image1),
19401873
p0(_p0), p0Dir(_p0Adj-_p0), p1(_p1), p1Dir(_p1Adj-_p1), length((float)norm(p0Dir)) {}
19411874
inline void operator()(const ImageRef& pt) {
1942-
const float l((float)norm(TexCoord(pt)-p0)/length);
19431875
// compute mean color
1944-
const TexCoord samplePos0(p0 + p0Dir * l);
1945-
const Color color0(image0.sample<Sampler,Color>(sampler, samplePos0));
1946-
const TexCoord samplePos1(p1 + p1Dir * l);
1947-
const Color color1(image1.sample<Sampler,Color>(sampler, samplePos1)/255.f);
1876+
const float l((float)norm(TexCoord(pt)-p0)/length);
1877+
const Color color0(image0.sample<Sampler,Color>(sampler, p0 + p0Dir * l));
1878+
const Color color1(image1.sample<Sampler,Color>(sampler, p1 + p1Dir * l)/255.f);
19481879
image(pt) = Color((color0 + color1) * 0.5f);
19491880
// set mask edge also
19501881
mask(pt) = border;
19511882
}
1952-
} data(image, mask, imageOrg, image1, p0, p0Adj, p1, p1Adj);
1883+
} data(image, mask, imageOrg, image1, p0, p0Adj, patch1.proj, patch1Adj.proj);
19531884
Image32F3::DrawLine(p0, p0Adj, data);
19541885
// skip remaining patches,
19551886
// as a manifold edge is shared by maximum two face (one in each patch), which we found already
@@ -1964,7 +1895,7 @@ void MeshTexture::LocalSeamLeveling()
19641895
const Image8U3& img(images[texturePatches[patch.idxPatch].label].image);
19651896
accumColor.Add(img.sample<Sampler,Color>(sampler, patch.proj)/255.f, 1.f);
19661897
}
1967-
const ImageRef pt(ROUND2INT(patch0.proj-offset));
1898+
const ImageRef pt(ROUND2INT(p0));
19681899
image(pt) = accumColor.Normalized();
19691900
mask(pt) = border;
19701901
}
@@ -2142,7 +2073,7 @@ void MeshTexture::GenerateTexture(bool bGlobalSeamLeveling, bool bLocalSeamLevel
21422073
// try again with a bigger texture
21432074
textureSize *= 2;
21442075
if (maxTextureSize > 0)
2145-
textureSize = MAXF(textureSize, maxTextureSize);
2076+
textureSize = MINF(textureSize, maxTextureSize);
21462077
unplacedRects.JoinRemove(newPlacedRects);
21472078
}
21482079
}

0 commit comments

Comments
 (0)