Skip to content

CenterAnImage

Repository source: CenterAnImage

Description

This moves (0,0) from the bottom left corner of the image to the center of the image.

Move the box widget around to get coordinates in the image.

Other languages

See (Cxx)

Question

If you have a question about this example, please use the VTK Discourse Forum

Code

CenterAnImage.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
from vtkmodules.vtkImagingCore import vtkImageChangeInformation
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleImage
from vtkmodules.vtkInteractionWidgets import (
    vtkBorderRepresentation,
    vtkBorderWidget
)
from vtkmodules.vtkRenderingCore import (
    vtkImageActor,
    vtkRenderer,
    vtkRenderWindow,
    vtkRenderWindowInteractor
)


def get_program_parameters():
    import argparse
    description = 'Center an image.'
    epilogue = '''
    Shift the image center to (0,0)
    '''
    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. Ox.jpg.')
    args = parser.parse_args()
    return args.file_name


def main():
    fn = 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

    # Read the image.
    image_reader: vtkImageReader2Factory = vtkImageReader2Factory().CreateImageReader2(str(fp))
    image_reader.file_name = fp

    colors = vtkNamedColors()

    # Shift the image center to (0,0).
    # dims = image_reader.output.dimensions

    change_information = vtkImageChangeInformation(center_image=True)
    image_reader >> change_information

    image = change_information.update().output

    image_actor = vtkImageActor()
    image_actor.mapper.input_data = image

    render_window = vtkRenderWindow()
    interactor = vtkRenderWindowInteractor()

    style = vtkInteractorStyleImage()
    interactor.interactor_style = style

    rep = vtkBorderRepresentation()
    rep.BuildRepresentation()
    rep.border_color = colors.GetColor3d('Chartreuse')
    border_widget = vtkBorderWidget(interactor=interactor, selectable=False, representation=rep)

    interactor.render_window = render_window

    renderer = vtkRenderer(background=colors.GetColor3d('Peru'))
    render_window.AddRenderer(renderer)

    renderer.AddActor(image_actor)

    renderer.ResetCamera()

    border_callback = BorderCallback(renderer, image_actor)

    border_widget.AddObserver('InteractionEvent', border_callback)
    border_widget.On()
    render_window.window_name = 'CenterAnImage'
    render_window.Render()
    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)

        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=8, d=2, pt="f")})')
            print(f'Upper right coordinate: ({fmt_floats(upper_right, w=8, 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()