Skip to content

Commit fc19df8

Browse files
authored
Merge pull request #198 from tbirdso/mutual-information-affine-example
ENH: Fix MutualInformationAffineExample and add Binder notebooks
2 parents 635c7d7 + 448db3f commit fc19df8

19 files changed

+2842
-283
lines changed

requirements.txt

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
async-generator==1.10
2+
atomicwrites==1.4.0
3+
backcall==0.2.0
4+
brotlipy==0.7.0
5+
certifi==2020.12.5
6+
colorama==0.4.4
7+
colorcet==2.0.2
8+
cycler==0.10.0
9+
decorator==4.4.2
10+
defusedxml==0.6.0
11+
entrypoints==0.3
12+
imageio==2.9.0
13+
iniconfig==1.1.1
14+
ipydatawidgets==4.2.0
15+
ipykernel==5.4.3
16+
ipympl==0.6.2
17+
ipython==7.19.0
18+
ipython-genutils==0.2.0
19+
ipywidgets==7.6.3
20+
itk==5.2rc2
21+
itk-boneenhancement==0.4.0
22+
itk-bonemorphometry==1.2.0
23+
itk-core==5.2rc2
24+
itk-filtering==5.2rc2
25+
itk-hasi==0.1.1
26+
itk-io==5.2rc2
27+
itk-meshtopolydata==0.6.3
28+
itk-numerics==5.2rc2
29+
itk-registration==5.2rc2
30+
itk-segmentation==5.2rc2
31+
itkwidgets==0.32.0
32+
jedi==0.18.0
33+
Jinja2==2.11.2
34+
json5==0.9.5
35+
jupyter-client==6.1.11
36+
jupyter-core==4.7.0
37+
jupyterlab==2.2.6
38+
jupyterlab-widgets==1.0.0
39+
kiwisolver==1.3.1
40+
MarkupSafe==1.1.1
41+
matplotlib==3.3.3
42+
mistune==0.8.4
43+
numpy==1.19.5
44+
param==1.10.1
45+
parso==0.8.1
46+
pickleshare==0.7.5
47+
Pillow==8.1.0
48+
pluggy==0.13.1
49+
prompt-toolkit==3.0.10
50+
py==1.10.0
51+
pyct==0.4.8
52+
Pygments==2.7.4
53+
pyparsing==2.4.7
54+
pytest==6.2.1
55+
python-dateutil==2.8.1
56+
pyzmq==20.0.0
57+
six==1.15.0
58+
testpath==0.4.4
59+
toml==0.10.2
60+
tornado==6.1
61+
traitlets==5.0.5
62+
traittypes==0.2.1
63+
wcwidth==0.2.5
64+
webencodings==0.5.1
65+
widgetsnbextension==3.5.1
66+
wincertstore==0.2
67+
zstandard==0.15.1

src/Core/Transform/CMakeLists.txt

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -29,15 +29,13 @@ compare_to_baseline(EXAMPLE_NAME GlobalRegistrationTwoImagesAffine
2929
output
3030
)
3131

32-
# TODO revisit input/baseline images and example paramters
33-
# before reenabling
34-
#add_example(MutualInformationAffine)
35-
#compare_to_baseline(EXAMPLE_NAME MutualInformationAffine
36-
# BASELINE_PREFIX
37-
# fixed
38-
# moving
39-
# output
40-
# )
32+
add_example(MutualInformationAffine)
33+
compare_to_baseline(EXAMPLE_NAME MutualInformationAffine
34+
BASELINE_PREFIX
35+
fixed
36+
moving
37+
OutputBaseline
38+
)
4139

4240
add_example(GlobalRegistrationTwoImagesBSpline)
4341
compare_to_baseline(EXAMPLE_NAME GlobalRegistrationTwoImagesBSpline

src/Core/Transform/MutualInformationAffine/CMakeLists.txt

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,5 +22,8 @@ install(FILES Code.cxx CMakeLists.txt
2222

2323
enable_testing()
2424
add_test(NAME MutualInformationAffineTest
25-
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/MutualInformationAffine)
25+
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/MutualInformationAffine
26+
fixed.png
27+
moving.png
28+
output.png)
2629

src/Core/Transform/MutualInformationAffine/Code.cxx

Lines changed: 47 additions & 127 deletions
Original file line numberDiff line numberDiff line change
@@ -27,38 +27,32 @@
2727
#include "itkEllipseSpatialObject.h"
2828
#include "itkSpatialObjectToImageFilter.h"
2929
#include "itkImageFileWriter.h"
30+
#include "itkImageFileReader.h"
3031

3132
constexpr unsigned int Dimension = 2;
3233
using PixelType = unsigned char;
3334

3435
using ImageType = itk::Image<PixelType, Dimension>;
3536

36-
static void
37-
CreateEllipseImage(ImageType::Pointer image);
38-
static void
39-
CreateCircleImage(ImageType::Pointer image);
40-
4137
int
42-
main(int itkNotUsed(argc), char * itkNotUsed(argv)[])
38+
main(int argc, char * argv[])
4339
{
44-
// Generate synthetic fixed and moving images
45-
ImageType::Pointer fixedImage = ImageType::New();
46-
CreateCircleImage(fixedImage);
47-
ImageType::Pointer movingImage = ImageType::New();
48-
CreateEllipseImage(movingImage);
49-
50-
// Write the two synthetic inputs
51-
using WriterType = itk::ImageFileWriter<ImageType>;
40+
using ReaderType = itk::ImageFileReader<ImageType>;
5241

53-
WriterType::Pointer fixedWriter = WriterType::New();
54-
fixedWriter->SetFileName("fixed.png");
55-
fixedWriter->SetInput(fixedImage);
56-
fixedWriter->Update();
42+
if (argc < 4)
43+
{
44+
std::cout << "Usage: " << argv[0] << " imageFile1 imageFile2 outputFile" << std::endl;
45+
return EXIT_FAILURE;
46+
}
47+
ReaderType::Pointer fixedReader = ReaderType::New();
48+
fixedReader->SetFileName(argv[1]);
49+
fixedReader->Update();
50+
ImageType::Pointer fixedImage = fixedReader->GetOutput();
5751

58-
WriterType::Pointer movingWriter = WriterType::New();
59-
movingWriter->SetFileName("moving.png");
60-
movingWriter->SetInput(movingImage);
61-
movingWriter->Update();
52+
ReaderType::Pointer movingReader = ReaderType::New();
53+
movingReader->SetFileName(argv[2]);
54+
movingReader->Update();
55+
ImageType::Pointer movingImage = movingReader->GetOutput();
6256

6357
// We use floats internally
6458
using InternalImageType = itk::Image<float, Dimension>;
@@ -113,8 +107,8 @@ main(int itkNotUsed(argc), char * itkNotUsed(argv)[])
113107
// which have been normalized to a mean of zero and unit variance. We
114108
// will follow this empirical rule in this example.
115109

116-
metric->SetFixedImageStandardDeviation(0.4);
117-
metric->SetMovingImageStandardDeviation(0.4);
110+
metric->SetFixedImageStandardDeviation(5.0);
111+
metric->SetMovingImageStandardDeviation(5.0);
118112

119113
registration->SetFixedImage(fixedSmoother->GetOutput());
120114
registration->SetMovingImage(movingSmoother->GetOutput());
@@ -172,11 +166,8 @@ main(int itkNotUsed(argc), char * itkNotUsed(argv)[])
172166

173167
metric->SetNumberOfSpatialSamples(numberOfSamples);
174168

175-
// optimizer->SetLearningRate( 15.0 ); //"All the sampled point mapped to outside of the moving image"
176-
// optimizer->SetLearningRate( 1.0 );
177-
optimizer->SetLearningRate(0.1);
178-
optimizer->SetNumberOfIterations(1000);
179-
optimizer->MaximizeOn(); // We want to maximize mutual information (the default of the optimizer is to minimize)
169+
// For consistent results when regression testing.
170+
metric->ReinitializeSeed(121212);
180171

181172
// Note that large values of the learning rate will make the optimizer
182173
// unstable. Small values, on the other hand, may result in the optimizer
@@ -193,6 +184,30 @@ main(int itkNotUsed(argc), char * itkNotUsed(argv)[])
193184
// optimizer step length is proportional to the Metric values themselves.
194185
// Metrics with large values will require you to use smaller values for the
195186
// learning rate in order to maintain a similar optimizer behavior.
187+
optimizer->SetLearningRate(1.0);
188+
189+
// Note that the only stop condition for the v3 GradientDescentOptimizer class
190+
// is that the maximum number of iterations is reached.
191+
// For the option to exit early on convergence use GradientDescentOptimizerv4
192+
// with an accompanying v4 metric class.
193+
optimizer->SetNumberOfIterations(200);
194+
optimizer->MaximizeOn(); // We want to maximize mutual information (the default of the optimizer is to minimize)
195+
196+
auto scales = optimizer->GetScales();
197+
198+
// Let optimizer take
199+
// large steps along translation parameters,
200+
// moderate steps along rotational parameters,
201+
// and small steps along scale parameters
202+
scales.SetSize(6);
203+
scales.SetElement(0, 100);
204+
scales.SetElement(1, 0.5);
205+
scales.SetElement(2, 0.5);
206+
scales.SetElement(3, 100);
207+
scales.SetElement(4, 0.0001);
208+
scales.SetElement(5, 0.0001);
209+
210+
optimizer->SetScales(scales);
196211

197212
try
198213
{
@@ -240,107 +255,12 @@ main(int itkNotUsed(argc), char * itkNotUsed(argv)[])
240255
resample->SetOutputDirection(fixedImage->GetDirection());
241256
resample->SetDefaultPixelValue(100);
242257

258+
using WriterType = itk::ImageFileWriter<ImageType>;
259+
243260
WriterType::Pointer writer = WriterType::New();
244-
writer->SetFileName("output.png");
261+
writer->SetFileName(argv[3]);
245262
writer->SetInput(resample->GetOutput());
246263
writer->Update();
247264

248265
return EXIT_SUCCESS;
249266
}
250-
251-
252-
void
253-
CreateEllipseImage(ImageType::Pointer image)
254-
{
255-
using EllipseType = itk::EllipseSpatialObject<Dimension>;
256-
257-
using SpatialObjectToImageFilterType = itk::SpatialObjectToImageFilter<EllipseType, ImageType>;
258-
259-
SpatialObjectToImageFilterType::Pointer imageFilter = SpatialObjectToImageFilterType::New();
260-
261-
ImageType::SizeType size;
262-
size[0] = 100;
263-
size[1] = 100;
264-
265-
imageFilter->SetSize(size);
266-
267-
ImageType::SpacingType spacing;
268-
spacing.Fill(1);
269-
imageFilter->SetSpacing(spacing);
270-
271-
EllipseType::Pointer ellipse = EllipseType::New();
272-
EllipseType::ArrayType radiusArray;
273-
radiusArray[0] = 10;
274-
radiusArray[1] = 20;
275-
ellipse->SetRadiusInObjectSpace(radiusArray);
276-
277-
using TransformType = EllipseType::TransformType;
278-
TransformType::Pointer transform = TransformType::New();
279-
transform->SetIdentity();
280-
281-
TransformType::OutputVectorType translation;
282-
translation[0] = 65;
283-
translation[1] = 45;
284-
transform->Translate(translation, false);
285-
286-
ellipse->SetObjectToParentTransform(transform);
287-
288-
imageFilter->SetInput(ellipse);
289-
290-
ellipse->SetDefaultInsideValue(255);
291-
ellipse->SetDefaultOutsideValue(0);
292-
imageFilter->SetUseObjectValue(true);
293-
imageFilter->SetOutsideValue(0);
294-
295-
imageFilter->Update();
296-
297-
image->Graft(imageFilter->GetOutput());
298-
}
299-
300-
void
301-
CreateCircleImage(ImageType::Pointer image)
302-
{
303-
using EllipseType = itk::EllipseSpatialObject<Dimension>;
304-
305-
using SpatialObjectToImageFilterType = itk::SpatialObjectToImageFilter<EllipseType, ImageType>;
306-
307-
SpatialObjectToImageFilterType::Pointer imageFilter = SpatialObjectToImageFilterType::New();
308-
309-
ImageType::SizeType size;
310-
size[0] = 100;
311-
size[1] = 100;
312-
313-
imageFilter->SetSize(size);
314-
315-
ImageType::SpacingType spacing;
316-
spacing.Fill(1);
317-
imageFilter->SetSpacing(spacing);
318-
319-
EllipseType::Pointer ellipse = EllipseType::New();
320-
EllipseType::ArrayType radiusArray;
321-
radiusArray[0] = 10;
322-
radiusArray[1] = 10;
323-
ellipse->SetRadiusInObjectSpace(radiusArray);
324-
325-
using TransformType = EllipseType::TransformType;
326-
TransformType::Pointer transform = TransformType::New();
327-
transform->SetIdentity();
328-
329-
TransformType::OutputVectorType translation;
330-
translation[0] = 50;
331-
translation[1] = 50;
332-
transform->Translate(translation, false);
333-
334-
ellipse->SetObjectToParentTransform(transform);
335-
336-
imageFilter->SetInput(ellipse);
337-
338-
ellipse->SetDefaultInsideValue(255);
339-
ellipse->SetDefaultOutsideValue(0);
340-
imageFilter->SetUseObjectValue(true);
341-
imageFilter->SetOutsideValue(0);
342-
343-
imageFilter->Update();
344-
345-
image->Graft(imageFilter->GetOutput());
346-
}

src/Core/Transform/MutualInformationAffine/Documentation.rst

Lines changed: 18 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ Mutual Information Affine
44
.. index::
55
single: AffineTransform
66
single: MutualInformationImageToImageMetric
7+
single: GradientDescentOptimizer
78

89
Synopsis
910
--------
@@ -25,20 +26,21 @@ Results
2526

2627
moving.png
2728

28-
.. figure:: output.png
29+
.. figure:: OutputBaseline.png
2930
:scale: 70%
30-
:alt: output.png
31+
:alt: OutputBaseline.png
3132

32-
output.png
33+
OutputBaseline.png
3334

3435
Output::
3536

36-
Optimizer stop condition: GradientDescentOptimizer: Maximum number of iterations (1000) exceeded.
37-
Final Parameters: [2.03502150081667, 0.4815329593844025, -0.524998748481682, 0.5494736862921054, 0.016827072908497744, -0.018211282766070345]
37+
Optimizer stop condition: GradientDescentOptimizer: Maximum number of iterations (200) exceeded.
38+
Final Parameters: [1.0028069041777101, -0.009647107048100798, -0.010717116855425006, 1.0029040091646872, 13.26279335943067, 12.226979744206531]
39+
3840
Result =
39-
Iterations = 1000
40-
Metric value = -0.000162484
41-
Numb. Samples = 100
41+
Iterations = 200
42+
Metric value = 0.000971698
43+
Numb. Samples = 567
4244

4345
Code
4446
----
@@ -49,8 +51,16 @@ C++
4951
.. literalinclude:: Code.cxx
5052
:lines: 18-
5153

54+
55+
Binder
56+
------
57+
.. image:: https://mybinder.org/badge_logo.svg
58+
:target: https://mybinder.org/v2/gh/InsightSoftwareConsortium/ITKExamples/master?filepath=src%2FCore%2FTransform%2FMutualInformationAffine%2FMutualInformationAffine.ipynb
59+
60+
5261
Classes demonstrated
5362
--------------------
5463

5564
.. breathelink:: itk::AffineTransform
5665
.. breathelink:: itk::MutualInformationImageToImageMetric
66+
.. breathelink:: itk::GradientDescentOptimizer

0 commit comments

Comments
 (0)