Skip to content

Commit 50dea9f

Browse files
authored
Merge pull request #91 from dzenanz/issue66
2 parents d022cb6 + c586297 commit 50dea9f

File tree

2 files changed

+154
-0
lines changed

2 files changed

+154
-0
lines changed

test/CMakeLists.txt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ set(CuberilleTests
77
CuberilleTest02.cxx
88
CuberilleTest03.cxx
99
CuberilleTest04.cxx
10+
CuberilleTest_Issue66.cxx
1011
)
1112

1213
CreateTestDriver(Cuberille "${Cuberille-Test_LIBRARIES}" "${CuberilleTests}")
@@ -351,6 +352,9 @@ add_test(
351352
# 25 # Maximum number of steps
352353
#)
353354

355+
add_test(NAME CuberilleIssue66
356+
COMMAND ${itk-module}TestDriver CuberilleTest_Issue66 )
357+
354358
itk_add_test(NAME CuberilleTestDirectionMatrix
355359
COMMAND ${itk-module}TestDriver CuberilleTest02 )
356360

test/CuberilleTest_Issue66.cxx

Lines changed: 150 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,150 @@
1+
/*=========================================================================
2+
*
3+
* Copyright NumFOCUS
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* https://www.apache.org/licenses/LICENSE-2.0.txt
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*
17+
*=========================================================================*/
18+
19+
#include <itkImage.h>
20+
#include <itkQuadEdgeMesh.h>
21+
#include <itkCuberilleImageToMeshFilter.h>
22+
#include <itkMeshFileWriter.h>
23+
#include <itkImageFileWriter.h>
24+
25+
const unsigned int Dimension = 3;
26+
using TPixel = unsigned char;
27+
using TImage = itk::Image<TPixel, Dimension>;
28+
using TMesh = itk::Mesh<double, 3>;
29+
// using TMesh = itk::QuadEdgeMesh<double, 3>;
30+
using TExtract = itk::CuberilleImageToMeshFilter<TImage, TMesh>;
31+
using TMeshWriter = itk::MeshFileWriter<TMesh>;
32+
33+
/* This is description from https://github.com/InsightSoftwareConsortium/ITKCuberille/issues/66
34+
35+
Imagine an image with only two foreground pixels, offset by {1, 1, 0}. When extracting the surface of this image as a
36+
mesh, it is necessary to duplicate the coincident edge, creating two disconnected cubes, in order to ensure that the
37+
result is manifold. A slice through the z plane would show:
38+
39+
*---*---*
40+
| A | |
41+
*---*---*
42+
| | B |
43+
*---*---*
44+
Now imagine that these foreground pixels are still flanked by background pixels in the z plain, but are "connected" into
45+
the same connected component by foreground pixels in the two adjacent z plains:
46+
47+
Z-1 Z Z+1
48+
*---*---* *---*---* *---*---*
49+
| X | X | | A | | | X | X |
50+
*---*---* *---*---* *---*---*
51+
| | X | | | B | | | X |
52+
*---*---* *---*---* *---*---*
53+
Here, the coincident edge of pixels A and B must again be duplicated; but unlike the first example, the two distinct
54+
edges actually connect the same two vertices. Quite reasonably, when asked to create a face which would require an edge
55+
between two vertices, itk::QuadEdgeMesh checks:
56+
https://github.com/InsightSoftwareConsortium/ITK/blob/v5.4.5/Modules/Core/QuadEdgeMesh/include/itkQuadEdgeMesh.hxx#L1212-L1221
57+
to see whether there is an existing edge, and refuses to create a new
58+
edge if one already exists.
59+
60+
Most of the time, this is what we want--but in the case described above, it acts as a bug. It's not immediately obvious
61+
to me how to detect when this check is and is not necessary; as I work through this problem, I thought I would post it
62+
as an issue in case anyone else had any insight--pun intended. ;-D
63+
64+
*/
65+
66+
TImage::Pointer
67+
case1()
68+
{
69+
const auto image = TImage::New();
70+
TImage::RegionType region({ { 0, 0, 0 } }, { { 5, 4, 4 } });
71+
image->SetBufferedRegion(region);
72+
image->AllocateInitialized(); // zero filled image
73+
74+
image->SetPixel({ { 1, 1, 1 } }, 1); // A
75+
image->SetPixel({ { 2, 2, 1 } }, 1); // B
76+
77+
image->SetPixel({ { 1, 1, 0 } }, 1); // C
78+
image->SetPixel({ { 1, 2, 0 } }, 1); // D
79+
image->SetPixel({ { 2, 2, 0 } }, 1); // E
80+
81+
image->SetPixel({ { 1, 1, 2 } }, 1); // F
82+
image->SetPixel({ { 1, 2, 2 } }, 1); // G
83+
image->SetPixel({ { 2, 2, 2 } }, 1); // H
84+
85+
return image;
86+
}
87+
88+
TImage::Pointer
89+
case2()
90+
{
91+
const auto image = TImage::New();
92+
TImage::RegionType region({ { 0, 0, 0 } }, { { 5, 4, 4 } });
93+
image->SetBufferedRegion(region);
94+
image->AllocateInitialized(); // zero filled image
95+
96+
image->SetPixel({ { 1, 1, 1 } }, 1); // A
97+
image->SetPixel({ { 2, 1, 1 } }, 1); // B
98+
99+
image->SetPixel({ { 3, 1, 1 } }, 1); // C
100+
image->SetPixel({ { 1, 2, 1 } }, 1); // D
101+
image->SetPixel({ { 3, 2, 1 } }, 1); // E
102+
103+
image->SetPixel({ { 1, 2, 2 } }, 1); // F
104+
image->SetPixel({ { 2, 2, 2 } }, 1); // G
105+
image->SetPixel({ { 3, 2, 2 } }, 1); // H
106+
107+
return image;
108+
}
109+
110+
111+
int
112+
CuberilleTest_Issue66(int argc, char * argv[])
113+
{
114+
int countFailed = 0;
115+
for (const auto image : { case1(), case2() })
116+
{
117+
118+
const auto extract = TExtract::New();
119+
extract->SetInput(image);
120+
extract->SavePixelAsCellDataOn();
121+
extract->GenerateTriangleFacesOff();
122+
extract->ProjectVerticesToIsoSurfaceOff();
123+
124+
const auto m_writer = TMeshWriter::New();
125+
m_writer->SetInput(extract->GetOutput());
126+
m_writer->SetFileName("mesh.obj");
127+
m_writer->Update();
128+
129+
const auto n_cell = extract->GetOutput()->GetNumberOfCells();
130+
const auto n_data = extract->GetOutput()->GetCellData()->Size();
131+
132+
if (n_cell != n_data)
133+
{
134+
std::cout << "This fails if using itk::QuadEdgeMesh." << std::endl;
135+
std::cout << "n_cell: " << n_cell << std::endl;
136+
std::cout << "n_data: " << n_data << std::endl;
137+
++countFailed;
138+
}
139+
else
140+
{
141+
std::cout << "This succeeds if using itk::Mesh." << std::endl;
142+
}
143+
}
144+
145+
if (countFailed == 0)
146+
{
147+
return EXIT_SUCCESS;
148+
}
149+
return EXIT_FAILURE;
150+
}

0 commit comments

Comments
 (0)