def main(): # Callback required for FURY def keypress_callback(obj, _): key = obj.GetKeySym().lower() nonlocal clusters_linewidth, background_linewidth nonlocal curr_streamlines_actor, concat_streamlines_actor, show_curr_actor iterator = len(accepted_streamlines) + len(rejected_streamlines) renwin = interactor_style.GetInteractor().GetRenderWindow() renderer = interactor_style.GetCurrentRenderer() if key == 'c' and iterator < len(sft_accepted_on_size): if show_curr_actor: renderer.rm(concat_streamlines_actor) renwin.Render() show_curr_actor = False logging.info('Streamlines rendering OFF') else: renderer.add(concat_streamlines_actor) renderer.rm(curr_streamlines_actor) renderer.add(curr_streamlines_actor) renwin.Render() show_curr_actor = True logging.info('Streamlines rendering ON') return if key == 'q': show_manager.exit() if iterator < len(sft_accepted_on_size): logging.warning( 'Early exit, everything remaining to be rejected.') return if key in ['a', 'r'] and iterator < len(sft_accepted_on_size): if key == 'a': accepted_streamlines.append(iterator) choices.append('a') logging.info('Accepted file %s', filename_accepted_on_size[iterator]) elif key == 'r': rejected_streamlines.append(iterator) choices.append('r') logging.info('Rejected file %s', filename_accepted_on_size[iterator]) iterator += 1 if key == 'z': if iterator > 0: last_choice = choices.pop() if last_choice == 'r': rejected_streamlines.pop() else: accepted_streamlines.pop() logging.info('Rewind on step.') iterator -= 1 else: logging.warning('Cannot rewind, first element.') if key in ['a', 'r', 'z'] and iterator < len(sft_accepted_on_size): renderer.rm(curr_streamlines_actor) curr_streamlines = sft_accepted_on_size[iterator].streamlines curr_streamlines_actor = actor.line(curr_streamlines, opacity=0.8, linewidth=clusters_linewidth) renderer.add(curr_streamlines_actor) if iterator == len(sft_accepted_on_size): print('No more cluster, press q to exit') renderer.rm(curr_streamlines_actor) renwin.Render() parser = _build_args_parser() args = parser.parse_args() assert_inputs_exist(parser, args.in_bundles) assert_outputs_exist(parser, args, [args.out_accepted, args.out_rejected]) if args.out_accepted_dir: assert_output_dirs_exist_and_empty(parser, args, args.out_accepted_dir, create_dir=True) if args.out_rejected_dir: assert_output_dirs_exist_and_empty(parser, args, args.out_rejected_dir, create_dir=True) if args.verbose: logging.basicConfig(level=logging.INFO) if args.min_cluster_size < 1: parser.error('Minimum cluster size must be at least 1.') clusters_linewidth = args.clusters_linewidth background_linewidth = args.background_linewidth # To accelerate procedure, clusters can be discarded based on size # Concatenation is to give spatial context sft_accepted_on_size, filename_accepted_on_size = [], [] sft_rejected_on_size, filename_rejected_on_size = [], [] concat_streamlines = [] for filename in args.in_bundles: if not is_header_compatible(args.in_bundles[0], filename): return basename = os.path.basename(filename) sft = load_tractogram_with_reference(parser, args, filename, bbox_check=False) if len(sft) >= args.min_cluster_size: sft_accepted_on_size.append(sft) filename_accepted_on_size.append(basename) concat_streamlines.extend(sft.streamlines) else: logging.info('File %s has %s streamlines, automatically rejected.', filename, len(sft)) sft_rejected_on_size.append(sft) filename_rejected_on_size.append(basename) if not filename_accepted_on_size: parser.error('No cluster survived the cluster_size threshold.') logging.info('%s clusters to be classified.', len(sft_accepted_on_size)) # The clusters are sorted by size for simplicity/efficiency tuple_accepted = zip( *sorted(zip(sft_accepted_on_size, filename_accepted_on_size), key=lambda x: len(x[0]), reverse=True)) sft_accepted_on_size, filename_accepted_on_size = tuple_accepted # Initialize the actors, scene, window, observer concat_streamlines_actor = actor.line(concat_streamlines, colors=(1, 1, 1), opacity=args.background_opacity, linewidth=background_linewidth) curr_streamlines_actor = actor.line(sft_accepted_on_size[0].streamlines, opacity=0.8, linewidth=clusters_linewidth) scene = window.Scene() interactor_style = interactor.CustomInteractorStyle() show_manager = window.ShowManager(scene, size=(800, 800), reset_camera=False, interactor_style=interactor_style) scene.add(concat_streamlines_actor) scene.add(curr_streamlines_actor) interactor_style.AddObserver('KeyPressEvent', keypress_callback) # Lauch rendering and selection procedure choices, accepted_streamlines, rejected_streamlines = [], [], [] show_curr_actor = True show_manager.start() # Early exit means everything else is rejected missing = len(args.in_bundles) - len(choices) - len(sft_rejected_on_size) len_accepted = len(sft_accepted_on_size) rejected_streamlines.extend(range(len_accepted - missing, len_accepted)) if missing > 0: logging.info('%s clusters automatically rejected from early exit', missing) # Save accepted clusters (by GUI) accepted_streamlines = save_clusters(sft_accepted_on_size, accepted_streamlines, args.out_accepted_dir, filename_accepted_on_size) accepted_sft = StatefulTractogram(accepted_streamlines, sft_accepted_on_size[0], Space.RASMM) save_tractogram(accepted_sft, args.out_accepted, bbox_valid_check=False) # Save rejected clusters (by GUI) rejected_streamlines = save_clusters(sft_accepted_on_size, rejected_streamlines, args.out_rejected_dir, filename_accepted_on_size) # Save rejected clusters (by size) rejected_streamlines.extend( save_clusters(sft_rejected_on_size, range(len(sft_rejected_on_size)), args.out_rejected_dir, filename_rejected_on_size)) rejected_sft = StatefulTractogram(rejected_streamlines, sft_accepted_on_size[0], Space.RASMM) save_tractogram(rejected_sft, args.out_rejected, bbox_valid_check=False)
def test_custom_interactor_style_events(recording=False): print("Using VTK {}".format(vtk.vtkVersion.GetVTKVersion())) filename = "test_custom_interactor_style_events.log.gz" recording_filename = pjoin(DATA_DIR, filename) scene = window.Scene() # the show manager allows to break the rendering process # in steps so that the widgets can be added properly interactor_style = interactor.CustomInteractorStyle() show_manager = window.ShowManager(scene, size=(800, 800), reset_camera=False, interactor_style=interactor_style) # Create a cursor, a circle that will follow the mouse. polygon_source = vtk.vtkRegularPolygonSource() polygon_source.GeneratePolygonOff() # Only the outline of the circle. polygon_source.SetNumberOfSides(50) polygon_source.SetRadius(10) # polygon_source.SetRadius polygon_source.SetCenter(0, 0, 0) mapper = vtk.vtkPolyDataMapper2D() vtk_utils.set_input(mapper, polygon_source.GetOutputPort()) cursor = vtk.vtkActor2D() cursor.SetMapper(mapper) cursor.GetProperty().SetColor(1, 0.5, 0) scene.add(cursor) def follow_mouse(iren, obj): obj.SetPosition(*iren.event.position) iren.force_render() interactor_style.add_active_prop(cursor) interactor_style.add_callback(cursor, "MouseMoveEvent", follow_mouse) # create some minimalistic streamlines lines = [ np.array([[-1, 0, 0.], [1, 0, 0.]]), np.array([[-1, 1, 0.], [1, 1, 0.]]) ] colors = np.array([[1., 0., 0.], [0.3, 0.7, 0.]]) tube1 = actor.streamtube([lines[0]], colors[0]) tube2 = actor.streamtube([lines[1]], colors[1]) scene.add(tube1) scene.add(tube2) # Define some counter callback. states = defaultdict(lambda: 0) def counter(iren, _obj): states[iren.event.name] += 1 # Assign the counter callback to every possible event. for event in [ "CharEvent", "MouseMoveEvent", "KeyPressEvent", "KeyReleaseEvent", "LeftButtonPressEvent", "LeftButtonReleaseEvent", "RightButtonPressEvent", "RightButtonReleaseEvent", "MiddleButtonPressEvent", "MiddleButtonReleaseEvent" ]: interactor_style.add_callback(tube1, event, counter) # Add callback to scale up/down tube1. def scale_up_obj(iren, obj): counter(iren, obj) scale = np.asarray(obj.GetScale()) + 0.1 obj.SetScale(*scale) iren.force_render() iren.event.abort() # Stop propagating the event. def scale_down_obj(iren, obj): counter(iren, obj) scale = np.array(obj.GetScale()) - 0.1 obj.SetScale(*scale) iren.force_render() iren.event.abort() # Stop propagating the event. interactor_style.add_callback(tube2, "MouseWheelForwardEvent", scale_up_obj) interactor_style.add_callback(tube2, "MouseWheelBackwardEvent", scale_down_obj) # Add callback to hide/show tube1. def toggle_visibility(iren, obj): key = iren.event.key if key.lower() == "v": obj.SetVisibility(not obj.GetVisibility()) iren.force_render() interactor_style.add_active_prop(tube1) interactor_style.add_active_prop(tube2) interactor_style.remove_active_prop(tube2) interactor_style.add_callback(tube1, "CharEvent", toggle_visibility) if recording: show_manager.record_events_to_file(recording_filename) print(list(states.items())) else: show_manager.play_events_from_file(recording_filename) msg = ("Wrong count for '{}'.") expected = [('CharEvent', 6), ('KeyPressEvent', 6), ('KeyReleaseEvent', 6), ('MouseMoveEvent', 1652), ('LeftButtonPressEvent', 1), ('RightButtonPressEvent', 1), ('MiddleButtonPressEvent', 2), ('LeftButtonReleaseEvent', 1), ('MouseWheelForwardEvent', 3), ('MouseWheelBackwardEvent', 1), ('MiddleButtonReleaseEvent', 2), ('RightButtonReleaseEvent', 1)] # Useful loop for debugging. for event, count in expected: if states[event] != count: print("{}: {} vs. {} (expected)".format( event, states[event], count)) for event, count in expected: npt.assert_equal(states[event], count, err_msg=msg.format(event))
import vtk from fury import utils as vtk_utils from fury import actor, window, interactor scene = window.Scene() # the show manager allows to break the rendering process # in steps so that the widgets can be added properly interactor_style = interactor.CustomInteractorStyle() show_manager = window.ShowManager(scene, size=(800, 800), reset_camera=False, interactor_style=interactor_style) # Create a cursor, a circle that will follow the mouse. polygon_source = vtk.vtkRegularPolygonSource() polygon_source.GeneratePolygonOff() # Only the outline of the circle. polygon_source.SetNumberOfSides(50) polygon_source.SetRadius(10) # polygon_source.SetRadius polygon_source.SetCenter(0, 0, 0) mapper = vtk.vtkPolyDataMapper2D() vtk_utils.set_input(mapper, polygon_source.GetOutputPort()) cursor = vtk.vtkActor2D() cursor.SetMapper(mapper) cursor.GetProperty().SetColor(1, 0.5, 0) scene.add(cursor) def follow_mouse(iren, obj):