updates tifffile with upstream changes

This commit is contained in:
jwittenbach
2016-04-19 12:43:45 -04:00
parent bfb6175485
commit ad89b14d4f
2 changed files with 337 additions and 153 deletions
+130 -38
View File
@@ -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 <http://www.python.org>`_
* `Numpy 1.9.2 <http://www.numpy.org>`_
* `CPython 2.7 or 3.5 <http://www.python.org>`_
* `Numpy 1.10 <http://www.numpy.org>`_
* 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
+207 -115
View File
@@ -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 <http://www.python.org>`_ (64 bit recommended)
* `Numpy 1.10 <http://www.numpy.org>`_
* `Matplotlib 1.5 <http://www.matplotlib.org>`_ (optional for plotting)
* `Tifffile.c 2015.08.17 <http://www.lfd.uci.edu/~gohlke/>`_
* `Tifffile.c 2016.04.13 <http://www.lfd.uci.edu/~gohlke/>`_
(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 <http://luispedro.org/software/imread>`_
* `PyLibTiff <http://code.google.com/p/pylibtiff>`_
* `Imread <https://github.com/luispedro/imread>`_
* `PyLibTiff <https://github.com/pearu/pylibtiff>`_
* `SimpleITK <http://www.simpleitk.org>`_
* `PyLSM <https://launchpad.net/pylsm>`_
* `PyMca.TiffIO.py <http://pymca.sourceforge.net/>`_ (same as fabio.TiffIO)
* `PyMca.TiffIO.py <https://github.com/vasole/pymca>`_ (same as fabio.TiffIO)
* `BioImageXD.Readers <http://www.bioimagexd.net/>`_
* `Cellcognition.io <http://cellcognition.org/>`_
* `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='<B', bitspersample=(5, 6, 5), rescale=True):
return result.reshape(-1)
@_replace_by('_tifffile.reverse_bitorder')
def reverse_bitorder(data):
"""In-place reverse bits in each byte.
"""Reverse bits in each byte of byte string or numpy array.
Decode data where pixels with lower column values are stored in the
lower-order bits of the bytes (fill_order == 'lsb2msb').
Parameters
----------
data : ndarray
The data to be bit reversed in-place.
Pixels with lower column values stored in the lower-order bits
of the bytes (fill_order == 'lsb2msb').
data : byte string or ndarray
The data to be bit reversed. If byte string, a new bit-reversed byte
string is returned. Numpy arrays are bit-reversed in-place.
Examples
--------
>>> 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