From 6874807897ffe9a5d85978bd7213f70edb5516ec Mon Sep 17 00:00:00 2001 From: Andrew Maclean <andrew.amaclean@gmail.com> Date: Mon, 18 Nov 2024 11:45:13 +1100 Subject: [PATCH] Added DotProduct, ImageDifference, ImageDivergence, ImageEllipsoidSource --- src/PythonicAPI.md | 4 + src/PythonicAPI/Images/DotProduct.md | 5 + src/PythonicAPI/Images/DotProduct.py | 150 ++++++++++++++++++ src/PythonicAPI/Images/ImageConvolve.py | 6 +- src/PythonicAPI/Images/ImageDifference.md | 3 + src/PythonicAPI/Images/ImageDifference.py | 81 ++++++++++ src/PythonicAPI/Images/ImageDivergence.py | 77 +++++++++ .../Images/ImageEllipsoidSource.py | 60 +++++++ .../PythonicAPI/Images/TestDotProduct.png | 3 + .../Images/TestImageDifference.png | 3 + .../Images/TestImageDivergence.png | 3 + .../Images/TestImageEllipsoidSource.png | 3 + 12 files changed, 395 insertions(+), 3 deletions(-) create mode 100644 src/PythonicAPI/Images/DotProduct.md create mode 100644 src/PythonicAPI/Images/DotProduct.py create mode 100644 src/PythonicAPI/Images/ImageDifference.md create mode 100644 src/PythonicAPI/Images/ImageDifference.py create mode 100644 src/PythonicAPI/Images/ImageDivergence.py create mode 100644 src/PythonicAPI/Images/ImageEllipsoidSource.py create mode 100644 src/Testing/Baseline/PythonicAPI/Images/TestDotProduct.png create mode 100644 src/Testing/Baseline/PythonicAPI/Images/TestImageDifference.png create mode 100644 src/Testing/Baseline/PythonicAPI/Images/TestImageDivergence.png create mode 100644 src/Testing/Baseline/PythonicAPI/Images/TestImageEllipsoidSource.png diff --git a/src/PythonicAPI.md b/src/PythonicAPI.md index ea4b939af97..9bbe5b55f13 100644 --- a/src/PythonicAPI.md +++ b/src/PythonicAPI.md @@ -598,6 +598,7 @@ See [this tutorial](http://www.vtk.org/Wiki/VTK/Tutorials/3DDataTypes) for a bri | Example Name | Description | Image | | -------------- | ------------- | ------- | [BackgroundImage](/PythonicAPI/Images/BackgroundImage) | Display an image as the "background" of a scene, and render a superquadric in front of it. +[DotProduct](/PythonicAPI/Images/DotProduct) | Compute the pixel-wise dot product of two vector images. [ExtractComponents](/PythonicAPI/Images/ExtractComponents) | Extract components of an image. This can be used to get, for example, the red channel of an image. [ImageCityBlockDistance](/PythonicAPI/Images/ImageCityBlockDistance) | Compute the Manhattan distance from every point to every black point in a binary image. [MarkKeypoints](/PythonicAPI/Images/MarkKeypoints) | Mark keypoints in an image. @@ -618,6 +619,9 @@ See [this tutorial](http://www.vtk.org/Wiki/VTK/Tutorials/3DDataTypes) for a bri [ImageContinuousErode3D](/PythonicAPI/Images/ImageContinuousErode3D) | Erode an image. [ImageConvolve](/PythonicAPI/Images/ImageConvolve) | Convolve an image with a kernel. [ImageCorrelation](/PythonicAPI/Images/ImageCorrelation) | Correlate two images. +[ImageDifference](/PythonicAPI/Images/ImageDifference) | Compute the difference image of two images. +[ImageDivergence](/PythonicAPI/Images/ImageDivergence) | Divergence of a vector field. +[ImageEllipsoidSource](/PythonicAPI/Images/ImageEllipsoidSource) | Create an image of an ellipsoid. [ImageGradient](/PythonicAPI/VisualizationAlgorithms/ImageGradient) | Create an imaging pipeline to visualize gradient information. [IsoSubsample](/PythonicAPI/ImageProcessing/IsoSubsample) | This figure demonstrates aliasing that occurs when a high-frequency signal is subsampled. High frequencies appear as low frequency artifacts. The left image is an isosurface of a skull after subsampling. The right image used a low-pass filter before subsampling to reduce aliasing. [MorphologyComparison](/PythonicAPI/ImageProcessing/MorphologyComparison) | This figure demonstrates various binary filters that can alter the shape of segmented regions. diff --git a/src/PythonicAPI/Images/DotProduct.md b/src/PythonicAPI/Images/DotProduct.md new file mode 100644 index 00000000000..f8811a9f4c2 --- /dev/null +++ b/src/PythonicAPI/Images/DotProduct.md @@ -0,0 +1,5 @@ +### Description + +This example demonstrates how to take the pixel-wise dot product of two vector images. The output is a scalar image. + +Two images, each 2x2x1, are created and filled with 3-vectors. The dot product of each pair of corresponding pixels is produced by the vtkImageDotProduct filter and output to the screen. diff --git a/src/PythonicAPI/Images/DotProduct.py b/src/PythonicAPI/Images/DotProduct.py new file mode 100644 index 00000000000..95e4b472662 --- /dev/null +++ b/src/PythonicAPI/Images/DotProduct.py @@ -0,0 +1,150 @@ +#!/usr/bin/env python3 + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonCore import VTK_FLOAT +from vtkmodules.vtkCommonDataModel import vtkImageData +from vtkmodules.vtkFiltersSources import vtkArrowSource +from vtkmodules.vtkImagingCore import vtkImageCast +from vtkmodules.vtkImagingMath import ( + vtkImageDotProduct, + vtkImageMathematics +) +from vtkmodules.vtkRenderingCore import ( + vtkRenderer, + vtkRenderWindow, + vtkRenderWindowInteractor, vtkImageActor, vtkGlyph3DMapper, vtkActor +) + + +def main(): + colors = vtkNamedColors() + + # Create an image + image1 = vtkImageData(extent=(0, 1, 0, 1, 0, 0)) + image1.AllocateScalars(VTK_FLOAT, 3) + + # Fill the image with vectors. + coord = [0] * 3 + image1.SetScalarComponentFromFloat(*coord, 0, 1.0) + image1.SetScalarComponentFromFloat(*coord, 1, 0.0) + image1.SetScalarComponentFromFloat(*coord, 2, 0.0) + + coord[0] = 0 + coord[1] = 1 + coord[2] = 0 + image1.SetScalarComponentFromFloat(*coord, 0, 0.0) + image1.SetScalarComponentFromFloat(*coord, 1, 1.0) + image1.SetScalarComponentFromFloat(*coord, 2, 0.0) + + coord[0] = 1 + coord[1] = 0 + coord[2] = 0 + image1.SetScalarComponentFromFloat(*coord, 0, 1.0) + image1.SetScalarComponentFromFloat(*coord, 1, 0.0) + image1.SetScalarComponentFromFloat(*coord, 2, 0.0) + + coord[0] = 1 + coord[1] = 1 + coord[2] = 0 + image1.SetScalarComponentFromFloat(*coord, 0, 0.0) + image1.SetScalarComponentFromFloat(*coord, 1, 1.0) + image1.SetScalarComponentFromFloat(*coord, 2, 0.0) + + # Create another image. + image2 = vtkImageData(extent=(0, 1, 0, 1, 0, 0)) + image2.AllocateScalars(VTK_FLOAT, 3) + + # Fill the image with vectors. + coord = [0] * 3 + image2.SetScalarComponentFromFloat(*coord, 0, 1.0) + image2.SetScalarComponentFromFloat(*coord, 1, 0.0) + image2.SetScalarComponentFromFloat(*coord, 2, 0.0) + + coord[0] = 0 + coord[1] = 1 + coord[2] = 0 + image2.SetScalarComponentFromFloat(*coord, 0, 1.0) + image2.SetScalarComponentFromFloat(*coord, 1, 0.0) + image2.SetScalarComponentFromFloat(*coord, 2, 0.0) + + coord[0] = 1 + coord[1] = 0 + coord[2] = 0 + image2.SetScalarComponentFromFloat(*coord, 0, 0.5) + image2.SetScalarComponentFromFloat(*coord, 1, 0.0) + image2.SetScalarComponentFromFloat(*coord, 2, 0.0) + + coord[0] = 1 + coord[1] = 1 + coord[2] = 0 + image2.SetScalarComponentFromFloat(*coord, 0, 0.5) + image2.SetScalarComponentFromFloat(*coord, 1, 0.0) + image2.SetScalarComponentFromFloat(*coord, 2, 0.0) + + # Compute the dot product of the images pixel wise. + dot_product_filter = vtkImageDotProduct(input1_data=image1, input2_data=image2) + dot_product_filter.update() + print(f'Output is of type: {dot_product_filter.output.GetScalarTypeAsString()}') + + image_math = vtkImageMathematics(constant_k=255.0) + image_math.SetOperationToMultiplyByK() + + image_cast = vtkImageCast() + image_cast.SetOutputScalarTypeToUnsignedChar() + + dot_product_actor = vtkImageActor() + dot_product_filter >> image_math >> image_cast >> dot_product_actor.mapper + + # Display output to the terminal. + for i in range(0, 2): + for j in range(0, 2): + pixel = dot_product_filter.output.GetScalarComponentAsFloat(i, j, 0, 0) + print(f'Pixel ({i}, {j}): {pixel}') + image1.GetPointData().SetActiveVectors('ImageScalars') + image2.GetPointData().SetActiveVectors('ImageScalars') + + arrow_source = vtkArrowSource() + + glyph3d_mapper1 = vtkGlyph3DMapper(source_connection=arrow_source.output_port, input_data=image1) + actor1 = vtkActor(mapper=glyph3d_mapper1) + + glyph3d_mapper2 = vtkGlyph3DMapper(source_connection=arrow_source.output_port, input_data=image2) + actor2 = vtkActor(mapper=glyph3d_mapper2) + + # Define viewport ranges (x_min, y_min, x_max, y_max). + left_viewport = (0.0, 0.0, 0.33, 1.0) + center_viewport = (0.33, 0.0, 0.66, 1.0) + right_viewport = (0.66, 0.0, 1.0, 1.0) + + # Set up the render window. + render_window = vtkRenderWindow(size=(600, 200), window_name='DotProduct') + + # Set up the renderers and actors. + left_renderer = vtkRenderer(viewport=left_viewport, background=colors.GetColor3d('DimGray')) + render_window.AddRenderer(left_renderer) + left_renderer.AddActor(actor1) + + center_renderer = vtkRenderer(viewport=center_viewport, background=colors.GetColor3d('DimGray')) + render_window.AddRenderer(center_renderer) + center_renderer.AddActor(actor2) + + right_renderer = vtkRenderer(viewport=right_viewport, background=colors.GetColor3d('DimGray')) + render_window.AddRenderer(right_renderer) + right_renderer.AddActor(dot_product_actor) + + # Set up the render window interactor. + render_window_interactor = vtkRenderWindowInteractor() + render_window_interactor.render_window = render_window + + # Render and start interaction. + render_window.Render() + render_window_interactor.Initialize() + render_window_interactor.Start() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Images/ImageConvolve.py b/src/PythonicAPI/Images/ImageConvolve.py index 745f14ba2f9..346d1df5a1a 100644 --- a/src/PythonicAPI/Images/ImageConvolve.py +++ b/src/PythonicAPI/Images/ImageConvolve.py @@ -43,19 +43,19 @@ def main(): left_viewport = (0.0, 0.0, 0.5, 1.0) right_viewport = (0.5, 0.0, 1.0, 1.0) - # Setup renderer + # Set up the renderers. original_renderer = vtkRenderer(viewport=left_viewport, background=colors.GetColor3d('Sienna')) original_renderer.AddActor(original_actor) convolved_renderer = vtkRenderer(viewport=right_viewport, background=colors.GetColor3d('RoyalBlue')) convolved_renderer.AddActor(convolved_actor) - # Setup the render window. + # Set up the render window. render_window = vtkRenderWindow(size=(600, 300), window_name='ImageConvolve') render_window.AddRenderer(original_renderer) render_window.AddRenderer(convolved_renderer) - # Setup the render window interactor. + # Set up the render window interactor. render_window_interactor = vtkRenderWindowInteractor() style = vtkInteractorStyleImage() render_window_interactor.interactor_style = style diff --git a/src/PythonicAPI/Images/ImageDifference.md b/src/PythonicAPI/Images/ImageDifference.md new file mode 100644 index 00000000000..a47045a4954 --- /dev/null +++ b/src/PythonicAPI/Images/ImageDifference.md @@ -0,0 +1,3 @@ +### Description + +This example makes a large square and a smaller square and subtracts them, leaving the outline of a square. diff --git a/src/PythonicAPI/Images/ImageDifference.py b/src/PythonicAPI/Images/ImageDifference.py new file mode 100644 index 00000000000..eeca4003092 --- /dev/null +++ b/src/PythonicAPI/Images/ImageDifference.py @@ -0,0 +1,81 @@ +#!/usr/bin/env python3 + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkImagingCore import vtkImageDifference +from vtkmodules.vtkImagingSources import vtkImageCanvasSource2D +from vtkmodules.vtkRenderingCore import ( + vtkImageActor, + vtkRenderer, + vtkRenderWindow, + vtkRenderWindowInteractor +) + + +def main(): + colors = vtkNamedColors() + + draw_color1 = colors.GetColor3ub('Black') + draw_color2 = colors.GetColor3ub('Wheat') + # Create an image + source1 = vtkImageCanvasSource2D(extent=(0, 100, 0, 100, 0, 0), number_of_scalar_components=3) + source1.SetScalarTypeToUnsignedChar() + source1.draw_color = tuple(draw_color1) + source1.FillBox(0, 100, 0, 100) + source1.draw_color = tuple(draw_color2) + source1.FillBox(10, 90, 10, 90) + source1.update() + + # Create another image + source2 = vtkImageCanvasSource2D(extent=(0, 100, 0, 100, 0, 0), number_of_scalar_components=3) + source2.SetScalarTypeToUnsignedChar() + source2.draw_color = tuple(draw_color1) + source2.FillBox(0, 100, 0, 100) + source2.draw_color = tuple(draw_color2) + source2.FillBox(20, 80, 20, 80) + source2.update() + + difference_filter = vtkImageDifference(input_connection=source1.output_port, image_connection=source2.output_port) + + # Define viewport ranges (x_min, y_min, x_max, y_max). + left_viewport = (0.0, 0.0, 0.33, 1.0) + center_viewport = (0.33, 0.0, 0.66, 1.0) + right_viewport = (0.66, 0.0, 1.0, 1.0) + + # Set up the render window. + render_window = vtkRenderWindow(size=(300, 100), window_name='ImageDifference') + + # Set up the renderers and actors. + left_renderer = vtkRenderer(viewport=left_viewport, background=colors.GetColor3d('Mint')) + render_window.AddRenderer(left_renderer) + left_actor = vtkImageActor() + source1 >> left_actor.mapper + left_renderer.AddActor(left_actor) + + center_renderer = vtkRenderer(viewport=center_viewport, background=colors.GetColor3d('Mint')) + render_window.AddRenderer(center_renderer) + center_actor = vtkImageActor() + source2 >> center_actor.mapper + center_renderer.AddActor(center_actor) + + right_renderer = vtkRenderer(viewport=right_viewport, background=colors.GetColor3d('Peacock')) + render_window.AddRenderer(right_renderer) + right_actor = vtkImageActor() + difference_filter >> right_actor.mapper + right_renderer.AddActor(right_actor) + + # Set up the render window interactor. + render_window_interactor = vtkRenderWindowInteractor() + render_window_interactor.render_window = render_window + + # Render and start interaction. + render_window.Render() + render_window_interactor.Initialize() + render_window_interactor.Start() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Images/ImageDivergence.py b/src/PythonicAPI/Images/ImageDivergence.py new file mode 100644 index 00000000000..2018e600a7a --- /dev/null +++ b/src/PythonicAPI/Images/ImageDivergence.py @@ -0,0 +1,77 @@ +#!/usr/bin/env python3 + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkImagingCore import vtkImageCast +from vtkmodules.vtkImagingGeneral import vtkImageGradient +from vtkmodules.vtkImagingMath import vtkImageDivergence +from vtkmodules.vtkImagingSources import vtkImageMandelbrotSource +from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage +from vtkmodules.vtkRenderingCore import ( + vtkRenderer, + vtkRenderWindow, + vtkRenderWindowInteractor, vtkImageActor +) + + +def main(): + colors = vtkNamedColors() + + # Create an image. + source = vtkImageMandelbrotSource() + + original_cast_filter = vtkImageCast() + original_cast_filter.SetOutputScalarTypeToFloat() + source >> original_cast_filter + + # Compute the gradient (to produce a vector field). + gradient_filter = vtkImageGradient() + source >> gradient_filter + + divergence_filter = vtkImageDivergence() + source >> gradient_filter >> divergence_filter + + divergence_cast_filter = vtkImageCast() + source >> gradient_filter >> divergence_filter >> divergence_cast_filter + + # Create actors + original_actor = vtkImageActor() + original_cast_filter >> original_actor.mapper + + divergence_actor = vtkImageActor() + divergence_cast_filter >> divergence_actor.mapper + + # Define viewport ranges (x_min, y_min, x_max, y_max). + left_viewport = (0.0, 0.0, 0.5, 1.0) + right_viewport = (0.5, 0.0, 1.0, 1.0) + + # Setup the renderers. + original_renderer = vtkRenderer(viewport=left_viewport, background=colors.GetColor3d('Sienna')) + original_renderer.AddActor(original_actor) + + divergence_renderer = vtkRenderer(viewport=right_viewport, background=colors.GetColor3d('RoyalBlue')) + divergence_renderer.AddActor(divergence_actor) + + # Set up the render window. + render_window = vtkRenderWindow(size=(600, 300), window_name='ImageDivergence') + render_window.AddRenderer(original_renderer) + render_window.AddRenderer(divergence_renderer) + + # Set up the render window interactor. + render_window_interactor = vtkRenderWindowInteractor() + style = vtkInteractorStyleImage() + render_window_interactor.interactor_style = style + + # Render and start the interaction. + render_window_interactor.render_window = render_window + render_window.Render() + render_window_interactor.Initialize() + + render_window_interactor.Start() + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Images/ImageEllipsoidSource.py b/src/PythonicAPI/Images/ImageEllipsoidSource.py new file mode 100644 index 00000000000..4d748d35285 --- /dev/null +++ b/src/PythonicAPI/Images/ImageEllipsoidSource.py @@ -0,0 +1,60 @@ +#!/usr/bin/env python3 + +from pathlib import Path + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkIOImage import vtkImageReader2Factory +from vtkmodules.vtkImagingCore import vtkImageCast +from vtkmodules.vtkImagingGeneral import vtkImageCityBlockDistance +from vtkmodules.vtkImagingSources import vtkImageEllipsoidSource +from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage +from vtkmodules.vtkRenderingCore import ( + vtkImageActor, + vtkRenderer, + vtkRenderWindow, + vtkRenderWindowInteractor +) + + +def main(): + colors = vtkNamedColors() + + # Create an image. + source = vtkImageEllipsoidSource(whole_extent=(0, 20, 0, 20, 0, 0), center=(10, 10, 0), radius=(3, 4, 5)) + + cast_filter = vtkImageCast() + cast_filter.SetOutputScalarTypeToUnsignedChar() + + # Create an actor. + actor = vtkImageActor() + source >> cast_filter >> actor.mapper + + # Set up the renderers. + renderer = vtkRenderer(background = colors.GetColor3d('Sienna')) + renderer.AddActor(actor) + + # Set up the render window. + render_window = vtkRenderWindow(window_name = 'ImageEllipsoidSource') + render_window.AddRenderer(renderer) + + # Set up the render window interactor. + render_window_interactor = vtkRenderWindowInteractor() + style = vtkInteractorStyleImage() + render_window_interactor.interactor_style = style + + # Render and start the interaction. + render_window_interactor.render_window = render_window + + render_window.Render() + render_window_interactor.Initialize() + + render_window_interactor.Start() + + +if __name__ == '__main__': + main() + diff --git a/src/Testing/Baseline/PythonicAPI/Images/TestDotProduct.png b/src/Testing/Baseline/PythonicAPI/Images/TestDotProduct.png new file mode 100644 index 00000000000..d72ebd8d389 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Images/TestDotProduct.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7f4ece80ceb968f87e5dd99c828f231121402388d8f0e1d45300e86456de31b3 +size 7382 diff --git a/src/Testing/Baseline/PythonicAPI/Images/TestImageDifference.png b/src/Testing/Baseline/PythonicAPI/Images/TestImageDifference.png new file mode 100644 index 00000000000..00e8e73d0fb --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Images/TestImageDifference.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2cd723a7a18bb8b759b2eeeb486435e20b5be2285cfe3b6bf092d8b0c22700c5 +size 726 diff --git a/src/Testing/Baseline/PythonicAPI/Images/TestImageDivergence.png b/src/Testing/Baseline/PythonicAPI/Images/TestImageDivergence.png new file mode 100644 index 00000000000..9a308325842 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Images/TestImageDivergence.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:afa542696084ece75f0753fa7e8c477538dee046f17aabfa2f4cbf407557215c +size 65962 diff --git a/src/Testing/Baseline/PythonicAPI/Images/TestImageEllipsoidSource.png b/src/Testing/Baseline/PythonicAPI/Images/TestImageEllipsoidSource.png new file mode 100644 index 00000000000..018c9c69aab --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Images/TestImageEllipsoidSource.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eb5a2f76d8519143b7f037bec681936f4f10be8dcedbf2ce6fa7c66c1fa64eb1 +size 4500 -- GitLab