Source code for bioformats.formatreader

# Python-bioformats is distributed under the GNU General Public
# License, but this file is licensed under the more permissive BSD
# license.  See the accompanying file LICENSE for details.
#
# Copyright (c) 2009-2014 Broad Institute
# All rights reserved.

'''formatreader.py - mechanism to wrap a bioformats ReaderWrapper and ImageReader

Example:
    import bioformats.formatreader as biordr

    env = biordr.get_env()

    ChannelSeparator = biordr.make_reader_wrapper_class(env, 'loci/formats/ChannelSeparator')
    ImageReader = biordr.make_image_reader_class(env)

    cs = ChannelSeparator(ImageReader('/path/to/file.tif'))

    my_red_image, my_green_image, my_blue_image = \
        [cs.open_bytes(cs.getIndex(0,i,0)) for i in range(3)]

'''

from __future__ import absolute_import, unicode_literals

__version__ = "$Revision$"

import logging
logger = logging.getLogger(__name__)
import errno
import numpy as np
import os
import sys

if sys.version_info.major == 3:
    from urllib.request import urlopen, urlparse, url2pathname
    from urllib.parse import unquote
else:
    from urllib import url2pathname
    from urllib2 import urlopen, urlparse, unquote
    urlparse = urlparse.urlparse

import shutil
import tempfile
import traceback

import javabridge as jutil
import bioformats
from . import metadatatools as metadatatools
import javabridge as javabridge

OMERO_READER_IMPORTED = False
try:
    from omero_reader import OmeroReader, OMERO_IMPORTED
    from omero_reader.utils import omero_reader_enabled
    OMERO_READER_IMPORTED = True
except ImportError:
    pass

K_OMERO_SERVER = "omero_server"
K_OMERO_PORT = "omero_port"
K_OMERO_USER = "omero_user"
K_OMERO_SESSION_ID = "omero_session_id"
K_OMERO_CONFIG_FILE = "omero_config_file"
'''The cleartext password - only used if password is provided on command-line'''
K_OMERO_PASSWORD = "omero_password"

def make_format_tools_class():
    '''Get a wrapper for the loci/formats/FormatTools class

    The FormatTools class has many of the constants needed by
    other classes as statics.
    '''
    class FormatTools(object):
        '''A wrapper for loci.formats.FormatTools

        See http://hudson.openmicroscopy.org.uk/job/LOCI/javadoc/loci/formats/FormatTools.html
        '''
        env = jutil.get_env()
        klass = env.find_class('loci/formats/FormatTools')
        CAN_GROUP = jutil.get_static_field(klass, 'CAN_GROUP','I')
        CANNOT_GROUP = jutil.get_static_field(klass, 'CANNOT_GROUP','I')
        DOUBLE = jutil.get_static_field(klass, 'DOUBLE','I')
        FLOAT = jutil.get_static_field(klass, 'FLOAT', 'I')
        INT16 = jutil.get_static_field(klass, 'INT16', 'I')
        INT32 = jutil.get_static_field(klass, 'INT32', 'I')
        INT8 = jutil.get_static_field(klass, 'INT8', 'I')
        MUST_GROUP = jutil.get_static_field(klass, 'MUST_GROUP', 'I')
        UINT16 = jutil.get_static_field(klass, 'UINT16', 'I')
        UINT32 = jutil.get_static_field(klass, 'UINT32', 'I')
        UINT8 = jutil.get_static_field(klass, 'UINT8', 'I')

        @classmethod
        def getPixelTypeString(cls, pixel_type):
            return jutil.static_call('loci/formats/FormatTools', 'getPixelTypeString', '(I)Ljava/lang/String;', pixel_type)

    return FormatTools

def make_iformat_reader_class():
    '''Bind a Java class that implements IFormatReader to a Python class

    Returns a class that implements IFormatReader through calls to the
    implemented class passed in. The returned class can be subclassed to
    provide additional bindings.
    '''
    class IFormatReader(object):
        '''A wrapper for loci.formats.IFormatReader

        See http://hudson.openmicroscopy.org.uk/job/LOCI/javadoc/loci/formats/ImageReader.html
        '''
        close = jutil.make_method('close','()V',
                                  'Close the currently open file and free memory')
        getDimensionOrder = jutil.make_method('getDimensionOrder',
                                              '()Ljava/lang/String;',
                                              'Return the dimension order as a five-character string, e.g. "XYCZT"')
        getGlobalMetadata = jutil.make_method('getGlobalMetadata',
                                        '()Ljava/util/Hashtable;',
                                        'Obtains the hashtable containing the global metadata field/value pairs')
        getMetadata = getGlobalMetadata
        getMetadataValue = jutil.make_method('getMetadataValue',
                                             '(Ljava/lang/String;)'
                                             'Ljava/lang/Object;',
                                             'Look up a specific metadata value from the store')
        getSeriesMetadata = jutil.make_method('getSeriesMetadata',
                                              '()Ljava/util/Hashtable;',
                                              'Obtains the hashtable contaning the series metadata field/value pairs')
        getSeriesCount = jutil.make_method('getSeriesCount',
                                           '()I',
                                           'Return the # of image series in the file')
        getSeries = jutil.make_method('getSeries', '()I',
                                      'Return the currently selected image series')
        getImageCount = jutil.make_method('getImageCount',
                                          '()I','Determines the number of images in the current file')
        getIndex = jutil.make_method('getIndex', '(III)I',
                                     'Get the plane index given z, c, t')
        getRGBChannelCount = jutil.make_method('getRGBChannelCount',
                                               '()I','Gets the number of channels per RGB image (if not RGB, this returns 1')
        getSizeC = jutil.make_method('getSizeC', '()I',
                                     'Get the number of color planes')
        getSizeT = jutil.make_method('getSizeT', '()I',
                                     'Get the number of frames in the image')
        getSizeX = jutil.make_method('getSizeX', '()I',
                                     'Get the image width')
        getSizeY = jutil.make_method('getSizeY', '()I',
                                     'Get the image height')
        getSizeZ = jutil.make_method('getSizeZ', '()I',
                                     'Get the image depth')
        getPixelType = jutil.make_method('getPixelType', '()I',
                                         'Get the pixel type: see FormatTools for types')
        isLittleEndian = jutil.make_method('isLittleEndian',
                                           '()Z','Return True if the data is in little endian order')
        isRGB = jutil.make_method('isRGB', '()Z',
                                  'Return True if images in the file are RGB')
        isInterleaved = jutil.make_method('isInterleaved', '()Z',
                                          'Return True if image colors are interleaved within a plane')
        isIndexed = jutil.make_method('isIndexed', '()Z',
                                      'Return True if the raw data is indexes in a lookup table')
        openBytes = jutil.make_method('openBytes','(I)[B',
                                      'Get the specified image plane as a byte array')
        openBytesXYWH = jutil.make_method('openBytes','(IIIII)[B',
                                          '''Get the specified image plane as a byte array

                                          (corresponds to openBytes(int no, int x, int y, int w, int h))
                                          no - image plane number
                                          x,y - offset into image
                                          w,h - dimensions of image to return''')
        setSeries = jutil.make_method('setSeries','(I)V','Set the currently selected image series')
        setGroupFiles = jutil.make_method('setGroupFiles', '(Z)V',
                                          'Force reader to group or not to group files in a multi-file set')
        setMetadataStore = jutil.make_method('setMetadataStore',
                                             '(Lloci/formats/meta/MetadataStore;)V',
                                             'Sets the default metadata store for this reader.')
        setMetadataOptions = jutil.make_method('setMetadataOptions',
                                               '(Lloci/formats/in/MetadataOptions;)V',
                                               'Sets the metadata options used when reading metadata')
        isThisTypeS = jutil.make_method(
            'isThisType',
            '(Ljava/lang/String;)Z',
            'Return true if the filename might be handled by this reader')
        isThisTypeSZ = jutil.make_method(
            'isThisType',
            '(Ljava/lang/String;Z)Z',
            '''Return true if the named file is handled by this reader.

            filename - name of file

            allowOpen - True if the reader is allowed to open files
                        when making its determination
            ''')
        isThisTypeStream = jutil.make_method(
            'isThisType',
            '(Lloci/common/RandomAccessInputStream;)Z',
            '''Return true if the stream might be parseable by this reader.

            stream - the RandomAccessInputStream to be used to read the file contents

            Note that both isThisTypeS and isThisTypeStream must return true
            for the type to truly be handled.''')
        def setId(self, path):
            '''Set the name of the file'''
            jutil.call(self.o, 'setId',
                       '(Ljava/lang/String;)V',
                       path)

        getMetadataStore = jutil.make_method('getMetadataStore', '()Lloci/formats/meta/MetadataStore;',
                                             'Retrieves the current metadata store for this reader.')
        get8BitLookupTable = jutil.make_method(
            'get8BitLookupTable',
            '()[[B', 'Get a lookup table for 8-bit indexed images')
        get16BitLookupTable = jutil.make_method(
            'get16BitLookupTable',
            '()[[S', 'Get a lookup table for 16-bit indexed images')
        def get_class_name(self):
            return jutil.call(jutil.call(self.o, 'getClass', '()Ljava/lang/Class;'),
                              'getName', '()Ljava/lang/String;')

        @property
        def suffixNecessary(self):
            if self.get_class_name() == 'loci.formats.in.JPKReader':
                return True;
            env = jutil.get_env()
            klass = env.get_object_class(self.o)
            field_id = env.get_field_id(klass, "suffixNecessary", "Z")
            if field_id is None:
                return None
            return env.get_boolean_field(self.o, field_id)

        @property
        def suffixSufficient(self):
            if self.get_class_name() == 'loci.formats.in.JPKReader':
                return True;
            env = jutil.get_env()
            klass = env.get_object_class(self.o)
            field_id = env.get_field_id(klass, "suffixSufficient", "Z")
            if field_id is None:
                return None
            return env.get_boolean_field(self.o, field_id)


    return IFormatReader

def get_class_list():
    '''Return a wrapped instance of loci.formats.ClassList'''
    #
    # This uses the reader.txt file from inside the loci_tools.jar
    #
    class ClassList(object):
        remove_class = jutil.make_method(
            'removeClass', '(Ljava/lang/Class;)V',
            'Remove the given class from the class list')
        add_class = jutil.make_method(
            'addClass', '(Ljava/lang/Class;)V',
            'Add the given class to the back of the class list')
        get_classes = jutil.make_method(
            'getClasses', '()[Ljava/lang/Class;',
            'Get the classes in the list as an array')

        def __init__(self):
            env = jutil.get_env()
            class_name = 'loci/formats/ImageReader'
            klass = env.find_class(class_name)
            base_klass = env.find_class('loci/formats/IFormatReader')
            self.o = jutil.make_instance("loci/formats/ClassList",
                                         "(Ljava/lang/String;"
                                         "Ljava/lang/Class;" # base
                                         "Ljava/lang/Class;)V", # location in jar
                                         "readers.txt", base_klass, klass)
            problem_classes = [
                # BDReader will read all .tif files in an experiment if it's
                # called to load a .tif.
                #
                'loci.formats.in.BDReader',
                #
                # MRCReader will read .stk files which should be read
                # by MetamorphReader
                #
                'loci.formats.in.MRCReader'
                ]
            for problem_class in problem_classes:
                # Move to back
                klass = jutil.class_for_name(problem_class)
                self.remove_class(klass)
                self.add_class(klass)
    return ClassList()


def make_image_reader_class():
    '''Return an image reader class for the given Java environment'''
    env = jutil.get_env()
    class_name = 'loci/formats/ImageReader'
    klass = env.find_class(class_name)
    base_klass = env.find_class('loci/formats/IFormatReader')
    IFormatReader = make_iformat_reader_class()
    class_list = get_class_list()

    class ImageReader(IFormatReader):
        new_fn = jutil.make_new(class_name, '(Lloci/formats/ClassList;)V')
        def __init__(self):
            self.new_fn(class_list.o)
        getFormat = jutil.make_method('getFormat',
                                      '()Ljava/lang/String;',
                                      'Get a string describing the format of this file')
        getReader = jutil.make_method('getReader',
                                      '()Lloci/formats/IFormatReader;')
        def allowOpenToCheckType(self, allow):
            '''Allow the "isThisType" function to open files

            For the cluster, you want to tell potential file formats
            not to open the image file to test if it's their format.
            '''
            if not hasattr(self, "allowOpenToCheckType_method"):
                self.allowOpenToCheckType_method = None
                class_wrapper = jutil.get_class_wrapper(self.o)
                methods = class_wrapper.getMethods()
                for method in jutil.get_env().get_object_array_elements(methods):
                    m = jutil.get_method_wrapper(method)
                    if m.getName() in ('allowOpenToCheckType', 'setAllowOpenFiles'):
                        self.allowOpenToCheckType_method = m
            if self.allowOpenToCheckType_method is not None:
                object_class = env.find_class('java/lang/Object')
                jexception = jutil.get_env().exception_occurred()
                if jexception is not None:
                    raise jutil.JavaException(jexception)

                boolean_value = jutil.make_instance('java/lang/Boolean',
                                                    '(Z)V', allow)
                args = jutil.get_env().make_object_array(1, object_class)
                jexception = jutil.get_env().exception_occurred()
                if jexception is not None:
                    raise jutil.JavaException(jexception)
                jutil.get_env().set_object_array_element(args, 0, boolean_value)
                jexception = jutil.get_env().exception_occurred()
                if jexception is not None:
                    raise jutil.JavaException(jexception)
                self.allowOpenToCheckType_method.invoke(self.o, args)
    return ImageReader


def make_reader_wrapper_class(class_name):
    '''Make an ImageReader wrapper class

    class_name - the name of the wrapper class, for instance,
                 "loci/formats/ChannelSeparator"

    You can instantiate an instance of the wrapper class like this:
    rdr = ChannelSeparator(ImageReader())
    '''
    IFormatReader = make_iformat_reader_class()
    class ReaderWrapper(IFormatReader):
        __doc__ = '''A wrapper for %s

        See http://hudson.openmicroscopy.org.uk/job/LOCI/javadoc/loci/formats/ImageReader.html
        '''%class_name
        new_fn = jutil.make_new(class_name, '(Lloci/formats/IFormatReader;)V')
        def __init__(self, rdr):
            self.new_fn(rdr)

        setId = jutil.make_method('setId', '(Ljava/lang/String;)V',
                                  'Set the name of the data file')
    return ReaderWrapper

__has_omero_jars = None
def has_omero_packages():
    '''Return True if we can find the packages needed for OMERO

    In order to run OMERO, you'll need the OMERO client and ICE
    on your class path (not supplied with python-bioformats and
    specific to your server's version)
    '''
    global __has_omero_jars
    if __has_omero_jars is None:
        class_loader = jutil.static_call(
            "java/lang/ClassLoader", "getSystemClassLoader",
            "()Ljava/lang/ClassLoader;")
        for klass in ("Glacier2.PermissionDeniedException",
                      "loci.ome.io.OmeroReader", "omero.client"):
            try:
                jutil.call(
                    class_loader, "loadClass",
                    "(Ljava/lang/String;)Ljava/lang/Class;", klass)
            except:
                __has_omero_jars = False
                break
        else:
            __has_omero_jars = True
    return __has_omero_jars

__omero_server = None
__omero_username = None
__omero_session_id = None
__omero_port = None
__omero_config_file = None
#
# Only set if user enters password in plaintext on command-line
#
__omero_password = None

[docs]def set_omero_credentials(omero_server, omero_port, omero_username, omero_password): '''Set the credentials to be used to connect to the Omero server :param omero_server: DNS name of the server :param omero_port: use this port to connect to the server :param omero_username: log on as this user :param omero_password: log on using this password The session ID is valid after this function is called. An exception is thrown if the login fails. :func:`bioformats.omero_logout()` can be called to log out. ''' global __omero_server global __omero_username global __omero_session_id global __omero_port __omero_server = omero_server __omero_port = omero_port __omero_username = omero_username script = """ var client = Packages.omero.client(server, port); var serverFactory = client.createSession(user, password); client.getSessionId(); """ __omero_session_id = jutil.run_script(script, dict( server = __omero_server, port = __omero_port, user = __omero_username, password = omero_password)) return __omero_session_id
[docs]def get_omero_credentials(): '''Return a pickleable dictionary representing the Omero credentials. Call :func:`bioformats.use_omero_credentials` in some other process to use this. ''' if __omero_session_id is None: omero_login() return dict(omero_server = __omero_server, omero_port = __omero_port, omero_user = __omero_username, omero_session_id = __omero_session_id)
def omero_login(): global __omero_config_file global __omero_session_id global __omero_server global __omero_username global __omero_port global __omero_password if __omero_config_file is not None and os.path.isfile(__omero_config_file): env = jutil.get_env() config = env.make_object_array(1, env.find_class("java/lang/String")) env.set_object_array_element( config, 0, env.new_string("--Ice.Config=%s" % __omero_config_file)) script = """ var client = Packages.omero.client(config); client.createSession(); client.getSessionId(); """ __omero_session_id = jutil.run_script(script, dict(config=config)) elif all([x is not None for x in (__omero_server, __omero_port, __omero_username, __omero_password)]): set_omero_credentials(__omero_server, __omero_port, __omero_username, __omero_password) else: __omero_login_fn() return __omero_session_id
[docs]def omero_logout(): '''Abandon any current Omero session. ''' global __omero_session_id __omero_session_id = None
[docs]def use_omero_credentials(credentials): '''Use the session ID from an existing login as credentials. :param credentials: credentials from get_omero_credentials. ''' global __omero_server global __omero_username global __omero_session_id global __omero_port global __omero_config_file global __omero_password __omero_server = credentials.get(K_OMERO_SERVER, None) __omero_port = credentials.get(K_OMERO_PORT, None) __omero_username = credentials.get(K_OMERO_USER, None) __omero_session_id = credentials.get(K_OMERO_SESSION_ID, None) __omero_config_file = credentials.get(K_OMERO_CONFIG_FILE, None) __omero_password = credentials.get(K_OMERO_PASSWORD, None)
__omero_login_fn = None
[docs]def set_omero_login_hook(fn): '''Set the function to be called when a login to Omero is needed. ''' global __omero_login_fn __omero_login_fn = fn
def get_omero_reader(): '''Return an ``loci.ome.io.OMEROReader`` instance, wrapped as a FormatReader. ''' script = """ var rdr = new Packages.loci.ome.io.OmeroReader(); rdr.setServer(server); rdr.setPort(port); rdr.setUsername(username); rdr.setSessionID(sessionID); rdr; """ if __omero_session_id is None: omero_login() jrdr = jutil.run_script(script, dict( server = __omero_server, port = __omero_port, username = __omero_username, sessionID = __omero_session_id)) rdr = make_iformat_reader_class()() rdr.o = jrdr return rdr def load_using_bioformats_url(url, c=None, z=0, t=0, series=None, index=None, rescale = True, wants_max_intensity = False, channel_names = None): '''Load a file from Bio-formats via a URL ''' with ImageReader(url=url) as rdr: return rdr.read(c, z, t, series, index, rescale, wants_max_intensity, channel_names)
[docs]class ImageReader(object): '''Find the appropriate reader for a file. This class is meant to be harnessed to a scope like this: >>> with GetImageReader(path) as reader: >>> .... It uses `__enter__` and `__exit__` to manage the random access stream that can be used to cache the file contents in memory. ''' def __init__(self, path=None, url=None, perform_init=True): self.stream = None file_scheme = "file:" self.using_temp_file = False if url is not None: url = str(url) if url.lower().startswith(file_scheme): url = url2pathname(url[len(file_scheme):]) path = url self.path = path if path is None: if url.lower().startswith("omero:"): while True: # # We keep trying to contact the OMERO server via the # login dialog until the user gives up or we connect. # try: self.rdr = get_omero_reader() self.path = url if perform_init: self.init_reader() return except jutil.JavaException as e: je = e.throwable if jutil.is_instance_of( je, "loci/formats/FormatException"): je = jutil.call(je, "getCause", "()Ljava/lang/Throwable;") if jutil.is_instance_of( je, "Glacier2/PermissionDeniedException"): omero_logout() omero_login() else: logger.warn(e.message) for line in traceback.format_exc().split("\n"): logger.warn(line) if jutil.is_instance_of( je, "java/io/FileNotFoundException"): raise IOError( errno.ENOENT, "The file, \"%s\", does not exist." % path, path) e2 = IOError( errno.EINVAL, "Could not load the file as an image (see log for details)", path.encode('utf-8')) raise e2 else: # # Other URLS, copy them to a tempfile location # ext = url[url.rfind("."):] src = urlopen(url) dest_fd, self.path = tempfile.mkstemp(suffix=ext) try: dest = os.fdopen(dest_fd, 'wb') shutil.copyfileobj(src, dest) except: src.close() dest.close() os.remove(self.path) self.using_temp_file = True src.close() dest.close() urlpath = urlparse(url)[2] filename = unquote(urlpath.split("/")[-1]) else: if sys.platform.startswith("win"): self.path = self.path.replace("/", os.path.sep) filename = os.path.split(path)[1] if not os.path.isfile(self.path): raise IOError( errno.ENOENT, "The file, \"%s\", does not exist." % path, path) self.stream = jutil.make_instance('loci/common/RandomAccessInputStream', '(Ljava/lang/String;)V', self.path) self.rdr = None class_list = get_class_list() find_rdr_script = """ var classes = class_list.getClasses(); var rdr = null; var lc_filename = java.lang.String(filename.toLowerCase()); for (pass=0; pass < 3; pass++) { for (class_idx in classes) { var maybe_rdr = classes[class_idx].newInstance(); if (pass == 0) { if (maybe_rdr.isThisType(filename, false)) { rdr = maybe_rdr; break; } continue; } else if (pass == 1) { var suffixes = maybe_rdr.getSuffixes(); var suffix_found = false; for (suffix_idx in suffixes) { var suffix = java.lang.String(suffixes[suffix_idx]); suffix = suffix.toLowerCase(); if (lc_filename.endsWith(suffix)) { suffix_found = true; break; } } if (! suffix_found) continue; } if (maybe_rdr.isThisType(stream)) { rdr = maybe_rdr; break; } } if (rdr) break; } rdr; """ IFormatReader = make_iformat_reader_class() jrdr = jutil.run_script(find_rdr_script, dict(class_list = class_list, filename = filename, stream = self.stream)) if jrdr is None: raise ValueError("Could not find a Bio-Formats reader for %s", self.path) self.rdr = IFormatReader() self.rdr.o = jrdr if perform_init: self.init_reader() def __enter__(self): return self def __exit__(self, type_class, value, traceback): self.close()
[docs] def close(self): if hasattr(self, "rdr"): self.rdr.close() del self.rdr.o del self.rdr if hasattr(self, "stream") and self.stream is not None: jutil.call(self.stream, 'close', '()V') del self.stream if self.using_temp_file: os.remove(self.path) self.using_temp_file = False # # Run the Java garbage collector here. # jutil.static_call("java/lang/System", "gc","()V")
def init_reader(self): mdoptions = metadatatools.get_metadata_options(metadatatools.ALL) self.rdr.setMetadataOptions(mdoptions) self.rdr.setGroupFiles(False) self.metadata = metadatatools.createOMEXMLMetadata() self.rdr.setMetadataStore(self.metadata) try: self.rdr.setId(self.path) except jutil.JavaException as e: logger.warn(e.message) for line in traceback.format_exc().split("\n"): logger.warn(line) je = e.throwable if has_omero_packages() and jutil.is_instance_of( je, "Glacier2/PermissionDeniedException"): # Handle at a higher level raise if jutil.is_instance_of( je, "loci/formats/FormatException"): je = jutil.call(je, "getCause", "()Ljava/lang/Throwable;") if jutil.is_instance_of( je, "java/io/FileNotFoundException"): raise IOError( errno.ENOENT, "The file, \"%s\", does not exist." % path, path) e2 = IOError( errno.EINVAL, "Could not load the file as an image (see log for details)", self.path.encode('utf-8')) raise e2
[docs] def read(self, c = None, z = 0, t = 0, series = None, index = None, rescale = True, wants_max_intensity = False, channel_names = None, XYWH=None): '''Read a single plane from the image reader file. :param c: read from this channel. `None` = read color image if multichannel or interleaved RGB. :param z: z-stack index :param t: time index :param series: series for ``.flex`` and similar multi-stack formats :param index: if `None`, fall back to ``zct``, otherwise load the indexed frame :param rescale: `True` to rescale the intensity scale to 0 and 1; `False` to return the raw values native to the file. :param wants_max_intensity: if `False`, only return the image; if `True`, return a tuple of image and max intensity :param channel_names: provide the channel names for the OME metadata :param XYWH: a (x, y, w, h) tuple ''' FormatTools = make_format_tools_class() ChannelSeparator = make_reader_wrapper_class( "loci/formats/ChannelSeparator") env = jutil.get_env() if series is not None: self.rdr.setSeries(series) if XYWH is not None: assert isinstance(XYWH, tuple) and len(XYWH) == 4, "Invalid XYWH tuple" openBytes_func = lambda x: self.rdr.openBytesXYWH(x, XYWH[0], XYWH[1], XYWH[2], XYWH[3]) width, height = XYWH[2], XYWH[3] else: openBytes_func = self.rdr.openBytes width, height = self.rdr.getSizeX(), self.rdr.getSizeY() pixel_type = self.rdr.getPixelType() little_endian = self.rdr.isLittleEndian() if pixel_type == FormatTools.INT8: dtype = np.int8 scale = 255 elif pixel_type == FormatTools.UINT8: dtype = np.uint8 scale = 255 elif pixel_type == FormatTools.UINT16: dtype = '<u2' if little_endian else '>u2' scale = 65535 elif pixel_type == FormatTools.INT16: dtype = '<i2' if little_endian else '>i2' scale = 65535 elif pixel_type == FormatTools.UINT32: dtype = '<u4' if little_endian else '>u4' scale = 2**32 elif pixel_type == FormatTools.INT32: dtype = '<i4' if little_endian else '>i4' scale = 2**32-1 elif pixel_type == FormatTools.FLOAT: dtype = '<f4' if little_endian else '>f4' scale = 1 elif pixel_type == FormatTools.DOUBLE: dtype = '<f8' if little_endian else '>f8' scale = 1 max_sample_value = self.rdr.getMetadataValue('MaxSampleValue') if max_sample_value is not None: try: scale = jutil.call(max_sample_value, 'intValue', '()I') except: logger.warning("WARNING: failed to get MaxSampleValue for image. Intensities may be improperly scaled.") if index is not None: image = np.frombuffer(openBytes_func(index), dtype) if len(image) / height / width in (3,4): n_channels = int(len(image) / height / width) if self.rdr.isInterleaved(): image.shape = (height, width, n_channels) else: image.shape = (n_channels, height, width) image = image.transpose(1, 2, 0) else: image.shape = (height, width) elif self.rdr.isRGB() and self.rdr.isInterleaved(): index = self.rdr.getIndex(z,0,t) image = np.frombuffer(openBytes_func(index), dtype) image.shape = (height, width, self.rdr.getSizeC()) if image.shape[2] > 3: image = image[:, :, :3] elif c is not None and self.rdr.getRGBChannelCount() == 1: index = self.rdr.getIndex(z,c,t) image = np.frombuffer(openBytes_func(index), dtype) image.shape = (height, width) elif self.rdr.getRGBChannelCount() > 1: n_planes = self.rdr.getRGBChannelCount() rdr = ChannelSeparator(self.rdr) planes = [ np.frombuffer( (rdr.openBytes(rdr.getIndex(z,i,t)) if XYWH is None else rdr.openBytesXYWH(rdr.getIndex(z,i,t), XYWH[0], XYWH[1], XYWH[2], XYWH[3])), dtype ) for i in range(n_planes)] if len(planes) > 3: planes = planes[:3] elif len(planes) < 3: # > 1 and < 3 means must be 2 # see issue #775 planes.append(np.zeros(planes[0].shape, planes[0].dtype)) image = np.dstack(planes) image.shape=(height, width, 3) del rdr elif self.rdr.getSizeC() > 1: images = [ np.frombuffer(openBytes_func(self.rdr.getIndex(z,i,t)), dtype) for i in range(self.rdr.getSizeC())] image = np.dstack(images) image.shape = (height, width, self.rdr.getSizeC()) if not channel_names is None: metadata = metadatatools.MetadataRetrieve(self.metadata) for i in range(self.rdr.getSizeC()): index = self.rdr.getIndex(z, 0, t) channel_name = metadata.getChannelName(index, i) if channel_name is None: channel_name = metadata.getChannelID(index, i) channel_names.append(channel_name) elif self.rdr.isIndexed(): # # The image data is indexes into a color lookup-table # But sometimes the table is the identity table and just generates # a monochrome RGB image # index = self.rdr.getIndex(z,0,t) image = np.frombuffer(openBytes_func(index),dtype) if pixel_type in (FormatTools.INT16, FormatTools.UINT16): lut = self.rdr.get16BitLookupTable() if lut is not None: lut = np.array( [env.get_short_array_elements(d) for d in env.get_object_array_elements(lut)])\ .transpose() else: lut = self.rdr.get8BitLookupTable() if lut is not None: lut = np.array( [env.get_byte_array_elements(d) for d in env.get_object_array_elements(lut)])\ .transpose() image.shape = (height, width) if (lut is not None) \ and not np.all(lut == np.arange(lut.shape[0])[:, np.newaxis]): image = lut[image, :] else: index = self.rdr.getIndex(z,0,t) image = np.frombuffer(openBytes_func(index),dtype) image.shape = (height,width) if rescale: image = image.astype(np.float32) / float(scale) if wants_max_intensity: return image, scale return image
################### # # A cache mechanism for image readers # # CellProfiler's analysis worker will read image planes from the same # file across different jobs, so only a global cache of image readers # will work. Here, we try and keep around one reader per key - the key # typically being its image name in CellProfiler. We also need to clear # the cache globally. # #################### # The key cache associates key with path/url # This allows us to have two keys point to the same reader, e.g. read # multiple channels from a stack. __image_reader_key_cache = {} # The image reader cache associates path/url with a reader __image_reader_cache = {}
[docs]def get_image_reader(key, path=None, url=None): '''Make or find an image reader appropriate for the given path path - pathname to the reader on disk. key - use this key to keep only a single cache member associated with that key open at a time. ''' logger.debug("Getting image reader for: %s, %s, %s" % (key, path, url)) if key in __image_reader_key_cache: old_path, old_url = __image_reader_key_cache[key] old_count, rdr = __image_reader_cache[old_path, old_url] if old_path == path and old_url == url: return rdr release_image_reader(key) if (path, url) in __image_reader_cache: old_count, rdr = __image_reader_cache[path, url] else: # If OMERO.py is found on the PYTHONPATH and OMERO_READER_ENABLED # is True OMERO python reader can be used to directly request # the image pixels from the server. # Following this route gives almost 10x speed up. if OMERO_READER_IMPORTED and OMERO_IMPORTED and \ omero_reader_enabled() and \ url is not None and url.lower().startswith("omero:"): logger.debug("Initializing Python reader.") rdr = OmeroReader(__omero_server, __omero_session_id, url=url) else: logger.debug("Falling back to Java reader.") rdr = ImageReader(path=path, url=url) old_count = 0 __image_reader_cache[path, url] = (old_count+1, rdr) __image_reader_key_cache[key] = (path, url) return rdr
[docs]def release_image_reader(key): '''Tell the cache that it should flush the reference for the given key ''' if key in __image_reader_key_cache: path, url = __image_reader_key_cache[key] del __image_reader_key_cache[key] old_count, rdr = __image_reader_cache[path, url] if old_count == 1: rdr.close() del __image_reader_cache[path, url] else: __image_reader_cache[path, url] = (old_count-1, rdr)
[docs]def clear_image_reader_cache(): '''Get rid of any open image readers''' for use_count, rdr in __image_reader_cache.values(): logger.debug("Closing reader %s" % rdr) rdr.close() __image_reader_cache.clear() __image_reader_key_cache.clear()
def load_using_bioformats(path, c=None, z=0, t=0, series=None, index=None, rescale = True, wants_max_intensity = False, channel_names = None): '''Load the given image file using the Bioformats library. :param path: path to the file :param z: the frame index in the `z` (depth) dimension. :param t: the frame index in the time dimension. :param channel_names: `None` if you don't want them, a list which will be filled if you do. :returns: either a 2-d (grayscale) or 3-d (2-d + 3 RGB planes) image. ''' with ImageReader(path=path) as rdr: return rdr.read(c, z, t, series, index, rescale, wants_max_intensity, channel_names)
[docs]def get_omexml_metadata(path=None, url=None): '''Read the OME metadata from a file using Bio-formats :param path: path to the file :param groupfiles: utilize the groupfiles option to take the directory structure into account. :returns: the metdata as XML. ''' with ImageReader(path=path, url=url, perform_init=False) as rdr: # # Below, "in" is a keyword and Rhino's parser is just a little wonky I fear. # # It is critical that setGroupFiles be set to false, goodness knows # why, but if you don't the series count is wrong for flex files. # script = """ importClass(Packages.loci.common.services.ServiceFactory, Packages.loci.formats.services.OMEXMLService, Packages.loci.formats['in'].DefaultMetadataOptions, Packages.loci.formats['in'].MetadataLevel); reader.setGroupFiles(false); reader.setOriginalMetadataPopulated(true); var service = new ServiceFactory().getInstance(OMEXMLService); var metadata = service.createOMEXMLMetadata(); reader.setMetadataStore(metadata); reader.setMetadataOptions(new DefaultMetadataOptions(MetadataLevel.ALL)); reader.setId(path); var xml = service.getOMEXML(metadata); xml; """ xml = jutil.run_script(script, dict(path=rdr.path, reader = rdr.rdr)) return xml