From dad4830a510a483b82aa9735a03c29009f54fe7c Mon Sep 17 00:00:00 2001 From: Abhishek Yenpure Date: Mon, 16 Dec 2024 15:29:32 -0800 Subject: [PATCH] fix(lookup tables): Adding lookup table changes to explorers -- Adding to fix issue https://github.com/Kitware/pan3d/issues/143 --- pan3d/explorers/contour.py | 12 +++-- pan3d/explorers/globe.py | 18 +++++-- pan3d/explorers/slicer.py | 28 ++++++---- pan3d/utils/presets.py | 106 +------------------------------------ 4 files changed, 41 insertions(+), 123 deletions(-) diff --git a/pan3d/explorers/contour.py b/pan3d/explorers/contour.py index 5b865a6..f6f6dec 100644 --- a/pan3d/explorers/contour.py +++ b/pan3d/explorers/contour.py @@ -16,6 +16,7 @@ vtkLoopSubdivisionFilter, ) from vtkmodules.vtkFiltersCore import vtkAssignAttribute +from vtkmodules.vtkCommonCore import vtkLookupTable # VTK factory initialization from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa @@ -31,7 +32,7 @@ from trame.widgets import vuetify3 as v3, html from pan3d.utils.convert import to_float, to_image -from pan3d.utils.presets import apply_preset, COLOR_PRESETS +from pan3d.utils.presets import set_preset, PRESETS from pan3d.ui.vtk_view import Pan3DView from pan3d.ui.css import base, preview @@ -88,6 +89,8 @@ def __init__(self, server=None, local_rendering=None): # ------------------------------------------------------------------------- def _setup_vtk(self): + self.lut = vtkLookupTable() + self.renderer = vtkRenderer(background=(0.8, 0.8, 0.8)) self.interactor = vtkRenderWindowInteractor() self.render_window = vtkRenderWindow(off_screen_rendering=1) @@ -118,6 +121,7 @@ def _setup_vtk(self): input_connection=self.bands.output_port, scalar_visibility=1, interpolate_scalars_before_mapping=1, + lookup_table=self.lut, ) self.mapper.SelectColorArray("scalars") self.mapper.SetScalarModeToUsePointFieldData() @@ -366,7 +370,7 @@ def _build_ui(self, **kwargs): placeholder="Color Preset", prepend_inner_icon="mdi-palette", v_model=("color_preset", "Cool to Warm"), - items=("color_presets", COLOR_PRESETS), + items=("color_presets", list(PRESETS.keys())), hide_details=True, density="compact", flat=True, @@ -463,8 +467,8 @@ def _on_update_color_range( ): if self.last_preset != color_preset: self.last_preset = color_preset - apply_preset(self.actor, [color_min, color_max], color_preset) - self.state.preset_img = to_image(self.actor.mapper.lookup_table, 255) + set_preset(self.lut, color_preset) + self.state.preset_img = to_image(self.lut, 255) self.mapper.SetScalarRange(color_min, color_max) self.bands.GenerateValues(nb_contours, [color_min, color_max]) diff --git a/pan3d/explorers/globe.py b/pan3d/explorers/globe.py index 144aa44..902122f 100644 --- a/pan3d/explorers/globe.py +++ b/pan3d/explorers/globe.py @@ -11,6 +11,7 @@ from vtkmodules.vtkFiltersGeometry import vtkDataSetSurfaceFilter from vtkmodules.vtkInteractionWidgets import vtkOrientationMarkerWidget from vtkmodules.vtkRenderingAnnotation import vtkAxesActor +from vtkmodules.vtkCommonCore import vtkLookupTable import json import traceback @@ -25,7 +26,7 @@ from pan3d.xarray.algorithm import vtkXArrayRectilinearSource from pan3d.utils.convert import update_camera, to_image, to_float -from pan3d.utils.presets import apply_preset +from pan3d.utils.presets import set_preset from pan3d.ui.vtk_view import Pan3DView, Pan3DScalarBar from pan3d.ui.preview import SummaryToolbar, ControlPanel @@ -135,6 +136,8 @@ def _setup_vtk(self): get_continent_outlines, ) + self.lut = vtkLookupTable() + self.globe = get_globe() self.texture = get_globe_texture() self.gmapper = vtkPolyDataMapper(input_data_object=self.globe) @@ -156,7 +159,9 @@ def _setup_vtk(self): input_connection=self.dglobe.output_port ) - self.mapper = vtkPolyDataMapper(input_connection=self.geometry.output_port) + self.mapper = vtkPolyDataMapper( + input_connection=self.geometry.output_port, lookup_table=self.lut + ) self.actor = vtkActor(mapper=self.mapper, visibility=0) self.interactor.Initialize() @@ -306,10 +311,13 @@ def _on_color_preset( ): color_min = float(color_min) color_max = float(color_max) - color = nan_colors[nan_color] self.mapper.SetScalarRange(color_min, color_max) - apply_preset(self.actor, [color_min, color_max], color_preset, color) - self.state.preset_img = to_image(self.actor.mapper.lookup_table, 255) + + set_preset(self.lut, color_preset) + self.state.preset_img = to_image(self.lut, 255) + + color = nan_colors[nan_color] + self.lut.SetNanColor(color) self.ctrl.view_update() diff --git a/pan3d/explorers/slicer.py b/pan3d/explorers/slicer.py index 4b7d707..d483aa8 100644 --- a/pan3d/explorers/slicer.py +++ b/pan3d/explorers/slicer.py @@ -2,6 +2,8 @@ import json from pathlib import Path +from vtkmodules.vtkCommonCore import vtkLookupTable + from vtkmodules.vtkRenderingCore import ( vtkRenderer, vtkRenderWindowInteractor, @@ -11,6 +13,7 @@ vtkPolyDataMapper, vtkTextProperty, ) + from vtkmodules.vtkRenderingAnnotation import ( vtkScalarBarActor, ) @@ -38,7 +41,7 @@ from pan3d.ui.vtk_view import Pan3DView from pan3d.ui.common import NumericField -from pan3d.utils.presets import update_preset, use_preset, COLOR_PRESETS +from pan3d.utils.presets import set_preset, PRESETS @TrameApp() @@ -113,6 +116,9 @@ def _setup_vtk(self, source=None, import_state=None): color_range = ds.point_data[self.state.color_by].GetRange() + # Create lookup table + self.lut = vtkLookupTable() + # Build rendering pipeline self.renderer = vtkRenderer() self.interactor = vtkRenderWindowInteractor() @@ -125,7 +131,7 @@ def _setup_vtk(self, source=None, import_state=None): cutter.SetCutFunction(plane) cutter.input_connection = self.source.output_port slice_actor = vtkActor() - slice_mapper = vtkDataSetMapper() + slice_mapper = vtkDataSetMapper(lookup_table=self.lut) slice_mapper.SetInputConnection(cutter.GetOutputPort()) slice_mapper.SetScalarRange(*color_range) slice_mapper.SelectColorArray(self.state.color_by) @@ -139,7 +145,7 @@ def _setup_vtk(self, source=None, import_state=None): outline = vtkOutlineFilter() outline_actor = vtkActor() - outline_mapper = vtkPolyDataMapper() + outline_mapper = vtkPolyDataMapper(lookup_table=self.lut) outline.input_connection = self.source.output_port outline_mapper.SetInputConnection(outline.GetOutputPort()) outline_actor.SetMapper(outline_mapper) @@ -149,7 +155,7 @@ def _setup_vtk(self, source=None, import_state=None): self.outline_mapper = outline_mapper data_actor = vtkActor() - data_mapper = vtkDataSetMapper() + data_mapper = vtkDataSetMapper(lookup_table=self.lut) data_mapper.input_connection = self.source.output_port data_mapper.SetScalarRange(*color_range) data_actor.SetMapper(data_mapper) @@ -159,7 +165,7 @@ def _setup_vtk(self, source=None, import_state=None): self.data_mapper = data_mapper sbar_actor = vtkScalarBarActor() - sbar_actor.SetLookupTable(self.slice_mapper.GetLookupTable()) + sbar_actor.SetLookupTable(self.lut) sbar_actor.SetMaximumHeightInPixels(600) sbar_actor.SetMaximumWidthInPixels(100) sbar_actor.SetTitleRatio(0.2) @@ -294,7 +300,7 @@ def _on_colormap_change(self, cmap, **_): Performs all the steps necessary to visualize correct data when the color map is updated """ - use_preset(self.slice_actor, self.data_actor, self.sbar_actor, cmap) + set_preset(self.lut, cmap) self.ctrl.view_update() @change("logscale") @@ -302,7 +308,11 @@ def _on_log_scale_change(self, logscale, **_): """ Performs all the steps necessary when user toggles log scale for color map """ - update_preset(self.slice_actor, self.sbar_actor, logscale) + if logscale: + self.lut.SetScaleToLog10() + else: + self.lut.SetScaleToLinear() + self.ctrl.view_update() def _set_view_2D(self, axis): @@ -660,8 +670,8 @@ def _build_ui(self, *args, **kwargs): ) v3.VSelect( label="Preset", - v_model=("cmap", COLOR_PRESETS[0]), - items=("colormaps", COLOR_PRESETS), + v_model=("cmap", "Fast"), + items=("colormaps", list(PRESETS.keys())), outlined=True, **style, ) diff --git a/pan3d/utils/presets.py b/pan3d/utils/presets.py index 081611b..68f741a 100644 --- a/pan3d/utils/presets.py +++ b/pan3d/utils/presets.py @@ -2,7 +2,7 @@ from pathlib import Path import numpy as np -from vtkmodules.vtkRenderingCore import vtkColorTransferFunction, vtkActor +from vtkmodules.vtkRenderingCore import vtkColorTransferFunction from vtkmodules.vtkCommonCore import vtkLookupTable PRESETS = { @@ -75,107 +75,3 @@ def set_preset(lut: vtkLookupTable, preset_name: str, n_colors=255): rgb = colors.GetColor(x) lut.SetTableValue(i, *rgb) lut.Build() - - -# -------------------------------------------------------- - -hsv_colors = { - "Greyscale": { - "Hue": (0.0, 0.0), - "Saturation": (0.0, 0.0), - "Range": (0.0, 1.0), - }, - "Inv Greyscale": { - "Hue": (0.0, 0.666), - "Saturation": (0.0, 0.0), - "Range": (1.0, 0.0), - }, -} - -rgb_colors = {} -try: - data = json.loads(Path(__file__).with_name("presets.json").read_text()) - for cmap in data: - name = cmap["Name"] - srgb = np.array(cmap["RGBPoints"]) - tfunc = vtkColorTransferFunction() - for arr in np.split(srgb, len(srgb) / 4): - tfunc.AddRGBPoint(arr[0], arr[1], arr[2], arr[3]) - info = {"TF": tfunc, "Range": (srgb[0], srgb[-4])} - rgb_colors[name] = info -except Exception as e: - print("Error loading diverging color maps : ", e) - - -def convert_tfunc_to_lut(tfunc: vtkColorTransferFunction, tfrange): - lut = vtkLookupTable() - lut.SetNumberOfTableValues(256) - tflen = tfrange[1] - tfrange[0] - for i in range(256): - t = tfrange[0] + tflen * i / 255 - rgb = list(tfunc.GetColor(t)) - lut.SetTableValue(i, rgb[0], rgb[1], rgb[2]) - lut.Build() - return lut - - -def apply_preset(actor: vtkActor, srange, preset: str, nan_color=[0, 0, 0, 0]) -> None: - if preset in list(hsv_colors.keys()): - lut = vtkLookupTable() - lut.SetNumberOfTableValues(256) - mapper = actor.GetMapper() - mapper.SetLookupTable(lut) - preset = hsv_colors[preset] - hue = preset["Hue"] - sat = preset["Saturation"] - rng = preset["Range"] - lut.SetHueRange(hue[0], hue[1]) - lut.SetSaturationRange(sat[0], sat[1]) - lut.SetValueRange(rng[0], rng[1]) - lut.SetNanColor(nan_color) - lut.Build() - elif preset in list(rgb_colors.keys()): - info = rgb_colors[preset] - tfunc = info["TF"] - tfrange = info["Range"] - lut = convert_tfunc_to_lut(tfunc, tfrange) - lut.SetNanColor(nan_color) - mapper = actor.GetMapper() - mapper.SetLookupTable(lut) - mapper.SetScalarRange(srange[0], srange[1]) - - -def use_preset(sactor: vtkActor, dactor: vtkActor, sbar: vtkActor, preset: str) -> None: - """ - Given the slice, data, and scalar bar actor, applies the provided preset - and updates the actors and the scalar bar - """ - srange = sactor.GetMapper().GetScalarRange() - drange = dactor.GetMapper().GetScalarRange() - actors = [sactor, dactor] - ranges = [srange, drange] - for actor, range in zip(actors, ranges): - apply_preset(actor, range, preset) - sactor.GetMapper().SetScalarRange(srange[0], srange[1]) - dactor.GetMapper().SetScalarRange(drange[0], drange[1]) - sbar.SetLookupTable(sactor.GetMapper().GetLookupTable()) - - -def update_preset(actor: vtkActor, sbar: vtkActor, logcale: bool) -> None: - """ - Given an actor, scalar bar, and the option for whether to use log scale, - make changes to the lookup table for the actor, and update the scalar bar - """ - lut = actor.GetMapper().GetLookupTable() - if logcale: - lut.SetScaleToLog10() - else: - lut.SetScaleToLinear() - lut.Build() - sbar.SetLookupTable(lut) - - -COLOR_PRESETS = [ - *list(hsv_colors.keys()), - *list(rgb_colors.keys()), -]