Example #1
0
    def dryrun(self, token, arg):
        level=0; tnx=0; tny=0; tsz=512;
        vs = arg.split(',', 4)
        if len(vs)>0 and vs[0].isdigit(): level = int(vs[0])
        if len(vs)>1 and vs[1].isdigit(): tnx = int(vs[1])
        if len(vs)>2 and vs[2].isdigit(): tny = int(vs[2])
        if len(vs)>3 and vs[3].isdigit(): tsz = int(vs[3])

        dims = token.dims or {}
        width = dims.get('image_num_x', 0)
        height = dims.get('image_num_y', 0)
        if width<=tsz and height<=tsz:
            log.debug('Dryrun tile: Image is smaller than requested tile size, passing the whole image...')
            return token

        x = tnx * tsz
        y = tny * tsz
        if x>=width or y>=height:
            raise ImageServiceException(400, 'Tile: tile position outside of the image: %s,%s'%(tnx, tny))

        # the new tile service does not change the number of z points in the image and if contains all z will perform the operation
        info = {
            'image_num_x': tsz if width-x >= tsz else width-x,
            'image_num_y': tsz if height-y >= tsz else height-y,
            #'image_num_z': 1,
            #'image_num_t': 1,
        }

        base_name = '%s.tiles'%(token.data)
        ofname    = os.path.join(base_name, '%s_%.3d_%.3d_%.3d' % (tsz, level, tnx, tny))
        return token.setImage(ofname, fmt=default_format, dims=info)
Example #2
0
    def action(self, token, arg):
        ang = arg.lower()
        angles = [
            '0', '90', '-90', '270', '180', 'guess', 'flip_ud', 'mirror_lr'
        ]
        if ang == '270':
            ang = '-90'
        if ang not in angles:
            raise ImageServiceException(
                400, 'rotate: angle value not yet supported')

        ifile = token.first_input_file()
        ofile = '%s.rotated_%s' % (token.data, ang)
        log.debug('Rotate %s: %s to %s', token.resource_id, ifile, ofile)
        if ang == '0':
            ofile = ifile

        dims = token.dims or {}
        w, h = compute_rotated_size(int(dims.get('image_num_x', 0)),
                                    int(dims.get('image_num_y', 0)), ang)
        info = {
            'image_num_x': w,
            'image_num_y': h,
        }
        command = ['-rotate', ang]
        if ang == 'flip_ud':
            command = ['-flip']
        if ang == 'mirror_lr':
            command = ['-mirror']
        return self.server.enqueue(token,
                                   'rotate',
                                   ofile,
                                   fmt=default_format,
                                   command=command,
                                   dims=info)
Example #3
0
    def action(self, token, arg):
        arg = arg.lower()
        if arg not in ['min', 'max']:
            raise ImageServiceException(
                400,
                'IntensityProjection: parameter must be either "max" or "min"')

        ifile = token.first_input_file()
        ofile = '%s.iproject_%s' % (token.data, arg)
        log.debug('IntensityProjection %s: %s to %s with [%s]',
                  token.resource_id, ifile, ofile, arg)

        if arg == 'max':
            command = ['-projectmax']
        else:
            command = ['-projectmin']

        info = {
            'image_num_z': 1,
            'image_num_t': 1,
        }
        return self.server.enqueue(token,
                                   'intensityprojection',
                                   ofile,
                                   fmt=default_format,
                                   command=command,
                                   dims=info)
Example #4
0
    def action(self, token, arg):
        if not token.isFile():
            raise ImageServiceException(
                400, 'Rearrange3D: input is not an image...')
        log.debug('Rearrange3D %s: %s', token.resource_id, arg)
        arg = arg.lower()

        if arg not in ['xzy', 'yzx']:
            raise ImageServiceException(
                400, 'Rearrange3D: method is unsupported: [%s]' % arg)

        # if the image must be 3D, either z stack or t series
        dims = token.dims or {}
        x = dims['image_num_x']
        y = dims['image_num_y']
        z = dims['image_num_z']
        t = dims['image_num_t']
        if (z > 1 and t > 1) or (z == 1 and t == 1):
            raise ImageServiceException(
                400, 'Rearrange3D: only supports 3D images')

        nz = y if arg == 'xzy' else x
        info = {
            'image_num_x': x if arg == 'xzy' else y,
            'image_num_y': z,
            'image_num_z': nz if z > 1 else 1,
            'image_num_t': nz if t > 1 else 1,
        }
        ifile = token.first_input_file()
        ofile = '%s.rearrange3d_%s' % (token.data, arg)
        command = token.drainQueue()
        if not os.path.exists(ofile):
            command.extend(['-rearrange3d', '%s' % arg])
            # dima: fix the case of a fliped output for
            if arg == 'yzx':
                command.extend(['-flip'])
            self.server.imageconvert(token,
                                     ifile,
                                     ofile,
                                     fmt=default_format,
                                     extra=command)

        return token.setImage(ofile,
                              fmt=default_format,
                              dims=info,
                              input=ofile)
Example #5
0
    def action(self, token, arg):
        method = 'a'
        num_channels = 3
        arg = arg.lower()

        if arg == 'display':
            args = ['-fusemeta']
            argenc = 'display'
        elif arg == 'gray' or arg == 'grey':
            args = ['-fusegrey']
            num_channels = 1
            argenc = 'gray'
        else:
            args = ['-fusergb', arg]

            if ':' in arg:
                (arg, method) = arg.split(':', 1)
            elif '.' in arg:
                (arg, method) = arg.split('.', 1)
            argenc = ''.join([
                hex(int(i)).replace('0x', '')
                for i in arg.replace(';', ',').split(',') if i is not ''
            ])

            # test if all channels are valid
            dims = token.dims or {}
            img_num_c = misc.safeint(dims.get('image_num_c'), 0)
            groups = [i for i in arg.split(';') if i is not '']
            # if there are more groups than input channels
            if len(groups) > img_num_c:
                groups = groups[img_num_c:]
                for i, g in enumerate(groups):
                    channels = [misc.safeint(i, 0) for i in g.split(',')]
                    # we can skip 0 weighted channels even if they are outside of the image channel range
                    if max(channels) > 0:
                        raise ImageServiceException(
                            400,
                            'Fuse: requested fusion of channel outside bounds %s>%s (%s)'
                            % (img_num_c + i, img_num_c, g))

        if method != 'a':
            args.extend(['-fusemethod', method])

        info = {
            'image_num_c': num_channels,
        }

        ifile = token.first_input_file()
        ofile = '%s.fuse_%s_%s' % (token.data, argenc, method)
        log.debug('Fuse %s: %s to %s with [%s:%s]', token.resource_id, ifile,
                  ofile, arg, method)

        return self.server.enqueue(token,
                                   'fuse',
                                   ofile,
                                   fmt=default_format,
                                   command=args,
                                   dims=info)
Example #6
0
    def action(self, token, arg):
        arg = arg.lower()
        args = arg.split(',')
        transform = args[0]
        params = args[1:]
        ifile = token.first_input_file()
        ofile = '%s.transform_%s'%(token.data, arg)
        log.debug('Transform %s: %s to %s with [%s]', token.resource_id, ifile, ofile, arg)

        if not transform in transforms:
            raise ImageServiceException(400, 'transform: requested transform is not yet supported')

        dims = token.dims or {}
        for n,v in transforms[transform]['require'].iteritems():
            if v != dims.get(n):
                raise ImageServiceException(400, 'transform: input image is incompatible, %s must be %s but is %s'%(n, v, dims.get(n)) )

        extra = copy.deepcopy(transforms[transform]['command'])
        if len(params)>0:
            extra.extend([','.join(params)])
        info = copy.deepcopy(transforms[transform]['info'])
        return self.server.enqueue(token, 'transform', ofile, fmt=default_format, command=extra, dims=info)
Example #7
0
    def action(self, token, arg):
        arg = arg.lower() or 'avg'
        if arg not in ['odd', 'even', 'avg']:
            raise ImageServiceException(
                400,
                'Deinterlace: parameter must be either "odd", "even" or "avg"')
        ifile = token.first_input_file()
        ofile = '%s.deinterlace_%s' % (token.data, arg)
        log.debug('Deinterlace %s: %s to %s', token.resource_id, ifile, ofile)

        return self.server.enqueue(token,
                                   'deinterlace',
                                   ofile,
                                   fmt=default_format,
                                   command=['-deinterlace', arg])
Example #8
0
    def action(self, token, arg):
        ms = ['f', 'd', 't', 'e', 'c', 'n', 'hounsfield']
        ds = ['8', '16', '32', '64']
        fs = ['u', 's', 'f']
        fs_map = {
            'u': 'unsigned integer',
            's': 'signed integer',
            'f': 'floating point'
        }
        cm = ['cs', 'cc']
        granularity = ['patch', 'plane'] #, 'volume', 'whole']

        d='d'; m='8'; f='u'; c='cs'; g='plane'
        arg = arg.lower()
        args = arg.split(',')
        if len(args)>0: d = args[0]
        if len(args)>1: m = args[1]
        if len(args)>2: f = args[2] or 'u'
        if len(args)>3: c = args[3] or 'cs'
        if m == 'hounsfield':
            if len(args)>4: window_center = args[4] or None
            if len(args)>5: window_width = args[5] or None
        else:
            if len(args)>4: g = args[4] or None

        if d is None or d not in ds:
            raise ImageServiceException(400, 'Depth: depth is unsupported: %s'%d)
        if m is None or m not in ms:
            raise ImageServiceException(400, 'Depth: method is unsupported: %s'%m )
        if f is not None and f not in fs:
            raise ImageServiceException(400, 'Depth: format is unsupported: %s'%f )
        if c is not None and c not in cm:
            raise ImageServiceException(400, 'Depth: channel mode is unsupported: %s'%c )
        if g is not None and g not in granularity:
            raise ImageServiceException(400, 'Depth: granularity mode is unsupported: %s'%g )
        if m == 'hounsfield' and (window_center is None or window_width is None):
            raise ImageServiceException(400, 'Depth: hounsfield enhancement requires window center and width' )

        ifile = token.first_input_file()
        ofile = '%s.depth_%s'%(token.data, arg)
        log.debug('Depth %s: %s to %s with [%s]', token.resource_id, ifile, ofile, arg)

        extra=[]
        if m == 'hounsfield':
            extra.extend(['-hounsfield', '%s,%s,%s,%s'%(d,f,window_center,window_width)])
        else:
            extra.extend(['-depth', arg])

        if g == 'patch':
            token.histogram = None

        dims = {
            'image_pixel_depth': d,
            'image_pixel_format': fs_map[f],
        }
        return self.server.enqueue(token, 'depth', ofile, fmt=default_format, command=extra, dims=dims)
Example #9
0
    def action(self, token, arg):
        if not token.isFile():
            raise ImageServiceException(
                400, 'Pixelcounter: input is not an image...')
        arg = safeint(arg.lower(), 256) - 1
        ifile = token.first_input_file()
        ofile = '%s.pixelcounter_%s.xml' % (token.data, arg)
        log.debug('Pixelcounter %s: %s to %s with [%s]', token.resource_id,
                  ifile, ofile, arg)

        command = token.drainQueue()
        if not os.path.exists(ofile):
            command.extend(['-pixelcounts', str(arg)])
            self.server.imageconvert(token, ifile, ofile, extra=command)

        return token.setXmlFile(fname=ofile)
Example #10
0
    def action(self, token, arg):
        if not token.isFile():
            raise ImageServiceException(400,
                                        'Histogram: input is not an image...')
        ifile = token.first_input_file()
        ofile = '%s.histogram.xml' % (token.data)
        log.debug('Histogram %s: %s to %s', token.resource_id, ifile, ofile)

        command = token.drainQueue()
        if not os.path.exists(ofile):
            # use resolution level if available to find the best estimate for very large images
            command.extend(['-ohstxml', ofile])

            dims = token.dims or {}
            num_x = int(dims.get('image_num_x', 0))
            num_y = int(dims.get('image_num_y', 0))
            width = 1024  # image sizes good enough for histogram estimation
            height = 1024  # image sizes good enough for histogram estimation

            # if image has multiple resolution levels find the closest one and request it
            num_l = dims.get('image_num_resolution_levels', 1)
            if num_l > 1 and '-res-level' not in token.getQueue():
                try:
                    scales = [
                        float(i) for i in dims.get(
                            'image_resolution_level_scales', '').split(',')
                    ]
                    sizes = [(round(num_x * i), round(num_y * i))
                             for i in scales]
                    relatives = [
                        max(width / float(sz[0]), height / float(sz[1]))
                        for sz in sizes
                    ]
                    relatives = [i if i <= 1 else 0 for i in relatives]
                    level = relatives.index(max(relatives))
                    command.extend(['-res-level', str(level)])
                except (Exception):
                    pass

            self.server.imageconvert(token, ifile, None, extra=command)

        return token.setXmlFile(fname=ofile)
Example #11
0
    def action(self, token, arg):
        arg = arg.lower()
        args = arg.split(',')
        if len(args) < 1:
            raise ImageServiceException(
                400, 'Threshold: requires at least one parameter')
        method = 'both'
        if len(args) > 1:
            method = args[1]
        arg = '%s,%s' % (args[0], method)
        ifile = token.first_input_file()
        ofile = '%s.threshold_%s' % (token.data, arg)
        log.debug('Threshold %s: %s to %s with [%s]', token.resource_id, ifile,
                  ofile, arg)

        return self.server.enqueue(token,
                                   'threshold',
                                   ofile,
                                   fmt=default_format,
                                   command=['-threshold', arg])
Example #12
0
    def action(self, token, arg):
        if not arg:
            raise ImageServiceException(
                400, 'SampleFrames: no frames to skip provided')

        ifile = token.first_input_file()
        ofile = '%s.framessampled_%s' % (token.data, arg)
        log.debug('SampleFrames %s: %s to %s with [%s]', token.resource_id,
                  ifile, ofile, arg)

        info = {
            'image_num_z': 1,
            'image_num_t': int(token.dims.get('image_num_p', 0)) / int(arg),
        }
        return self.server.enqueue(token,
                                   'sampleframes',
                                   ofile,
                                   fmt=default_format,
                                   command=['-sampleframes', arg],
                                   dims=info)
Example #13
0
    def action(self, token, arg):
        if not token.isFile():
            raise ImageServiceException(400,
                                        'Resize3D: input is not an image...')
        log.debug('Resize3D %s: %s', token.resource_id, arg)

        #size = tuple(map(int, arg.split(',')))
        ss = arg.split(',')
        size = [0, 0, 0]
        method = 'TC'
        aspectRatio = ''
        maxBounding = False
        textAddition = ''

        if len(ss) > 0 and ss[0].isdigit():
            size[0] = int(ss[0])
        if len(ss) > 1 and ss[1].isdigit():
            size[1] = int(ss[1])
        if len(ss) > 2 and ss[2].isdigit():
            size[2] = int(ss[2])
        if len(ss) > 3:
            method = ss[3].upper()
        if len(ss) > 4:
            textAddition = ss[4].upper()

        if len(ss) > 4 and (textAddition == 'AR'):
            aspectRatio = ',AR'
        if len(ss) > 4 and (textAddition == 'MX'):
            maxBounding = True
            aspectRatio = ',AR'

        if size[0] <= 0 and size[1] <= 0 and size[2] <= 0:
            raise ImageServiceException(
                400, 'Resize3D: size is unsupported: [%s]' % arg)

        if method not in ['NN', 'TL', 'TC']:
            raise ImageServiceException(
                400, 'Resize3D: method is unsupported: [%s]' % arg)

        # if the image is smaller and MX is used, skip resize
        dims = token.dims or {}
        w = dims.get('image_num_x', 0)
        h = dims.get('image_num_y', 0)
        z = dims.get('image_num_z', 1)
        t = dims.get('image_num_t', 1)
        d = max(z, t)
        if w == size[0] and h == size[1] and d == size[2]:
            return token
        if maxBounding and w <= size[0] and h <= size[1] and d <= size[2]:
            return token
        if (z > 1 and t > 1) or (z == 1 and t == 1):
            raise ImageServiceException(400,
                                        'Resize3D: only supports 3D images')

        ifile = token.first_input_file()
        ofile = '%s.size3d_%d,%d,%d,%s,%s' % (token.data, size[0], size[1],
                                              size[2], method, textAddition)
        log.debug('Resize3D %s: %s to %s', token.resource_id, ifile, ofile)

        width, height = compute_new_size(w, h, size[0], size[1],
                                         aspectRatio != '', maxBounding)
        zrestag = 'pixel_resolution_z' if z > 1 else 'pixel_resolution_t'
        info = {
            'image_num_x':
            width,
            'image_num_y':
            height,
            'image_num_z':
            size[2] if z > 1 else 1,
            'image_num_t':
            size[2] if t > 1 else 1,
            'pixel_resolution_x':
            dims.get('pixel_resolution_x', 0) * (w / float(width)),
            'pixel_resolution_y':
            dims.get('pixel_resolution_y', 0) * (h / float(height)),
            zrestag:
            dims.get(zrestag, 0) * (d / float(size[2])),
        }
        command = token.drainQueue()
        if not os.path.exists(ofile):
            # if image has multiple resolution levels find the closest one and request it
            num_l = dims.get('image_num_resolution_levels', 1)
            if num_l > 1 and '-res-level' not in command:
                try:
                    scales = [
                        float(i) for i in dims.get(
                            'image_resolution_level_scales', '').split(',')
                    ]
                    #log.debug('scales: %s',  scales)
                    sizes = [(round(w * i), round(h * i)) for i in scales]
                    #log.debug('scales: %s',  sizes)
                    relatives = [
                        max(size[0] / float(sz[0]), size[1] / float(sz[1]))
                        for sz in sizes
                    ]
                    #log.debug('relatives: %s',  relatives)
                    relatives = [i if i <= 1 else 0 for i in relatives]
                    #log.debug('relatives: %s',  relatives)
                    level = relatives.index(max(relatives))
                    command.extend(['-res-level', str(level)])
                except (Exception):
                    pass

            command.extend([
                '-resize3d',
                '%s,%s,%s,%s%s' %
                (size[0], size[1], size[2], method, aspectRatio)
            ])
            self.server.imageconvert(token,
                                     ifile,
                                     ofile,
                                     fmt=default_format,
                                     extra=command)

        return token.setImage(ofile,
                              fmt=default_format,
                              dims=info,
                              input=ofile)
Example #14
0
    def action(self, token, arg):
        log.debug('Resize %s: %s', token.resource_id, arg)

        #size = tuple(map(int, arg.split(',')))
        ss = arg.split(',')
        size = [0, 0]
        method = 'BL'
        aspectRatio = ''
        maxBounding = False
        textAddition = ''

        if len(ss) > 0 and ss[0].isdigit():
            size[0] = int(ss[0])
        if len(ss) > 1 and ss[1].isdigit():
            size[1] = int(ss[1])
        if len(ss) > 2:
            method = ss[2].upper()
        if len(ss) > 3:
            textAddition = ss[3].upper()

        if len(ss) > 3 and (textAddition == 'AR'):
            aspectRatio = ',AR'
        if len(ss) > 3 and (textAddition == 'MX'):
            maxBounding = True
            aspectRatio = ',AR'

        if size[0] <= 0 and size[1] <= 0:
            raise ImageServiceException(
                400, 'Resize: size is unsupported: [%s]' % arg)

        if method not in ['NN', 'BL', 'BC']:
            raise ImageServiceException(
                400, 'Resize: method is unsupported: [%s]' % arg)

        # if the image is smaller and MX is used, skip resize
        dims = token.dims or {}
        num_x = int(dims.get('image_num_x', 0))
        num_y = int(dims.get('image_num_y', 0))
        if maxBounding and num_x <= size[0] and num_y <= size[1]:
            log.debug(
                'Resize: Max bounding resize requested on a smaller image, skipping...'
            )
            return token
        if token.dryrun != True and (num_x <= 0 or num_y <= 0):
            raise ImageServiceException(
                400, 'Resize: image improperly decoded, has side of 0px')
        if token.dryrun != True and (size[0] <= 0
                                     or size[1] <= 0) and (num_x <= 0
                                                           or num_y <= 0):
            raise ImageServiceException(
                400,
                'Resize: new image size cannot be guessed due to missing info')

        ifile = token.first_input_file()
        ofile = '%s.size_%d,%d,%s,%s' % (token.data, size[0], size[1], method,
                                         textAddition)
        args = [
            '-resize',
            '%s,%s,%s%s' % (size[0], size[1], method, aspectRatio)
        ]
        try:
            width = height = 1
            width, height = compute_new_size(num_x, num_y, size[0], size[1],
                                             aspectRatio != '', maxBounding)
        except ZeroDivisionError:
            if token.dryrun == True:
                log.warning(
                    'Resize warning while guessing size %s: [%sx%s] to [%sx%s]',
                    token.resource_id, num_x, num_y, width, height)
            else:
                raise ImageServiceException(
                    400,
                    'Resize: new image size cannot be guessed due to missing info'
                )

        log.debug('Resize %s: [%sx%s] to [%sx%s] for [%s] to [%s]',
                  token.resource_id, num_x, num_y, width, height, ifile, ofile)

        # if image has multiple resolution levels find the closest one and request it
        num_l = dims.get('image_num_resolution_levels', 1)
        if num_l > 1 and '-res-level' not in token.getQueue():
            try:
                scales = [
                    float(i) for i in dims.get('image_resolution_level_scales',
                                               '').split(',')
                ]
                #log.debug('scales: %s',  scales)
                sizes = [(round(num_x * i), round(num_y * i)) for i in scales]
                #log.debug('scales: %s',  sizes)
                relatives = [
                    max(width / float(sz[0]), height / float(sz[1]))
                    for sz in sizes
                ]
                #log.debug('relatives: %s',  relatives)
                relatives = [i if i <= 1 else 0 for i in relatives]
                #log.debug('relatives: %s',  relatives)
                level = relatives.index(max(relatives))
                args.extend(['-res-level', str(level)])
            except (Exception):
                pass

        info = {
            'image_num_x':
            width,
            'image_num_y':
            height,
            'pixel_resolution_x':
            dims.get('pixel_resolution_x', 0) * (num_x / float(width)),
            'pixel_resolution_y':
            dims.get('pixel_resolution_y', 0) * (num_y / float(height)),
        }
        return self.server.enqueue(token,
                                   'resize',
                                   ofile,
                                   fmt=default_format,
                                   command=args,
                                   dims=info)
Example #15
0
    def action(self, token, arg):
        '''arg = l,tnx,tny,tsz'''
        if not token.isFile():
            raise ImageServiceException(400, 'Tile: input is not an image...' )
        level=0; tnx=0; tny=0; tsz=512;
        vs = arg.split(',', 4)
        if len(vs)>0 and vs[0].isdigit(): level = int(vs[0])
        if len(vs)>1 and vs[1].isdigit(): tnx = int(vs[1])
        if len(vs)>2 and vs[2].isdigit(): tny = int(vs[2])
        if len(vs)>3 and vs[3].isdigit(): tsz = int(vs[3])
        log.debug( 'Tile: l:%d, tnx:%d, tny:%d, tsz:%d' % (level, tnx, tny, tsz) )

        # if input image is smaller than the requested tile size
        dims = token.dims or {}
        width = dims.get('image_num_x', 0)
        height = dims.get('image_num_y', 0)
        if width<=tsz and height<=tsz:
            log.debug('Image is smaller than requested tile size, passing the whole image...')
            return token

        # construct a sliced filename
        ifname    = token.first_input_file()
        base_name = '%s.tiles'%(token.data)
        _mkdir( base_name )
        ofname    = os.path.join(base_name, '%s_%.3d_%.3d_%.3d' % (tsz, level, tnx, tny))
        hist_name = os.path.join(base_name, '%s_histogram'%(tsz))

        # if input image does not contain tile pyramid, create one and pass it along
        if dims.get('image_num_resolution_levels', 0)<2 or dims.get('tile_num_x', 0)<1:
            pyramid = '%s.pyramid.tif'%(token.data)
            command = token.drainQueue()
            if not os.path.exists(pyramid):
                #command.extend(['-ohst', hist_name])
                command.extend(['-options', 'compression lzw tiles %s pyramid subdirs'%default_tile_size])
                log.debug('Generate tiled pyramid %s: from %s to %s with %s', token.resource_id, ifname, pyramid, command )
                r = self.server.imageconvert(token, ifname, pyramid, fmt=default_format, extra=command)
                if r is None:
                    raise ImageServiceException(500, 'Tile: could not generate pyramidal file' )
            # ensure the file was created
            with Locks(pyramid, failonread=(not block_tile_reads)) as l:
                if l.locked is False: # dima: never wait, respond immediately
                    fff = (width*height) / (10000*10000)
                    raise ImageServiceFuture((15*fff,30*fff))

            # compute the number of pyramidal levels
            # sz = max(width, height)
            # num_levels = math.ceil(math.log(sz, 2)) - math.ceil(math.log(min_level_size, 2)) + 1
            # scales = [1/float(pow(2,i)) for i in range(0, num_levels)]
            # info = {
            #     'image_num_resolution_levels': num_levels,
            #     'image_resolution_level_scales': ',',join([str(i) for i in scales]),
            #     'tile_num_x': default_tile_size,
            #     'tile_num_y': default_tile_size,
            #     'converter': ConverterImgcnv.name,
            # }

            # load the number of pyramidal levels from the file
            info2 = self.server.getImageInfo(filename=pyramid)
            info = {
                'image_num_resolution_levels': info2.get('image_num_resolution_levels'),
                'image_resolution_level_scales': info2.get('image_resolution_level_scales'),
                'tile_num_x': info2.get('tile_num_x'),
                'tile_num_y': info2.get('tile_num_y'),
                'converter': info2.get('converter'),
            }
            log.debug('Updating original input to pyramidal version %s: %s -> %s', token.resource_id, ifname, pyramid )
            token.setImage(ofname, fmt=default_format, dims=info, input=pyramid)
            ifname = pyramid


        # compute output tile size
        dims = token.dims or {}
        x = tnx * tsz
        y = tny * tsz
        if x>=width or y>=height:
            raise ImageServiceException(400, 'Tile: tile position outside of the image: %s,%s'%(tnx, tny))

        # the new tile service does not change the number of z points in the image and if contains all z will perform the operation
        info = {
            'image_num_x': tsz if width-x >= tsz else width-x,
            'image_num_y': tsz if height-y >= tsz else height-y,
            #'image_num_z': 1,
            #'image_num_t': 1,
        }

        #log.debug('Inside pyramid dims: %s', dims)
        #log.debug('Inside pyramid input: %s', token.first_input_file() )
        #log.debug('Inside pyramid data: %s', token.data )

        # extract individual tile from pyramidal tiled image
        if dims.get('image_num_resolution_levels', 0)>1 and dims.get('tile_num_x', 0)>0:
            # dima: maybe better to test converter, if imgcnv then enqueue, otherwise proceed with the converter path
            if dims.get('converter', '') == ConverterImgcnv.name:
                c = self.server.converters[ConverterImgcnv.name]
                r = c.tile(token, ofname, level, tnx, tny, tsz)
                if r is not None:
                    if not os.path.exists(hist_name):
                        # write the histogram file is missing
                        c.writeHistogram(token, ofnm=hist_name)
                # if decoder returned a list of operations for imgcnv to enqueue
                if isinstance(r, list):
                    #r.extend([ '-ihst', hist_name])
                    token.histogram = hist_name
                    return self.server.enqueue(token, 'tile', ofname, fmt=default_format, command=r, dims=info)

            # try other decoders to read tiles
            ofname = '%s.tif'%ofname
            if os.path.exists(ofname):
                return token.setImage(ofname, fmt=default_format, dims=info, hist=hist_name, input=ofname)
            else:
                r = None
                for n,c in self.server.converters.iteritems():
                    if n == ConverterImgcnv.name: continue
                    if callable( getattr(c, "tile", None) ):
                        r = c.tile(token, ofname, level, tnx, tny, tsz)
                        if r is not None:
                            if not os.path.exists(hist_name):
                                # write the histogram file if missing
                                c.writeHistogram(token, ofnm=hist_name)
                            return token.setImage(ofname, fmt=default_format, dims=info, hist=hist_name, input=ofname)

        raise ImageServiceException(500, 'Tile could not be extracted')
Example #16
0
    def run(cls, ifnm, ofnm, args, **kw):
        '''converts input filename into output using exact arguments as provided in args'''
        if not cls.installed:
            return None
        failonread = kw.get('failonread') or (not block_reads)
        tmp = None
        with Locks(ifnm, ofnm, failonexist=True) as l:
            if l.locked:  # the file is not being currently written by another process
                command = [cls.CONVERTERCOMMAND]
                command.extend(args)
                log.debug('Run dylib command: %s', misc.tounicode(command))
                proceed = True
                if ofnm is not None and os.path.exists(
                        ofnm) and os.path.getsize(ofnm) > 16:
                    if kw.get('nooverwrite', False) is True:
                        proceed = False
                        log.warning(
                            'Run: output exists before command [%s], skipping',
                            misc.tounicode(ofnm))
                    else:
                        log.warning(
                            'Run: output exists before command [%s], overwriting',
                            misc.tounicode(ofnm))
                if proceed is True:
                    #command, tmp = misc.start_nounicode_win(ifnm, command)
                    retcode, out = call_imgcnvlib(command)
                    #misc.end_nounicode_win(tmp)
                    if retcode == 100 or retcode == 101:  # some error in libbioimage, retry once
                        log.error('Libioimage retcode %s: retry once: %s',
                                  retcode, command)
                        retcode, out = call_imgcnvlib(command)
                    if retcode == 99:
                        # in case of a timeout
                        log.info('Run: timed-out for [%s]',
                                 misc.tounicode(command))
                        if ofnm is not None and os.path.exists(ofnm):
                            os.remove(ofnm)
                        raise ImageServiceException(
                            412, 'Requested timeout reached')
                    if retcode != 0:
                        log.info('Run: returned [%s] for [%s]', retcode,
                                 misc.tounicode(command))
                        return None
                    if ofnm is None:
                        return str(retcode)
                    # output file does not exist for some operations, like tiles
                    # tile command does not produce a file with this filename
                    # if not os.path.exists(ofnm):
                    #     log.error ('Run: output does not exist after command [%s]', ofnm)
                    #     return None
            elif l.locked is False:  # dima: never wait, respond immediately
                raise ImageServiceFuture((1, 15))

        # make sure the write of the output file have finished
        if ofnm is not None and os.path.exists(ofnm):
            with Locks(ofnm, failonread=failonread) as l:
                if l.locked is False:  # dima: never wait, respond immediately
                    raise ImageServiceFuture((1, 15))

        # safeguard for incorrectly converted files, sometimes only the tiff header can be written
        # empty lock files are automatically removed before by lock code
        if os.path.exists(
                ofnm) and os.path.getsize(ofnm) < cls.MINIMUM_FILE_SIZE:
            log.error(
                'Run: output file is smaller than %s bytes, probably an error, removing [%s]',
                cls.MINIMUM_FILE_SIZE, ofnm)
            os.remove(ofnm)
            return None
        return ofnm
Example #17
0
    def action(self, token, arg):
        if not token.isFile():
            raise ImageServiceException(400, 'Roi: input is not an image...')
        rois = []
        for a in arg.split(';'):
            vs = a.split(',', 4)
            x1 = int(vs[0]) if len(vs) > 0 and vs[0].isdigit() else 0
            y1 = int(vs[1]) if len(vs) > 1 and vs[1].isdigit() else 0
            x2 = int(vs[2]) if len(vs) > 2 and vs[2].isdigit() else 0
            y2 = int(vs[3]) if len(vs) > 3 and vs[3].isdigit() else 0
            rois.append((x1, y1, x2, y2))
        x1, y1, x2, y2 = rois[0]

        if x1 <= 0 and x2 <= 0 and y1 <= 0 and y2 <= 0:
            raise ImageServiceException(400, 'ROI: region is not provided')

        ifile = token.first_input_file()
        otemp = token.data
        ofile = '%s.roi_%d,%d,%d,%d' % (token.data, x1 - 1, y1 - 1, x2 - 1,
                                        y2 - 1)
        log.debug('ROI %s: %s to %s', token.resource_id, ifile, ofile)

        if len(rois) == 1:
            info = {
                'image_num_x': x2 - x1,
                'image_num_y': y2 - y1,
            }
            command = [
                '-roi',
                '%s,%s,%s,%s' % (x1 - 1, y1 - 1, x2 - 1, y2 - 1)
            ]
            return self.server.enqueue(token,
                                       'roi',
                                       ofile,
                                       fmt=default_format,
                                       command=command,
                                       dims=info)

        # remove pre-computed ROIs
        rois = [
            (_x1, _y1, _x2, _y2) for _x1, _y1, _x2, _y2 in rois
            if not os.path.exists('%s.roi_%d,%d,%d,%d' %
                                  (otemp, _x1 - 1, _y1 - 1, _x2 - 1, _y2 - 1))
        ]

        lfile = '%s.rois' % (otemp)
        command = token.drainQueue()
        if not os.path.exists(ofile) or len(rois) > 0:
            # global ROI lock on this input since we can't lock on all individual outputs
            with Locks(ifile, lfile, failonexist=True) as l:
                if l.locked:  # the file is not being currently written by another process
                    s = ';'.join([
                        '%s,%s,%s,%s' % (x1 - 1, y1 - 1, x2 - 1, y2 - 1)
                        for x1, y1, x2, y2 in rois
                    ])
                    command.extend(['-roi', s])
                    command.extend(
                        ['-template',
                         '%s.roi_{x1},{y1},{x2},{y2}' % otemp])
                    self.server.imageconvert(token,
                                             ifile,
                                             ofile,
                                             fmt=default_format,
                                             extra=command)
                    # ensure the virtual locking file is not removed
                    with open(lfile, 'wb') as f:
                        f.write('#Temporary locking file')
                elif l.locked is False:  # dima: never wait, respond immediately
                    raise ImageServiceFuture((1, 15))

        # ensure the operation is finished
        if os.path.exists(lfile):
            with Locks(lfile, failonread=(not block_reads)) as l:
                if l.locked is False:  # dima: never wait, respond immediately
                    raise ImageServiceFuture((1, 15))

        info = {
            'image_num_x': x2 - x1,
            'image_num_y': y2 - y1,
        }
        return token.setImage(ofile,
                              fmt=default_format,
                              dims=info,
                              input=ofile)
Example #18
0
    def action(self, token, arg):
        if not token.isFile():
            raise ImageServiceException(400,
                                        'Format: input is not an image...')

        args = arg.lower().split(',')
        fmt = default_format
        if len(args) > 0:
            fmt = args.pop(0).lower()

        stream = False
        if 'stream' in args:
            stream = True
            args.remove('stream')

        # avoid doing anything if requested format is in requested format
        dims = token.dims or {}
        if dims.get('format', '').lower() == fmt and not token.hasQueue():
            log.debug('%s: Input is in requested format, avoid reconvert...',
                      token.resource_id)
            return token

        if fmt not in self.server.writable_formats:
            raise ImageServiceException(
                400, 'Requested format [%s] is not writable' % fmt)

        name_extra = '' if len(args) <= 0 else '.%s' % '.'.join(args)
        ext = self.server.converters.defaultExtension(fmt)
        ifile = token.first_input_file()
        ofile = '%s.%s%s.%s' % (token.data, name_extra, fmt, ext)
        log.debug('Format %s: %s -> %s with %s opts=[%s]', token.resource_id,
                  ifile, ofile, fmt, args)

        if not os.path.exists(ofile):
            extra = token.drainQueue()
            queue_size = len(extra)
            if len(args) > 0:
                extra.extend(['-options', (' ').join(args)])
            elif fmt in ['jpg', 'jpeg']:
                extra.extend(['-options', 'quality 95 progressive yes'])

            r = None
            #if dims.get('converter', '') == ConverterImgcnv.name:

            # first try first converter that supports this output format
            c = self.server.writable_formats[fmt]
            first_name = c.name
            # if there are any operations to be run on the output
            if c.name == ConverterImgcnv.name or queue_size < 1:
                r = c.convert(token, ofile, fmt, extra=extra)

            # try using other converters directly
            if r is None:
                log.debug('%s could not convert [%s] to [%s] format' %
                          (first_name, ifile, fmt))
                log.debug('Trying other converters directly')
                for n, c in self.server.converters.iteritems():
                    if n == first_name:
                        continue
                    if n == ConverterImgcnv.name or queue_size < 1:
                        r = c.convert(token, ofile, fmt, extra=extra)
                    if r is not None and os.path.exists(ofile):
                        break

            # using ome-tiff as intermediate if everything failed
            if r is None:
                log.debug(
                    'None of converters could connvert [%s] to [%s] format' %
                    (ifile, fmt))
                log.debug('Converting to OME-TIFF and then to desired output')
                r = self.server.imageconvert(token,
                                             ifile,
                                             ofile,
                                             fmt=fmt,
                                             extra=extra,
                                             try_imgcnv=False)

            if r is None:
                log.error(
                    'Format %s: %s could not convert with [%s] format [%s] -> [%s]',
                    token.resource_id, c.CONVERTERCOMMAND, fmt, ifile, ofile)
                raise ImageServiceException(
                    415, 'Could not convert into %s format' % fmt)

        if stream:
            fpath = ofile.split('/')
            filename = '%s_%s.%s' % (token.resource_name,
                                     fpath[len(fpath) - 1], ext)
            token.setFile(fname=ofile)
            token.outFileName = filename
            token.input = ofile
        else:
            token.setImage(fname=ofile, fmt=fmt, input=ofile)

        # if (ofile != ifile) and (fmt != 'raw'):
        #     try:
        #         info = self.server.getImageInfo(filename=ofile)
        #         if int(info['image_num_p'])>1:
        #             if 'image_num_z' in token.dims: info['image_num_z'] = token.dims['image_num_z']
        #             if 'image_num_t' in token.dims: info['image_num_t'] = token.dims['image_num_t']
        #         token.dims = info
        #     except Exception:
        #         pass

        #log.debug('Token: %s', str(token))

        if (ofile != ifile):
            info = {
                'format': fmt,
            }
            if fmt == 'jpeg':
                info.update({
                    'image_pixel_depth':
                    8,
                    'image_pixel_format':
                    'unsigned integer',
                    'image_num_c':
                    min(4, int(dims.get('image_num_c', 0))),
                })
            elif fmt not in [
                    'tiff', 'bigtiff', 'ome-tiff', 'ome-bigtiff', 'raw'
            ]:
                info = self.server.getImageInfo(filename=ofile)
                info.update({
                    'image_num_z': dims.get('image_num_z', ''),
                    'image_num_t': dims.get('image_num_t', ''),
                })
            token.dims.update(info)

        return token
Example #19
0
    def slice(cls, token, ofnm, z, t, roi=None, **kw):
        '''extract Z,T plane from input filename into output in OME-TIFF format'''
        ifnm = token.first_input_file()
        series = token.series

        log.debug('Slice: z=%s t=%s roi=%s series=%s for [%s]', z, t, roi,
                  series, ifnm)
        z1, z2 = z
        t1, t2 = t
        x1, x2, y1, y2 = roi
        fmt = kw.get('fmt', 'bigtiff')
        info = token.dims or {}

        #command = ['-o', ofnm, '-t', fmt]
        command = []
        if token.series is not None and token.series != 0:
            command.extend(['-path', token.series])

        if t2 == 0:
            t2 = t1
        if z2 == 0:
            z2 = z1

        pages = []
        for ti in range(t1, t2 + 1):
            for zi in range(z1, z2 + 1):
                if info.get('image_num_t', 1) == 1:
                    page_num = zi
                elif info.get('image_num_z', 1) == 1:
                    page_num = ti
                elif info.get('dimensions', 'XYCZT').replace(
                        ' ', '').startswith('XYCT') is False:
                    page_num = (ti - 1) * info.get('image_num_z', 1) + zi
                else:
                    page_num = (zi - 1) * info.get('image_num_t', 1) + ti
                pages.append(page_num)

        log.debug('slice pages: %s', pages)

        # separate normal and multi-file series
        if token.is_multifile_series() is False:
            log.debug('Slice for single-file series')
            #command.extend(['-i', ifnm])
            command.extend(['-page', ','.join([str(p) for p in pages])])
        else:
            # use first image of the series, need to check for separate channels here
            log.debug('Slice for multi-file series')
            files = token.input
            meta = token.meta or {}
            channels = meta.get('image_num_c', 0)

            #log.debug('Slice for multi-file series: %s', files)
            #if len(pages)==1 and (x1==x2 or y1==y2) and channels<=1:
            if len(pages) == 1 and channels <= 1:
                # in multi-file case and only one page is requested with no ROI, return with no re-conversion
                #misc.dolink(files[pages[0]-1], ofnm)
                token.input = files[pages[0] - 1]
                #command.extend(['-i', token.input])
            else:
                # in case of many pages we might have to write input filenames as a file
                #fl = '%s.files'%ofnm
                #cls.write_files(files, fl)
                #command.extend(['-il', fl])
                #command.extend(['-page', ','.join([str(p) for p in pages])])

                if channels > 1:
                    # dima: since we are writing a non ome-tiff file, proper geometry is irrelevant but number of channels is
                    geom = '1,1,%s' % (channels)
                    command.extend(['-geometry', geom])
                    cpages = []
                    for p in [p - 1 for p in pages]:
                        for c in range(channels):
                            cpages.append(p * channels + c)
                    token.input = [files[p] for p in cpages]
                else:
                    token.input = [files[p - 1] for p in pages]

        # roi
        if not x1 == x2 or not y1 == y2:
            if not x1 == x2:
                if x1 > 0: x1 = x1 - 1
                if x2 > 0: x2 = x2 - 1
            if not y1 == y2:
                if y1 > 0: y1 = y1 - 1
                if y2 > 0: y2 = y2 - 1
            command.extend(['-roi', '%s,%s,%s,%s' % (x1, y1, x2, y2)])

        # other dimensions: -slice fov:345,rotation:23
        nd = []
        for k, v in kw.iteritems():
            if k in cls.extended_dimension_names:
                if len(v) > 1:
                    raise ImageServiceException(
                        responses.UNPROCESSABLE_ENTITY,
                        'Ranges in extended dimensions are not yet supported')
                nd.append('%s:%s' % (k, v[0]))
        if len(nd) > 0:
            command.extend(['-slice', ','.join(nd)])

        #return cls.run(ifnm, ofnm, command )
        return command
Example #20
0
    def action(self, token, arg):
        ifile = token.first_input_file()
        metacache = '%s.meta' % (token.data)
        log.debug('Meta: %s -> %s', ifile, metacache)

        if not os.path.exists(metacache):
            meta = {}
            if not os.path.exists(ifile):
                raise ImageServiceException(404,
                                            'Meta: Input file not found...')

            dims = token.dims or {}
            converter = None
            if dims.get('converter', None) in self.server.converters:
                converter = dims.get('converter')
                meta = self.server.converters[converter].meta(token)

            if meta is None:
                # exhaustively iterate over converters to find supporting one
                for c in self.server.converters.itervalues():
                    if c.name == dims.get('converter'): continue
                    meta = c.meta(token)
                    converter = c.name
                    if meta is not None and len(meta) > 0:
                        break

            if meta is None or len(meta) < 1:
                raise ImageServiceException(
                    415, 'Meta: file format is not supported...')

            # overwrite fileds forced by the fileds stored in the resource image_meta
            if token.meta is not None:
                meta.update(token.meta)
            meta['converter'] = converter
            if token.is_multifile_series() is True:
                meta['file_mode'] = 'multi-file'
            else:
                meta['file_mode'] = 'single-file'

            # construct an XML tree
            image = etree.Element('resource',
                                  uri='/%s/%s?meta' %
                                  (self.server.base_url, token.resource_id))
            tags_map = {}
            for k, v in meta.iteritems():
                if k.startswith('DICOM/'): continue
                k = safeunicode(k)
                v = safeunicode(v)
                tl = k.split('/')
                parent = image
                for i in range(0, len(tl)):
                    tn = '/'.join(tl[0:i + 1])
                    if not tn in tags_map:
                        tp = etree.SubElement(parent, 'tag', name=tl[i])
                        tags_map[tn] = tp
                        parent = tp
                    else:
                        parent = tags_map[tn]
                try:
                    parent.set('value', v)
                except ValueError:
                    pass

            if meta['format'] == 'DICOM':
                node = etree.SubElement(image, 'tag', name='DICOM')
                ConverterImgcnv.meta_dicom(ifile,
                                           series=token.series,
                                           token=token,
                                           xml=node)

            log.debug('Meta %s: storing metadata into %s', token.resource_id,
                      metacache)
            xmlstr = etree.tostring(image)
            with open(metacache, 'w') as f:
                f.write(xmlstr)
            return token.setXml(xmlstr)

        log.debug('Meta %s: reading metadata from %s', token.resource_id,
                  metacache)
        return token.setXmlFile(metacache)
Example #21
0
    def action(self, token, arg):
        '''arg = x1-x2,y1-y2,z|z1-z2,t|t1-t2
           or
           arg = z:V1-V2,fov:V,...
        '''

        if not token.isFile():
            raise ImageServiceException(400, 'Slice: input is not an image...')

        # parse arguments
        x1 = 0
        x2 = 0
        y1 = 0
        y2 = 0
        z1 = 0
        z2 = 0
        t1 = 0
        t2 = 0
        extended_dims = {}
        extended_str = ''
        if ':' not in arg:
            # the standard way of defining dimensions
            vs = [[safeint(i, 0) for i in vs.split('-', 1)]
                  for vs in arg.split(',')]
            for v in vs:
                if len(v) < 2: v.append(0)
            for v in range(len(vs) - 4):
                vs.append([0, 0])
            x1, x2 = vs[0]
            y1, y2 = vs[1]
            z1, z2 = vs[2]
            t1, t2 = vs[3]
        else:
            # the extended way of defining dimensions
            for a in arg.split(','):
                k, v = a.split(':', 1)
                if k not in self.dimension_names: continue
                extended_dims[k] = [safeint(i, 0) for i in v.split('-', 1)]
            # update x,y,z and t from the new style args
            x1, x2 = pop_dimension_range(extended_dims, 'x')
            y1, y2 = pop_dimension_range(extended_dims, 'y')
            z1, z2 = pop_dimension_range(extended_dims, 'z')
            t1, t2 = pop_dimension_range(extended_dims, 't')
            # update extended string part
            if len(extended_dims) > 0:
                # get a string of extended dimensions in the defined order
                ddd = [
                    get_dimension_string(extended_dims, k)
                    for k in self.dimension_names if k in extended_dims
                ]
                extended_str = '.%s' % (','.join(ddd))

        # in case slices request an exact copy, skip
        if x1 == 0 and x2 == 0 and y1 == 0 and y2 == 0 and z1 == 0 and z2 == 0 and t1 == 0 and t2 == 0 and len(
                extended_dims) < 1:
            return token

        dims = token.dims or {}
        if x1 <= 1 and x2 >= dims.get('image_num_x', 1):
            x1 = 0
            x2 = 0
        if y1 <= 1 and y2 >= dims.get('image_num_y', 1):
            y1 = 0
            y2 = 0
        if z1 <= 1 and z2 >= dims.get('image_num_z', 1):
            z1 = 0
            z2 = 0
        if t1 <= 1 and t2 >= dims.get('image_num_t', 1):
            t1 = 0
            t2 = 0

        # check image bounds
        if z1 > dims.get('image_num_z', 1):
            raise ImageServiceException(
                400,
                'Slice: requested Z slice outside of image bounds: [%s]' % z1)
        if z2 > dims.get('image_num_z', 1):
            raise ImageServiceException(
                400,
                'Slice: requested Z slice outside of image bounds: [%s]' % z2)
        if t1 > dims.get('image_num_t', 1):
            raise ImageServiceException(
                400,
                'Slice: requested T plane outside of image bounds: [%s]' % t1)
        if t2 > dims.get('image_num_t', 1):
            raise ImageServiceException(
                400,
                'Slice: requested T plane outside of image bounds: [%s]' % t2)

        # shortcuts are only possible with no ROIs are requested
        if x1 == x2 == 0 and y1 == y2 == 0:
            # shortcut if input image has only one T and Z
            if dims.get('image_num_z', 1) <= 1 and dims.get(
                    'image_num_t', 1) <= 1 and len(extended_dims) < 1:
                log.debug(
                    'Slice: plane requested on image with no T or Z planes, skipping...'
                )
                return token
            # shortcut if asking for all slices with only a specific time point in an image with only one time pont
            if z1 == z2 == 0 and t1 <= 1 and dims.get(
                    'image_num_t', 1) <= 1 and len(extended_dims) < 1:
                log.debug(
                    'Slice: T plane requested on image with no T planes, skipping...'
                )
                return token
            # shortcut if asking for all time points with only a specific z slice in an image with only one z slice
            if t1 == t2 == 0 and z1 <= 1 and dims.get(
                    'image_num_z', 1) <= 1 and len(extended_dims) < 1:
                log.debug(
                    'Slice: Z plane requested on image with no Z planes, skipping...'
                )
                return token

        if z1 == z2 == 0:
            z1 = 1
            z2 = dims.get('image_num_z', 1)
        if t1 == t2 == 0:
            t1 = 1
            t2 = dims.get('image_num_t', 1)

        # construct a sliced filename
        ifname = token.first_input_file()
        ofname = '%s.%d-%d,%d-%d,%d-%d,%d-%d%s.ome.tif' % (
            token.data, x1, x2, y1, y2, z1, z2, t1, t2, extended_str)
        log.debug('Slice %s: from [%s] to [%s]', token.resource_id, ifname,
                  ofname)

        new_w = x2 - x1
        new_h = y2 - y1
        new_z = max(1, z2 - z1 + 1)
        new_t = max(1, t2 - t1 + 1)
        info = {
            'image_num_z': new_z,
            'image_num_t': new_t,
        }
        if new_w > 0: info['image_num_x'] = new_w + 1
        if new_h > 0: info['image_num_y'] = new_h + 1

        meta = token.meta or {}
        unsupported_multifile = False
        if token.is_multifile_series() is True and (z2 == 0 or z2 == z1) and (
                t2 == 0 or t2 == t1) and x1 == x2 and y1 == y2 and meta.get(
                    'image_num_c', 0) == 0:
            unsupported_multifile = True

        if dims.get(
                'converter',
                '') == ConverterImgcnv.name or unsupported_multifile is True:
            r = ConverterImgcnv.slice(token,
                                      ofname,
                                      z=(z1, z2),
                                      t=(t1, t2),
                                      roi=(x1, x2, y1, y2),
                                      fmt=default_format,
                                      **extended_dims)
            # if decoder returned a list of operations for imgcnv to enqueue
            if isinstance(r, list):
                return self.server.enqueue(token,
                                           'slice',
                                           ofname,
                                           fmt=default_format,
                                           command=r,
                                           dims=info)

        # slice the image
        if not os.path.exists(ofname):
            intermediate = '%s.ome.tif' % token.data

            if 'converter' in dims and dims.get(
                    'converter') in self.server.converters:
                r = self.server.converters[dims.get('converter')].slice(
                    token,
                    ofname,
                    z=(z1, z2),
                    t=(t1, t2),
                    roi=(x1, x2, y1, y2),
                    fmt=default_format,
                    intermediate=intermediate)

            # if desired converter failed, perform exhaustive conversion
            if r is None:
                for n, c in self.server.converters.iteritems():
                    if n in [ConverterImgcnv.name, dims.get('converter')]:
                        continue
                    r = c.slice(token,
                                ofname,
                                z=(z1, z2),
                                t=(t1, t2),
                                roi=(x1, x2, y1, y2),
                                fmt=default_format,
                                intermediate=intermediate)
                    if r is not None:
                        break

            if r is None:
                log.error('Slice %s: could not generate slice for [%s]',
                          token.resource_id, ifname)
                raise ImageServiceException(415, 'Could not generate slice')

            # if decoder returned a list of operations for imgcnv to enqueue
            if isinstance(r, list):
                return self.server.enqueue(token,
                                           'slice',
                                           ofname,
                                           fmt=default_format,
                                           command=r,
                                           dims=info)

        return token.setImage(ofname,
                              fmt=default_format,
                              dims=info,
                              input=ofname)
Example #22
0
    def action(self, token, arg):
        ss = arg.split(',')
        size = [safeint(ss[0], 128) if len(ss)>0 else 128,
                safeint(ss[1], 128) if len(ss)>1 else 128]
        method = ss[2].upper() if len(ss)>2 and len(ss[2])>0 else 'BC'
        preproc = ss[3].lower() if len(ss)>3 and len(ss[3])>0 else ''
        preprocc = ',%s'%preproc if len(preproc)>0 else '' # attempt to keep the filename backward compatible
        fmt = ss[4].lower() if len(ss)>4 and len(ss[4])>0 else 'jpeg'

        if size[0]<=0 and size[1]<=0:
            raise ImageServiceException(400, 'Thumbnail: size is unsupported [%s]'%arg)

        if method not in ['NN', 'BL', 'BC']:
            raise ImageServiceException(400, 'Thumbnail: method is unsupported [%s]'%arg)

        if preproc not in ['', 'mid', 'mip', 'nip']:
            raise ImageServiceException(400, 'Thumbnail: method is unsupported [%s]'%arg)

        ext = self.server.converters.defaultExtension(fmt)
        ifile = token.first_input_file()
        ofile = '%s.thumb_%s,%s,%s%s.%s'%(token.data, size[0], size[1], method, preprocc, ext)

        dims = token.dims or {}
        num_x = int(dims.get('image_num_x', 0))
        num_y = int(dims.get('image_num_y', 0))

        try:
            width, height = compute_new_size(num_x, num_y, size[0], size[1], keep_aspect_ratio=True, no_upsample=True)
        except ZeroDivisionError:
            raise ImageServiceException(400, 'Thumbnail: new image size cannot be guessed due to missing info' )

        info = {
            'image_num_x': width,
            'image_num_y': height,
            'image_num_c': 3,
            'image_num_z': 1,
            'image_num_t': 1,
            'image_pixel_depth': 8,
            'format': fmt,
        }

        # if image can be decoded with imageconvert, enqueue
        if dims.get('converter', '') == ConverterImgcnv.name:
            r = ConverterImgcnv.thumbnail(token, ofile, size[0], size[1], method=method, preproc=preproc, fmt=fmt)
            if isinstance(r, list):
                return self.server.enqueue(token, 'thumbnail', ofile, fmt=fmt, command=r, dims=info)

        # if image requires other decoder
        if not os.path.exists(ofile):
            intermediate = '%s.ome.tif'%(token.data)

            r = None
            if 'converter' in dims and dims.get('converter') in self.server.converters:
                r = self.server.converters[dims.get('converter')].thumbnail(token, ofile, size[0], size[1], method=method, intermediate=intermediate, preproc=preproc, fmt=fmt)

            # if desired converter failed, perform exhaustive conversion
            if r is None:
                for n,c in self.server.converters.iteritems():
                    if n in [ConverterImgcnv.name, dims.get('converter')]: continue
                    r = c.thumbnail(token, ofile, size[0], size[1], method=method, intermediate=intermediate, preproc=preproc, fmt=fmt)
                    if r is not None:
                        break
            if r is None:
                log.error('Thumbnail %s: could not generate thumbnail for [%s]', token.resource_id, ifile)
                raise ImageServiceException(415, 'Could not generate thumbnail' )
            if isinstance(r, list):
                return self.server.enqueue(token, 'thumbnail', ofile, fmt=fmt, command=r, dims=info)

        return token.setImage(ofile, fmt=fmt, dims=info, input=ofile)