diff --git a/src/PythonicAPI.md b/src/PythonicAPI.md index afec87410b86a9ac9e4410d4690e82bdca93798c..17064f7cc99806d00f31a04f60c7d508fcf92999 100644 --- a/src/PythonicAPI.md +++ b/src/PythonicAPI.md @@ -401,6 +401,7 @@ See [this tutorial](http://www.vtk.org/Wiki/VTK/Tutorials/3DDataTypes) for a bri [Blow](/PythonicAPI/Visualization/Blow) | Ten frames from a blow molding finite element analysis. [CameraModel1](/PythonicAPI/Visualization/CameraModel1) | Illustrate camera movement around the focal point. [CameraModel2](/PythonicAPI/Visualization/CameraModel2) | Illustrate camera movement centered at the camera position. +[CaptionActor2D](/PythonicAPI/Visualization/CaptionActor2D) | Draw a caption/bubble pointing to a particular point. [ClipSphereCylinder](/PythonicAPI/VisualizationAlgorithms/ClipSphereCylinder) | A plane clipped with a sphere and an ellipse. The two transforms place each implicit function into the appropriate position. Two outputs are generated by the clipper. [ColoredAnnotatedCube](/PythonicAPI/Visualization/ColoredAnnotatedCube) | How to color the individual faces of an annotated cube. [CollisionDetection](/PythonicAPI/Visualization/CollisionDetection) | Collison between two spheres. @@ -463,6 +464,8 @@ 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. +[MarkKeypoints](/PythonicAPI/Images/MarkKeypoints) | Mark keypoints in an image. + ## Image Processing diff --git a/src/PythonicAPI/IO/ImportToExport.py b/src/PythonicAPI/IO/ImportToExport.py index f9d33190f77d4d880a813867068ba0609c298c6d..8c90aaf2e3f29f067c75f7e9734701d0affbdd4e 100644 --- a/src/PythonicAPI/IO/ImportToExport.py +++ b/src/PythonicAPI/IO/ImportToExport.py @@ -1,4 +1,4 @@ -#!/usr/bin/env python +#!/usr/bin/env python3 from pathlib import Path diff --git a/src/PythonicAPI/ImageProcessing/HybridMedianComparison.py b/src/PythonicAPI/ImageProcessing/HybridMedianComparison.py index 2ed525b9597cbf77d113624ba5e00ae02d455224..5234ea92f432184df801d13cc306cd5611f5def3 100755 --- a/src/PythonicAPI/ImageProcessing/HybridMedianComparison.py +++ b/src/PythonicAPI/ImageProcessing/HybridMedianComparison.py @@ -120,7 +120,7 @@ def main(): iren = vtkRenderWindowInteractor() iren.render_window = ren_win style = vtkInteractorStyleImage() - iren.interactor_stype = style + iren.interactor_style = style viewports = dict() VP_Params = namedtuple('VP_Params', ['viewport', 'border']) diff --git a/src/PythonicAPI/Images/MarkKeypoints.py b/src/PythonicAPI/Images/MarkKeypoints.py new file mode 100644 index 0000000000000000000000000000000000000000..0ae9209024548fd7e2687010eea425e7cf29eed1 --- /dev/null +++ b/src/PythonicAPI/Images/MarkKeypoints.py @@ -0,0 +1,163 @@ +#!/usr/bin/env python3 + +from dataclasses import dataclass + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkCommonTransforms import vtkTransform +from vtkmodules.vtkFiltersGeneral import vtkTransformPolyDataFilter +from vtkmodules.vtkImagingSources import vtkImageCanvasSource2D +from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage +from vtkmodules.vtkRenderingCore import ( + vtkActor2D, + vtkCoordinate, + vtkImageActor, + vtkPolyDataMapper2D, + vtkRenderWindowInteractor, + vtkRenderWindow, + vtkRenderer +) +from vtkmodules.vtkRenderingFreeType import vtkVectorText + + +def main(): + colors = vtkNamedColors() + + draw_color1 = tuple(colors.GetColor3ub('DimGray')) + draw_color2 = tuple(colors.GetColor3ub('HotPink')) + + # Create a blank, gray image. + drawing = vtkImageCanvasSource2D(scalar_type=ImageCanvasSource2D.ScalarType.VTK_UNSIGNED_CHAR, + number_of_scalar_components=3, + extent=(0, 20, 0, 50, 0, 0), draw_color=draw_color1) + drawing.FillBox(0, 20, 0, 50) + + # Draw a circle of radius 5 centered at (9,10). + drawing.draw_color = draw_color2 + drawing.DrawCircle(9, 10, 5) + + actor = vtkImageActor() + drawing >> actor.mapper + + ren = vtkRenderer(background=colors.GetColor3d('SkyBlue'), + background2=colors.GetColor3d('MidnightBlue'), + gradient_background=2) + ren_win = vtkRenderWindow(window_name='MarkKeypoints', number_of_layers=2) + ren_win.AddRenderer(ren) + ren_win.AddRenderer(ren) + iren = vtkRenderWindowInteractor() + iren.render_window = ren_win + + ren.AddActor(actor) + + ren_win.Render() + + style = MyStyle() + iren.interactor_style = style + style.default_renderer = ren + style.current_renderer = ren + iren.Start() + + +class MyStyle(vtkInteractorStyleImage): + + def __init__(self): + super().__init__() + + self.AddObserver('LeftButtonPressEvent', self.OnLeftButtonDown) + self.count = 0 + + def OnLeftButtonDown(self, obj, event): + self.interactor.picker.Pick(self.interactor.GetEventPosition()[0], + self.interactor.GetEventPosition()[1], + 0, + self.current_renderer) + picked = self.interactor.picker.pick_position + self.add_number(picked) + + # Forward events. + super().OnLeftButtonDown() + + super().interactor.Render() + + def add_number(self, p): + colors = vtkNamedColors() + p = list(p) + if p[0] == 0 and p[1] == 0: + # Not in the box. + return + s = f'adding marker at: {p[0]:6.4f} {p[1]:6.4f}' + + # # Normally, with an image you would do: + # s = self.image.spacing + # o = self.image.origin + # p[0] = int((p[0] - o[0]) / s[0] + 0.5) + # p[1] = int((p[1] - o[1]) / s[1] + 0.5) + # Here we do: + p[0] = int(p[0]) + 0.5 + p[1] = int(p[1]) + 0.5 + s += f' -> {p[0]:3.1f} {p[1]:3.1f}' + + # Create an actor for the text + text_source = vtkVectorText(text=str(self.count)) + # Get the bounds of the text. + text_source.update() + bounds = text_source.output.bounds + # Transform the polydata to be centered over the pick position. + center = (0.5 * (bounds[1] + bounds[0]), 0.5 * (bounds[3] + bounds[2]), 0.0) + trans = vtkTransform() + trans.Translate(-center[0], -center[1], 0) + trans.Translate(p[0], p[1], 0) + + tpd = vtkTransformPolyDataFilter(transform=trans) + + coordinate = vtkCoordinate(coordinate_system=Coordinate.CoordinateSystem.VTK_WORLD) + + # Create a mapper. + mapper = vtkPolyDataMapper2D(transform_coordinate=coordinate) + text_source >> tpd >> mapper + + actor = vtkActor2D(mapper=mapper) + actor.property.color = colors.GetColor3d('Yellow') + + self.current_renderer.AddViewProp(actor) + print(f'For point: {self.count:3d} {s}') + + self.count += 1 + + +@dataclass(frozen=True) +class Coordinate: + @dataclass(frozen=True) + class CoordinateSystem: + VTK_DISPLAY: int = 0 + VTK_NORMALIZED_DISPLAY: int = 1 + VTK_VIEWPORT: int = 2 + VTK_NORMALIZED_VIEWPORT: int = 3 + VTK_VIEW: int = 4 + VTK_POSE: int = 5 + VTK_WORLD: int = 6 + VTK_USERDEFINED: int = 7 + + +@dataclass(frozen=True) +class ImageCanvasSource2D: + @dataclass(frozen=True) + class ScalarType: + VTK_CHAR: int = 2 + VTK_UNSIGNED_CHAR: int = 3 + VTK_SHORT: int = 4 + VTK_UNSIGNED_SHORT: int = 5 + VTK_INT: int = 6 + VTK_UNSIGNED_INT: int = 7 + VTK_LONG: int = 8 + VTK_UNSIGNED_LONG: int = 9 + VTK_FLOAT: int = 10 + VTK_DOUBLE: int = 11 + + +if __name__ == '__main__': + main() diff --git a/src/PythonicAPI/Images/TestMarkKeypoints.png b/src/PythonicAPI/Images/TestMarkKeypoints.png new file mode 100644 index 0000000000000000000000000000000000000000..7d8740479c470b88d16039ed755b657981281553 Binary files /dev/null and b/src/PythonicAPI/Images/TestMarkKeypoints.png differ diff --git a/src/PythonicAPI/Picking/HighlightPickedActor.py b/src/PythonicAPI/Picking/HighlightPickedActor.py index 20ccb746c3793295becc6bea3bd95da83f1d89a9..6f4052368be17813a357ff5b7ba9ce9abc1d437d 100755 --- a/src/PythonicAPI/Picking/HighlightPickedActor.py +++ b/src/PythonicAPI/Picking/HighlightPickedActor.py @@ -101,13 +101,13 @@ class MouseInteractorHighLightActor(vtkInteractorStyleTrackballCamera): def __init__(self, parent=None): super().__init__() - self.AddObserver("LeftButtonPressEvent", self.left_button_press_event) + self.AddObserver("LeftButtonPressEvent", self.LeftButtonPressEvent) self.new_picked_actor = None self.last_picked_actor = None self.last_picked_property = vtkProperty() - def left_button_press_event(self, obj, event): + def LeftButtonPressEvent(self, obj, event): click_pos = self.interactor.GetEventPosition() picker = vtkPropPicker() @@ -134,7 +134,7 @@ class MouseInteractorHighLightActor(vtkInteractorStyleTrackballCamera): # Save the last picked actor. self.last_picked_actor = self.new_picked_actor - self.OnLeftButtonDown() + super().OnLeftButtonDown() return diff --git a/src/PythonicAPI/Picking/HighlightWithSilhouette.py b/src/PythonicAPI/Picking/HighlightWithSilhouette.py index 98ce0e66a0cbb380503501081c21b598339fe081..229b14a7ee6c561a83d9ad2e900d469e605ed76e 100755 --- a/src/PythonicAPI/Picking/HighlightWithSilhouette.py +++ b/src/PythonicAPI/Picking/HighlightWithSilhouette.py @@ -120,13 +120,13 @@ class MouseInteractorHighLightActor(vtkInteractorStyleTrackballCamera): def __init__(self, silhouette=None, silhouette_actor=None): super().__init__() - self.AddObserver("LeftButtonPressEvent", self.on_left_button_down) + self.AddObserver("LeftButtonPressEvent", self.OnLeftButtonDown) self.last_picked_actor = None self.silhouette = silhouette self.silhouette_actor = silhouette_actor - def on_left_button_down(self, obj, event): + def OnLeftButtonDown(self, obj, event): click_pos = self.interactor.GetEventPosition() # Pick from this location. @@ -144,7 +144,7 @@ class MouseInteractorHighLightActor(vtkInteractorStyleTrackballCamera): self.GetDefaultRenderer().AddActor(self.silhouette_actor) # Forward events - self.OnLeftButtonDown() + super().OnLeftButtonDown() return def set_silhouette(self, silhouette): diff --git a/src/PythonicAPI/Visualization/CaptionActor2D.py b/src/PythonicAPI/Visualization/CaptionActor2D.py new file mode 100644 index 0000000000000000000000000000000000000000..c8aa93fa2d340b2dc15bbb83f5089651c1335a44 --- /dev/null +++ b/src/PythonicAPI/Visualization/CaptionActor2D.py @@ -0,0 +1,143 @@ +#!/usr/bin/env python3 + +from dataclasses import dataclass + +# noinspection PyUnresolvedReferences +import vtkmodules.vtkInteractionStyle +# noinspection PyUnresolvedReferences +import vtkmodules.vtkRenderingOpenGL2 +from vtkmodules.vtkCommonColor import vtkNamedColors +from vtkmodules.vtkImagingSources import vtkImageCanvasSource2D +from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage +from vtkmodules.vtkRenderingAnnotation import vtkCaptionActor2D +from vtkmodules.vtkRenderingCore import ( + vtkImageActor, + vtkRenderWindowInteractor, + vtkRenderWindow, + vtkRenderer +) + + +def main(): + colors = vtkNamedColors() + + draw_color1 = tuple(colors.GetColor3ub('DimGray')) + draw_color2 = tuple(colors.GetColor3ub('HotPink')) + + # Create a blank, gray image. + drawing = vtkImageCanvasSource2D(scalar_type=ImageCanvasSource2D.ScalarType.VTK_UNSIGNED_CHAR, + number_of_scalar_components=3, + extent=(0, 20, 0, 50, 0, 0), draw_color=draw_color1) + drawing.FillBox(0, 20, 0, 50) + + # Draw a circle of radius 5 centered at (9,10). + drawing.draw_color = draw_color2 + drawing.DrawCircle(9, 10, 5) + + actor = vtkImageActor() + drawing >> actor.mapper + + ren = vtkRenderer(background=colors.GetColor3d('SkyBlue'), + background2=colors.GetColor3d('MidnightBlue'), + gradient_background=2) + ren_win = vtkRenderWindow(window_name='CaptionActor2D', number_of_layers=2) + ren_win.AddRenderer(ren) + ren_win.AddRenderer(ren) + iren = vtkRenderWindowInteractor() + iren.render_window = ren_win + + ren.AddActor(actor) + + ren_win.Render() + + style = MyStyle() + iren.interactor_style = style + style.default_renderer = ren + style.current_renderer = ren + iren.Start() + + +class MyStyle(vtkInteractorStyleImage): + + def __init__(self): + super().__init__() + + self.AddObserver('LeftButtonPressEvent', self.OnLeftButtonDown) + self.count = 0 + + def OnLeftButtonDown(self, obj, event): + self.interactor.picker.Pick(self.interactor.GetEventPosition()[0], + self.interactor.GetEventPosition()[1], + 0, + self.current_renderer) + picked = self.interactor.picker.pick_position + self.add_number(picked) + + # Forward events. + super().OnLeftButtonDown() + + super().interactor.Render() + + def add_number(self, p): + colors = vtkNamedColors() + p = list(p) + if p[0] == 0 and p[1] == 0: + # Not in the box. + return + s = f'adding marker at: {p[0]:6.4f} {p[1]:6.4f}' + + # # Normally, with an image you would do: + # s = self.image.spacing + # o = self.image.origin + # p[0] = int((p[0] - o[0]) / s[0] + 0.5) + # p[1] = int((p[1] - o[1]) / s[1] + 0.5) + # Here we do: + p[0] = int(p[0]) + 0.5 + p[1] = int(p[1]) + 0.5 + s += f' -> {p[0]:3.1f} {p[1]:3.1f}' + + # Create an actor for the text + caption_actor = vtkCaptionActor2D(caption=str(self.count), attachment_point=p, border=False, + three_dimensional_leader=False) + caption_actor.caption_text_property.bold = False + caption_actor.caption_text_property.italic = False + caption_actor.caption_text_property.shadow = False + + self.current_renderer.AddViewProp(caption_actor) + print(f'For point: {self.count:3d} {s}') + + self.count += 1 + + +@dataclass(frozen=True) +class Coordinate: + @dataclass(frozen=True) + class CoordinateSystem: + VTK_DISPLAY: int = 0 + VTK_NORMALIZED_DISPLAY: int = 1 + VTK_VIEWPORT: int = 2 + VTK_NORMALIZED_VIEWPORT: int = 3 + VTK_VIEW: int = 4 + VTK_POSE: int = 5 + VTK_WORLD: int = 6 + VTK_USERDEFINED: int = 7 + + +@dataclass(frozen=True) +class ImageCanvasSource2D: + @dataclass(frozen=True) + class ScalarType: + VTK_CHAR: int = 2 + VTK_UNSIGNED_CHAR: int = 3 + VTK_SHORT: int = 4 + VTK_UNSIGNED_SHORT: int = 5 + VTK_INT: int = 6 + VTK_UNSIGNED_INT: int = 7 + VTK_LONG: int = 8 + VTK_UNSIGNED_LONG: int = 9 + VTK_FLOAT: int = 10 + VTK_DOUBLE: int = 11 + + +if __name__ == '__main__': + main() diff --git a/src/Testing/Baseline/PythonicAPI/Images/TestMarkKeypoints.png b/src/Testing/Baseline/PythonicAPI/Images/TestMarkKeypoints.png new file mode 100644 index 0000000000000000000000000000000000000000..9c77bcb63757be8b0719f29c23e9c015864676d3 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Images/TestMarkKeypoints.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e04cb920f2c81398d2454c749e3d9f2cb6f3a89ea589972f827713b7a7c04f74 +size 44688 diff --git a/src/Testing/Baseline/PythonicAPI/Visualization/TestCaptionActor2D.png b/src/Testing/Baseline/PythonicAPI/Visualization/TestCaptionActor2D.png new file mode 100644 index 0000000000000000000000000000000000000000..9c77bcb63757be8b0719f29c23e9c015864676d3 --- /dev/null +++ b/src/Testing/Baseline/PythonicAPI/Visualization/TestCaptionActor2D.png @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e04cb920f2c81398d2454c749e3d9f2cb6f3a89ea589972f827713b7a7c04f74 +size 44688