From ad89b14d4f7734144103ed63fd1981c6f69a23d5 Mon Sep 17 00:00:00 2001 From: jwittenbach Date: Tue, 19 Apr 2016 12:43:45 -0400 Subject: [PATCH] updates tifffile with upstream changes --- skimage/external/tifffile/tifffile.c | 168 +++++++++++--- skimage/external/tifffile/tifffile.py | 322 +++++++++++++++++--------- 2 files changed, 337 insertions(+), 153 deletions(-) diff --git a/skimage/external/tifffile/tifffile.c b/skimage/external/tifffile/tifffile.c index 56300589..a11220c5 100644 --- a/skimage/external/tifffile/tifffile.c +++ b/skimage/external/tifffile/tifffile.c @@ -10,12 +10,12 @@ Refer to the tifffile.py module for documentation and tests. :Organization: Laboratory for Fluorescence Dynamics, University of California, Irvine -:Version: 2015.08.17 +:Version: 2016.04.13 Requirements ------------ -* `CPython 2.7 or 3.4 `_ -* `Numpy 1.9.2 `_ +* `CPython 2.7 or 3.5 `_ +* `Numpy 1.10 `_ * A Python distutils compatible C compiler (build) Install @@ -32,8 +32,8 @@ Use this Python distutils setup script to build the extension module:: License ------- -Copyright (c) 2008-2015, Christoph Gohlke -Copyright (c) 2008-2015, The Regents of the University of California +Copyright (c) 2008-2016, Christoph Gohlke +Copyright (c) 2008-2016, The Regents of the University of California Produced at the Laboratory for Fluorescence Dynamics All rights reserved. @@ -62,7 +62,7 @@ ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ -#define _VERSION_ "2015.08.17" +#define _VERSION_ "2016.04.13" #define WIN32_LEAN_AND_MEAN #define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION @@ -359,6 +359,94 @@ int unpackbits( /*****************************************************************************/ /* Python functions */ + +/** Reverse bits in bytes of byte string or ndarray. */ +char py_reverse_bitorder_doc[] = + "Reverse bits in each byte of byte string or numpy array."; + +static PyObject* +py_reverse_bitorder(PyObject *obj, PyObject *args) +{ + PyObject *dataobj = NULL; + PyObject *result = NULL; + PyArray_Descr *dtype = NULL; + PyArrayIterObject *iter = NULL; + unsigned char *dataptr = NULL; + unsigned char *resultptr = NULL; + Py_ssize_t size, stride; + Py_ssize_t i, j; + int axis = -1; + + if (!PyArg_ParseTuple(args, "O", &dataobj)) + return NULL; + + Py_INCREF(dataobj); + + if (PyBytes_Check(dataobj)) { + dataptr = (unsigned char *)PyBytes_AS_STRING(dataobj); + size = PyBytes_GET_SIZE(dataobj); + + result = PyBytes_FromStringAndSize(NULL, size); + if (result == NULL) { + PyErr_Format(PyExc_MemoryError, "unable to allocate result"); + goto _fail; + } + resultptr = (unsigned char *)PyBytes_AS_STRING(result); + + Py_BEGIN_ALLOW_THREADS + for (i = 0; i < size; i++) { + /* http://graphics.stanford.edu/~seander/bithacks.html + #ReverseByteWith64Bits */ + *resultptr++ = (unsigned char)(((*dataptr++ * 0x80200802ULL) & + 0x0884422110ULL) * 0x0101010101ULL >> 32); + } + Py_END_ALLOW_THREADS + + Py_DECREF(dataobj); + return result; + } + else if (PyArray_Check(dataobj)) { + dtype = PyArray_DTYPE((PyArrayObject*) dataobj); + if (dtype->elsize == 0) { + PyErr_Format(PyExc_ValueError, "can not handle dtype"); + goto _fail; + } + iter = (PyArrayIterObject *)PyArray_IterAllButAxis(dataobj, &axis); + size = PyArray_DIM((PyArrayObject *)dataobj, axis); + stride = PyArray_STRIDE((PyArrayObject *)dataobj, axis); + stride -= dtype->elsize; + + Py_BEGIN_ALLOW_THREADS + while (iter->index < iter->size) { + dataptr = (unsigned char *)iter->dataptr; + for(i = 0; i < size; i++) { + for(j = 0; j < dtype->elsize; j++) { + *dataptr++ = (unsigned char)(((*dataptr * 0x80200802ULL) & + 0x0884422110ULL) * 0x0101010101ULL >> 32); + } + dataptr += stride; + } + PyArray_ITER_NEXT(iter); + } + Py_END_ALLOW_THREADS + + Py_DECREF(iter); + Py_DECREF(dataobj); + Py_RETURN_NONE; + } + else { + PyErr_Format(PyExc_TypeError, "not a byte string or ndarray"); + goto _fail; + } + + _fail: + Py_XDECREF(dataobj); + Py_XDECREF(result); + Py_XDECREF(iter); + return NULL; +} + + /** Unpack tightly packed integers. */ char py_unpackints_doc[] = "Unpack groups of bits into numpy array."; @@ -386,8 +474,8 @@ py_unpackints(PyObject *obj, PyObject *args, PyObject *kwds) Py_INCREF(byteobj); if (((itemsize < 1) || (itemsize > 32)) && (itemsize != 64)) { - PyErr_Format(PyExc_ValueError, "itemsize out of range"); - goto _fail; + PyErr_Format(PyExc_ValueError, "itemsize out of range"); + goto _fail; } if (!PyBytes_Check(byteobj)) { @@ -400,12 +488,12 @@ py_unpackints(PyObject *obj, PyObject *args, PyObject *kwds) bytesize = (int)ceil(itemsize / 8.0); storagesize = bytesize < 3 ? bytesize : bytesize > 4 ? 8 : 4; if ((encoded_len < bytesize) || (encoded_len > SSIZE_MAX / storagesize)) { - PyErr_Format(PyExc_ValueError, "data size out of range"); - goto _fail; + PyErr_Format(PyExc_ValueError, "data size out of range"); + goto _fail; } if (dtype->elsize != storagesize) { - PyErr_Format(PyExc_TypeError, "dtype.elsize doesn't fit itemsize"); - goto _fail; + PyErr_Format(PyExc_TypeError, "dtype.elsize does not fit itemsize"); + goto _fail; } if (runlen == 0) { @@ -427,17 +515,16 @@ py_unpackints(PyObject *obj, PyObject *args, PyObject *kwds) decoded = (char *)PyArray_DATA(result); for (i = 0; i < decoded_len; i+=runlen) { - if (NO_ERROR != - unpackbits((unsigned char *) encoded, - (ssize_t) encoded_len, - (int) itemsize, - (ssize_t) runlen, - (unsigned char *) decoded)) { - PyErr_Format(PyExc_ValueError, "unpackbits() failed"); - goto _fail; + if (NO_ERROR != unpackbits((unsigned char *) encoded, + (ssize_t) encoded_len, + (int) itemsize, + (ssize_t) runlen, + (unsigned char *) decoded)) { + PyErr_Format(PyExc_ValueError, "unpackbits() failed"); + goto _fail; } encoded += (Py_ssize_t)(((uint64_t)runlen * (uint64_t)itemsize + - (uint64_t)skipbits) / 8); + (uint64_t)skipbits) / 8); decoded += runlen * storagesize; } @@ -446,19 +533,22 @@ py_unpackints(PyObject *obj, PyObject *args, PyObject *kwds) case 2: { uint16_t *d = (uint16_t *)PyArray_DATA(result); for (i = 0; i < PyArray_SIZE(result); i++) { - *d = SWAP2BYTES(*d); d++; + *d = SWAP2BYTES(*d); + d++; } break; } case 4: { uint32_t *d = (uint32_t *)PyArray_DATA(result); for (i = 0; i < PyArray_SIZE(result); i++) { - *d = SWAP4BYTES(*d); d++; + *d = SWAP4BYTES(*d); + d++; } break; } case 8: { uint64_t *d = (uint64_t *)PyArray_DATA(result); for (i = 0; i < PyArray_SIZE(result); i++) { - *d = SWAP8BYTES(*d); d++; + *d = SWAP8BYTES(*d); + d++; } break; } } @@ -526,7 +616,7 @@ py_decodepackbits(PyObject *obj, PyObject *args) } Py_END_ALLOW_THREADS - result = PyBytes_FromStringAndSize(0, decoded_len); + result = PyBytes_FromStringAndSize(NULL, decoded_len); if (result == NULL) { PyErr_Format(PyExc_MemoryError, "failed to allocate decoded string"); goto _fail; @@ -708,7 +798,7 @@ py_decodelzw(PyObject *obj, PyObject *args) newresult = table[code]; newresult->ref++; result_len += newresult->len; - *(struct BYTE_STRING **)decoded_ptr++ = newresult; + *(struct BYTE_STRING **)decoded_ptr++ = newresult; } } else { if (code < table_len) { @@ -732,7 +822,7 @@ py_decodelzw(PyObject *obj, PyObject *args) if (oldcode < 256) { newentry->len = 2; newentry->str = table2 + (oldcode << 9) + - ((unsigned char)c << 1); + ((unsigned char)c << 1); } else { len = table[oldcode]->len; newentry->len = len + 1; @@ -790,8 +880,8 @@ py_decodelzw(PyObject *obj, PyObject *args) PyEval_RestoreThread(_save); if (code != 257) { - PyErr_WarnEx(NULL, - "py_decodelzw encountered unexpected end of stream", 1); + PyErr_WarnEx( + NULL, "py_decodelzw encountered unexpected end of stream", 1); } /* result = ''.join(decoded) */ @@ -888,6 +978,8 @@ static PyMethodDef module_methods[] = { py_decodelzw_doc}, {"decode_packbits", (PyCFunction)py_decodepackbits, METH_VARARGS, py_decodepackbits_doc}, + {"reverse_bitorder", (PyCFunction)py_reverse_bitorder, METH_VARARGS, + py_reverse_bitorder_doc}, {NULL, NULL, 0, NULL} /* Sentinel */ }; @@ -910,15 +1002,15 @@ static int module_clear(PyObject *m) { } static struct PyModuleDef moduledef = { - PyModuleDef_HEAD_INIT, - "_tifffile", - NULL, - sizeof(struct module_state), - module_methods, - NULL, - module_traverse, - module_clear, - NULL + PyModuleDef_HEAD_INIT, + "_tifffile", + NULL, + sizeof(struct module_state), + module_methods, + NULL, + module_traverse, + module_clear, + NULL }; #define INITERROR return NULL diff --git a/skimage/external/tifffile/tifffile.py b/skimage/external/tifffile/tifffile.py index 03fcef7c..172c8c17 100644 --- a/skimage/external/tifffile/tifffile.py +++ b/skimage/external/tifffile/tifffile.py @@ -58,18 +58,26 @@ For command line usage run `python tifffile.py --help` :Organization: Laboratory for Fluorescence Dynamics, University of California, Irvine -:Version: 2016.02.22 +:Version: 2016.04.18 Requirements ------------ * `CPython 2.7 or 3.5 `_ (64 bit recommended) * `Numpy 1.10 `_ * `Matplotlib 1.5 `_ (optional for plotting) -* `Tifffile.c 2015.08.17 `_ +* `Tifffile.c 2016.04.13 `_ (recommended for faster decoding of PackBits and LZW encoded strings) Revisions --------- +2016.04.18 + Pass 1932 tests. + TiffWriter, imread, and imsave accept open binary file streams. +2016.04.13 + Correctly handle reversed fill order in 2 and 4 bps images (bug fix). + Implement reverse_bitorder in C. +2016.03.18 + Fixed saving additional ImageJ metadata. 2016.02.22 Pass 1920 tests. Write 8 bytes double tag values using offset if necessary (bug fix). @@ -171,11 +179,11 @@ Tested on little-endian platforms only. Other Python packages and modules for reading bio-scientific TIFF files: -* `Imread `_ -* `PyLibTiff `_ +* `Imread `_ +* `PyLibTiff `_ * `SimpleITK `_ * `PyLSM `_ -* `PyMca.TiffIO.py `_ (same as fabio.TiffIO) +* `PyMca.TiffIO.py `_ (same as fabio.TiffIO) * `BioImageXD.Readers `_ * `Cellcognition.io `_ * `CellProfiler.bioformats @@ -254,14 +262,18 @@ except ImportError: lzma = None try: - from . import _tifffile + if __package__: + from . import _tifffile + else: + import _tifffile except ImportError: warnings.warn( "failed to import the optional _tifffile C extension module.\n" - "Loading of some compressed images will be slow.\n" + "Loading of some compressed images will be very slow.\n" "Tifffile.c can be obtained at http://www.lfd.uci.edu/~gohlke/") -__version__ = '2016.02.22' + +__version__ = '2016.04.18' __docformat__ = 'restructuredtext en' __all__ = ( 'imsave', 'imread', 'imshow', 'TiffFile', 'TiffWriter', 'TiffSequence', @@ -269,15 +281,15 @@ __all__ = ( 'FileHandle', 'lazyattr', 'natural_sorted', 'decode_lzw', 'stripnull') -def imsave(filename, data, **kwargs): +def imsave(file, data, **kwargs): """Write image data to TIFF file. Refer to the TiffWriter class and member functions for documentation. Parameters ---------- - filename : str - Name of file to write. + file : str or binary stream + File name or writable binary stream, such as a open file or BytesIO. data : array_like Input image. The last dimensions are assumed to be image depth, height, width, and samples. @@ -291,7 +303,6 @@ def imsave(filename, data, **kwargs): Examples -------- >>> data = numpy.random.rand(2, 5, 3, 301, 219) - >>> metadata = {'axes': 'TZCYX'} >>> imsave('temp.tif', data, compress=6, metadata={'axes': 'TZCYX'}) """ @@ -305,7 +316,7 @@ def imsave(filename, data, **kwargs): data.size*data.dtype.itemsize > 2000*2**20): tifargs['bigtiff'] = True - with TiffWriter(filename, **tifargs) as tif: + with TiffWriter(file, **tifargs) as tif: tif.save(data, **kwargs) @@ -339,16 +350,17 @@ class TiffWriter(object): 'smin_sample_value': 340, 'smax_sample_value': 341, 'image_depth': 32997, 'tile_depth': 32998} - def __init__(self, filename, bigtiff=False, byteorder=None, + def __init__(self, file, bigtiff=False, byteorder=None, software='tifffile.py', imagej=False): - """Create a new TIFF file for writing. + """Open a TIFF file for writing. Use bigtiff=True when creating files larger than 2 GB. Parameters ---------- - filename : str - Name of file to write. + file : str, binary stream, or FileHandle + File name or writable binary stream, such as a open file + or BytesIO. bigtiff : bool If True, the BigTIFF format is used. byteorder : {'<', '>'} @@ -393,7 +405,7 @@ class TiffWriter(object): self._data_byte_counts = None # byte counts per plane self._tag_offsets = None # strip or tile offset tag code - self._fh = open(filename, 'wb') + self._fh = FileHandle(file, mode='wb', size=0) self._fh.write({'<': b'II', '>': b'MM'}[byteorder]) if bigtiff: @@ -528,7 +540,7 @@ class TiffWriter(object): self._data_shape = (self._data_shape[0] + 1,) + data.shape if not compress: # write contiguous data, write ifds/tags later - data.tofile(fh) + fh.write_array(data) return if photometric not in (None, 'minisblack', 'miniswhite', @@ -916,7 +928,7 @@ class TiffWriter(object): strip_byte_counts.append(len(t)) fh.write(t) else: - chunk.tofile(fh) + fh.write_array(chunk) fh.flush() elif compress: for plane in data[pageindex]: @@ -924,7 +936,7 @@ class TiffWriter(object): strip_byte_counts.append(len(plane)) fh.write(plane) else: - data.tofile(fh) # if this fails try update Python and numpy + fh.write_array(data) # update strip/tile offsets and byte_counts if necessary pos = fh.tell() @@ -1099,8 +1111,9 @@ def imread(files, **kwargs): Parameters ---------- - files : str or list - File name, glob pattern, or list of file names. + files : str, binary stream, or sequence + File name, seekable binary stream, glob pattern, or sequence of + file names. key : int, slice, or sequence of page indices Defines which pages to return as array. series : int @@ -1140,10 +1153,10 @@ def imread(files, **kwargs): files = glob.glob(files) if not files: raise ValueError('no files found') - if len(files) == 1: + if not hasattr(files, 'seek') and len(files) == 1: files = files[0] - if isinstance(files, basestring): + if isinstance(files, basestring) or hasattr(files, 'seek'): with TiffFile(files, **kwargs_file) as tif: return tif.asarray(**kwargs) else: @@ -1226,7 +1239,8 @@ class TiffFile(object): thousands of pages. """ - self._fh = FileHandle(arg, name=name, offset=offset, size=size) + self._fh = FileHandle(arg, mode='rb', + name=name, offset=offset, size=size) self.offset_size = None self.pages = [] self._multifile = bool(multifile) @@ -2317,6 +2331,7 @@ class TiffPage(object): image_depth = self.image_depth typecode = self.parent.byteorder + dtype bits_per_sample = self.bits_per_sample + lsb2msb = self.fill_order == 'lsb2msb' byte_counts, offsets = self._byte_counts_offsets @@ -2340,6 +2355,8 @@ class TiffPage(object): fh.seek(offsets[0]) result = fh.read_array(typecode, product(shape)) result = result.astype('=' + dtype) + if lsb2msb: + reverse_bitorder(result) else: if self.is_contig: runlen *= self.samples_per_pixel @@ -2380,7 +2397,11 @@ class TiffPage(object): tw, tl, td, pl = 0, 0, 0, 0 for offset, bytecount in zip(offsets, byte_counts): fh.seek(offset) - tile = unpack(decompress(fh.read(bytecount))) + tile = fh.read(bytecount) + if lsb2msb: + tile = reverse_bitorder(tile) + tile = decompress(tile) + tile = unpack(tile) try: tile.shape = tile_shape except ValueError: @@ -2414,6 +2435,8 @@ class TiffPage(object): for offset, bytecount in zip(offsets, byte_counts): fh.seek(offset) strip = fh.read(bytecount) + if lsb2msb: + strip = reverse_bitorder(strip) strip = decompress(strip) strip = unpack(strip) size = min(result.size, strip.size, strip_size, @@ -2431,8 +2454,6 @@ class TiffPage(object): numpy.cumsum(result, axis=-2, dtype=dtype, out=result) elif self.predictor == 'float': result = decode_floats(result) - if self.fill_order == 'lsb2msb': - reverse_bitorder(result) if colormapped and self.is_indexed: if self.color_map.shape[1] >= 2**bits_per_sample: # FluoView and LSM might fail here @@ -3037,6 +3058,7 @@ class TiffSequence(object): ---------- files : str, or sequence of str Glob pattern or sequence of file names. + Binary streams are not supported. imread : function or class Image read function or class with asarray function returning numpy array from single file. @@ -3051,6 +3073,8 @@ class TiffSequence(object): files = list(files) if not files: raise ValueError("no files found") + if not isinstance(files[0], basestring): + raise ValueError("not a file name") #if not os.path.isfile(files[0]): # raise ValueError("file not found") self.files = files @@ -3226,11 +3250,15 @@ class TiffTags(Record): class FileHandle(object): """Binary file handle. - * Handle embedded files (for CZI within CZI files). - * Allow to re-open closed files (for multi file formats such as OME-TIFF). - * Read numpy arrays and records from file like objects. + A limited, special purpose file handler that: + + * handles embedded files (for CZI within CZI files) + * allows to re-open closed files (for multi file formats, such as OME-TIFF) + * reads and writes numpy arrays and records from file like objects + + Only 'rb' and 'wb' modes are supported. Concurrently reading and writing + of the same stream is untested. - Only binary read, seek, tell, and close are supported on embedded files. When initialized from another file handle, do not use it unless this FileHandle is closed. @@ -3248,20 +3276,21 @@ class FileHandle(object): All attributes are read-only. """ - __slots__ = ('_fh', '_arg', '_mode', '_name', '_dir', + __slots__ = ('_fh', '_file', '_mode', '_name', '_dir', '_offset', '_size', '_close', 'is_file') - def __init__(self, arg, mode='rb', name=None, offset=None, size=None): + def __init__(self, file, mode='rb', name=None, offset=None, size=None): """Initialize file handle from file name or another file handle. Parameters ---------- - arg : str, File, or FileHandle - File name or open file handle. + file : str, binary stream, or FileHandle + File name or seekable binary stream, such as a open file + or BytesIO. mode : str - File open mode in case 'arg' is a file name. + File open mode in case 'file' is a file name. Must be 'rb' or 'wb'. name : str - Optional name of file in case 'arg' is a file handle. + Optional name of file in case 'file' is a binary stream. offset : int Optional start position of embedded file. By default this is the current file position. @@ -3271,7 +3300,7 @@ class FileHandle(object): """ self._fh = None - self._arg = arg + self._file = file self._mode = mode self._name = name self._dir = '' @@ -3286,39 +3315,56 @@ class FileHandle(object): if self._fh: return # file is open - if isinstance(self._arg, basestring): + if isinstance(self._file, basestring): # file name - self._arg = os.path.abspath(self._arg) - self._dir, self._name = os.path.split(self._arg) - self._fh = open(self._arg, self._mode) + self._file = os.path.abspath(self._file) + self._dir, self._name = os.path.split(self._file) + self._fh = open(self._file, self._mode) self._close = True if self._offset is None: self._offset = 0 - elif isinstance(self._arg, FileHandle): + elif isinstance(self._file, FileHandle): # FileHandle - self._fh = self._arg._fh + self._fh = self._file._fh if self._offset is None: self._offset = 0 - self._offset += self._arg._offset + self._offset += self._file._offset self._close = False if not self._name: if self._offset: - name, ext = os.path.splitext(self._arg._name) + name, ext = os.path.splitext(self._file._name) self._name = "%s@%i%s" % (name, self._offset, ext) else: - self._name = self._arg._name - self._dir = self._arg._dir - else: - # open file object - self._fh = self._arg + self._name = self._file._name + if self._mode and self._mode != self._file._mode: + raise ValueError('FileHandle has wrong mode') + self._mode = self._file._mode + self._dir = self._file._dir + elif hasattr(self._file, 'seek'): + # binary stream: open file, BytesIO + try: + self._file.tell() + except Exception: + raise ValueError("binary stream is not seekable") + self._fh = self._file if self._offset is None: - self._offset = self._arg.tell() + self._offset = self._file.tell() self._close = False if not self._name: try: self._dir, self._name = os.path.split(self._fh.name) except AttributeError: - self._name = "Unnamed stream" + self._name = "Unnamed binary stream" + try: + self._mode = self._fh.mode + except AttributeError: + pass + else: + raise ValueError("The first parameter must be a file name, " + "seekable binary stream, or FileHandle") + + # if self._mode not in (None, 'rb', 'wb', 'r+b', 'rb+', 'w+b', 'wb+'): + # raise ValueError('file mode not supported: %s' % self._mode) if self._offset: self._fh.seek(self._offset) @@ -3341,6 +3387,14 @@ class FileHandle(object): size = self._size return self._fh.read(size) + def write(self, bytestring): + """Write bytestring to file.""" + return self._fh.write(bytestring) + + def flush(self): + """Flush write buffers if applicable.""" + return self._fh.flush() + def memmap_array(self, dtype, shape, offset=0, mode='r', order='C'): """Return numpy.memmap of data stored in file.""" if not self.is_file: @@ -3381,6 +3435,14 @@ class FileHandle(object): byteorder=byteorder) return rec[0] if shape == 1 else rec + def write_array(self, data): + """Write numpy array to binary file.""" + try: + data.tofile(self._fh) + except Exception: + # BytesIO + self._fh.write(data.tostring()) + def tell(self): """Return file's current position.""" return self._fh.tell() - self._offset @@ -3391,7 +3453,7 @@ class FileHandle(object): if whence == 0: self._fh.seek(self._offset + offset, whence) return - elif whence == 2: + elif whence == 2 and self._size > 0: self._fh.seek(self._offset + self._size + offset, 0) return self._fh.seek(offset, whence) @@ -3881,7 +3943,7 @@ def imagej_description_dict(description): def imagej_description(shape, rgb=None, colormaped=False, version='1.11a', - hyperstack=None, mode=None, loop=None, kwargs={}): + hyperstack=None, mode=None, loop=None, **kwargs): """Return ImageJ image decription from data shape as byte string. ImageJ can handle up to 6 dimensions in order TZCYXS. @@ -3995,21 +4057,24 @@ def image_description(shape, colormaped=False, **metadata): def _replace_by(module_function, package=__package__, warn=False): """Try replace decorated function by module.function.""" + try: + from importlib import import_module + except ImportError: + warnings.warn('could not import module importlib') + return lambda func: func + def decorate(func, module_function=module_function, warn=warn): try: - modname, function = module_function.split('.') - if package is None: - full_name = modname + module, function = module_function.split('.') + if package: + module = import_module('.' + module, package=package) else: - full_name = package + '.' + modname - if modname == '_tifffile': - func, oldfunc = getattr(_tifffile, function), func - else: - module = __import__(full_name, fromlist=[modname]) - func, oldfunc = getattr(module, function), func + module = import_module(module) + func, oldfunc = getattr(module, function), func globals()['__old_' + func.__name__] = oldfunc except Exception: - warnings.warn("failed to import %s" % module_function) + if warn: + warnings.warn("failed to import %s" % module_function) return func return decorate @@ -4295,59 +4360,73 @@ def unpack_rgb(data, dtype='>> reverse_bitorder(b'\x01\x64') # doctest: +SKIP + b'\x80&' + >>> data = numpy.array([1, 666], dtype='uint16') >>> reverse_bitorder(data) >>> data array([ 128, 16473], dtype=uint16) """ - reverse = numpy.array([ - 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, - 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, - 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, - 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, - 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, - 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, - 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, - 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, - 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, - 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, - 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, - 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, - 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, - 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, - 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, - 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, - 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, - 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, - 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, - 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, - 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, - 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, - 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, - 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, - 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, - 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, - 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, - 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, - 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, - 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, - 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, - 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF], dtype='uint8') - view = data.view('uint8') - numpy.take(reverse, view, out=view) + try: + # numpy array + view = data.view('uint8') + table = numpy.array([ + 0x00, 0x80, 0x40, 0xC0, 0x20, 0xA0, 0x60, 0xE0, + 0x10, 0x90, 0x50, 0xD0, 0x30, 0xB0, 0x70, 0xF0, + 0x08, 0x88, 0x48, 0xC8, 0x28, 0xA8, 0x68, 0xE8, + 0x18, 0x98, 0x58, 0xD8, 0x38, 0xB8, 0x78, 0xF8, + 0x04, 0x84, 0x44, 0xC4, 0x24, 0xA4, 0x64, 0xE4, + 0x14, 0x94, 0x54, 0xD4, 0x34, 0xB4, 0x74, 0xF4, + 0x0C, 0x8C, 0x4C, 0xCC, 0x2C, 0xAC, 0x6C, 0xEC, + 0x1C, 0x9C, 0x5C, 0xDC, 0x3C, 0xBC, 0x7C, 0xFC, + 0x02, 0x82, 0x42, 0xC2, 0x22, 0xA2, 0x62, 0xE2, + 0x12, 0x92, 0x52, 0xD2, 0x32, 0xB2, 0x72, 0xF2, + 0x0A, 0x8A, 0x4A, 0xCA, 0x2A, 0xAA, 0x6A, 0xEA, + 0x1A, 0x9A, 0x5A, 0xDA, 0x3A, 0xBA, 0x7A, 0xFA, + 0x06, 0x86, 0x46, 0xC6, 0x26, 0xA6, 0x66, 0xE6, + 0x16, 0x96, 0x56, 0xD6, 0x36, 0xB6, 0x76, 0xF6, + 0x0E, 0x8E, 0x4E, 0xCE, 0x2E, 0xAE, 0x6E, 0xEE, + 0x1E, 0x9E, 0x5E, 0xDE, 0x3E, 0xBE, 0x7E, 0xFE, + 0x01, 0x81, 0x41, 0xC1, 0x21, 0xA1, 0x61, 0xE1, + 0x11, 0x91, 0x51, 0xD1, 0x31, 0xB1, 0x71, 0xF1, + 0x09, 0x89, 0x49, 0xC9, 0x29, 0xA9, 0x69, 0xE9, + 0x19, 0x99, 0x59, 0xD9, 0x39, 0xB9, 0x79, 0xF9, + 0x05, 0x85, 0x45, 0xC5, 0x25, 0xA5, 0x65, 0xE5, + 0x15, 0x95, 0x55, 0xD5, 0x35, 0xB5, 0x75, 0xF5, + 0x0D, 0x8D, 0x4D, 0xCD, 0x2D, 0xAD, 0x6D, 0xED, + 0x1D, 0x9D, 0x5D, 0xDD, 0x3D, 0xBD, 0x7D, 0xFD, + 0x03, 0x83, 0x43, 0xC3, 0x23, 0xA3, 0x63, 0xE3, + 0x13, 0x93, 0x53, 0xD3, 0x33, 0xB3, 0x73, 0xF3, + 0x0B, 0x8B, 0x4B, 0xCB, 0x2B, 0xAB, 0x6B, 0xEB, + 0x1B, 0x9B, 0x5B, 0xDB, 0x3B, 0xBB, 0x7B, 0xFB, + 0x07, 0x87, 0x47, 0xC7, 0x27, 0xA7, 0x67, 0xE7, + 0x17, 0x97, 0x57, 0xD7, 0x37, 0xB7, 0x77, 0xF7, + 0x0F, 0x8F, 0x4F, 0xCF, 0x2F, 0xAF, 0x6F, 0xEF, + 0x1F, 0x9F, 0x5F, 0xDF, 0x3F, 0xBF, 0x7F, 0xFF], dtype='uint8') + numpy.take(table, view, out=view) + except AttributeError: + # byte string + # TODO: use string translate + data = numpy.fromstring(data, dtype='uint8') + reverse_bitorder(data) + return data.tostring() def apply_colormap(image, colormap, contig=True): @@ -5760,6 +5839,21 @@ def _app_show(): pyplot.show() +def askopenfilename(**kwargs): + """Return file name(s) from Tkinter's file open dialog.""" + try: + from Tkinter import Tk + import tkFileDialog as filedialog + except ImportError: + from tkinter import Tk, filedialog + root = Tk() + root.withdraw() + root.update() + filenames = filedialog.askopenfilename(**kwargs) + root.destroy() + return filenames + + def main(argv=None): """Command line usage main function.""" if float(sys.version[0:3]) < 2.6: @@ -5809,14 +5903,12 @@ def main(argv=None): doctest.testmod() return 0 if not path: - try: - import tkFileDialog as filedialog - except ImportError: - from tkinter import filedialog - path = filedialog.askopenfilename(filetypes=[ - ("TIF files", "*.tif"), ("LSM files", "*.lsm"), - ("STK files", "*.stk"), ("allfiles", "*")]) - #parser.error("No file specified") + path = askopenfilename( + title="Select a TIFF file", + filetypes=[("TIF files", "*.tif"), ("LSM files", "*.lsm"), + ("STK files", "*.stk"), ("allfiles", "*")]) + if not path: + parser.error("No file specified") if settings.test: test_tifffile(path, settings.verbose) return 0