Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
35 changes: 30 additions & 5 deletions src/omero_cli_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
from omero.model import Screen
from omero.model import Dataset
from omero.model import Project
from omero.rtypes import rint
from omero.model import StatsInfoI
from omero.rtypes import rint, rdouble
from omero.util import pydict_text_io

from omero import UnloadedEntityException
Expand Down Expand Up @@ -97,6 +98,8 @@
label: <string> Channel name
start: <float> Start of rendering window, optional (needs end)
end: <float> End of rendering window, optional (needs start)
min: <float> Minimum pixel intensity, optional (needs max)
max: <float> Maximum pixel intensity, optional (needs min)
<int>:
...
greyscale: <bool> Greyscale rendering, optional
Expand All @@ -123,9 +126,12 @@
# Omitted fields will keep their current values.
# Omitted channels will not be disabled unless --disable is used.
# If the file specifies to turn off a channel (active: False) then the
# other settings like min, max, and color which might be specified for
# other settings like start, end, and color which might be specified for
# that channel in the same file will be ignored, however the channel
# name (label) is still taken into account.
# If min and max have not been set on the channel (no StatsInfo on the
# channel) then you must set both. If max and min already set, each can
# be updated individually.
"""

TEST_HELP = """Test that underlying pixel data is available
Expand Down Expand Up @@ -642,6 +648,7 @@ def _read_channels(self, data):
cindices = []
rangelist = []
colourlist = []
minmaxlist = []
for (i, c) in newchannels.items():
if c.label:
namedict[i] = c.label
Expand All @@ -651,15 +658,17 @@ def _read_channels(self, data):
cindices.append(i)
rangelist.append([c.start, c.end])
colourlist.append(c.color)
return (namedict, cindices, rangelist, colourlist)
minmaxlist.append([c.min, c.max])
rv = (namedict, cindices, rangelist, colourlist, minmaxlist)
return rv

@gateway_required
def set(self, args):
""" Implements the 'set' command """
data = self._load_rendering_settings(
args.channels, session=self.client.getSession())
(namedict, cindices, rangelist, colourlist) = self._read_channels(
data)
(namedict, cindices, rangelist, colourlist, minmaxlist) = \
self._read_channels(data)
greyscale = data.get('greyscale', None)
if greyscale is not None:
self.ctx.dbg('greyscale=%s' % greyscale)
Expand Down Expand Up @@ -696,6 +705,22 @@ def set(self, args):
if len(active_channels) > 0:
img.set_active_channels(active_channels)

# Set statsInfo min & max
for minmax, ch in zip(minmaxlist, img.getChannels(noRE=True)):
if minmax[0] is None and minmax[1] is None:
continue
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would one ever want to null the value that's in the stats info? If so, this continue would prevent that, right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, OK. So if I put min: null into the yaml then this line fails self.min = float(d['min']) if 'min' in d else None.
I could work around that, but I'd need to set self.min to some value that indicates 'unset', since None is already used to mean "Don't change anything".
In JavaScript we have undefined and null to choose from, but Python only has None!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thinking of the YAML contract from a round-tripping perspective, what it the behavior of omero render info for an Image for which the min/max has been nulled in stats info? Does it default to the full pixel type range?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sbesson Yes. E.g. $ omero render info Image:9621401 for IDR ...min=0.0,max=65535.0 for all channels.

si = ch.getStatsInfo()
if si is None:
si = StatsInfoI()
else:
si = si._obj
if minmax[0] is not None:
si.globalMin = rdouble(minmax[0])
if minmax[1] is not None:
si.globalMax = rdouble(minmax[1])
ch._obj.statsInfo = si
ch.save()

if def_z:
img.setDefaultZ(def_z - 1)
if def_t:
Expand Down
25 changes: 25 additions & 0 deletions test/integration/clitest/test_render.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,6 +214,11 @@ def assert_channel_rdef(self, channel, rdef, version=2):
end = 'end' if version > 1 else 'max'
assert channel.getWindowStart() == rdef.get(start, 0)
assert channel.getWindowEnd() == rdef.get(end, 255)
if version > 1:
if rdef.get('min') is not None:
assert channel.getWindowMin() == rdef.get('min')
if rdef.get('max') is not None:
assert channel.getWindowMax() == rdef.get('max')

def assert_image_rmodel(self, img, greyscale):
assert img.isGreyscaleRenderingModel() == greyscale
Expand Down Expand Up @@ -291,6 +296,26 @@ def test_set_windows(self, version, windows, tmpdir):
self.cli.invoke(self.args, strict=True)
self.assert_target_rdef(self.idonly, rd)

def test_set_minmax(self, tmpdir):
self.create_image()
rd = self.get_render_def(version=2)
# Need to set min AND max or neither (StatsInfo needs both)...
rd['channels'][1]['min'] = 50
rd['channels'][1]['max'] = 200
rd['channels'][2]['min'] = 0
rd['channels'][2]['max'] = 500
rdfile = tmpdir.join('render-test.json')
rdfile.write(json.dumps(rd))
self.args += ["set", self.idonly, str(rdfile)]
self.cli.invoke(self.args, strict=True)
self.assert_target_rdef(self.idonly, rd)
# But it is possible to update min OR max alone
rd = self.get_render_def(version=2)
rd['channels'][1]['min'] = 25
rdfile.write(json.dumps(rd))
self.cli.invoke(self.args, strict=True)
self.assert_target_rdef(self.idonly, rd)

@pytest.mark.parametrize('target_name', sorted(SUPPORTED))
@pytest.mark.parametrize('sizec', [1, 2])
def test_set_target(self, target_name, sizec, tmpdir):
Expand Down