Mercurial > repos > iuc > idr_download_by_ids
view idr_download_by_ids.py @ 5:e08b1dc0480c draft
"planemo upload for repository https://github.com/galaxyproject/tools-iuc/tree/master/tools/idr_download commit 5b2ba6c849aa344470fe1bb25f063dc3f8f790ae"
author | iuc |
---|---|
date | Mon, 08 Jun 2020 08:36:41 -0400 |
parents | 11036f6197d6 |
children | 5c743356df83 |
line wrap: on
line source
import argparse import os import sys import tarfile import time from tempfile import TemporaryFile from libtiff import TIFF from PIL import Image from omero.gateway import BlitzGateway # noqa from omero.constants.namespaces import NSBULKANNOTATIONS # noqa def warn(message, image_identifier, warn_skip=False): message = message.rstrip() if warn_skip: if message[-1] in ['.', '!', '?']: skip_msg = ' Skipping download!' else: skip_msg = '. Skipping download!' else: skip_msg = '' print( 'ImageSpecWarning for {0}: {1}{2}' .format( image_identifier, message, skip_msg ), file=sys.stderr ) def find_channel_index(image, channel_name): channel_name = channel_name.lower() for n, channel in enumerate(image.getChannelLabels()): if channel_name == channel.lower(): return n # Check map annotation for information (this is necessary for some images) for ann in image.listAnnotations(NSBULKANNOTATIONS): pairs = ann.getValue() for p in pairs: if p[0] == "Channels": channels = p[1].replace(" ", "").split(";") for n, c in enumerate(channels): for value in c.split(':'): if channel_name == value.lower(): return n return -1 def get_clipping_region(image, x, y, w, h): # If the (x, y) coordinate falls outside the image boundaries, we # cannot just shift it because that would render the meaning of # w and h undefined (should width and height be decreased or the whole # region be shifted to keep them fixed?). # It may be better to abort in this situation. if x < 0 or y < 0: raise ValueError( 'Too small upper left coordinate ({0}, {1}) for clipping region.' .format(x, y) ) size_x = image.getSizeX() size_y = image.getSizeY() if x >= size_x or y >= size_y: raise ValueError( 'Upper left coordinate ({0}, {1}) of clipping region lies ' 'outside of image.' .format(x, y) ) # adjust width and height to the image dimensions if w <= 0 or x + w > size_x: w = size_x - x if h <= 0 or y + h > size_y: h = size_y - y return [x, y, w, h] def confine_plane(image, z): if z < 0: z = 0 else: max_z = image.getSizeZ() - 1 if z > max_z: z = max_z return z def confine_frame(image, t): if t < 0: t = 0 else: max_t = image.getSizeT() - 1 if t > max_t: t = max_t return t def get_image_array(image, tile, z, c, t): pixels = image.getPrimaryPixels() try: selection = pixels.getTile(theZ=z, theT=t, theC=c, tile=tile) except Exception: warning = '{0} (ID: {1})'.format(image.getName(), image.getId()) warn('Could not download the requested region', warning) return return selection def download_image_data( image_ids, channel=None, z_stack=0, frame=0, coord=(0, 0), width=0, height=0, region_spec='rectangle', skip_failed=False, download_tar=False ): # basic argument sanity checks and adjustments prefix = 'image-' # normalize image ids by stripping off prefix if it exists image_ids = [ iid[len(prefix):] if iid[:len(prefix)] == prefix else iid for iid in image_ids ] if region_spec not in ['rectangle', 'center']: raise ValueError( 'Got unknown value "{0}" as region_spec argument' .format(region_spec) ) # connect to idr conn = BlitzGateway('public', 'public', host='idr.openmicroscopy.org', secure=True) conn.connect() if download_tar: # create an archive file to write images to archive = tarfile.open('images.tar', mode='w') try: for image_id in image_ids: image_warning_id = 'Image-ID: {0}'.format(image_id) try: image_id = int(image_id) except ValueError: image = None else: try: image = conn.getObject("Image", image_id) except Exception as e: # respect skip_failed on unexpected errors if skip_failed: warn(str(e), image_warning_id, warn_skip=True) continue else: raise if image is None: if skip_failed: warn( 'Unable to find an image with this ID in the ' 'database.', image_warning_id, warn_skip=True ) continue raise ValueError( '{0}: Unable to find an image with this ID in the ' 'database. Aborting!' .format(image_warning_id) ) try: # try to extract image properties # if anything goes wrong here skip the image # or abort. image_name = os.path.splitext(image.getName())[0] image_warning_id = '{0} (ID: {1})'.format( image_name, image_id ) if region_spec == 'rectangle': tile = get_clipping_region(image, *coord, width, height) elif region_spec == 'center': tile = get_clipping_region( image, *_center_to_ul(*coord, width, height) ) ori_z, z_stack = z_stack, confine_plane(image, z_stack) ori_frame, frame = frame, confine_frame(image, frame) num_channels = image.getSizeC() if channel is None: channel_index = 0 else: channel_index = find_channel_index(image, channel) except Exception as e: # respect skip_failed on unexpected errors if skip_failed: warn(str(e), image_warning_id, warn_skip=True) continue else: raise # region sanity checks and warnings if tile[2] < width or tile[3] < height: # The downloaded image region will have smaller dimensions # than the specified width x height. warn( 'Downloaded image dimensions ({0} x {1}) will be smaller ' 'than the specified width and height ({2} x {3}).' .format(tile[2], tile[3], width, height), image_warning_id ) # z-stack sanity checks and warnings if z_stack != ori_z: warn( 'Specified image plane ({0}) is out of bounds. Using {1} ' 'instead.' .format(ori_z, z_stack), image_warning_id ) # frame sanity checks and warnings if frame != ori_frame: warn( 'Specified image frame ({0}) is out of bounds. Using ' 'frame {1} instead.' .format(ori_frame, frame), image_warning_id ) # channel index sanity checks and warnings if channel is None: if num_channels > 1: warn( 'No specific channel selected for multi-channel ' 'image. Using first of {0} channels.' .format(num_channels), image_warning_id ) else: if channel_index == -1 or channel_index >= num_channels: if skip_failed: warn( str(channel) + ' is not a known channel name for this image.', image_warning_id, warn_skip=True ) continue else: raise ValueError( '"{0}" is not a known channel name for image {1}. ' 'Aborting!' .format(channel, image_warning_id) ) # download and save the region as TIFF fname = '__'.join( [image_name, str(image_id)] + [str(x) for x in tile] ) try: if fname[-5:] != '.tiff': fname += '.tiff' fname = fname.replace(' ', '_') im_array = get_image_array(image, tile, z_stack, channel_index, frame) # pack images into tarball if download_tar: tar_img = Image.fromarray(im_array) # Use TemporaryFile() for intermediate storage of images. # TO DO: could this be improved by using # SpooledTemporaryFile with a suitable max_size? with TemporaryFile() as buf: tar_img.save(buf, format='TIFF') tarinfo = tarfile.TarInfo(name=fname) buf.seek(0, 2) tarinfo.size = buf.tell() tarinfo.mtime = time.time() buf.seek(0) archive.addfile(tarinfo=tarinfo, fileobj=buf) else: # save image as individual file try: tiff = TIFF.open(fname, mode='w') tiff.write_image(im_array) finally: tiff.close() except Exception as e: if skip_failed: # respect skip_failed on unexpected errors warn(str(e), image_warning_id, warn_skip=True) continue else: raise finally: # close the archive file,if it is created if download_tar: archive.close() # Close the connection conn.close() def _center_to_ul(center_x, center_y, width, height): if width > 0: ext_x = (width - 1) // 2 ul_x = max([center_x - ext_x, 0]) width = center_x + ext_x + 1 - ul_x else: ul_x = 0 if height > 0: ext_y = (height - 1) // 2 ul_y = max([center_y - ext_y, 0]) height = center_y + ext_y + 1 - ul_y else: ul_y = 0 return ul_x, ul_y, width, height if __name__ == "__main__": p = argparse.ArgumentParser() p.add_argument( 'image_ids', nargs='*', default=[], help='one or more IDR image ids for which to retrieve data (default: ' 'read ids from stdin).' ) p.add_argument( '-c', '--channel', help='name of the channel to retrieve data for ' '(note: the first channel of each image will be downloaded if ' 'left unspecified)' ) region = p.add_mutually_exclusive_group() region.add_argument( '--rectangle', nargs=4, type=int, default=argparse.SUPPRESS, help='specify a clipping region for the image as x y width height, ' 'where x and y give the upper left coordinate of the rectangle ' 'to clip to. Set width and height to 0 to extend the rectangle ' 'to the actual size of the image.' ) region.add_argument( '--center', nargs=4, type=int, default=argparse.SUPPRESS, help='specify a clipping region for the image as x y width height, ' 'where x and y define the center of a width x height rectangle. ' 'Set either width or height to 0 to extend the region to the ' 'actual size of the image along the x- or y-axis.\n' 'Note: Even values for width and height will be rounded down to ' 'the nearest odd number.' ) p.add_argument( '-f', '--frame', type=int, default=0 ) p.add_argument( '-z', '--z-stack', type=int, default=0 ) p.add_argument( '--skip-failed', action='store_true' ) p.add_argument( '--download-tar', action='store_true' ) args = p.parse_args() if not args.image_ids: args.image_ids = sys.stdin.read().split() if 'center' in args: args.coord, args.width, args.height = ( args.center[:2], args.center[2], args.center[3] ) args.region_spec = 'center' del args.center elif 'rectangle' in args: args.coord, args.width, args.height = ( args.rectangle[:2], args.rectangle[2], args.rectangle[3] ) args.region_spec = 'rectangle' del args.rectangle download_image_data(**vars(args))