Esempio n. 1
0
def test_add_models_to_foreground_renderer(vtk_overlay_with_gradient_image):
    liver = [
        sm.VTKSurfaceModel('tests/data/models/Liver/liver.vtk',
                           (1.0, 1.0, 1.0))
    ]
    tumors = [
        sm.VTKSurfaceModel('tests/data/models/Liver/liver_tumours.vtk',
                           (1.0, 1.0, 1.0))
    ]
    image, widget, _, app = vtk_overlay_with_gradient_image

    #If no layer is specified, default is 0
    widget.add_vtk_models(liver)

    foreground_actors = widget.foreground_renderer.GetActors()
    assert foreground_actors.GetNumberOfItems() == 1

    # Explicitly specify use of foreground renderer
    widget.add_vtk_models(tumors, 1)

    foreground_actors = widget.foreground_renderer.GetActors()
    assert foreground_actors.GetNumberOfItems() == 2

    # Check overlay renderer is empty
    overlay_renderer_actors = widget.generic_overlay_renderer.GetActors()
    assert overlay_renderer_actors.GetNumberOfItems() == 0
    def __init__(self, background_file, model_file, camera_file):
        super(ManualRegistrationMainWidget, self).__init__()

        if not background_file:
            raise ValueError("Background image must be specified")
        if not model_file:
            raise ValueError("VTK model must be specified")
        if not camera_file:
            raise ValueError("Camera matrix must be specified")

        self.setContentsMargins(0, 0, 0, 0)
        self.viewer = ow.VTKOverlayWindow()
        self.viewer_size_policy = \
            QSizePolicy(QSizePolicy.Expanding, QSizePolicy.Expanding)
        self.viewer.setSizePolicy(self.viewer_size_policy)

        self.interactor_style = vtk.vtkInteractorStyleTrackballActor()
        self.viewer.SetInteractorStyle(self.interactor_style)

        self.vertical_layout = QtWidgets.QVBoxLayout(self)
        self.vertical_layout.setContentsMargins(0, 0, 0, 0)
        self.vertical_layout.addWidget(self.viewer)

        background = cv2.imread(background_file)
        model = sm.VTKSurfaceModel(model_file, [1.0, 1.0, 1.0], opacity=0.5)
        camera_matrix = np.loadtxt(camera_file)

        self.viewer.set_video_image(background)
        self.viewer.set_camera_matrix(camera_matrix)
        self.viewer.add_vtk_models([model])
Esempio n. 3
0
def test_preop_from_vtkdata():
    """ Voxelise from already loaded vtkdata, rather than from file name."""
    input_file = 'tests/data/voxelisation/liver_downsample.stl'
    model = vtk_surface_model.VTKSurfaceModel(input_file, [0.0, 1.0, 0.0])

    size = 0.3
    grid_elements = 64

    grid = voxelise.voxelise(input_mesh=model.get_vtk_source_data(),
                             scale_input=0.001,
                             center=True,
                             size=size,
                             grid_elements=grid_elements)

    # Check dimensions correct
    cell_dims = [0, 0, 0]
    grid.GetCellDims(cell_dims)
    assert cell_dims == [63, 63, 63]

    # Check array name is correct
    numpy_data = voxelise.extract_array_from_grid(grid, 'preoperativeSurface')
    print("Numpy data", numpy_data)

    # Cells 'inside' the liver have negative values, so this should be
    # consistent
    cells_in_liver = numpy_data < 0
    assert np.count_nonzero(cells_in_liver) == 14628
def run_demo(input_file, output_file, transform_file):
    """
    Takes a poly data input, transforms it by a 4x4 matrix, writes output.

    :param input_file: Input poly data as .vtk, .vtp, .stl or .ply file.
    :param output_file: Output poly data as .vtk file.
    :param transform_file: 4x4 matrix in plain text (e.g. numpy.savetxt) format.
    :return:
    """
    if not output_file.endswith('.vtk'):
        raise ValueError('Currently, only .vtk outputs are supported.')

    model = sm.VTKSurfaceModel(input_file,
                               colour=[1.0, 1.0, 1.0],
                               visibility=True)

    matrix = np.loadtxt(transform_file)
    transform = mu.create_vtk_matrix_from_numpy(matrix)
    model.set_model_transform(transform)

    # this line can go, when scikit-surgeryvtk updates to 0.22.0
    model.transform_filter.Update()

    writer = vtk.vtkPolyDataWriter()
    writer.SetFileName(output_file)
    writer.SetInputData(model.transform_filter.GetOutput())
    writer.Write()
Esempio n. 5
0
def test_add_model_to_background_renderer_raises_error(
        vtk_overlay_with_gradient_image):
    surface = [
        sm.VTKSurfaceModel('tests/data/models/Liver/liver.vtk',
                           (1.0, 1.0, 1.0))
    ]
    image, widget, _, app = vtk_overlay_with_gradient_image

    with pytest.raises(ValueError):
        widget.add_vtk_models(surface, layer=0)
Esempio n. 6
0
def test_surface_model_overlay(vtk_overlay_with_gradient_image):

    image, widget, _, app = vtk_overlay_with_gradient_image
    surface = [
        sm.VTKSurfaceModel('tests/data/models/Liver/liver.vtk',
                           (1.0, 1.0, 1.0))
    ]
    widget.add_vtk_models(surface)
    widget.resize(512, 256)
    widget.show()
    widget.Render()
Esempio n. 7
0
    def __load_surface(self, config):

        if 'file' in config.keys():
            file_name = config['file']
        else:
            raise KeyError("No 'file' section defined in config")

        if 'opacity' in config.keys():
            opacity = config['opacity']
        else:
            raise KeyError("No 'opacity' section defined in config")

        if 'visibility' in config.keys():
            visibility = config['visibility']
        else:
            raise KeyError("No 'visibility' section defined in config")

        if 'colour' in config.keys():
            colour = config['colour']
        else:
            raise KeyError("No 'colour' section defined in config")

        if 'pickable' in config.keys():
            pickable = config['pickable']
        else:
            raise KeyError("No 'pickable' section defined in config")

        colour_as_float = [colour[0] / 255.0,
                           colour[1] / 255.0,
                           colour[2] / 255.0
                           ]

        tmp_name = file_name
        if self.directory_prefix is not None:
            tmp_name = os.path.join(self.directory_prefix, tmp_name)

        model = sm.VTKSurfaceModel(tmp_name,
                                   colour_as_float,
                                   visibility,
                                   opacity,
                                   pickable)

        if 'texture' in config.keys():
            texture_file = config['texture']
            if self.directory_prefix is not None:
                texture_file = os.path.join(self.directory_prefix, texture_file)
            model.set_texture(texture_file)

        if 'no shading' in config.keys():
            no_shading = config['no shading']
            model.set_no_shading(no_shading)

        return model
Esempio n. 8
0
def load_file_of_points(file_name):
    """
    Loads an MITK .mps file, or .txt file (rows of x y z).
    :param file_name: string containing path to a valid file
    :return: Nx3 ndarray
    """
    extension = os.path.splitext(file_name)[1]
    if extension == '.mps':
        _, points = lmps.load_mps(file_name)
    elif extension == '.vtk':
        model = sm.VTKSurfaceModel(file_name, (1.0, 1.0, 1.0))
        points = model.get_points_as_numpy()
    else:
        points = np.loadtxt(file_name)
    return points
Esempio n. 9
0
def test_add_models_to_overlay_renderer(vtk_overlay_with_gradient_image):
    liver = [
        sm.VTKSurfaceModel('tests/data/models/Liver/liver.vtk',
                           (1.0, 1.0, 1.0))
    ]
    tumors = [
        sm.VTKSurfaceModel('tests/data/models/Liver/liver_tumours.vtk',
                           (1.0, 1.0, 1.0))
    ]
    image, widget, _, app = vtk_overlay_with_gradient_image

    widget.add_vtk_models(liver, 2)

    overlay_actors = widget.generic_overlay_renderer.GetActors()
    assert overlay_actors.GetNumberOfItems() == 1

    widget.add_vtk_models(tumors, 2)

    overlay_actors = widget.generic_overlay_renderer.GetActors()
    assert overlay_actors.GetNumberOfItems() == 2

    # Check foreground is empty
    foreground_actors = widget.foreground_renderer.GetActors()
    assert foreground_actors.GetNumberOfItems() == 0
Esempio n. 10
0
    def set_target_liver(self):
        #pylint:disable=attribute-defined-outside-init, unexpected-keyword-arg
        """ Set position of target liver. """
        liver_model = 'tests/data/overlay/liver.vtk'
        self.target_model = sm.VTKSurfaceModel(liver_model, [0.5, 0.5, 0.5],
                                               pickable=False)
        self.target_actor = self.target_model.actor

        self.target_x = -300 + 600 * random.random()
        self.target_y = -200 + 400 * random.random()
        self.target_actor.SetOrigin(0, 0, 0)

        scale = 0.5 + random.random() * 1.0
        self.target_actor.SetScale(scale, scale, scale)

        self.target_actor.RotateX(180*random.random())
        self.target_actor.RotateY(90*random.random())

        self.target_actor.SetPosition(self.target_x, self.target_y, 0)
def test_camera_projection(setup_vtk_overlay_window):

    vtk_overlay, factory, vtk_std_err, app = setup_vtk_overlay_window

    # See data:
    # chessboard_14_10_3_no_ID.txt - 3D chessboard coordinates
    # left-1095.png - image taken of chessboard
    # left-1095.png.points.txt - detected 2D image points
    # calib.intrinsic.txt - top 3x3 matrix are intrinsic parameters
    # left-1095.extrinsic.txt - model to camera matrix, a.k.a camera extrinsics, a.k.a pose

    # Load 3D points
    model_points_file = 'tests/data/calibration/chessboard_14_10_3_no_ID.txt'
    model_points = np.loadtxt(model_points_file)
    number_model_points = model_points.shape[0]
    model_polydata = [sm.VTKSurfaceModel('tests/data/calibration/chessboard_14_10_3.vtk', (1.0, 1.0, 0.0))]

    # Load images
    left_image = cv2.imread('tests/data/calibration/left-1095-undistorted.png')
    left_mask = cv2.imread('tests/data/calibration/left-1095-undistorted-mask.png')
    left_mask = cv2.cvtColor(left_mask, cv2.COLOR_RGB2GRAY)

    # Load 2D points
    image_points_file ='tests/data/calibration/left-1095-undistorted.png.points.txt'
    image_points = np.loadtxt(image_points_file)
    number_image_points = image_points.shape[0]

    # Load intrinsics for projection matrix.
    intrinsics_file = 'tests/data/calibration/calib.left.intrinsic.txt'
    intrinsics = np.loadtxt(intrinsics_file)

    # Load extrinsics for camera pose (position, orientation).
    extrinsics_file = 'tests/data/calibration/left-1095.extrinsic.txt'
    extrinsics = np.loadtxt(extrinsics_file)

    # OpenCV maps from chessboard to camera.
    # Assume chessboard == world, so the input matrix is world_to_camera.
    # We need camera_to_world to position the camera in world coordinates.
    # So, invert it.
    camera_to_world = np.linalg.inv(extrinsics)

    assert number_model_points == 140
    assert number_image_points == 140
    assert len(image_points) == 140
    assert len(model_points) == 140

    screen = app.primaryScreen()
    width = left_image.shape[1]
    height = left_image.shape[0]
    while width >= screen.geometry().width() or height >= screen.geometry().height():
        width //= 2
        height //= 2

    vtk_overlay.add_vtk_models(model_polydata)
    vtk_overlay.set_video_image(left_image)
    vtk_overlay.set_camera_pose(camera_to_world)
    vtk_overlay.resize(width, height)
    vtk_overlay.show()

    vtk_renderer = vtk_overlay.get_foreground_renderer()
    vtk_camera = vtk_overlay.get_foreground_camera()
    vtk_renderwindow_size = vtk_overlay.GetRenderWindow().GetSize()

    # Wierdly, vtkRenderWindow, sometimes seems to use the wrong resolution,
    # like its trying to render at high resolution, maybe for anti-aliasing or averaging?
    scale_x = left_image.shape[1] / vtk_renderwindow_size[0]
    scale_y = left_image.shape[0] / vtk_renderwindow_size[1]

    # Print out some debug. On some displays, the widget size and the size of the vtkRenderWindow don't match.
    six.print_('Left image = (' + str(left_image.shape[1]) + ', ' + str(left_image.shape[0]) + ')')
    six.print_('Chosen size = (' + str(width) + ', ' + str(height) + ')')
    six.print_('Render window = ' + str(vtk_overlay.GetRenderWindow().GetSize()))
    six.print_('Widget = (' + str(vtk_overlay.width()) + ', ' + str(vtk_overlay.height()) + ')')
    six.print_('Viewport = ' + str(vtk_renderer.GetViewport()))
    six.print_('Scale = ' + str(scale_x) + ', ' + str(scale_y))

    # Set intrisic parameters, which internally sets vtkCamera vars.
    vtk_overlay.set_camera_matrix(intrinsics)

    # Compute the rms error, using a vtkCoordinate loop, which is slow.
    rms = pu.compute_rms_error(model_points,
                               image_points,
                               vtk_renderer,
                               scale_x,
                               scale_y,
                               left_image.shape[0]
                               )
    six.print_('rms using VTK =' + str(rms))

    # Now check the rms error, using an OpenCV projection, which should be faster.
    projected_points = pu.project_points(model_points,
                                         camera_to_world,
                                         intrinsics
                                         )

    counter = 0
    rms_opencv = 0
    for counter in range(0, number_model_points):
        i_c = image_points[counter]
        dx = projected_points[counter][0][0] - float(i_c[0])
        dy = projected_points[counter][0][1] - float(i_c[1])
        rms_opencv += (dx * dx + dy * dy)
        counter += 1
    rms_opencv /= float(counter)
    rms_opencv = np.sqrt(rms_opencv)

    six.print_('rms using OpenCV =' + str(rms_opencv))

    assert rms < 1.2
    assert rms_opencv < 0.7

    model_polydata_points = model_polydata[0].get_points_as_numpy()
    model_polydata_normals = model_polydata[0].get_normals_as_numpy()

    six.print_('model_points=' + str(model_polydata_points))

    projected_facing_points = pu.project_facing_points(model_polydata_points,
                                                       model_polydata_normals,
                                                       camera_to_world,
                                                       intrinsics
                                                       )

    assert projected_facing_points.shape[0] == 4
    assert projected_facing_points.shape[2] == 2

    # Can't think how to do this more efficiently yet.
    masked = []
    for point_index in range(projected_facing_points.shape[0]):
        x = projected_facing_points[point_index][0][0]
        y = projected_facing_points[point_index][0][1]
        val = left_mask[int(y), int(x)]
        six.print_('p=' + str(x) + ', ' + str(y))
        if int(x) >= 0 \
            and int(x) < left_mask.shape[1] \
            and int(y) >= 0 \
            and int(y) < left_mask.shape[0] \
            and val > 0:
            masked.append((x, y))

    assert len(masked) == 2
    def __init__(self, video_source, input_volume, input_surface):
        super().__init__(video_source)

        # Start by loading some data.
        if os.path.isdir(input_volume):
            reader = vtk.vtkDICOMImageReader()
            reader.SetDirectoryName(input_volume)

        elif input_volume.endswith(('.nii', '.nii.gz')):
            reader = vtk.vtkNIFTIImageReader()
            reader.SetFileName(input_volume)

        else:
            raise TypeError("Invalid input volume specified")

        reader.Update()

        self.model = sm.VTKSurfaceModel(input_surface,
                                        [0.5, 0.5, 0.5], opacity=0.5)
        self.vtk_overlay_window.add_vtk_models([self.model])

         # Calculate the center of the volume
        self.x_min, self.x_max, self.y_min, self.y_max, self.z_min, self.z_max \
            = reader.GetExecutive().GetWholeExtent(
                reader.GetOutputInformation(0))

        self.num_x = self.x_max - self.x_min
        self.num_y = self.y_max - self.y_min
        self.num_z = self.z_max - self.z_min

        self.x_spacing, self.y_spacing, self.z_spacing = \
            reader.GetOutput().GetSpacing()
        self.x_0, self.y_0, self.z_0 = reader.GetOutput().GetOrigin()

        self.center =\
         [self.x_spacing * (self.x_min + 0.5 * (self.x_min + self.x_max)),
          self.x_spacing * (self.y_min + 0.5 * (self.y_min + self.y_max)),
          self.z_spacing * (self.z_min + 0.5 * (self.z_min + self.z_max))]

        self.reslice_center = \
        [-0.5 * self.x_spacing * (self.num_x - 1),
         -0.5 * self.y_spacing * (self.num_y - 1),
         -0.5 * self.z_spacing * (self.num_z - 1)]

        # Setup reslice driver
        self.reslice = vtk.vtkImageReslice()
        self.reslice.SetInputConnection(reader.GetOutputPort())

        # Specific values to make the skull example look nicer.
        self.reslice.SetOutputExtent(100, 400, 150, 400, 25, 175)
        self.reslice.SetOutputOrigin(self.reslice_center[0],
                                     self.reslice_center[1], 0)

        self.reslice.SetInterpolationModeToLinear()

        self.reslice_x_angle = 0
        self.reslice_y_angle = 0
        self.reslice_z_angle = 0

        # Create a greyscale lookup table
        table = vtk.vtkLookupTable()
        table.SetRange(-1000, 1000) # image intensity range
        table.SetValueRange(0.1, 0.9) # from black to white
        table.SetSaturationRange(0.0, 0.0) # no color saturation
        table.SetRampToLinear()
        table.Build()

        # Map the image through the lookup table
        color = vtk.vtkImageMapToColors()
        color.SetLookupTable(table)
        color.SetInputConnection(self.reslice.GetOutputPort())

        # Display the image
        self.reslice_actor = vtk.vtkImageActor()
        self.reslice_actor.GetMapper().SetInputConnection(color.GetOutputPort())

        self.vtk_overlay_window.add_vtk_actor(self.reslice_actor)

        self.update_reslice()
Esempio n. 13
0
    def get_models(self, directory_name):
        """
        Loads models from the given directory.

        :param directory_name: string, readable directory name.
        :raises: TypeError, ValueError, RuntimeError
        """
        LOGGER.info("Loading models from %s", directory_name)

        # Reset
        files = []
        self.models = []

        # This may well throw FileNotFoundError which is fine.
        # If its not valid I want the Exception raised.
        files = os.listdir(directory_name)
        files.sort()

        # Loop through each file, trying to load it.
        counter = 0

        for filename in files:

            full_path = os.path.join(directory_name, filename)

            try:
                model = sm.VTKSurfaceModel(full_path, (1.0, 1.0, 1.0))
                model_name = os.path.splitext(model.get_name())[0]
                model.set_name(model_name)

                # New behaviour, if we provide defaults in a file, use them.
                if self.configuration_data:
                    if model_name in self.configuration_data.keys():
                        model_defaults = self.configuration_data[model_name]

                        if 'opacity' in model_defaults.keys():
                            opacity = model_defaults['opacity']
                            model.set_opacity(opacity)

                        if 'visibility' in model_defaults.keys():
                            visibility = model_defaults['visibility']
                            model.set_visibility(visibility)

                        if 'colour' in model_defaults.keys():
                            colour = model_defaults['colour']
                            colour_as_float = [
                                colour[0] / 255.0, colour[1] / 255.0,
                                colour[2] / 255.0
                            ]
                            model.set_colour(colour_as_float)

                        if 'pickable' in model_defaults.keys():
                            pickable = model_defaults['pickable']
                            model.set_pickable(pickable)

                        if 'texture' in model_defaults.keys():
                            texture_file = model_defaults['texture']
                            texture_file_path = os.path.join(
                                directory_name, texture_file)
                            model.set_texture(texture_file_path)

                        if 'no shading' in model_defaults.keys():
                            no_shading = model_defaults['no shading']
                            model.set_no_shading(no_shading)

                else:
                    # Original behaviour (see previous version in git)
                    # Either load colour from file, or we just pick a
                    # colour based on an index.
                    if filename in self.colours:
                        model_colour = self.colours[filename]
                    else:
                        LOGGER.info("Filename %s not found in colours.txt",
                                    filename)
                        model_colour = self.colours[str(counter)]
                    model.set_colour(model_colour)

                # Finally, add to list, increment counter.
                self.models.append(model)
                LOGGER.info("Loaded model from %s", full_path)
                counter += 1

            except ValueError:
                # Presume wrong type of file.
                LOGGER.info("Didn't load vtk_surface_model: %s", full_path)

        if not self.models:
            LOGGER.info("No valid model files in given directory")

        LOGGER.info("Loaded models from %s", directory_name)
Esempio n. 14
0
 def setup_liver_overlay(self):
     """ Positio overlay liver"""
     liver_model = 'tests/data/overlay/liver.vtk'
     self.model = sm.VTKSurfaceModel(liver_model, [1.0, 0.0, 0.0])
     self.vtk_overlay_window.add_vtk_models([self.model])
Esempio n. 15
0
 def setup_circle_overlay(self):
     """Position overlay sphere """
     sphere_model = 'tests/data/overlay/sphere.vtk'
     self.model = sm.VTKSurfaceModel(sphere_model, [0.5, 0.5, 0.5])
     self.vtk_overlay_window.add_vtk_models([self.model])