Skip to content

Commit aaf2d81

Browse files
committed
ENH: Multi-component vmin, vmax
1 parent 93a6b74 commit aaf2d81

File tree

2 files changed

+60
-18
lines changed

2 files changed

+60
-18
lines changed

itkwidgets/widget_viewer.py

Lines changed: 36 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -215,12 +215,12 @@ class Viewer(ViewerParent):
215215
help="Region of interest: [[lower_x, lower_y, lower_z), (upper_x, upper_y, upper_z]]")\
216216
.tag(sync=True, **array_serialization)\
217217
.valid(shape_constraints(2, 3))
218-
vmin = CFloat(
218+
vmin = List(trait=CFloat(),
219219
default_value=None,
220220
allow_none=True,
221221
help="Value that maps to the minimum of image colormap.").tag(
222222
sync=True)
223-
vmax = CFloat(
223+
vmax = List(trait=CFloat(),
224224
default_value=None,
225225
allow_none=True,
226226
help="Value that maps to the maximum of image colormap.").tag(
@@ -345,6 +345,14 @@ def __init__(self, **kwargs): # noqa: C901
345345
proposal = {'value': kwargs['cmap']}
346346
cmap_list = self._validate_cmap(proposal)
347347
kwargs['cmap'] = cmap_list
348+
if 'vmin' in kwargs and kwargs['vmin'] is not None:
349+
proposal = {'value': kwargs['vmin']}
350+
vmin_list = self._validate_vmin(proposal)
351+
kwargs['vmin'] = vmin_list
352+
if 'vmax' in kwargs and kwargs['vmax'] is not None:
353+
proposal = {'value': kwargs['vmax']}
354+
vmax_list = self._validate_vmax(proposal)
355+
kwargs['vmax'] = vmax_list
348356
self.observe(self._on_geometries_changed, ['geometries'])
349357
have_label_map = 'label_map' in kwargs and kwargs['label_map'] is not None
350358
if have_label_map:
@@ -587,6 +595,26 @@ def _validate_cmap(self, proposal):
587595
else:
588596
return [value]
589597

598+
@validate('vmin')
599+
def _validate_vmin(self, proposal):
600+
value = proposal['value']
601+
if value is None:
602+
return None
603+
elif isinstance(value, list):
604+
return value
605+
else:
606+
return [value]
607+
608+
@validate('vmax')
609+
def _validate_vmax(self, proposal):
610+
value = proposal['value']
611+
if value is None:
612+
return None
613+
elif isinstance(value, list):
614+
return value
615+
else:
616+
return [value]
617+
590618
@validate('point_set_colors')
591619
def _validate_point_set_colors(self, proposal):
592620
value = proposal['value']
@@ -807,13 +835,13 @@ def view(image=None, # noqa: C901
807835
label_map_blend : float, default: 0.5
808836
Label map blend with intensity image, from 0.0 to 1.0.
809837
810-
vmin: float, default: None
811-
Value that maps to the minimum of image colormap. Defaults to minimum of
812-
the image pixel buffer.
838+
vmin: list of floats, default: Minimum of the image pixel buffer
839+
Value that maps to the minimum of image colormap. A single value
840+
can be provided or a list for multi-component images.
813841
814-
vmax: float, default: None
815-
Value that maps to the minimum of image colormap. Defaults to maximum of
816-
the image pixel buffer.
842+
vmax: list of floats, default: Maximum of the image pixel buffer
843+
Value that maps to the minimum of image colormap. A single value can
844+
be provided or a list for multi-component images.
817845
818846
cmap: list of strings
819847
default:

js/lib/viewer.js

Lines changed: 24 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -798,13 +798,21 @@ const ViewerView = widgets.DOMWidgetView.extend({
798798

799799
const onChangeColorRange = (component, colorRange) => {
800800
const vmin = this.model.get('vmin')
801-
if (colorRange[0] !== vmin) {
802-
this.model.set('vmin', colorRange[0])
801+
if (vmin === null) {
802+
vmin = []
803+
}
804+
if (colorRange[0] !== vmin[component]) {
805+
vmin[component] = colorRange[0]
806+
this.model.set('vmin', vmin)
803807
this.model.save_changes()
804808
}
805809
const vmax = this.model.get('vmax')
806-
if (colorRange[1] !== vmax) {
807-
this.model.set('vmax', colorRange[1])
810+
if (vmax === null) {
811+
vmax = []
812+
}
813+
if (colorRange[1] !== vmax[component]) {
814+
vmax[component] = colorRange[1]
815+
this.model.set('vmax', vmax)
808816
this.model.save_changes()
809817
}
810818
}
@@ -1514,18 +1522,24 @@ const ViewerView = widgets.DOMWidgetView.extend({
15141522
vmin_changed: function () {
15151523
const vmin = this.model.get('vmin')
15161524
if (vmin !== null && this.model.hasOwnProperty('itkVtkViewer')) {
1517-
const colorRange = this.model.itkVtkViewer.getColorRange(0).slice()
1518-
colorRange[0] = vmin
1519-
this.model.itkVtkViewer.setColorRange(0, colorRange)
1525+
const rendered_image = this.model.get('rendered_image')
1526+
for (let component = 0; component < rendered_image.imageType.components; component++) {
1527+
const colorRange = this.model.itkVtkViewer.getColorRange(component).slice()
1528+
colorRange[0] = vmin[component]
1529+
this.model.itkVtkViewer.setColorRange(component, colorRange)
1530+
}
15201531
}
15211532
},
15221533

15231534
vmax_changed: function () {
15241535
const vmax = this.model.get('vmax')
15251536
if (vmax !== null && this.model.hasOwnProperty('itkVtkViewer')) {
1526-
const colorRange = this.model.itkVtkViewer.getColorRange(0).slice()
1527-
colorRange[1] = vmax
1528-
this.model.itkVtkViewer.setColorRange(0, colorRange)
1537+
const rendered_image = this.model.get('rendered_image')
1538+
for (let component = 0; component < rendered_image.imageType.components; component++) {
1539+
const colorRange = this.model.itkVtkViewer.getColorRange(component).slice()
1540+
colorRange[1] = vmax[component]
1541+
this.model.itkVtkViewer.setColorRange(component, colorRange)
1542+
}
15291543
}
15301544
},
15311545

0 commit comments

Comments
 (0)