File: itkImageMaskSpatialObjectTest2.cxx

package info (click to toggle)
insighttoolkit5 5.4.3-5
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 704,384 kB
  • sloc: cpp: 783,592; ansic: 628,724; xml: 44,704; fortran: 34,250; python: 22,874; sh: 4,078; pascal: 2,636; lisp: 2,158; makefile: 464; yacc: 328; asm: 205; perl: 203; lex: 146; tcl: 132; javascript: 98; csh: 81
file content (223 lines) | stat: -rw-r--r-- 8,497 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
/*=========================================================================
 *
 *  Copyright NumFOCUS
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *         https://www.apache.org/licenses/LICENSE-2.0.txt
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.
 *
 *=========================================================================*/
// Disable warning for long symbol names in this file only

/*
 * This is a test file for the itkImageMaskSpatialObject class.
 * The supported pixel types does not include itkRGBPixel, itkRGBAPixel, etc...
 * So far it only allows to manage images of simple types like unsigned short,
 * unsigned int, or itk::Vector<...>.
 */

/*
 * This test addresses bug
 * https://public.kitware.com/Bug/view.php?id=0006340
 *
 */

#include "itkImageRegionIterator.h"
#include "itkImageMaskSpatialObject.h"
#include "itkMath.h"
#include "itkEuler3DTransform.h"

int
itkImageMaskSpatialObjectTest2(int, char *[])
{
  constexpr unsigned int VDimension = 3;
  int                    retval = EXIT_SUCCESS;

  using ImageMaskSpatialObject = itk::ImageMaskSpatialObject<VDimension>;
  using PixelType = ImageMaskSpatialObject::PixelType;
  using ImageType = itk::Image<PixelType, VDimension>;
  using Iterator = itk::ImageRegionIterator<ImageType>;

  // Direction was not taken into account in the image spatial object
  // explicitly test using images with directions set.
  // Also explicitly uses nonzero origin, non identity scales
  // to fully test the commonly encountered cases from the real world

  auto image = ImageType::New();

  // Set the direction for a non-oriented image
  // to better test the frequently encountered case
  // Use non axis aligned image directions
  auto tfm = itk::Euler3DTransform<itk::SpacePrecisionType>::New();
  tfm->SetRotation(30.0 * itk::Math::pi_over_180, 15.0 * itk::Math::pi_over_180, 10.0 * itk::Math::pi_over_180);
  const ImageType::DirectionType direction = tfm->GetMatrix();
  image->SetDirection(direction);

  const ImageType::SizeType size = { { 50, 50, 50 } };
  ImageType::PointType      origin;
  origin[0] = 1.51;
  origin[1] = 2.10;
  origin[2] = -300;
  image->SetOrigin(origin);

  ImageType::SpacingType spacing;
  spacing[0] = 0.5;
  spacing[1] = 0.7;
  spacing[2] = 1.1;
  image->SetSpacing(spacing);
  constexpr unsigned int     index_offset = 6543;
  const ImageType::IndexType index = { { index_offset, index_offset, index_offset } };

  ImageType::RegionType region{ index, size };
  image->SetRegions(region);
  image->AllocateInitialized();

  ImageType::RegionType  insideRegion;
  constexpr unsigned int INSIDE_SIZE = 30;
  constexpr unsigned int INSIDE_INDEX = index_offset + 10;
  {
    const ImageType::SizeType insideSize = { { INSIDE_SIZE, INSIDE_SIZE, INSIDE_SIZE } };
    insideRegion.SetSize(insideSize);
  }
  {
    const ImageType::IndexType insideIndex = { { INSIDE_INDEX, INSIDE_INDEX, INSIDE_INDEX } };
    insideRegion.SetIndex(insideIndex);
  }
  {
    Iterator it(image, insideRegion);
    it.GoToBegin();
    while (!it.IsAtEnd())
    {
      it.Set(itk::NumericTraits<PixelType>::max());
      ++it;
    }
  }

  auto maskSO = ImageMaskSpatialObject::New();
  maskSO->SetImage(image);
  maskSO->Update();

  { // Replicate use of MaskSpatialObject behavior from itk::ImageToImageMetric.hxx
    Iterator itr(image, region);
    itr.GoToBegin();
    while (!itr.IsAtEnd())
    {
      const ImageType::IndexType constIndex = itr.GetIndex();

      ImageType::PointType point;
      image->TransformIndexToPhysicalPoint(constIndex, point);
      const bool isInsideTest = maskSO->IsInsideInWorldSpace(point);

      double outsideIfZeroValue;
      maskSO->ValueAtInWorldSpace(point, outsideIfZeroValue);
      if (isInsideTest && itk::Math::AlmostEquals(outsideIfZeroValue, 0.0))
      {
        std::cerr << "ERROR: ValueAtInWorldSpace is wrong. " << outsideIfZeroValue
                  << " << computed, but should not be very close to 0.0." << std::endl;
        std::cerr << "     : Index=" << constIndex << "\n     : PhysicalPoint=" << point << '.' << std::endl;
        retval = EXIT_FAILURE;
        break;
      }
      ++itr;
    }
  }

  { // Test region based is inside
    Iterator itr(image, region);
    itr.GoToBegin();
    while (!itr.IsAtEnd())
    {
      const ImageType::IndexType constIndex = itr.GetIndex();
      const bool                 reference = insideRegion.IsInside(constIndex);

      ImageType::PointType point;
      image->TransformIndexToPhysicalPoint(constIndex, point);
      const bool test = maskSO->IsInsideInWorldSpace(point);
      if (test != reference)
      {
        std::cerr << "Error in the evaluation of maskSO->IsInsideInWorldSpace() " << std::endl;
        std::cerr << "Index failed = " << constIndex << std::endl;
        std::cerr << "Point failed = " << point << std::endl;
        std::cerr << "Image is a: " << image->GetNameOfClass() << std::endl;
        std::cerr << "Direction is: " << std::endl << image->GetDirection() << std::endl;
        retval = EXIT_FAILURE;
        break;
      }
      // Should be the same as WorldSpace since there is no hierarchy.
      const bool test_object_space = maskSO->IsInsideInObjectSpace(point);
      if (test != test_object_space)
      {
        std::cerr << "IsInsideInObjectSpace !=  IsInsideInWorldSpace for object that does not have hierarchy."
                  << std::endl;
        std::cerr << "Index failed = " << constIndex << std::endl;
        std::cerr << "Point failed = " << point << std::endl;
        std::cerr << "Image is a: " << image->GetNameOfClass() << std::endl;
        std::cerr << "Direction is: " << std::endl << image->GetDirection() << std::endl;
        std::cerr << std::endl;
        retval = EXIT_FAILURE;
        break;
      }
      ++itr;
    }
  }

  if (retval == EXIT_SUCCESS)
  {
    std::cout << "Test with " << image->GetNameOfClass() << " passed." << std::endl;
  }

  // Check if insideregion is properly computed at the image boundary
  {
    ImageType::IndexType startPointIndex = { { INSIDE_SIZE - 2, INSIDE_SIZE - 2, INSIDE_SIZE - 2 } };
    ImageType::IndexType endPointIndex = {
      { INSIDE_INDEX + INSIDE_SIZE + 2, INSIDE_INDEX + INSIDE_SIZE + 2, INSIDE_INDEX + INSIDE_SIZE + 2 }
    };
    ImageType::PointType startPoint;
    ImageType::PointType endPoint;
    image->TransformIndexToPhysicalPoint(startPointIndex, startPoint);
    image->TransformIndexToPhysicalPoint(endPointIndex, endPoint);

    // Traverse along the line that goes through mask boundaries and
    // check if the value and the mask is consistent
    const auto numberOfSteps =
      static_cast<int>(std::sqrt(static_cast<double>(INSIDE_SIZE * INSIDE_SIZE + INSIDE_SIZE * INSIDE_SIZE +
                                                     INSIDE_SIZE * INSIDE_SIZE)) *
                       100.0);
    const ImageType::SpacingType incrementVector = (endPoint - startPoint) / static_cast<double>(numberOfSteps);
    ImageType::PointType         point = startPoint;
    for (int i = 0; i < numberOfSteps; ++i)
    {
      point += incrementVector;
      const bool isInside = maskSO->IsInsideInWorldSpace(point);
      double     value{};
      maskSO->ValueAtInWorldSpace(point, value);
      const bool isZero = (itk::Math::ExactlyEquals(value, PixelType{}));
      if ((isInside && isZero) || (!isInside && !isZero))
      {
        ImageType::IndexType pointIndex = image->TransformPhysicalPointToIndex(point);
        std::cerr
          << "Error in the evaluation ValueAt and IsInside (all the points inside the mask shall have non-zero value) "
          << std::endl;
        std::cerr << "isInside = " << isInside << std::endl;
        std::cerr << "value = " << value << std::endl;
        std::cerr << "Index failed = " << pointIndex << std::endl;
        std::cerr << "Point failed = " << point << std::endl;
        std::cerr << "Image is a: " << image->GetNameOfClass() << std::endl;
        retval = EXIT_FAILURE;
        break;
      }
    }
  }


  std::cout << "Test finished" << std::endl;
  return retval;
}