ImageRegion
Repository source: ImageRegion
Description¶
This example shows how to get the image coordinates of the corners of a BorderWidget.
A perspective projection (-p
) can be used instead of the parallel camera projection. If this is done, the borders of the widget can appear to be outside the image bounds.
Other languages
See (Cxx)
Question
If you have a question about this example, please use the VTK Discourse Forum
Code¶
ImageRegion.py
#!/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,
vtkJPEGReader
)
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage
from vtkmodules.vtkInteractionWidgets import (
vtkBorderRepresentation,
vtkBorderWidget
)
from vtkmodules.vtkRenderingCore import (
vtkImageActor,
vtkPropPicker,
vtkRenderer,
vtkRenderWindow,
vtkRenderWindowInteractor
)
def get_program_parameters():
import argparse
description = 'Get the coordinates of a region in an image.'
epilogue = '''
'''
parser = argparse.ArgumentParser(description=description, epilog=epilogue,
formatter_class=argparse.RawDescriptionHelpFormatter)
parser.add_argument('file_name', help='The image file name to use e.g. Gourds2.jpg.')
parser.add_argument('-p', '--perspective', action='store_false', help='Use perspective projection.')
args = parser.parse_args()
return args.file_name, args.perspective
def main():
fn, parallel_projection = get_program_parameters()
fp = Path(fn)
file_check = True
if not fp.is_file():
print(f'Missing image file: {fp}.')
file_check = False
if not file_check:
return
use_factory = True
# Read the image.
if use_factory:
image_reader: vtkImageReader2Factory = vtkImageReader2Factory().CreateImageReader2(str(fp))
image_reader.file_name = fp
else:
image_reader: vtkJPEGReader = vtkJPEGReader(file_name=fp)
if not image_reader.CanReadFile(fp):
print(f'Error: cannot read {fp}')
return
image_reader.update()
extent = image_reader.output.extent
# x_min, x_max, y_min, y_max, z_in, z_max
print(f'extent: ({fmt_floats(extent, w=0, d=2, pt="f")})')
colors = vtkNamedColors()
image_actor = vtkImageActor()
image_reader >> image_actor.mapper
render_window = vtkRenderWindow()
interactor = vtkRenderWindowInteractor()
style = vtkInteractorStyleImage()
interactor.interactor_style = style
rep = vtkBorderRepresentation()
rep.BuildRepresentation()
rep.border_color = colors.GetColor3d('Lime')
border_widget = vtkBorderWidget(interactor=interactor, selectable=False, representation=rep)
interactor.render_window = render_window
# Setup both renderers.
renderer = vtkRenderer(background=colors.GetColor3d('DarkSlateGray'))
render_window.AddRenderer(renderer)
renderer.AddActor(image_actor)
renderer.active_camera.parallel_projection = parallel_projection
renderer.ResetCamera()
border_callback = BorderCallback(renderer, image_actor)
border_widget.AddObserver('InteractionEvent', border_callback)
render_window.window_name = 'ImageRegion'
render_window.Render()
border_widget.On()
interactor.Start()
class BorderCallback:
def __init__(self, renderer, actor):
self.renderer = renderer
self.image_actor = actor
def __call__(self, caller, ev):
border_widget = caller
# Get the world coordinates of the two corners of the box.
lower_left_coordinate = border_widget.representation.position_coordinate
lower_left = lower_left_coordinate.GetComputedWorldValue(self.renderer)
# lower_left = lower_left_coordinate.GetComputedViewportValue(self.renderer)
upper_right_coordinate = border_widget.representation.position2_coordinate
upper_right = upper_right_coordinate.GetComputedWorldValue(self.renderer)
# Get the bounds (x_min, x_max, y_min, y_max, z_min, z_max)
bounds = self.image_actor.bounds
inside = lower_left[0] > bounds[0] and upper_right[0] < bounds[1] and \
lower_left[1] > bounds[2] and upper_right[1] < bounds[3]
if inside:
print(f'Lower left coordinate: ({fmt_floats(lower_left, w=0, d=2, pt="f")})')
print(f'Upper right coordinate: ({fmt_floats(upper_right, w=0, d=2, pt="f")})')
picker = vtkPropPicker()
ll_pt = list(lower_left)
ll_pt[2] = 0.0
picker.Pick3DPoint(ll_pt, self.renderer)
# picker.Pick(ll_pt, self.renderer)
path = picker.GetPath()
valid_pick = False
if path:
num_items = path.number_of_items
if num_items == 1:
print(f'There is {num_items} item in the path.')
else:
print(f'There are {num_items} items in the path.')
path.InitTraversal()
for i in range(0, num_items):
node = path.GetNextNode()
picked_prop = node.view_prop
if self.image_actor == picked_prop:
print('Correct actor picked.')
valid_pick = True
break
if valid_pick:
# pos = picker.pick_position
pos = picker.selection_point
print(f'Lower left pick: ({fmt_floats(pos, w=0, d=2, pt="f")})')
ur_pt = list(upper_right)
ur_pt[2] = 0.0
picker.Pick3DPoint(ur_pt, self.renderer)
path = picker.GetPath()
valid_pick = False
if path:
num_items = path.number_of_items
if num_items == 1:
print(f'There is {num_items} item in the path.')
else:
print(f'There are {num_items} items in the path.')
path.InitTraversal()
for i in range(0, num_items):
node = path.GetNextNode()
picked_prop = node.view_prop
if self.image_actor == picked_prop:
print('Correct actor picked.')
valid_pick = True
break
if valid_pick:
# pos = picker.pick_position
pos = picker.selection_point
print(f'Upper right pick: ({fmt_floats(pos, w=0, d=2, pt="f")})')
else:
print('The box is NOT inside the image.')
def fmt_floats(v, w=0, d=6, pt='g'):
"""
Pretty print a list or tuple of floats.
:param v: The list or tuple of floats.
:param w: Total width of the field.
:param d: The number of decimal places.
:param pt: The presentation type, 'f', 'g' or 'e'.
:return: A string.
"""
pt = pt.lower()
if pt not in ['f', 'g', 'e']:
pt = 'f'
return ', '.join([f'{element:{w}.{d}{pt}}' for element in v])
if __name__ == '__main__':
main()