Skip to content

Commit

Permalink
Merge pull request #147 from ayenpure/fix_lut_application
Browse files Browse the repository at this point in the history
fix(lookup tables): Adding lookup table changes to explorers
  • Loading branch information
jourdain authored Dec 17, 2024
2 parents a6b7769 + dad4830 commit 8259773
Show file tree
Hide file tree
Showing 4 changed files with 41 additions and 123 deletions.
12 changes: 8 additions & 4 deletions pan3d/explorers/contour.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
vtkLoopSubdivisionFilter,
)
from vtkmodules.vtkFiltersCore import vtkAssignAttribute
from vtkmodules.vtkCommonCore import vtkLookupTable

# VTK factory initialization
from vtkmodules.vtkInteractionStyle import vtkInteractorStyleSwitch # noqa
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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])
Expand Down
18 changes: 13 additions & 5 deletions pan3d/explorers/globe.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down Expand Up @@ -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)
Expand All @@ -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()
Expand Down Expand Up @@ -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()

Expand Down
28 changes: 19 additions & 9 deletions pan3d/explorers/slicer.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
import json
from pathlib import Path

from vtkmodules.vtkCommonCore import vtkLookupTable

from vtkmodules.vtkRenderingCore import (
vtkRenderer,
vtkRenderWindowInteractor,
Expand All @@ -11,6 +13,7 @@
vtkPolyDataMapper,
vtkTextProperty,
)

from vtkmodules.vtkRenderingAnnotation import (
vtkScalarBarActor,
)
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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()
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand All @@ -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)
Expand Down Expand Up @@ -294,15 +300,19 @@ 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")
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):
Expand Down Expand Up @@ -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,
)
Expand Down
106 changes: 1 addition & 105 deletions pan3d/utils/presets.py
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand Down Expand Up @@ -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()),
]

0 comments on commit 8259773

Please sign in to comment.