mirror of
https://github.com/wassname/scikit-image.git
synced 2026-07-21 12:50:27 +08:00
Merge pull request #1189 from blink1073/fix-pil-plugin
Fix PIL handling of non-uint8 images and add `tifffile` support
This commit is contained in:
@@ -6,6 +6,7 @@ source = skimage
|
||||
include = */skimage/*
|
||||
omit =
|
||||
*/setup.py
|
||||
*/skimage/io/_plugins/tifffile.py
|
||||
|
||||
[report]
|
||||
exclude_lines =
|
||||
|
||||
+2
-1
@@ -23,6 +23,7 @@ before_install:
|
||||
- sudo apt-get update
|
||||
|
||||
- travis_retry pip install wheel flake8 coveralls nose
|
||||
- travis_retry pip uninstall -y numpy
|
||||
|
||||
# on Python 2.7, use the system versions of numpy, scipy, and matplotlib
|
||||
# and the minimum version of cython and networkx
|
||||
@@ -32,7 +33,7 @@ before_install:
|
||||
pip install https://github.com/networkx/networkx/archive/networkx-1.8.tar.gz;
|
||||
fi
|
||||
|
||||
- travis_retry pip install -r requirements.txt $WHEELHOUSE;
|
||||
- travis_retry pip install -r requirements.txt $WHEELHOUSE;
|
||||
- python check_bento_build.py
|
||||
|
||||
install:
|
||||
|
||||
@@ -157,6 +157,9 @@ Library:
|
||||
Extension: skimage.graph._ncut_cy
|
||||
Sources:
|
||||
skimage/graph/_ncut_cy.pyx
|
||||
Extension: skimage.io._plugins._tifffile
|
||||
Sources:
|
||||
skimage/io/_plugins/tifffile.c
|
||||
|
||||
Executable: skivi
|
||||
Module: skimage.scripts.skivi
|
||||
|
||||
@@ -27,4 +27,4 @@
|
||||
failing (useful for installing from third party sources)
|
||||
- Feel free to cancel a build rather than waiting for it to go to completion
|
||||
if you have made a change to that branch.
|
||||
- A VM with 64bit Ubuntu 12.04 is a huge help.
|
||||
- A VM with 64bit Ubuntu 12.04 is a huge help for debugging.
|
||||
|
||||
@@ -1,7 +1,14 @@
|
||||
"""Testing utilities."""
|
||||
|
||||
|
||||
import os
|
||||
import re
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from skimage import (
|
||||
data, io, img_as_uint, img_as_float, img_as_int, img_as_ubyte)
|
||||
from numpy import testing
|
||||
import numpy as np
|
||||
|
||||
|
||||
SKIP_RE = re.compile("(\s*>>>.*?)(\s*)#\s*skip\s+if\s+(.*)$")
|
||||
@@ -75,3 +82,93 @@ def doctest_skip_parser(func):
|
||||
new_lines.append(code)
|
||||
func.__doc__ = "\n".join(new_lines)
|
||||
return func
|
||||
|
||||
|
||||
def roundtrip(img, plugin, suffix):
|
||||
"""Save and read an image using a specified plugin"""
|
||||
if not '.' in suffix:
|
||||
suffix = '.' + suffix
|
||||
temp_file = NamedTemporaryFile(suffix=suffix, delete=False)
|
||||
temp_file.close()
|
||||
fname = temp_file.name
|
||||
io.imsave(fname, img, plugin=plugin)
|
||||
new = io.imread(fname, plugin=plugin)
|
||||
try:
|
||||
os.remove(fname)
|
||||
except Exception:
|
||||
pass
|
||||
return new
|
||||
|
||||
|
||||
def color_check(plugin, fmt='png'):
|
||||
"""Check roundtrip behavior for color images.
|
||||
|
||||
All major input types should be handled as ubytes and read
|
||||
back correctly.
|
||||
"""
|
||||
img = img_as_ubyte(data.chelsea())
|
||||
r1 = roundtrip(img, plugin, fmt)
|
||||
testing.assert_allclose(img, r1)
|
||||
|
||||
img2 = img > 128
|
||||
r2 = roundtrip(img2, plugin, fmt)
|
||||
testing.assert_allclose(img2.astype(np.uint8), r2)
|
||||
|
||||
img3 = img_as_float(img)
|
||||
r3 = roundtrip(img3, plugin, fmt)
|
||||
testing.assert_allclose(r3, img)
|
||||
|
||||
img4 = img_as_int(img)
|
||||
if fmt.lower() in (('tif', 'tiff')):
|
||||
img4 -= 100
|
||||
r4 = roundtrip(img4, plugin, fmt)
|
||||
testing.assert_allclose(r4, img4)
|
||||
else:
|
||||
r4 = roundtrip(img4, plugin, fmt)
|
||||
testing.assert_allclose(r4, img_as_ubyte(img4))
|
||||
|
||||
img5 = img_as_uint(img)
|
||||
r5 = roundtrip(img5, plugin, fmt)
|
||||
testing.assert_allclose(r5, img)
|
||||
|
||||
|
||||
def mono_check(plugin, fmt='png'):
|
||||
"""Check the roundtrip behavior for images that support most types.
|
||||
|
||||
All major input types should be handled.
|
||||
"""
|
||||
|
||||
img = img_as_ubyte(data.moon())
|
||||
r1 = roundtrip(img, plugin, fmt)
|
||||
testing.assert_allclose(img, r1)
|
||||
|
||||
img2 = img > 128
|
||||
r2 = roundtrip(img2, plugin, fmt)
|
||||
testing.assert_allclose(img2.astype(np.uint8), r2)
|
||||
|
||||
img3 = img_as_float(img)
|
||||
r3 = roundtrip(img3, plugin, fmt)
|
||||
if r3.dtype.kind == 'f':
|
||||
testing.assert_allclose(img3, r3)
|
||||
else:
|
||||
testing.assert_allclose(r3, img_as_uint(img))
|
||||
|
||||
img4 = img_as_int(img)
|
||||
if fmt.lower() in (('tif', 'tiff')):
|
||||
img4 -= 100
|
||||
r4 = roundtrip(img4, plugin, fmt)
|
||||
testing.assert_allclose(r4, img4)
|
||||
else:
|
||||
r4 = roundtrip(img4, plugin, fmt)
|
||||
testing.assert_allclose(r4, img_as_uint(img4))
|
||||
|
||||
img5 = img_as_uint(img)
|
||||
r5 = roundtrip(img5, plugin, fmt)
|
||||
testing.assert_allclose(r5, img5)
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
color_check('pil')
|
||||
mono_check('pil')
|
||||
mono_check('pil', 'bmp')
|
||||
mono_check('pil', 'tiff')
|
||||
|
||||
@@ -0,0 +1,8 @@
|
||||
|
||||
_plugins.tifffile.py
|
||||
|
||||
Copyright (c) 2008-2014, Christoph Gohlke
|
||||
Copyright (c) 2008-2014, The Regents of the University of California
|
||||
Produced at the Laboratory for Fluorescence Dynamics
|
||||
|
||||
Distributed under the terms of the BSD License.
|
||||
@@ -1,4 +1,4 @@
|
||||
__all__ = ['imread']
|
||||
__all__ = ['imread', 'imsave']
|
||||
|
||||
import numpy as np
|
||||
|
||||
@@ -11,9 +11,10 @@ except ImportError:
|
||||
"http://pypi.python.org/pypi/PIL/) "
|
||||
"for further instructions.")
|
||||
|
||||
from skimage.util import img_as_ubyte
|
||||
from skimage.util import img_as_ubyte, img_as_uint
|
||||
|
||||
from six import string_types
|
||||
from .tifffile import imread as tif_imread, imsave as tif_imsave
|
||||
|
||||
|
||||
def imread(fname, dtype=None):
|
||||
@@ -26,8 +27,24 @@ def imread(fname, dtype=None):
|
||||
dtype : numpy dtype object or string specifier
|
||||
Specifies data type of array elements.
|
||||
|
||||
Notes
|
||||
-----
|
||||
Tiff files are handled by Christophe Golhke's tifffile.py [1]_, and support many
|
||||
advanced image types including multi-page and floating point.
|
||||
|
||||
All other files are read using the Python Imaging Libary.
|
||||
See PIL docs [2]_ for a list of supported formats.
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html
|
||||
.. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
|
||||
|
||||
"""
|
||||
if hasattr(fname, 'lower') and dtype is None:
|
||||
if fname.lower().endswith(('.tiff', '.tif')):
|
||||
return tif_imread(fname)
|
||||
|
||||
im = Image.open(fname)
|
||||
try:
|
||||
# this will raise an IOError if the file is not readable
|
||||
@@ -58,6 +75,8 @@ def pil_to_ndarray(im, dtype=None):
|
||||
elif im.mode.startswith('I;16'):
|
||||
shape = im.size
|
||||
dtype = '>u2' if im.mode.endswith('B') else '<u2'
|
||||
if 'S' in im.mode:
|
||||
dtype = dtype.replace('u', 'i')
|
||||
im = np.fromstring(im.tostring(), dtype)
|
||||
im.shape = shape[::-1]
|
||||
elif 'A' in im.mode:
|
||||
@@ -100,38 +119,41 @@ def ndarray_to_pil(arr, format_str=None):
|
||||
Refer to ``imsave``.
|
||||
|
||||
"""
|
||||
arr = np.asarray(arr).squeeze()
|
||||
|
||||
if arr.ndim not in (2, 3):
|
||||
raise ValueError("Invalid shape for image array: %s" % arr.shape)
|
||||
|
||||
if arr.ndim == 3:
|
||||
if arr.shape[2] not in (3, 4):
|
||||
raise ValueError("Invalid number of channels in image array.")
|
||||
|
||||
# Image is floating point, assume in [0, 1]
|
||||
if np.issubdtype(arr.dtype, float):
|
||||
arr = arr * 255
|
||||
|
||||
arr = arr.astype(np.uint8)
|
||||
|
||||
if arr.ndim == 2:
|
||||
mode = 'L'
|
||||
|
||||
elif arr.shape[2] in (3, 4):
|
||||
arr = img_as_ubyte(arr)
|
||||
mode = {3: 'RGB', 4: 'RGBA'}[arr.shape[2]]
|
||||
|
||||
# Force all integers to bytes
|
||||
arr = arr.astype(np.uint8)
|
||||
elif format_str in ['png', 'PNG']:
|
||||
mode = 'I;16'
|
||||
mode_base = 'I'
|
||||
|
||||
try:
|
||||
img = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),
|
||||
arr.tostring())
|
||||
except AttributeError:
|
||||
img = Image.fromstring(mode, (arr.shape[1], arr.shape[0]),
|
||||
arr.tostring())
|
||||
if arr.dtype.kind == 'f':
|
||||
arr = img_as_uint(arr)
|
||||
|
||||
return img
|
||||
elif arr.max() < 256 and arr.min() >= 0:
|
||||
arr = arr.astype(np.uint8)
|
||||
mode = mode_base = 'L'
|
||||
|
||||
else:
|
||||
arr = img_as_uint(arr)
|
||||
|
||||
else:
|
||||
arr = img_as_ubyte(arr)
|
||||
mode = 'L'
|
||||
mode_base = 'L'
|
||||
|
||||
if arr.ndim == 2:
|
||||
im = Image.new(mode_base, arr.T.shape)
|
||||
im.fromstring(arr.tostring(), 'raw', mode)
|
||||
|
||||
else:
|
||||
try:
|
||||
im = Image.frombytes(mode, (arr.shape[1], arr.shape[0]),
|
||||
arr.tostring())
|
||||
except AttributeError:
|
||||
im = Image.fromstring(mode, (arr.shape[1], arr.shape[0]),
|
||||
arr.tostring())
|
||||
return im
|
||||
|
||||
|
||||
def imsave(fname, arr, format_str=None):
|
||||
@@ -151,14 +173,55 @@ def imsave(fname, arr, format_str=None):
|
||||
|
||||
Notes
|
||||
-----
|
||||
Currently, only 8-bit precision is supported.
|
||||
Tiff files are handled by Christophe Golhke's tifffile.py [1]_,
|
||||
and support many advanced image types including multi-page and
|
||||
floating point.
|
||||
|
||||
All other image formats use the Python Imaging Libary.
|
||||
See PIL docs [2]_ for a list of other supported formats.
|
||||
All images besides single channel PNGs are converted using `img_as_uint8`.
|
||||
Single Channel PNGs have the following behavior:
|
||||
- Integer values in [0, 255] and Boolean types -> img_as_uint8
|
||||
- Floating point and other integers -> img_as_uint16
|
||||
|
||||
References
|
||||
----------
|
||||
.. [1] http://www.lfd.uci.edu/~gohlke/code/tifffile.py.html
|
||||
.. [2] http://pillow.readthedocs.org/en/latest/handbook/image-file-formats.html
|
||||
"""
|
||||
# default to PNG if file-like object
|
||||
if not isinstance(fname, string_types) and format_str is None:
|
||||
format_str = "PNG"
|
||||
# Check for png in filename
|
||||
if (isinstance(fname, string_types)
|
||||
and fname.lower().endswith(".png")):
|
||||
format_str = "PNG"
|
||||
|
||||
img = ndarray_to_pil(arr, format_str=None)
|
||||
arr = np.asanyarray(arr).squeeze()
|
||||
|
||||
if arr.dtype.kind == 'b':
|
||||
arr = arr.astype(np.uint8)
|
||||
|
||||
use_tif = False
|
||||
if hasattr(fname, 'lower'):
|
||||
if fname.lower().endswith(('.tiff', '.tif')):
|
||||
use_tif = True
|
||||
if not format_str is None:
|
||||
if format_str.lower() in ['tiff', 'tif']:
|
||||
use_tif = True
|
||||
|
||||
if use_tif:
|
||||
tif_imsave(fname, arr)
|
||||
return
|
||||
|
||||
if arr.ndim not in (2, 3):
|
||||
raise ValueError("Invalid shape for image array: %s" % arr.shape)
|
||||
|
||||
if arr.ndim == 3:
|
||||
if arr.shape[2] not in (3, 4):
|
||||
raise ValueError("Invalid number of channels in image array.")
|
||||
|
||||
img = ndarray_to_pil(arr, format_str=format_str)
|
||||
img.save(fname, format=format_str)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,962 @@
|
||||
|
||||
|
||||
/* tifffile.c
|
||||
|
||||
A Python C extension module for decoding PackBits and LZW encoded TIFF data.
|
||||
|
||||
Refer to the tifffile.py module for documentation and tests.
|
||||
|
||||
:Author:
|
||||
`Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>`_
|
||||
|
||||
:Organization:
|
||||
Laboratory for Fluorescence Dynamics, University of California, Irvine
|
||||
|
||||
:Version: 2013.11.05
|
||||
|
||||
Install
|
||||
-------
|
||||
Use this Python distutils setup script to build the extension module::
|
||||
|
||||
# setup.py
|
||||
# Usage: ``python setup.py build_ext --inplace``
|
||||
from distutils.core import setup, Extension
|
||||
import numpy
|
||||
setup(name='_tifffile',
|
||||
ext_modules=[Extension('_tifffile', ['tifffile.c'],
|
||||
include_dirs=[numpy.get_include()])])
|
||||
|
||||
License
|
||||
-------
|
||||
Copyright (c) 2008-2014, Christoph Gohlke
|
||||
Copyright (c) 2008-2014, The Regents of the University of California
|
||||
Produced at the Laboratory for Fluorescence Dynamics
|
||||
All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
* Neither the name of the copyright holders nor the names of any
|
||||
contributors may be used to endorse or promote products derived
|
||||
from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
||||
ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
||||
LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
||||
CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
||||
SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
||||
INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
||||
CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
||||
ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _VERSION_ "2013.11.05"
|
||||
|
||||
#define WIN32_LEAN_AND_MEAN
|
||||
#define NPY_NO_DEPRECATED_API NPY_1_7_API_VERSION
|
||||
|
||||
#include "Python.h"
|
||||
#include "string.h"
|
||||
#include "numpy/arrayobject.h"
|
||||
|
||||
/* little endian by default */
|
||||
#ifndef MSB
|
||||
#define MSB 1
|
||||
#endif
|
||||
|
||||
#if MSB
|
||||
#define LSB 0
|
||||
#define BOC '<'
|
||||
#else
|
||||
#define LSB 1
|
||||
#define BOC '>'
|
||||
#endif
|
||||
|
||||
#define NO_ERROR 0
|
||||
#define VALUE_ERROR -1
|
||||
|
||||
#ifdef _MSC_VER
|
||||
typedef unsigned __int8 uint8_t;
|
||||
typedef unsigned __int16 uint16_t;
|
||||
typedef unsigned __int32 uint32_t;
|
||||
typedef unsigned __int64 uint64_t;
|
||||
#ifdef _WIN64
|
||||
typedef __int64 ssize_t;
|
||||
typedef signed __int64 intptr_t;
|
||||
typedef unsigned __int64 uintptr_t;
|
||||
#define SSIZE_MAX (9223372036854775808)
|
||||
#else
|
||||
typedef int ssize_t;
|
||||
typedef _W64 signed int intptr_t;
|
||||
typedef _W64 unsigned int uintptr_t;
|
||||
#define SSIZE_MAX (2147483648)
|
||||
#endif
|
||||
#else
|
||||
/* non MS compilers */
|
||||
#include <stdint.h>
|
||||
#include <limits.h>
|
||||
#endif
|
||||
|
||||
#define SWAP2BYTES(x) \
|
||||
((((x) >> 8) & 0x00FF) | (((x) & 0x00FF) << 8))
|
||||
|
||||
#define SWAP4BYTES(x) \
|
||||
((((x) >> 24) & 0x00FF) | (((x)&0x00FF) << 24) | \
|
||||
(((x) >> 8 ) & 0xFF00) | (((x)&0xFF00) << 8))
|
||||
|
||||
#define SWAP8BYTES(x) \
|
||||
((((x) >> 56) & 0x00000000000000FF) | (((x) >> 40) & 0x000000000000FF00) | \
|
||||
(((x) >> 24) & 0x0000000000FF0000) | (((x) >> 8) & 0x00000000FF000000) | \
|
||||
(((x) << 8) & 0x000000FF00000000) | (((x) << 24) & 0x0000FF0000000000) | \
|
||||
(((x) << 40) & 0x00FF000000000000) | (((x) << 56) & 0xFF00000000000000))
|
||||
|
||||
struct BYTE_STRING {
|
||||
unsigned int ref; /* reference count */
|
||||
unsigned int len; /* length of string */
|
||||
char *str; /* pointer to bytes */
|
||||
};
|
||||
|
||||
typedef union {
|
||||
uint8_t b[2];
|
||||
uint16_t i;
|
||||
} u_uint16;
|
||||
|
||||
typedef union {
|
||||
uint8_t b[4];
|
||||
uint32_t i;
|
||||
} u_uint32;
|
||||
|
||||
typedef union {
|
||||
uint8_t b[8];
|
||||
uint64_t i;
|
||||
} u_uint64;
|
||||
|
||||
/*****************************************************************************/
|
||||
/* C functions */
|
||||
|
||||
/* Return mask for itemsize bits */
|
||||
unsigned char bitmask(const int itemsize) {
|
||||
unsigned char result = 0;
|
||||
unsigned char power = 1;
|
||||
int i;
|
||||
for (i = 0; i < itemsize; i++) {
|
||||
result += power;
|
||||
power *= 2;
|
||||
}
|
||||
return result << (8 - itemsize);
|
||||
}
|
||||
|
||||
/** Unpack sequence of tigthly packed 1-32 bit integers.
|
||||
|
||||
Native byte order will be returned.
|
||||
|
||||
Input data array should be padded to the next 16, 32 or 64-bit boundary
|
||||
if itemsize not in (1, 2, 4, 8, 16, 24, 32, 64).
|
||||
|
||||
*/
|
||||
int unpackbits(
|
||||
unsigned char *data,
|
||||
const ssize_t size, /** size of data in bytes */
|
||||
const int itemsize, /** number of bits in integer */
|
||||
ssize_t numitems, /** number of items to unpack */
|
||||
unsigned char *result /** buffer to store unpacked items */
|
||||
)
|
||||
{
|
||||
ssize_t i, j, k, storagesize;
|
||||
unsigned char value;
|
||||
/* Input validation is done in wrapper function */
|
||||
storagesize = (ssize_t)(ceil(itemsize / 8.0));
|
||||
storagesize = storagesize < 3 ? storagesize : storagesize > 4 ? 8 : 4;
|
||||
switch (itemsize) {
|
||||
case 8:
|
||||
case 16:
|
||||
case 32:
|
||||
case 64:
|
||||
memcpy(result, data, numitems*storagesize);
|
||||
return NO_ERROR;
|
||||
case 1:
|
||||
for (i = 0, j = 0; i < numitems/8; i++) {
|
||||
value = data[i];
|
||||
result[j++] = (value & (unsigned char)(128)) >> 7;
|
||||
result[j++] = (value & (unsigned char)(64)) >> 6;
|
||||
result[j++] = (value & (unsigned char)(32)) >> 5;
|
||||
result[j++] = (value & (unsigned char)(16)) >> 4;
|
||||
result[j++] = (value & (unsigned char)(8)) >> 3;
|
||||
result[j++] = (value & (unsigned char)(4)) >> 2;
|
||||
result[j++] = (value & (unsigned char)(2)) >> 1;
|
||||
result[j++] = (value & (unsigned char)(1));
|
||||
}
|
||||
if (numitems % 8) {
|
||||
value = data[i];
|
||||
switch (numitems % 8) {
|
||||
case 7: result[j+6] = (value & (unsigned char)(2)) >> 1;
|
||||
case 6: result[j+5] = (value & (unsigned char)(4)) >> 2;
|
||||
case 5: result[j+4] = (value & (unsigned char)(8)) >> 3;
|
||||
case 4: result[j+3] = (value & (unsigned char)(16)) >> 4;
|
||||
case 3: result[j+2] = (value & (unsigned char)(32)) >> 5;
|
||||
case 2: result[j+1] = (value & (unsigned char)(64)) >> 6;
|
||||
case 1: result[j] = (value & (unsigned char)(128)) >> 7;
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
case 2:
|
||||
for (i = 0, j = 0; i < numitems/4; i++) {
|
||||
value = data[i];
|
||||
result[j++] = (value & (unsigned char)(192)) >> 6;
|
||||
result[j++] = (value & (unsigned char)(48)) >> 4;
|
||||
result[j++] = (value & (unsigned char)(12)) >> 2;
|
||||
result[j++] = (value & (unsigned char)(3));
|
||||
}
|
||||
if (numitems % 4) {
|
||||
value = data[i];
|
||||
switch (numitems % 4) {
|
||||
case 3: result[j+2] = (value & (unsigned char)(12)) >> 2;
|
||||
case 2: result[j+1] = (value & (unsigned char)(48)) >> 4;
|
||||
case 1: result[j] = (value & (unsigned char)(192)) >> 6;
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
case 4:
|
||||
for (i = 0, j = 0; i < numitems/2; i++) {
|
||||
value = data[i];
|
||||
result[j++] = (value & (unsigned char)(240)) >> 4;
|
||||
result[j++] = (value & (unsigned char)(15));
|
||||
}
|
||||
if (numitems % 2) {
|
||||
value = data[i];
|
||||
result[j] = (value & (unsigned char)(240)) >> 4;
|
||||
}
|
||||
return NO_ERROR;
|
||||
case 24:
|
||||
j = k = 0;
|
||||
for (i = 0; i < numitems; i++) {
|
||||
result[j++] = 0;
|
||||
result[j++] = data[k++];
|
||||
result[j++] = data[k++];
|
||||
result[j++] = data[k++];
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
/* 3, 5, 6, 7 */
|
||||
if (itemsize < 8) {
|
||||
int shr = 16;
|
||||
u_uint16 value, mask, tmp;
|
||||
j = k = 0;
|
||||
value.b[MSB] = data[j++];
|
||||
value.b[LSB] = data[j++];
|
||||
mask.b[MSB] = bitmask(itemsize);
|
||||
mask.b[LSB] = 0;
|
||||
for (i = 0; i < numitems; i++) {
|
||||
shr -= itemsize;
|
||||
tmp.i = (value.i & mask.i) >> shr;
|
||||
result[k++] = tmp.b[LSB];
|
||||
if (shr < itemsize) {
|
||||
value.b[MSB] = value.b[LSB];
|
||||
value.b[LSB] = data[j++];
|
||||
mask.i <<= 8 - itemsize;
|
||||
shr += 8;
|
||||
} else {
|
||||
mask.i >>= itemsize;
|
||||
}
|
||||
}
|
||||
return NO_ERROR;
|
||||
}
|
||||
/* 9, 10, 11, 12, 13, 14, 15 */
|
||||
if (itemsize < 16) {
|
||||
int shr = 32;
|
||||
u_uint32 value, mask, tmp;
|
||||
mask.i = 0;
|
||||
j = k = 0;
|
||||
#if MSB
|
||||
for (i = 3; i >= 0; i--) {
|
||||
value.b[i] = data[j++];
|
||||
}
|
||||
mask.b[3] = 0xFF;
|
||||
mask.b[2] = bitmask(itemsize-8);
|
||||
for (i = 0; i < numitems; i++) {
|
||||
shr -= itemsize;
|
||||
tmp.i = (value.i & mask.i) >> shr;
|
||||
result[k++] = tmp.b[0]; /* swap bytes */
|
||||
result[k++] = tmp.b[1];
|
||||
if (shr < itemsize) {
|
||||
value.b[3] = value.b[1];
|
||||
value.b[2] = value.b[0];
|
||||
value.b[1] = data[j++];
|
||||
value.b[0] = data[j++];
|
||||
mask.i <<= 16 - itemsize;
|
||||
shr += 16;
|
||||
} else {
|
||||
mask.i >>= itemsize;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* not implemented */
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
/* 17, 18, 19, 20, 21, 22, 23, 25, 26, 27, 28, 29, 30, 31 */
|
||||
if (itemsize < 32) {
|
||||
int shr = 64;
|
||||
u_uint64 value, mask, tmp;
|
||||
mask.i = 0;
|
||||
j = k = 0;
|
||||
#if MSB
|
||||
for (i = 7; i >= 0; i--) {
|
||||
value.b[i] = data[j++];
|
||||
}
|
||||
mask.b[7] = 0xFF;
|
||||
mask.b[6] = 0xFF;
|
||||
mask.b[5] = itemsize > 23 ? 0xFF : bitmask(itemsize-16);
|
||||
mask.b[4] = itemsize < 24 ? 0x00 : bitmask(itemsize-24);
|
||||
for (i = 0; i < numitems; i++) {
|
||||
shr -= itemsize;
|
||||
tmp.i = (value.i & mask.i) >> shr;
|
||||
result[k++] = tmp.b[0]; /* swap bytes */
|
||||
result[k++] = tmp.b[1];
|
||||
result[k++] = tmp.b[2];
|
||||
result[k++] = tmp.b[3];
|
||||
if (shr < itemsize) {
|
||||
value.b[7] = value.b[3];
|
||||
value.b[6] = value.b[2];
|
||||
value.b[5] = value.b[1];
|
||||
value.b[4] = value.b[0];
|
||||
value.b[3] = data[j++];
|
||||
value.b[2] = data[j++];
|
||||
value.b[1] = data[j++];
|
||||
value.b[0] = data[j++];
|
||||
mask.i <<= 32 - itemsize;
|
||||
shr += 32;
|
||||
} else {
|
||||
mask.i >>= itemsize;
|
||||
}
|
||||
}
|
||||
#else
|
||||
/* Not implemented */
|
||||
#endif
|
||||
return NO_ERROR;
|
||||
}
|
||||
return VALUE_ERROR;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Python functions */
|
||||
|
||||
/** Unpack tightly packed integers. */
|
||||
char py_unpackints_doc[] = "Unpack groups of bits into numpy array.";
|
||||
|
||||
static PyObject*
|
||||
py_unpackints(PyObject *obj, PyObject *args, PyObject *kwds)
|
||||
{
|
||||
PyObject *byteobj = NULL;
|
||||
PyArrayObject *result = NULL;
|
||||
PyArray_Descr *dtype = NULL;
|
||||
char *encoded = NULL;
|
||||
char *decoded = NULL;
|
||||
Py_ssize_t encoded_len = 0;
|
||||
Py_ssize_t decoded_len = 0;
|
||||
Py_ssize_t runlen = 0;
|
||||
Py_ssize_t i;
|
||||
int storagesize, bytesize;
|
||||
int itemsize = 0;
|
||||
int skipbits = 0;
|
||||
static char *kwlist[] = {"data", "dtype", "itemsize", "runlen", NULL};
|
||||
|
||||
if (!PyArg_ParseTupleAndKeywords(args, kwds, "OO&i|i", kwlist,
|
||||
&byteobj, PyArray_DescrConverter, &dtype, &itemsize, &runlen))
|
||||
return NULL;
|
||||
|
||||
Py_INCREF(byteobj);
|
||||
|
||||
if (((itemsize < 1) || (itemsize > 32)) && (itemsize != 64)) {
|
||||
PyErr_Format(PyExc_ValueError, "itemsize out of range");
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (!PyBytes_Check(byteobj)) {
|
||||
PyErr_Format(PyExc_TypeError, "expected byte string as input");
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
encoded = PyBytes_AS_STRING(byteobj);
|
||||
encoded_len = PyBytes_GET_SIZE(byteobj);
|
||||
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;
|
||||
}
|
||||
if (dtype->elsize != storagesize) {
|
||||
PyErr_Format(PyExc_TypeError, "dtype.elsize doesn't fit itemsize");
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
if (runlen == 0) {
|
||||
runlen = (Py_ssize_t)(((uint64_t)encoded_len*8) / (uint64_t)itemsize);
|
||||
}
|
||||
skipbits = (Py_ssize_t)(((uint64_t)runlen * (uint64_t)itemsize) % 8);
|
||||
if (skipbits > 0) {
|
||||
skipbits = 8 - skipbits;
|
||||
}
|
||||
decoded_len = (Py_ssize_t)((uint64_t)runlen * (((uint64_t)encoded_len*8) /
|
||||
((uint64_t)runlen*(uint64_t)itemsize + (uint64_t)skipbits)));
|
||||
|
||||
result = (PyArrayObject *)PyArray_SimpleNew(1, &decoded_len,
|
||||
dtype->type_num);
|
||||
if (result == NULL) {
|
||||
PyErr_Format(PyExc_MemoryError, "unable to allocate output array");
|
||||
goto _fail;
|
||||
}
|
||||
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;
|
||||
}
|
||||
encoded += (Py_ssize_t)(((uint64_t)runlen * (uint64_t)itemsize +
|
||||
(uint64_t)skipbits) / 8);
|
||||
decoded += runlen * storagesize;
|
||||
}
|
||||
|
||||
if ((dtype->byteorder != BOC) && (itemsize % 8 == 0)) {
|
||||
switch (dtype->elsize) {
|
||||
case 2: {
|
||||
uint16_t *d = (uint16_t *)PyArray_DATA(result);
|
||||
for (i = 0; i < PyArray_SIZE(result); i++) {
|
||||
*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++;
|
||||
}
|
||||
break; }
|
||||
case 8: {
|
||||
uint64_t *d = (uint64_t *)PyArray_DATA(result);
|
||||
for (i = 0; i < PyArray_SIZE(result); i++) {
|
||||
*d = SWAP8BYTES(*d); d++;
|
||||
}
|
||||
break; }
|
||||
}
|
||||
}
|
||||
Py_DECREF(byteobj);
|
||||
Py_DECREF(dtype);
|
||||
return PyArray_Return(result);
|
||||
|
||||
_fail:
|
||||
Py_XDECREF(byteobj);
|
||||
Py_XDECREF(result);
|
||||
Py_XDECREF(dtype);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/** Decode TIFF PackBits encoded string. */
|
||||
char py_decodepackbits_doc[] = "Return TIFF PackBits decoded string.";
|
||||
|
||||
static PyObject *
|
||||
py_decodepackbits(PyObject *obj, PyObject *args)
|
||||
{
|
||||
int n;
|
||||
char e;
|
||||
char *decoded = NULL;
|
||||
char *encoded = NULL;
|
||||
char *encoded_end = NULL;
|
||||
char *encoded_pos = NULL;
|
||||
unsigned int encoded_len;
|
||||
unsigned int decoded_len;
|
||||
PyObject *byteobj = NULL;
|
||||
PyObject *result = NULL;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &byteobj))
|
||||
return NULL;
|
||||
|
||||
if (!PyBytes_Check(byteobj)) {
|
||||
PyErr_Format(PyExc_TypeError, "expected byte string as input");
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
Py_INCREF(byteobj);
|
||||
encoded = PyBytes_AS_STRING(byteobj);
|
||||
encoded_len = (unsigned int)PyBytes_GET_SIZE(byteobj);
|
||||
|
||||
/* release GIL: byte/string objects are immutable */
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
/* determine size of decoded string */
|
||||
encoded_pos = encoded;
|
||||
encoded_end = encoded + encoded_len;
|
||||
decoded_len = 0;
|
||||
while (encoded_pos < encoded_end) {
|
||||
n = (int)*encoded_pos++;
|
||||
if (n >= 0) {
|
||||
n++;
|
||||
if (encoded_pos+n > encoded_end)
|
||||
n = (int)(encoded_end - encoded_pos);
|
||||
encoded_pos += n;
|
||||
decoded_len += n;
|
||||
} else if (n > -128) {
|
||||
encoded_pos++;
|
||||
decoded_len += 1-n;
|
||||
}
|
||||
}
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
result = PyBytes_FromStringAndSize(0, decoded_len);
|
||||
if (result == NULL) {
|
||||
PyErr_Format(PyExc_MemoryError, "failed to allocate decoded string");
|
||||
goto _fail;
|
||||
}
|
||||
decoded = PyBytes_AS_STRING(result);
|
||||
|
||||
Py_BEGIN_ALLOW_THREADS
|
||||
|
||||
/* decode string */
|
||||
encoded_end = encoded + encoded_len;
|
||||
while (encoded < encoded_end) {
|
||||
n = (int)*encoded++;
|
||||
if (n >= 0) {
|
||||
n++;
|
||||
if (encoded+n > encoded_end)
|
||||
n = (int)(encoded_end - encoded);
|
||||
/* memmove(decoded, encoded, n); decoded += n; encoded += n; */
|
||||
while (n--)
|
||||
*decoded++ = *encoded++;
|
||||
} else if (n > -128) {
|
||||
n = 1 - n;
|
||||
e = *encoded++;
|
||||
/* memset(decoded, e, n); decoded += n; */
|
||||
while (n--)
|
||||
*decoded++ = e;
|
||||
}
|
||||
}
|
||||
Py_END_ALLOW_THREADS
|
||||
|
||||
Py_DECREF(byteobj);
|
||||
return result;
|
||||
|
||||
_fail:
|
||||
Py_XDECREF(byteobj);
|
||||
Py_XDECREF(result);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
/** Decode TIFF LZW encoded string. */
|
||||
char py_decodelzw_doc[] = "Return TIFF LZW decoded string.";
|
||||
|
||||
static PyObject *
|
||||
py_decodelzw(PyObject *obj, PyObject *args)
|
||||
{
|
||||
PyThreadState *_save = NULL;
|
||||
PyObject *byteobj = NULL;
|
||||
PyObject *result = NULL;
|
||||
int i, j;
|
||||
unsigned int encoded_len = 0;
|
||||
unsigned int decoded_len = 0;
|
||||
unsigned int result_len = 0;
|
||||
unsigned int table_len = 0;
|
||||
unsigned int len;
|
||||
unsigned int code, c, oldcode, mask, shr;
|
||||
uint64_t bitcount, bitw;
|
||||
char *encoded = NULL;
|
||||
char *result_ptr = NULL;
|
||||
char *table2 = NULL;
|
||||
char *cptr;
|
||||
struct BYTE_STRING *decoded = NULL;
|
||||
struct BYTE_STRING *decoded_ptr = NULL;
|
||||
struct BYTE_STRING *table[4096];
|
||||
struct BYTE_STRING *newentry, *newresult, *t;
|
||||
int little_endian = 0;
|
||||
|
||||
if (!PyArg_ParseTuple(args, "O", &byteobj))
|
||||
return NULL;
|
||||
|
||||
if (!PyBytes_Check(byteobj)) {
|
||||
PyErr_Format(PyExc_TypeError, "expected byte string as input");
|
||||
goto _fail;
|
||||
}
|
||||
|
||||
Py_INCREF(byteobj);
|
||||
encoded = PyBytes_AS_STRING(byteobj);
|
||||
encoded_len = (unsigned int)PyBytes_GET_SIZE(byteobj);
|
||||
/*
|
||||
if (encoded_len >= 512 * 1024 * 1024) {
|
||||
PyErr_Format(PyExc_ValueError, "encoded data > 512 MB not supported");
|
||||
goto _fail;
|
||||
}
|
||||
*/
|
||||
/* release GIL: byte/string objects are immutable */
|
||||
_save = PyEval_SaveThread();
|
||||
|
||||
if ((*encoded != -128) || ((*(encoded+1) & 128))) {
|
||||
PyEval_RestoreThread(_save);
|
||||
PyErr_Format(PyExc_ValueError,
|
||||
"strip must begin with CLEAR code");
|
||||
goto _fail;
|
||||
}
|
||||
little_endian = (*(unsigned short *)encoded) & 128;
|
||||
|
||||
/* allocate buffer for codes and pointers */
|
||||
decoded_len = 0;
|
||||
len = (encoded_len + encoded_len/9) * sizeof(decoded);
|
||||
decoded = PyMem_Malloc(len * sizeof(void *));
|
||||
if (decoded == NULL) {
|
||||
PyEval_RestoreThread(_save);
|
||||
PyErr_Format(PyExc_MemoryError, "failed to allocate decoded");
|
||||
goto _fail;
|
||||
}
|
||||
memset((void *)decoded, 0, len * sizeof(void *));
|
||||
decoded_ptr = decoded;
|
||||
|
||||
/* cache strings of length 2 */
|
||||
cptr = table2 = PyMem_Malloc(256*256*2 * sizeof(char));
|
||||
if (table2 == NULL) {
|
||||
PyEval_RestoreThread(_save);
|
||||
PyErr_Format(PyExc_MemoryError, "failed to allocate table2");
|
||||
goto _fail;
|
||||
}
|
||||
for (i = 0; i < 256; i++) {
|
||||
for (j = 0; j < 256; j++) {
|
||||
*cptr++ = (char)i;
|
||||
*cptr++ = (char)j;
|
||||
}
|
||||
}
|
||||
|
||||
memset(table, 0, sizeof(table));
|
||||
table_len = 258;
|
||||
bitw = 9;
|
||||
shr = 23;
|
||||
mask = 4286578688;
|
||||
bitcount = 0;
|
||||
result_len = 0;
|
||||
code = 0;
|
||||
oldcode = 0;
|
||||
|
||||
while ((unsigned int)((bitcount + bitw) / 8) <= encoded_len) {
|
||||
/* read next code */
|
||||
code = *((unsigned int *)((void *)(encoded + (bitcount / 8))));
|
||||
if (little_endian)
|
||||
code = SWAP4BYTES(code);
|
||||
code <<= (unsigned int)(bitcount % 8);
|
||||
code &= mask;
|
||||
code >>= shr;
|
||||
bitcount += bitw;
|
||||
|
||||
if (code == 257) /* end of information */
|
||||
break;
|
||||
|
||||
if (code == 256) { /* clearcode */
|
||||
/* initialize table and switch to 9 bit */
|
||||
while (table_len > 258) {
|
||||
t = table[--table_len];
|
||||
t->ref--;
|
||||
if (t->ref == 0) {
|
||||
if (t->len > 2)
|
||||
PyMem_Free(t->str);
|
||||
PyMem_Free(t);
|
||||
}
|
||||
}
|
||||
bitw = 9;
|
||||
shr = 23;
|
||||
mask = 4286578688;
|
||||
|
||||
/* read next code */
|
||||
code = *((unsigned int *)((void *)(encoded + (bitcount / 8))));
|
||||
if (little_endian)
|
||||
code = SWAP4BYTES(code);
|
||||
code <<= bitcount % 8;
|
||||
code &= mask;
|
||||
code >>= shr;
|
||||
bitcount += bitw;
|
||||
|
||||
if (code == 257) /* end of information */
|
||||
break;
|
||||
|
||||
/* decoded.append(table[code]) */
|
||||
if (code < 256) {
|
||||
result_len++;
|
||||
*((int *)decoded_ptr++) = code;
|
||||
} else {
|
||||
newresult = table[code];
|
||||
newresult->ref++;
|
||||
result_len += newresult->len;
|
||||
*(struct BYTE_STRING **)decoded_ptr++ = newresult;
|
||||
}
|
||||
} else {
|
||||
if (code < table_len) {
|
||||
/* code is in table */
|
||||
/* newresult = table[code]; */
|
||||
/* newentry = table[oldcode] + table[code][0] */
|
||||
/* decoded.append(newresult); table.append(newentry) */
|
||||
if (code < 256) {
|
||||
c = code;
|
||||
*((unsigned int *)decoded_ptr++) = code;
|
||||
result_len++;
|
||||
} else {
|
||||
newresult = table[code];
|
||||
newresult->ref++;
|
||||
c = (unsigned int) *newresult->str;
|
||||
*(struct BYTE_STRING **)decoded_ptr++ = newresult;
|
||||
result_len += newresult->len;
|
||||
}
|
||||
newentry = PyMem_Malloc(sizeof(struct BYTE_STRING));
|
||||
newentry->ref = 1;
|
||||
if (oldcode < 256) {
|
||||
newentry->len = 2;
|
||||
newentry->str = table2 + (oldcode << 9) +
|
||||
((unsigned char)c << 1);
|
||||
} else {
|
||||
len = table[oldcode]->len;
|
||||
newentry->len = len + 1;
|
||||
newentry->str = PyMem_Malloc(newentry->len);
|
||||
if (newentry->str == NULL)
|
||||
break;
|
||||
memmove(newentry->str, table[oldcode]->str, len);
|
||||
newentry->str[len] = c;
|
||||
}
|
||||
table[table_len++] = newentry;
|
||||
} else {
|
||||
/* code is not in table */
|
||||
/* newentry = newresult = table[oldcode] + table[oldcode][0] */
|
||||
/* decoded.append(newresult); table.append(newentry) */
|
||||
newresult = PyMem_Malloc(sizeof(struct BYTE_STRING));
|
||||
newentry = newresult;
|
||||
newentry->ref = 2;
|
||||
if (oldcode < 256) {
|
||||
newentry->len = 2;
|
||||
newentry->str = table2 + 514*oldcode;
|
||||
} else {
|
||||
len = table[oldcode]->len;
|
||||
newentry->len = len + 1;
|
||||
newentry->str = PyMem_Malloc(newentry->len);
|
||||
if (newentry->str == NULL)
|
||||
break;
|
||||
memmove(newentry->str, table[oldcode]->str, len);
|
||||
newentry->str[len] = *table[oldcode]->str;
|
||||
}
|
||||
table[table_len++] = newentry;
|
||||
*(struct BYTE_STRING **)decoded_ptr++ = newresult;
|
||||
result_len += newresult->len;
|
||||
}
|
||||
}
|
||||
oldcode = code;
|
||||
/* increase bit-width if necessary */
|
||||
switch (table_len) {
|
||||
case 511:
|
||||
bitw = 10;
|
||||
shr = 22;
|
||||
mask = 4290772992;
|
||||
break;
|
||||
case 1023:
|
||||
bitw = 11;
|
||||
shr = 21;
|
||||
mask = 4292870144;
|
||||
break;
|
||||
case 2047:
|
||||
bitw = 12;
|
||||
shr = 20;
|
||||
mask = 4293918720;
|
||||
}
|
||||
}
|
||||
|
||||
PyEval_RestoreThread(_save);
|
||||
|
||||
if (code != 257) {
|
||||
PyErr_WarnEx(NULL,
|
||||
"py_decodelzw encountered unexpected end of stream", 1);
|
||||
}
|
||||
|
||||
/* result = ''.join(decoded) */
|
||||
decoded_len = (unsigned int)(decoded_ptr - decoded);
|
||||
decoded_ptr = decoded;
|
||||
result = PyBytes_FromStringAndSize(0, result_len);
|
||||
if (result == NULL) {
|
||||
PyErr_Format(PyExc_MemoryError, "failed to allocate decoded string");
|
||||
goto _fail;
|
||||
}
|
||||
result_ptr = PyBytes_AS_STRING(result);
|
||||
|
||||
_save = PyEval_SaveThread();
|
||||
|
||||
while (decoded_len--) {
|
||||
code = *((unsigned int *)decoded_ptr);
|
||||
if (code < 256) {
|
||||
*result_ptr++ = (char)code;
|
||||
} else {
|
||||
t = *((struct BYTE_STRING **)decoded_ptr);
|
||||
memmove(result_ptr, t->str, t->len);
|
||||
result_ptr += t->len;
|
||||
if (--t->ref == 0) {
|
||||
if (t->len > 2)
|
||||
PyMem_Free(t->str);
|
||||
PyMem_Free(t);
|
||||
}
|
||||
}
|
||||
decoded_ptr++;
|
||||
}
|
||||
PyMem_Free(decoded);
|
||||
|
||||
while (table_len-- > 258) {
|
||||
t = table[table_len];
|
||||
if (t->len > 2)
|
||||
PyMem_Free(t->str);
|
||||
PyMem_Free(t);
|
||||
}
|
||||
PyMem_Free(table2);
|
||||
|
||||
PyEval_RestoreThread(_save);
|
||||
|
||||
Py_DECREF(byteobj);
|
||||
return result;
|
||||
|
||||
_fail:
|
||||
if (table2 != NULL)
|
||||
PyMem_Free(table2);
|
||||
if (decoded != NULL) {
|
||||
/* Bug? are decoded_ptr and decoded_len correct? */
|
||||
while (decoded_len--) {
|
||||
code = *((unsigned int *) decoded_ptr);
|
||||
if (code > 258) {
|
||||
t = *((struct BYTE_STRING **) decoded_ptr);
|
||||
if (--t->ref == 0) {
|
||||
if (t->len > 2)
|
||||
PyMem_Free(t->str);
|
||||
PyMem_Free(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
PyMem_Free(decoded);
|
||||
}
|
||||
while (table_len-- > 258) {
|
||||
t = table[table_len];
|
||||
if (t->len > 2)
|
||||
PyMem_Free(t->str);
|
||||
PyMem_Free(t);
|
||||
}
|
||||
|
||||
Py_XDECREF(byteobj);
|
||||
Py_XDECREF(result);
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*****************************************************************************/
|
||||
/* Create Python module */
|
||||
|
||||
char module_doc[] =
|
||||
"A Python C extension module for decoding PackBits and LZW encoded "
|
||||
"TIFF data.\n\n"
|
||||
"Refer to the tifffile.py module for documentation and tests.\n\n"
|
||||
"Authors:\n Christoph Gohlke <http://www.lfd.uci.edu/~gohlke/>\n"
|
||||
" Laboratory for Fluorescence Dynamics, University of California, Irvine."
|
||||
"\n\nVersion: %s\n";
|
||||
|
||||
static PyMethodDef module_methods[] = {
|
||||
#if MSB
|
||||
{"unpackints", (PyCFunction)py_unpackints, METH_VARARGS|METH_KEYWORDS,
|
||||
py_unpackints_doc},
|
||||
#endif
|
||||
{"decodelzw", (PyCFunction)py_decodelzw, METH_VARARGS,
|
||||
py_decodelzw_doc},
|
||||
{"decodepackbits", (PyCFunction)py_decodepackbits, METH_VARARGS,
|
||||
py_decodepackbits_doc},
|
||||
{NULL, NULL, 0, NULL} /* Sentinel */
|
||||
};
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
|
||||
struct module_state {
|
||||
PyObject *error;
|
||||
};
|
||||
|
||||
#define GETSTATE(m) ((struct module_state*)PyModule_GetState(m))
|
||||
|
||||
static int module_traverse(PyObject *m, visitproc visit, void *arg) {
|
||||
Py_VISIT(GETSTATE(m)->error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int module_clear(PyObject *m) {
|
||||
Py_CLEAR(GETSTATE(m)->error);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct PyModuleDef moduledef = {
|
||||
PyModuleDef_HEAD_INIT,
|
||||
"_tifffile",
|
||||
NULL,
|
||||
sizeof(struct module_state),
|
||||
module_methods,
|
||||
NULL,
|
||||
module_traverse,
|
||||
module_clear,
|
||||
NULL
|
||||
};
|
||||
|
||||
#define INITERROR return NULL
|
||||
|
||||
PyMODINIT_FUNC
|
||||
PyInit__tifffile(void)
|
||||
|
||||
#else
|
||||
|
||||
#define INITERROR return
|
||||
|
||||
PyMODINIT_FUNC
|
||||
init_tifffile(void)
|
||||
|
||||
#endif
|
||||
{
|
||||
PyObject *module;
|
||||
|
||||
char *doc = (char *)PyMem_Malloc(sizeof(module_doc) + sizeof(_VERSION_));
|
||||
PyOS_snprintf(doc, sizeof(doc), module_doc, _VERSION_);
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
moduledef.m_doc = doc;
|
||||
module = PyModule_Create(&moduledef);
|
||||
#else
|
||||
module = Py_InitModule3("_tifffile", module_methods, doc);
|
||||
#endif
|
||||
|
||||
PyMem_Free(doc);
|
||||
|
||||
if (module == NULL)
|
||||
INITERROR;
|
||||
|
||||
if (_import_array() < 0) {
|
||||
Py_DECREF(module);
|
||||
INITERROR;
|
||||
}
|
||||
|
||||
{
|
||||
#if PY_MAJOR_VERSION < 3
|
||||
PyObject *s = PyString_FromString(_VERSION_);
|
||||
#else
|
||||
PyObject *s = PyUnicode_FromString(_VERSION_);
|
||||
#endif
|
||||
PyObject *dict = PyModule_GetDict(module);
|
||||
PyDict_SetItemString(dict, "__version__", s);
|
||||
Py_DECREF(s);
|
||||
}
|
||||
|
||||
#if PY_MAJOR_VERSION >= 3
|
||||
return module;
|
||||
#endif
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
try:
|
||||
from tifffile import imread, imsave
|
||||
from .tifffile import imread, imsave
|
||||
except ImportError:
|
||||
raise ImportError("The tifffile module could not be found.\n"
|
||||
"It can be obtained at "
|
||||
|
||||
@@ -44,10 +44,7 @@ plugin_meta_data = {}
|
||||
# the following preferences.
|
||||
preferred_plugins = {
|
||||
# Default plugins for all types (overridden by specific types below).
|
||||
'all': ['matplotlib', 'pil', 'qt', 'freeimage', 'null'],
|
||||
# Use PIL as the default imread plugin, since matplotlib (1.2.x)
|
||||
# is buggy (flips PNGs around, returns bytes as floats, etc.)
|
||||
'imread': ['pil'],
|
||||
'all': ['pil', 'matplotlib', 'qt', 'freeimage', 'null']
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -27,6 +27,10 @@ def configuration(parent_package='', top_path=None):
|
||||
sources=['_plugins/_histograms.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
|
||||
config.add_extension('_plugins._tifffile',
|
||||
sources=['_plugins/tifffile.c'],
|
||||
include_dirs=[get_numpy_include_dirs()])
|
||||
|
||||
return config
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -29,17 +29,5 @@ def test_imread_url():
|
||||
assert image.shape == (512, 512)
|
||||
|
||||
|
||||
@raises(RuntimeError)
|
||||
def test_imread_no_plugin():
|
||||
# tweak data path so that file URI works on both unix and windows.
|
||||
image_path = os.path.join(data_dir, 'lena.png')
|
||||
plugins = plugin_store['imread']
|
||||
plugin_store['imread'] = []
|
||||
try:
|
||||
io.imread(image_path)
|
||||
finally:
|
||||
plugin_store['imread'] = plugins
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_module_suite()
|
||||
|
||||
@@ -1,25 +1,22 @@
|
||||
import os.path
|
||||
import numpy as np
|
||||
from numpy.testing import *
|
||||
from numpy.testing.decorators import skipif
|
||||
from numpy.testing import (
|
||||
assert_array_equal, assert_array_almost_equal, assert_raises,
|
||||
assert_allclose, run_module_suite)
|
||||
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
from skimage import data_dir
|
||||
from skimage.io import (imread, imsave, use_plugin, reset_plugins,
|
||||
Image as ioImage)
|
||||
from skimage._shared.testing import mono_check, color_check
|
||||
|
||||
from six import BytesIO
|
||||
|
||||
|
||||
try:
|
||||
from PIL import Image
|
||||
from skimage.io._plugins.pil_plugin import pil_to_ndarray, ndarray_to_pil, _palette_is_grayscale
|
||||
use_plugin('pil')
|
||||
except ImportError:
|
||||
PIL_available = False
|
||||
else:
|
||||
PIL_available = True
|
||||
from PIL import Image
|
||||
from skimage.io._plugins.pil_plugin import (
|
||||
pil_to_ndarray, ndarray_to_pil, _palette_is_grayscale)
|
||||
use_plugin('pil')
|
||||
|
||||
np.random.seed(0)
|
||||
|
||||
@@ -39,8 +36,6 @@ def setup_module(self):
|
||||
pass
|
||||
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_imread_flatten():
|
||||
# a color image is flattened
|
||||
img = imread(os.path.join(data_dir, 'color.png'), flatten=True)
|
||||
@@ -51,7 +46,6 @@ def test_imread_flatten():
|
||||
assert np.sctype2char(img.dtype) in np.typecodes['AllInteger']
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_imread_palette():
|
||||
img = imread(os.path.join(data_dir, 'palette_gray.png'))
|
||||
assert img.ndim == 2
|
||||
@@ -59,7 +53,6 @@ def test_imread_palette():
|
||||
assert img.ndim == 3
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_palette_is_gray():
|
||||
gray = Image.open(os.path.join(data_dir, 'palette_gray.png'))
|
||||
assert _palette_is_grayscale(gray)
|
||||
@@ -67,7 +60,6 @@ def test_palette_is_gray():
|
||||
assert not _palette_is_grayscale(color)
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_bilevel():
|
||||
expected = np.zeros((10, 10))
|
||||
expected[::2] = 255
|
||||
@@ -76,7 +68,6 @@ def test_bilevel():
|
||||
assert_array_equal(img, expected)
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_imread_uint16():
|
||||
expected = np.load(os.path.join(data_dir, 'chessboard_GRAY_U8.npy'))
|
||||
img = imread(os.path.join(data_dir, 'chessboard_GRAY_U16.tif'))
|
||||
@@ -84,7 +75,6 @@ def test_imread_uint16():
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_repr_png():
|
||||
img_path = os.path.join(data_dir, 'camera.png')
|
||||
original_img = ioImage(imread(img_path))
|
||||
@@ -97,18 +87,16 @@ def test_repr_png():
|
||||
|
||||
assert np.all(original_img == round_trip)
|
||||
|
||||
@skipif(not PIL_available)
|
||||
|
||||
def test_imread_truncated_jpg():
|
||||
assert_raises((IOError, ValueError), imread,
|
||||
os.path.join(data_dir, 'truncated.jpg'))
|
||||
|
||||
# Big endian images not correctly loaded for PIL < 1.1.7
|
||||
# Renable test when PIL 1.1.7 is more common.
|
||||
@skipif(True)
|
||||
|
||||
def test_imread_uint16_big_endian():
|
||||
expected = np.load(os.path.join(data_dir, 'chessboard_GRAY_U8.npy'))
|
||||
img = imread(os.path.join(data_dir, 'chessboard_GRAY_U16B.tif'))
|
||||
assert img.dtype == np.dtype('>u2')
|
||||
assert img.dtype == np.uint16
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
@@ -135,21 +123,20 @@ class TestSave:
|
||||
x = np.ones(shape, dtype=dtype) * np.random.rand(*shape)
|
||||
|
||||
if np.issubdtype(dtype, float):
|
||||
yield self.verify_roundtrip, dtype, x, roundtrip_function(x), 255
|
||||
yield (self.verify_roundtrip, dtype, x,
|
||||
roundtrip_function(x), 255)
|
||||
else:
|
||||
x = (x * 255).astype(dtype)
|
||||
yield self.verify_roundtrip, dtype, x, roundtrip_function(x)
|
||||
yield (self.verify_roundtrip, dtype, x,
|
||||
roundtrip_function(x))
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_imsave_roundtrip_file(self):
|
||||
self.verify_imsave_roundtrip(self.roundtrip_file)
|
||||
self.verify_imsave_roundtrip(self.roundtrip_file)
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_imsave_roundtrip_pil_image(self):
|
||||
self.verify_imsave_roundtrip(self.roundtrip_pil_image)
|
||||
self.verify_imsave_roundtrip(self.roundtrip_pil_image)
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_imsave_filelike():
|
||||
shape = (2, 2)
|
||||
image = np.zeros(shape)
|
||||
@@ -165,7 +152,6 @@ def test_imsave_filelike():
|
||||
assert_allclose(out, image)
|
||||
|
||||
|
||||
@skipif(not PIL_available)
|
||||
def test_imexport_imimport():
|
||||
shape = (2, 2)
|
||||
image = np.zeros(shape)
|
||||
@@ -174,5 +160,36 @@ def test_imexport_imimport():
|
||||
assert out.shape == shape
|
||||
|
||||
|
||||
def test_all_color():
|
||||
color_check('pil')
|
||||
color_check('pil', 'bmp')
|
||||
|
||||
|
||||
def test_all_mono():
|
||||
mono_check('pil')
|
||||
mono_check('pil', 'tiff')
|
||||
|
||||
|
||||
class TestSaveTIF:
|
||||
def roundtrip(self, dtype, x):
|
||||
f = NamedTemporaryFile(suffix='.tif')
|
||||
fname = f.name
|
||||
f.close()
|
||||
imsave(fname, x)
|
||||
y = imread(fname)
|
||||
assert_array_equal(x, y)
|
||||
|
||||
def test_imsave_roundtrip(self):
|
||||
for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]:
|
||||
for dtype in (np.uint8, np.uint16, np.int16, np.float32,
|
||||
np.float64, np.bool):
|
||||
x = np.random.rand(*shape)
|
||||
|
||||
if not np.issubdtype(dtype, float) and not dtype == np.bool:
|
||||
x = (x * np.iinfo(dtype).max).astype(dtype)
|
||||
else:
|
||||
x = x.astype(dtype)
|
||||
yield self.roundtrip, dtype, x
|
||||
|
||||
if __name__ == "__main__":
|
||||
run_module_suite()
|
||||
|
||||
@@ -3,17 +3,14 @@ import skimage as si
|
||||
import skimage.io as sio
|
||||
import numpy as np
|
||||
|
||||
from numpy.testing import *
|
||||
from numpy.testing.decorators import skipif
|
||||
from numpy.testing import (
|
||||
assert_array_equal, assert_array_almost_equal, run_module_suite)
|
||||
|
||||
from tempfile import NamedTemporaryFile
|
||||
|
||||
try:
|
||||
import skimage.io._plugins.tifffile_plugin as tf
|
||||
_plugins = sio.plugin_order()
|
||||
TF_available = True
|
||||
sio.use_plugin('tifffile')
|
||||
except ImportError:
|
||||
TF_available = False
|
||||
_plugins = sio.plugin_order()
|
||||
sio.use_plugin('tifffile')
|
||||
|
||||
|
||||
np.random.seed(0)
|
||||
|
||||
@@ -22,7 +19,6 @@ def teardown():
|
||||
sio.reset_plugins()
|
||||
|
||||
|
||||
@skipif(not TF_available)
|
||||
def test_imread_uint16():
|
||||
expected = np.load(os.path.join(si.data_dir, 'chessboard_GRAY_U8.npy'))
|
||||
img = sio.imread(os.path.join(si.data_dir, 'chessboard_GRAY_U16.tif'))
|
||||
@@ -30,7 +26,6 @@ def test_imread_uint16():
|
||||
assert_array_almost_equal(img, expected)
|
||||
|
||||
|
||||
@skipif(not TF_available)
|
||||
def test_imread_uint16_big_endian():
|
||||
expected = np.load(os.path.join(si.data_dir, 'chessboard_GRAY_U8.npy'))
|
||||
img = sio.imread(os.path.join(si.data_dir, 'chessboard_GRAY_U16B.tif'))
|
||||
@@ -47,14 +42,16 @@ class TestSave:
|
||||
y = sio.imread(fname)
|
||||
assert_array_equal(x, y)
|
||||
|
||||
@skipif(not TF_available)
|
||||
def test_imsave_roundtrip(self):
|
||||
for shape in [(10, 10), (10, 10, 3), (10, 10, 4)]:
|
||||
for dtype in (np.uint8, np.uint16, np.float32, np.float64):
|
||||
x = np.ones(shape, dtype=dtype) * np.random.rand(*shape)
|
||||
for dtype in (np.uint8, np.uint16, np.float32, np.int16,
|
||||
np.float64):
|
||||
x = np.random.rand(*shape)
|
||||
|
||||
if not np.issubdtype(dtype, float):
|
||||
x = (x * 255).astype(dtype)
|
||||
x = (x * np.iinfo(dtype).max).astype(dtype)
|
||||
else:
|
||||
x = x.astype(dtype)
|
||||
yield self.roundtrip, dtype, x
|
||||
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
|
||||
import os
|
||||
from skimage import data, img_as_float, io
|
||||
from skimage import data, img_as_float, io, img_as_uint
|
||||
from skimage.viewer import ImageViewer, viewer_available
|
||||
from skimage.viewer.widgets import (
|
||||
Slider, OKCancelButtons, SaveButtons, ComboBox, CheckBox, Text)
|
||||
@@ -27,10 +27,10 @@ def test_check_box():
|
||||
cb.val = False
|
||||
assert_equal(cb.val, False)
|
||||
cb.val = 1
|
||||
assert_equal(cb.val, True)
|
||||
assert_equal(cb.val, True)
|
||||
cb.val = 0
|
||||
assert_equal(cb.val, False)
|
||||
|
||||
|
||||
|
||||
@skipif(not viewer_available)
|
||||
def test_combo_box():
|
||||
@@ -101,8 +101,8 @@ def test_save_buttons():
|
||||
sv.save_to_stack()
|
||||
sv.save_to_file(filename)
|
||||
|
||||
img = img_as_float(data.imread(filename))
|
||||
assert_almost_equal(img, viewer.image)
|
||||
img = data.imread(filename)
|
||||
assert_almost_equal(img, img_as_uint(viewer.image))
|
||||
|
||||
img = io.pop()
|
||||
assert_almost_equal(img, viewer.image)
|
||||
|
||||
Reference in New Issue
Block a user