Process objects are classed as sources, filter objects, or mappers.
Sources (such as readers) produce data, filter objects take in data and process it to produce new data, and mappers accept data for output either to a file or some other system.
Sometimes the term filter is used broadly to refer to all three types.
Typically data objects and process objects are connected together using the SetInput()
and GetOutput()
methods.
Generation of new outputs and pixel data does not occur until the Update()
method is called on the end of the pipeline (on the process object or the data object).
The data associated with multi-dimensional images is large and becoming larger.
Any practical image analysis system must address this fact in order to be useful in applications with multi-dimensional images. ITK addresses this problem via its data streaming facility.
Streaming is performed by splitting the image into non-overlapping regions at the end of the pipeline. The RequestedRegion then propagates up the pipeline.
There are three named ImageRegion's encountered in ITK:
For more information, see,
import itk
from packaging.version import parse
from importlib.metadata import version
if parse(version('itk')) < parse('5.3'):
raise ValueError("ITK greater than version 5.3.0 is required for this notebook")
import numpy as np
from itkwidgets import view
from ipywidgets import interactive
import ipywidgets as widgets
We can monitor when a filter gets called by registering a command that gets called when ProgressEvent's occur.
itk.auto_progress(2)
Start an image processing pipeline with a source, an ImageFileReader
.
file_name = "data/PacMan.png"
reader = itk.ImageFileReader.New(FileName=file_name)
Loading ITKPyUtils... done
Next, let's create a smoothing filter. To connect the pipeline, specify the Output
of the reader as the Input
to the smoother.
smoother = itk.RecursiveGaussianImageFilter.New(Input=reader.GetOutput())
At this point, no output images have been generated.
We have configured the simple pipeline:
reader -> smoother
print(f"reader's Output: {reader.GetOutput()}")
print(f"smoother's Output: {smoother.GetOutput()}")
reader's Output: Image (0x55a08f40f220) RTTI typeinfo: itk::Image<unsigned char, 2u> Reference Count: 3 Modified Time: 284 Debug: Off Object Name: Observers: none Source: (0x55a08dc1f3c0) Source output name: Primary Release Data: Off Data Released: False Global Release Data: Off PipelineMTime: 0 UpdateMTime: 0 RealTimeStamp: 0 seconds LargestPossibleRegion: Dimension: 2 Index: [0, 0] Size: [0, 0] BufferedRegion: Dimension: 2 Index: [0, 0] Size: [0, 0] RequestedRegion: Dimension: 2 Index: [0, 0] Size: [0, 0] Spacing: [1, 1] Origin: [0, 0] Direction: 1 0 0 1 IndexToPointMatrix: 1 0 0 1 PointToIndexMatrix: 1 0 0 1 Inverse Direction: 1 0 0 1 PixelContainer: ImportImageContainer (0x55a09028ad50) RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char> Reference Count: 1 Modified Time: 281 Debug: Off Object Name: Observers: none Pointer: 0 Container manages memory: true Size: 0 Capacity: 0 smoother's Output: Image (0x55a08f1f4b90) RTTI typeinfo: itk::Image<unsigned char, 2u> Reference Count: 2 Modified Time: 303 Debug: Off Object Name: Observers: none Source: (0x55a090f30520) Source output name: Primary Release Data: Off Data Released: False Global Release Data: Off PipelineMTime: 0 UpdateMTime: 0 RealTimeStamp: 0 seconds LargestPossibleRegion: Dimension: 2 Index: [0, 0] Size: [0, 0] BufferedRegion: Dimension: 2 Index: [0, 0] Size: [0, 0] RequestedRegion: Dimension: 2 Index: [0, 0] Size: [0, 0] Spacing: [1, 1] Origin: [0, 0] Direction: 1 0 0 1 IndexToPointMatrix: 1 0 0 1 PointToIndexMatrix: 1 0 0 1 Inverse Direction: 1 0 0 1 PixelContainer: ImportImageContainer (0x55a090c1b310) RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char> Reference Count: 1 Modified Time: 300 Debug: Off Object Name: Observers: none Pointer: 0 Container manages memory: true Size: 0 Capacity: 0
To generate the filter outputs, we must call Update()
on the filter at the end of the pipeline. In this case, it is the smoother.
smoother.Update()
print(f"reader's Output: {reader.GetOutput()}")
print(f"smoother's Output: {smoother.GetOutput()}")
reader's Output: Image (0x55a08f40f220) RTTI typeinfo: itk::Image<unsigned char, 2u> Reference Count: 3 Modified Time: 484 Debug: Off Object Name: Observers: none Source: (0x55a08dc1f3c0) Source output name: Primary Release Data: Off Data Released: False Global Release Data: Off PipelineMTime: 294 UpdateMTime: 485 RealTimeStamp: 0 seconds LargestPossibleRegion: Dimension: 2 Index: [0, 0] Size: [128, 128] BufferedRegion: Dimension: 2 Index: [0, 0] Size: [128, 128] RequestedRegion: Dimension: 2 Index: [0, 0] Size: [128, 128] Spacing: [1, 1] Origin: [0, 0] Direction: 1 0 0 1 IndexToPointMatrix: 1 0 0 1 PointToIndexMatrix: 1 0 0 1 Inverse Direction: 1 0 0 1 PixelContainer: ImportImageContainer (0x55a09028ad50) RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char> Reference Count: 1 Modified Time: 482 Debug: Off Object Name: Observers: none Pointer: 0x55a091265eb0 Container manages memory: true Size: 16384 Capacity: 16384 smoother's Output: Image (0x55a08f1f4b90) RTTI typeinfo: itk::Image<unsigned char, 2u> Reference Count: 2 Modified Time: 488 Debug: Off Object Name: Observers: none Source: (0x55a090f30520) Source output name: Primary Release Data: Off Data Released: False Global Release Data: Off PipelineMTime: 476 UpdateMTime: 489 RealTimeStamp: 0 seconds LargestPossibleRegion: Dimension: 2 Index: [0, 0] Size: [128, 128] BufferedRegion: Dimension: 2 Index: [0, 0] Size: [128, 128] RequestedRegion: Dimension: 2 Index: [0, 0] Size: [128, 128] Spacing: [1, 1] Origin: [0, 0] Direction: 1 0 0 1 IndexToPointMatrix: 1 0 0 1 PointToIndexMatrix: 1 0 0 1 Inverse Direction: 1 0 0 1 PixelContainer: ImportImageContainer (0x55a090c1b310) RTTI typeinfo: itk::ImportImageContainer<unsigned long, unsigned char> Reference Count: 1 Modified Time: 487 Debug: Off Object Name: Observers: none Pointer: 0x55a08d326160 Container manages memory: true Size: 16384 Capacity: 16384
Running itkImageFileReaderIUC2... done Running itkRecursiveGaussianImageFilterIUC2IUC2...
Let's view the images.
image = reader.GetOutput()
view(image, ui_collapsed=True)
Loading ITKBridgeNumPy... done
<itkwidgets.viewer.Viewer at 0x7fb1a4fe3910>
smoothed = smoother.GetOutput()
view(smoothed, ui_collapsed=True)
<itkwidgets.viewer.Viewer at 0x7fb19c2349d0>
If we call Update()
on the pipeline, the output pixel data is not needlessly generated because the pipeline is up-to-date.
smoother.Update()
However, if we change the amount of smoothing, new pixel data does need to be generated from the output of the smoother.
smoother.SetSigma(10.0)
smoother.Update()
Running itkRecursiveGaussianImageFilterIUC2IUC2... done
view(smoothed, ui_collapsed=True)
<itkwidgets.viewer.Viewer at 0x7fb19c235650>
Note, however, that the reader does generate its output because it is up-to-date and upstream from the smoother.
If we artificially modify the reader, both the reader and the smoother need to regenerate their outputs.
reader.Modified()
smoother.Update()
Running itkImageFileReaderIUC2... done Running itkRecursiveGaussianImageFilterIUC2IUC2...
Change the value of Sigma on the smoothing filter.
# fileName = 'data/PacMan.png'
# reader = itk.ImageFileReader.New(FileName=fileName)
# smoother = itk.RecursiveGaussianImageFilter.New(Input=reader.GetOutput())
# smoother.SetSigma(XX)
# smoother.Update()
# view(smoother.GetOutput())
# %load solutions/3_Image_Filtering_Exercise1.py
Call smoother.SetSigma(smoother.GetSigma())
then smoother.Update()
.
# %load solutions/3_Image_Filtering_Exercise2.py
The classes in ITK are organized into Modules, and collections of Modules are organized into Groups. Examine the Image Smoothing Module and the Filtering Group. Can any other smoothing or denoising classes be found?
# %load solutions/3_Image_Filtering_Exercise3.py
The answer example demonstrates how to combine ipywidgets
native interactive function to quickly create widgets coupled with an itkwidgets
viewer. This is an effective method to explore algorithm parameters.