-
Notifications
You must be signed in to change notification settings - Fork 10
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Parallelize the set command #33
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -23,6 +23,7 @@ | |
from builtins import str | ||
from builtins import range | ||
from builtins import object | ||
import concurrent.futures | ||
import sys | ||
import time | ||
import json | ||
|
@@ -396,6 +397,11 @@ def _configure(self, parser): | |
help="Local file or OriginalFile:ID which specifies the " | ||
"rendering settings") | ||
|
||
set_cmd.add_argument( | ||
"--batch", type=int, default=1, | ||
help="Batch size to process simultaneously" | ||
) | ||
|
||
test.add_argument( | ||
"--force", action="store_true", | ||
help="Force creation of pixel data file in binary " | ||
|
@@ -414,7 +420,7 @@ def _lookup(self, gateway, type, oid): | |
self.ctx.die(110, "No such %s: %s" % (type, oid)) | ||
return obj | ||
|
||
def render_images(self, gateway, object, batch=100): | ||
def load_images(self, gateway, object, batch=100): | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you explain your thoughts on this rename? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Because the method doesn't render any images, it just loads the images. I found the name |
||
""" | ||
Get the images. | ||
|
||
|
@@ -429,12 +435,12 @@ def render_images(self, gateway, object, batch=100): | |
|
||
if isinstance(object, list): | ||
for x in object: | ||
for rv in self.render_images(gateway, x, batch): | ||
for rv in self.load_images(gateway, x, batch): | ||
yield rv | ||
elif isinstance(object, Screen): | ||
scr = self._lookup(gateway, "Screen", object.id) | ||
for plate in scr.listChildren(): | ||
for rv in self.render_images(gateway, plate._obj, batch): | ||
for rv in self.load_images(gateway, plate._obj, batch): | ||
yield rv | ||
elif isinstance(object, Plate): | ||
plt = self._lookup(gateway, "Plate", object.id) | ||
|
@@ -455,7 +461,7 @@ def render_images(self, gateway, object, batch=100): | |
elif isinstance(object, Project): | ||
prj = self._lookup(gateway, "Project", object.id) | ||
for ds in prj.listChildren(): | ||
for rv in self.render_images(gateway, ds._obj, batch): | ||
for rv in self.load_images(gateway, ds._obj, batch): | ||
yield rv | ||
|
||
elif isinstance(object, Dataset): | ||
|
@@ -485,7 +491,7 @@ def render_images(self, gateway, object, batch=100): | |
def info(self, args): | ||
""" Implements the 'info' command """ | ||
first = True | ||
for img in self.render_images(self.gateway, args.object, batch=1): | ||
for img in self.load_images(self.gateway, args.object, batch=1): | ||
ro = RenderObject(img) | ||
if args.style == 'plain': | ||
self.ctx.out(ro) | ||
|
@@ -505,8 +511,8 @@ def info(self, args): | |
@gateway_required | ||
def copy(self, args): | ||
""" Implements the 'copy' command """ | ||
for src_img in self.render_images(self.gateway, args.object, batch=1): | ||
for targets in self.render_images(self.gateway, args.target): | ||
for src_img in self.load_images(self.gateway, args.object, batch=1): | ||
for targets in self.load_images(self.gateway, args.target): | ||
batch = dict() | ||
for target in targets: | ||
if target.id == src_img.id: | ||
|
@@ -549,13 +555,9 @@ def _generate_thumbs(self, images): | |
self.ctx.dbg("Image:%s got thumbnail in %2.2fs" % ( | ||
img.id, stop - start)) | ||
|
||
def _read_default_planes(self, img, data, ignore_errors=False): | ||
def _read_default_planes(self, img, def_z=None, def_t=None, ignore_errors=False): | ||
"""Read and validate the default planes""" | ||
|
||
# Read values from dictionary | ||
def_z = data['z'] if 'z' in data else None | ||
def_t = data['t'] if 't' in data else None | ||
|
||
# Minimal validation: default planes should be 1-indexed integers | ||
if (def_z is not None) and (def_z < 1 or int(def_z) != def_z): | ||
self.ctx.die( | ||
|
@@ -583,23 +585,78 @@ def _read_default_planes(self, img, data, ignore_errors=False): | |
def_t = None | ||
return (def_z, def_t) | ||
|
||
def _set_rend(self, def_z, def_t, img, cindices, greyscale, rangelist, | ||
colorlist, disable, skipthumbs): | ||
""" | ||
Set the rendering settings for one image | ||
:param def_z: The default z plane (int) | ||
:param def_t: The default t plane (int) | ||
:param img: The image (ImageWrapper) | ||
:param cindices: The channel indices (int list) | ||
:param greyscale: If the image is grey scale (boolean) | ||
:param rangelist: The range (min, max) (list of int lists) | ||
:param colorlist: The colors (list of hex strings) | ||
:param disable: Flag if other channels should be disabled (boolean) | ||
:param skipthumbs: Flag if the thumbnail generation should be skipped | ||
(boolean) | ||
:return: The image id (long) | ||
""" | ||
reactivatechannels = [] | ||
if not disable: | ||
# Calling set_active_channels will disable channels which | ||
# are not specified, have to keep track of them and | ||
# re-activate them later again | ||
imgchannels = img.getChannels() | ||
for ci, ch in enumerate(imgchannels, 1): | ||
if ci not in cindices and -ci not in cindices \ | ||
and ch.isActive(): | ||
reactivatechannels.append(ci) | ||
|
||
img.set_active_channels( | ||
cindices, windows=rangelist, colors=colorlist) | ||
if greyscale is not None: | ||
if greyscale: | ||
img.setGreyscaleRenderingModel() | ||
else: | ||
img.setColorRenderingModel() | ||
|
||
if len(reactivatechannels) > 0: | ||
img.set_active_channels(reactivatechannels) | ||
|
||
if def_z: | ||
img.setDefaultZ(def_z - 1) | ||
if def_t: | ||
img.setDefaultT(def_t - 1) | ||
|
||
try: | ||
img.saveDefaults() | ||
self.ctx.dbg( | ||
"Updated rendering settings for Image:%s" % img.id) | ||
if not skipthumbs: | ||
img.getThumbnail(size=(96,), direct=False, use_cached_ts=False) | ||
except Exception as e: | ||
self.ctx.err('ERROR: %s' % e) | ||
finally: | ||
img._closeRE() | ||
return img.id | ||
|
||
@gateway_required | ||
def set(self, args): | ||
""" Implements the 'set' command """ | ||
newchannels = {} | ||
data = pydict_text_io.load( | ||
settings = pydict_text_io.load( | ||
args.channels, session=self.client.getSession()) | ||
if 'channels' not in data: | ||
if 'channels' not in settings: | ||
self.ctx.die(104, "ERROR: No channels found in %s" % args.channels) | ||
|
||
version = _getversion(data) | ||
version = _getversion(settings) | ||
if version == 0: | ||
self.ctx.die(124, "ERROR: Cannot determine version. Specify" | ||
" version or use either start/end or min/max" | ||
" (not both).") | ||
|
||
# Read channel setttings from rendering dictionary | ||
for chindex, chdict in data['channels'].items(): | ||
for chindex, chdict in settings['channels'].items(): | ||
try: | ||
cindex = int(chindex) | ||
except Exception as e: | ||
|
@@ -617,15 +674,19 @@ def set(self, args): | |
105, "Invalid channel description: %s" % chdict) | ||
|
||
try: | ||
greyscale = data['greyscale'] | ||
print('greyscale=%s' % data['greyscale']) | ||
greyscale = settings['greyscale'] | ||
print('greyscale=%s' % settings['greyscale']) | ||
except KeyError: | ||
greyscale = None | ||
|
||
# Read values from dictionary | ||
def_z = settings['z'] if 'z' in settings else None | ||
def_t = settings['t'] if 't' in settings else None | ||
|
||
namedict = {} | ||
cindices = [] | ||
rangelist = [] | ||
colourlist = [] | ||
colorlist = [] | ||
for (i, c) in newchannels.items(): | ||
if c.label: | ||
namedict[i] = c.label | ||
|
@@ -634,56 +695,29 @@ def set(self, args): | |
else: | ||
cindices.append(i) | ||
rangelist.append([c.start, c.end]) | ||
colourlist.append(c.color) | ||
colorlist.append(c.color) | ||
|
||
iids = [] | ||
for img in self.render_images(self.gateway, args.object, batch=1): | ||
iids.append(img.id) | ||
|
||
(def_z, def_t) = self._read_default_planes( | ||
img, data, ignore_errors=args.ignore_errors) | ||
|
||
reactivatechannels = [] | ||
if not args.disable: | ||
# Calling set_active_channels will disable channels which | ||
# are not specified, have to keep track of them and | ||
# re-activate them later again | ||
imgchannels = img.getChannels() | ||
for ci, ch in enumerate(imgchannels, 1): | ||
if ci not in cindices and -ci not in cindices\ | ||
and ch.isActive(): | ||
reactivatechannels.append(ci) | ||
|
||
img.set_active_channels( | ||
cindices, windows=rangelist, colors=colourlist) | ||
if greyscale is not None: | ||
if greyscale: | ||
img.setGreyscaleRenderingModel() | ||
else: | ||
img.setColorRenderingModel() | ||
|
||
if len(reactivatechannels) > 0: | ||
img.set_active_channels(reactivatechannels) | ||
|
||
if def_z: | ||
img.setDefaultZ(def_z - 1) | ||
if def_t: | ||
img.setDefaultT(def_t - 1) | ||
|
||
try: | ||
img.saveDefaults() | ||
self.ctx.dbg( | ||
"Updated rendering settings for Image:%s" % img.id) | ||
if not args.skipthumbs: | ||
self._generate_thumbs([img]) | ||
except Exception as e: | ||
self.ctx.err('ERROR: %s' % e) | ||
finally: | ||
img._closeRE() | ||
|
||
if not iids: | ||
self.ctx.die(113, "ERROR: No images found for %s %d" % | ||
(args.object.__class__.__name__, args.object.id._val)) | ||
n_images = 0 | ||
for img_batch in self.load_images(self.gateway, args.object, | ||
batch=args.batch): | ||
n_images += len(img_batch) | ||
with concurrent.futures.ThreadPoolExecutor() as executor: | ||
future_image = {executor.submit(self._set_rend, def_z, | ||
def_t, img, cindices, greyscale, | ||
rangelist, colorlist, | ||
args.disable, args.skipthumbs): | ||
img for img in img_batch} | ||
for future in concurrent.futures.as_completed(future_image): | ||
img = future_image[future] | ||
try: | ||
iid = future.result() | ||
iids.append(iid) | ||
except Exception as exc: | ||
self.ctx.err('ERROR: Image %d: %s' % (img.id, exc)) | ||
|
||
if len(iids) < n_images: | ||
self.ctx.die(113, "Some errors occurred.") | ||
|
||
if namedict: | ||
self._update_channel_names(self.gateway, iids, namedict) | ||
|
@@ -695,7 +729,7 @@ def edit(self, args): | |
def test(self, args): | ||
""" Implements the 'test' command """ | ||
self.gateway.SERVICE_OPTS.setOmeroGroup('-1') | ||
for img in self.render_images(self.gateway, args.object, batch=1): | ||
for img in self.load_images(self.gateway, args.object, batch=1): | ||
self.test_per_pixel( | ||
self.client, img.getPrimaryPixels().id, args.force, args.thumb) | ||
|
||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have you considered a higher default?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could do. Just didn't want to change the default behaviour if the new option is omitted.