Skip to content

Commit 2571627

Browse files
committed
ENH: Added ImageMomentsCalculator test and Jupyter example.
1 parent 29859a9 commit 2571627

File tree

8 files changed

+320
-0
lines changed

8 files changed

+320
-0
lines changed

src/Filtering/ImageStatistics/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,5 @@ add_example(ComputeMinMaxVarianceMeanOfImage)
4545
#compare_to_baseline(EXAMPLE_NAME StatisticalPropertiesOfRegions
4646
# BASELINE_PREFIX OutputBaseline
4747
# )
48+
49+
add_example(CalculateImageMoments)
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
cmake_minimum_required(VERSION 3.10.2)
2+
3+
project( CalculateImageMoments )
4+
5+
find_package( ITK REQUIRED )
6+
include( ${ITK_USE_FILE} )
7+
8+
add_executable( CalculateImageMoments Code.cxx )
9+
target_link_libraries( CalculateImageMoments ${ITK_LIBRARIES} )
10+
11+
install( TARGETS CalculateImageMoments
12+
DESTINATION bin/ITKExamples/Filtering/ImageStatistics
13+
COMPONENT Runtime
14+
)
15+
16+
install( FILES Code.cxx CMakeLists.txt Code.py
17+
DESTINATION share/ITKExamples/Code/Filtering/ImageStatistics/CalculateImageMoments
18+
COMPONENT Code
19+
)
20+
21+
enable_testing()
22+
23+
set(input_image ${CMAKE_CURRENT_BINARY_DIR}/ellipse.mha)
24+
25+
add_test( NAME CalculateImageMomentsTest
26+
COMMAND ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/CalculateImageMoments
27+
${input_image}
28+
)
29+
30+
if( ITK_WRAP_PYTHON )
31+
find_package(PythonInterp REQUIRED)
32+
string( REPLACE . "Python." output_image "${output_image}" )
33+
add_test( NAME CalculateImageMomentsTestPython
34+
COMMAND ${PYTHON_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/Code.py
35+
${input_image}
36+
)
37+
endif()
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
{
2+
"cells": [
3+
{
4+
"cell_type": "code",
5+
"execution_count": 9,
6+
"id": "protecting-hobby",
7+
"metadata": {},
8+
"outputs": [],
9+
"source": [
10+
"import itk\n",
11+
"import matplotlib.pyplot as plt\n",
12+
"import numpy as np\n",
13+
"from ipywidgets import interact"
14+
]
15+
},
16+
{
17+
"cell_type": "code",
18+
"execution_count": 32,
19+
"id": "dried-demographic",
20+
"metadata": {},
21+
"outputs": [
22+
{
23+
"data": {
24+
"application/vnd.jupyter.widget-view+json": {
25+
"model_id": "12ddf6ab0ad54db19849293d0d20c252",
26+
"version_major": 2,
27+
"version_minor": 0
28+
},
29+
"text/plain": [
30+
"interactive(children=(IntSlider(value=30, description='tx'), IntSlider(value=50, description='ty'), IntSlider(…"
31+
]
32+
},
33+
"metadata": {},
34+
"output_type": "display_data"
35+
}
36+
],
37+
"source": [
38+
"output_size = [100, 100] # size of binary image containing the ellipse\n",
39+
"\n",
40+
"@interact(tx=(0,output_size[0],1), ty=(0,output_size[1],1), a=(5,50,1), b=(5,50,1), theta=(0,2*np.pi,0.1))\n",
41+
"def foo(tx=30, ty=50, a=5, b=10, theta=np.pi/4.0):\n",
42+
" '''\n",
43+
" Creates a binary image of an ellipse and calculates the image moments. Major (purple) and minor (green)\n",
44+
" principal axes are displayed.\n",
45+
" \n",
46+
" Parameters\n",
47+
" ==========\n",
48+
" tx, ty : translation x and y\n",
49+
" a, b : ellipse horizontal and vertical widths before rotation\n",
50+
" theta : angle of rotation (radians)\n",
51+
" '''\n",
52+
" \n",
53+
" # ellipse starts as unit circle, use world transform to define final ellipse\n",
54+
" ellipse = itk.EllipseSpatialObject[2].New()\n",
55+
" ellipse_transform = itk.AffineTransform[itk.D, 2].New()\n",
56+
" ellipse_transform.Scale([a, b])\n",
57+
" ellipse_transform.Rotate2D(theta)\n",
58+
" ellipse_transform.Translate([tx, ty])\n",
59+
" ellipse.SetObjectToWorldTransform(ellipse_transform)\n",
60+
"\n",
61+
" ellipse_img = itk.spatial_object_to_image_filter(input=ellipse, inside_value=1, outside_value=0, size=output_size)\n",
62+
"\n",
63+
" momentor = itk.ImageMomentsCalculator.New(Image=ellipse_img)\n",
64+
" momentor.Compute()\n",
65+
"\n",
66+
" centroid = momentor.GetCenterOfGravity()\n",
67+
" prin_axes = itk.array_from_matrix(momentor.GetPrincipalAxes())\n",
68+
" minor_axes = prin_axes[0]\n",
69+
" major_axes = prin_axes[1]\n",
70+
"\n",
71+
" fig, ax = plt.subplots(figsize=[8,8])\n",
72+
" plt.imshow(ellipse_img, cmap='gray')\n",
73+
" plt.scatter(centroid[0], centroid[1])\n",
74+
"\n",
75+
" minor_pt = centroid + minor_axes*5\n",
76+
" plt.plot([centroid[0], minor_pt[0]], [centroid[1], minor_pt[1]], color='green')\n",
77+
"\n",
78+
" major_pt = centroid + major_axes*5\n",
79+
" plt.plot([centroid[0], major_pt[0]], [centroid[1], major_pt[1]], color='purple')\n",
80+
"\n",
81+
" print(momentor)\n"
82+
]
83+
},
84+
{
85+
"cell_type": "code",
86+
"execution_count": null,
87+
"id": "several-label",
88+
"metadata": {},
89+
"outputs": [],
90+
"source": []
91+
}
92+
],
93+
"metadata": {
94+
"kernelspec": {
95+
"display_name": "Python 3",
96+
"language": "python",
97+
"name": "python3"
98+
},
99+
"language_info": {
100+
"codemirror_mode": {
101+
"name": "ipython",
102+
"version": 3
103+
},
104+
"file_extension": ".py",
105+
"mimetype": "text/x-python",
106+
"name": "python",
107+
"nbconvert_exporter": "python",
108+
"pygments_lexer": "ipython3",
109+
"version": "3.7.6"
110+
}
111+
},
112+
"nbformat": 4,
113+
"nbformat_minor": 5
114+
}
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
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+
* http://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 "itkImageFileReader.h"
20+
#include "itkImageFileWriter.h"
21+
#include "itkImageMomentsCalculator.h"
22+
23+
int
24+
main(int argc, char * argv[])
25+
{
26+
if (argc != 2)
27+
{
28+
std::cerr << "Usage: " << std::endl;
29+
std::cerr << argv[0];
30+
std::cerr << " <InputFileName>" << std::endl;
31+
std::cerr << "Prints image moments from unsigned short binary image.";
32+
std::cerr << std::endl;
33+
return EXIT_FAILURE;
34+
}
35+
const char * inputFileName = argv[1];
36+
37+
constexpr unsigned int Dimension = 2;
38+
39+
using PixelType = unsigned short;
40+
using ImageType = itk::Image<PixelType, Dimension>;
41+
42+
using ReaderType = itk::ImageFileReader<ImageType>;
43+
ReaderType::Pointer reader = ReaderType::New();
44+
reader->SetFileName(inputFileName);
45+
reader->Update();
46+
47+
// clang-format off
48+
using FilterType = itk::ImageMomentsCalculator<ImageType>;
49+
// clang-format on
50+
FilterType::Pointer filter = FilterType::New();
51+
filter->SetImage(reader->GetOutput());
52+
filter->Compute();
53+
filter->Print(std::cout, 0);
54+
55+
return EXIT_SUCCESS;
56+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#!/usr/bin/env python
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+
# http://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+
import sys
18+
import itk
19+
import argparse
20+
21+
if len(sys.argv) != 2:
22+
print("Usage: " + sys.argv[0] + "<InputFileName>")
23+
print("Prints image moment information from binary image.")
24+
sys.exit(1)
25+
26+
parser = argparse.ArgumentParser(description="Calculate Image Moments.")
27+
parser.add_argument("input_image")
28+
29+
args = parser.parse_args()
30+
input_image = itk.imread(args.input_image)
31+
momentor = itk.ImageMomentsCalculator.New(Image=input_image)
32+
momentor.Compute()
33+
print(momentor)
Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
Calculate Image Moments
2+
================================
3+
4+
.. index::
5+
single: ImageMomentsCalculator
6+
7+
Synopsis
8+
--------
9+
10+
11+
Calculate image moments from binary images and an interactive ellipse.
12+
13+
14+
Results
15+
-------
16+
17+
.. figure:: ellipse.mha
18+
:scale: 50%
19+
:alt: Input image
20+
21+
Input Image
22+
::
23+
Output
24+
ImageMomentsCalculator (000002037138BE90)
25+
RTTI typeinfo: class itk::ImageMomentsCalculator<class itk::Image<unsigned short,2> >
26+
Reference Count: 1
27+
Modified Time: 249
28+
Debug: Off
29+
Object Name:
30+
Observers:
31+
none
32+
Image: 000002037138FDD0
33+
Valid: 1
34+
Zeroth Moment about origin: 153
35+
First Moment about origin: [30, 50]
36+
Second Moment about origin: 15.0458 -8.8366
37+
-8.8366 15.0458
38+
39+
Center of Gravity: [30, 50]
40+
Second central moments: 15.0458 -8.8366
41+
-8.8366 15.0458
42+
43+
Principal Moments: [950, 3654]
44+
Principal axes: 0.707107 0.707107
45+
-0.707107 0.707107
46+
::
47+
48+
49+
Jupyter Notebook
50+
-----------------
51+
52+
.. image:: https://mybinder.org/badge_logo.svg
53+
:target: https://mybinder.org/v2/gh/InsightSoftwareConsortium/ITKExamples/master?filepath=src%2FFiltering%2FImageStatistics%2FImageMomentsCalculator%2FCalculateEllipseMoments.ipynb
54+
55+
Code
56+
----
57+
58+
C++
59+
...
60+
61+
.. literalinclude:: Code.cxx
62+
:language: c++
63+
:lines: 18-
64+
65+
Python
66+
......
67+
68+
.. literalinclude:: Code.py
69+
:language: python
70+
:lines: 1,16-
71+
72+
73+
Classes demonstrated
74+
--------------------
75+
76+
.. breathelink:: itk::ImageMomentsCalculator
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
a9dc7abe4d5745de06491d21addb54f561619732d71c98f250447c4fcf2c1444a60badbebaa3d646913e82fff75bfc753e2495c2d672d5bd7f85860fb8f8da0c

src/Filtering/ImageStatistics/index.rst

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,4 @@ ImageStatistics
99
ComputeMinMaxVarianceMeanOfImage/Documentation.rst
1010
ComputePCAShapeFromSample/Documentation.rst
1111
StatisticalPropertiesOfRegions/Documentation.rst
12+
CalculateImageMoments/Documentation.rst

0 commit comments

Comments
 (0)