Merge from 12-bit-tiff

This commit is contained in:
wiredfool 2013-11-21 21:41:54 -08:00
commit 94e3c75179
20 changed files with 168 additions and 138 deletions

View File

@ -475,7 +475,7 @@ class Image:
new.mode = im.mode
new.size = im.size
new.palette = self.palette
if im.mode == "P":
if im.mode == "P" and not new.palette:
from PIL import ImagePalette
new.palette = ImagePalette.ImagePalette()
try:

View File

@ -54,6 +54,10 @@ import collections
import itertools
import os
# Set these to true to force use of libtiff for reading or writing.
READ_LIBTIFF = False
WRITE_LIBTIFF= False
II = b"II" # little-endian (intel-style)
MM = b"MM" # big-endian (motorola-style)
@ -147,6 +151,7 @@ OPEN_INFO = {
(II, 1, 1, 1, (8,), ()): ("L", "L"),
(II, 1, 1, 1, (8,8), (2,)): ("LA", "LA"),
(II, 1, 1, 2, (8,), ()): ("L", "L;R"),
(II, 1, 1, 1, (12,), ()): ("I;16", "I;12"),
(II, 1, 1, 1, (16,), ()): ("I;16", "I;16"),
(II, 1, 2, 1, (16,), ()): ("I;16S", "I;16S"),
(II, 1, 1, 1, (32,), ()): ("I", "I;32N"),
@ -617,8 +622,8 @@ class TiffImageFile(ImageFile.ImageFile):
return args
def _load_libtiff(self):
""" Overload method triggered when we detect a g3/g4 tiff
Calls out to lib tiff """
""" Overload method triggered when we detect a compressed tiff
Calls out to libtiff """
pixel = Image.Image.load(self)
@ -633,7 +638,7 @@ class TiffImageFile(ImageFile.ImageFile):
raise IOError("Not exactly one tile")
d, e, o, a = self.tile[0]
d = Image._getdecoder(self.mode, d, a, self.decoderconfig)
d = Image._getdecoder(self.mode, 'libtiff', a, self.decoderconfig)
try:
d.setimage(self.im, e)
except ValueError:
@ -759,11 +764,11 @@ class TiffImageFile(ImageFile.ImageFile):
offsets = self.tag[STRIPOFFSETS]
h = getscalar(ROWSPERSTRIP, ysize)
w = self.size[0]
if self._compression in ["tiff_ccitt", "group3", "group4",
"tiff_jpeg", "tiff_adobe_deflate",
"tiff_thunderscan", "tiff_deflate",
"tiff_sgilog", "tiff_sgilog24",
"tiff_raw_16"]:
if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3", "group4",
"tiff_jpeg", "tiff_adobe_deflate",
"tiff_thunderscan", "tiff_deflate",
"tiff_sgilog", "tiff_sgilog24",
"tiff_raw_16"]:
## if Image.DEBUG:
## print "Activating g4 compression for whole file"
@ -810,7 +815,7 @@ class TiffImageFile(ImageFile.ImageFile):
# we're expecting image byte order. So, if the rawmode
# contains I;16, we need to convert from native to image
# byte order.
if self.mode in ('I;16B', 'I;16'):
if self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode:
rawmode = 'I;16N'
# Offset in the tile tuple is 0, we go from 0,0 to
@ -917,11 +922,12 @@ def _save(im, fp, filename):
ifd = ImageFileDirectory(prefix)
compression = im.encoderinfo.get('compression',im.info.get('compression','raw'))
libtiff = compression in ["tiff_ccitt", "group3", "group4",
"tiff_jpeg", "tiff_adobe_deflate",
"tiff_thunderscan", "tiff_deflate",
"tiff_sgilog", "tiff_sgilog24",
"tiff_raw_16"]
libtiff = WRITE_LIBTIFF or compression in ["tiff_ccitt", "group3", "group4",
"tiff_jpeg", "tiff_adobe_deflate",
"tiff_thunderscan", "tiff_deflate",
"tiff_sgilog", "tiff_sgilog24",
"tiff_raw_16"]
# required for color libtiff images
ifd[PLANAR_CONFIGURATION] = getattr(im, '_planar_configuration', 1)
@ -1057,8 +1063,8 @@ def _save(im, fp, filename):
if Image.DEBUG:
print (atts)
# libtiff always returns the bytes in native order.
# we're expecting image byte order. So, if the rawmode
# libtiff always expects the bytes in native order.
# we're storing image byte order. So, if the rawmode
# contains I;16, we need to convert from native to image
# byte order.
if im.mode in ('I;16B', 'I;16'):
@ -1066,7 +1072,7 @@ def _save(im, fp, filename):
a = (rawmode, compression, _fp, filename, atts)
# print (im.mode, compression, a, im.encoderconfig)
e = Image._getencoder(im.mode, compression, a, im.encoderconfig)
e = Image._getencoder(im.mode, 'libtiff', a, im.encoderconfig)
e.setimage(im.im, (0,0)+im.size)
while 1:
l, s, d = e.encode(16*1024) # undone, change to self.decodermaxblock

Binary file not shown.

BIN
Tests/images/12in16bit.tif Normal file

Binary file not shown.

Binary file not shown.

View File

@ -27,3 +27,22 @@ def test_optimize():
return len(file.getvalue())
assert_equal(test(0), 800)
assert_equal(test(1), 38)
def test_roundtrip():
out = tempfile('temp.gif')
im = lena()
im.save(out)
reread = Image.open(out)
assert_image_similar(reread.convert('RGB'), im, 50)
def test_roundtrip2():
#see https://github.com/python-imaging/Pillow/issues/403
out = 'temp.gif'#tempfile('temp.gif')
im = Image.open('Images/lena.gif')
im2 = im.copy()
im2.save(out)
reread = Image.open(out)
assert_image_similar(reread.convert('RGB'), lena(), 50)

View File

@ -1,10 +1,10 @@
from tester import *
from PIL import Image
from PIL import Image, TiffImagePlugin
codecs = dir(Image.core)
if "group4_encoder" not in codecs or "group4_decoder" not in codecs:
if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs:
skip("tiff support not available")
def _assert_noerr(im):
@ -118,7 +118,7 @@ def test_g3_compression():
assert_image_equal(reread, i)
def test_little_endian():
im = Image.open('Tests/images/12bit.deflate.tif')
im = Image.open('Tests/images/16bit.deflate.tif')
assert_equal(im.getpixel((0,0)), 480)
assert_equal(im.mode, 'I;16')
@ -143,7 +143,7 @@ def test_little_endian():
# on big endian, we'll get back mode = 'I;16B' here.
def test_big_endian():
im = Image.open('Tests/images/12bit.MM.deflate.tif')
im = Image.open('Tests/images/16bit.MM.deflate.tif')
assert_equal(im.getpixel((0,0)), 480)
assert_equal(im.mode, 'I;16B')
@ -178,6 +178,31 @@ def test_g4_string_info():
reread = Image.open(out)
assert_equal('temp.tif', reread.tag[269])
def test_12bit_rawmode():
""" Are we generating the same interpretation of the image as Imagemagick is? """
TiffImagePlugin.READ_LIBTIFF = True
#Image.DEBUG = True
im = Image.open('Tests/images/12bit.cropped.tif')
im.load()
TiffImagePlugin.READ_LIBTIFF = False
# to make the target --
# convert 12bit.cropped.tif -depth 16 tmp.tif
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
# so we need to unshift so that the integer values are the same.
im2 = Image.open('Tests/images/12in16bit.tif')
if Image.DEBUG:
print (im.getpixel((0,0)))
print (im.getpixel((0,1)))
print (im.getpixel((0,2)))
print (im2.getpixel((0,0)))
print (im2.getpixel((0,1)))
print (im2.getpixel((0,2)))
assert_image_equal(im, im2)
def test_blur():
# test case from irc, how to do blur on b/w image and save to compressed tif.

View File

@ -6,7 +6,7 @@ from test_file_libtiff import _assert_noerr
codecs = dir(Image.core)
if "group4_encoder" not in codecs or "group4_decoder" not in codecs:
if "libtiff_encoder" not in codecs or "libtiff_decoder" not in codecs:
skip("tiff support not available")
""" The small lena image was failing on open in the libtiff

View File

@ -74,7 +74,7 @@ def test_xyres_tiff():
def test_little_endian():
im = Image.open('Tests/images/12bit.cropped.tif')
im = Image.open('Tests/images/16bit.cropped.tif')
assert_equal(im.getpixel((0,0)), 480)
assert_equal(im.mode, 'I;16')
@ -89,7 +89,7 @@ def test_little_endian():
def test_big_endian():
im = Image.open('Tests/images/12bit.MM.cropped.tif')
im = Image.open('Tests/images/16bit.MM.cropped.tif')
assert_equal(im.getpixel((0,0)), 480)
assert_equal(im.mode, 'I;16B')
@ -102,3 +102,29 @@ def test_big_endian():
else:
assert_equal(b[0], b'\x01')
assert_equal(b[1], b'\xe0')
def test_12bit_rawmode():
""" Are we generating the same interpretation of the image as Imagemagick is? """
#Image.DEBUG = True
im = Image.open('Tests/images/12bit.cropped.tif')
# to make the target --
# convert 12bit.cropped.tif -depth 16 tmp.tif
# convert tmp.tif -evaluate RightShift 4 12in16bit2.tif
# imagemagick will auto scale so that a 12bit FFF is 16bit FFF0,
# so we need to unshift so that the integer values are the same.
im2 = Image.open('Tests/images/12in16bit.tif')
if Image.DEBUG:
print (im.getpixel((0,0)))
print (im.getpixel((0,1)))
print (im.getpixel((0,2)))
print (im2.getpixel((0,0)))
print (im2.getpixel((0,1)))
print (im2.getpixel((0,2)))
assert_image_equal(im, im2)

View File

@ -38,12 +38,12 @@ def test_8bit():
im = Image.open('Images/lena.jpg')
_test_float_conversion(im.convert('L'))
def test_12bit():
im = Image.open('Tests/images/12bit.cropped.tif')
def test_16bit():
im = Image.open('Tests/images/16bit.cropped.tif')
_test_float_conversion(im)
def test_12bit_workaround():
im = Image.open('Tests/images/12bit.cropped.tif')
def test_16bit_workaround():
im = Image.open('Tests/images/16bit.cropped.tif')
_test_float_conversion(im.convert('I'))

View File

@ -75,7 +75,7 @@ def _test_img_equals_nparray(img, np):
def test_16bit():
img = Image.open('Tests/images/12bit.cropped.tif')
img = Image.open('Tests/images/16bit.cropped.tif')
np_img = numpy.array(img)
_test_img_equals_nparray(img, np_img)
assert_equal(np_img.dtype, numpy.dtype('<u2'))

View File

@ -3316,27 +3316,8 @@ static PyMethodDef functions[] = {
#endif
{"tiff_lzw_decoder", (PyCFunction)PyImaging_TiffLzwDecoderNew, 1},
#ifdef HAVE_LIBTIFF
{"tiff_ccitt_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"group3_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"group4_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_jpeg_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_adobe_deflate_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_thunderscan_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_deflate_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_sgilog_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_sgilog24_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_raw_16_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"tiff_ccitt_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"group3_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"group4_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_jpeg_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_adobe_deflate_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_thunderscan_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_deflate_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_sgilog_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_sgilog24_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"tiff_raw_16_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
{"libtiff_decoder", (PyCFunction)PyImaging_LibTiffDecoderNew, 1},
{"libtiff_encoder", (PyCFunction)PyImaging_LibTiffEncoderNew, 1},
#endif
{"msp_decoder", (PyCFunction)PyImaging_MspDecoderNew, 1},
{"packbits_decoder", (PyCFunction)PyImaging_PackbitsDecoderNew, 1},

View File

@ -427,7 +427,6 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
char* mode;
char* rawmode;
char* compname;
int compression;
int fp;
if (! PyArg_ParseTuple(args, "sssi", &mode, &rawmode, &compname, &fp))
@ -435,46 +434,6 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
TRACE(("new tiff decoder %s\n", compname));
/* UNDONE -- we can probably do almost any arbitrary compression here,
* since we're effective passing in the whole file in one shot and
* getting back the data row by row. V2 maybe
*/
if (strcasecmp(compname, "tiff_ccitt") == 0) {
compression = COMPRESSION_CCITTRLE;
} else if (strcasecmp(compname, "group3") == 0) {
compression = COMPRESSION_CCITTFAX3;
} else if (strcasecmp(compname, "group4") == 0) {
compression = COMPRESSION_CCITTFAX4;
} else if (strcasecmp(compname, "tiff_jpeg") == 0) {
compression = COMPRESSION_OJPEG;
} else if (strcasecmp(compname, "tiff_adobe_deflate") == 0) {
compression = COMPRESSION_ADOBE_DEFLATE;
} else if (strcasecmp(compname, "tiff_thunderscan") == 0) {
compression = COMPRESSION_THUNDERSCAN;
} else if (strcasecmp(compname, "tiff_deflate") == 0) {
compression = COMPRESSION_DEFLATE;
} else if (strcasecmp(compname, "tiff_sgilog") == 0) {
compression = COMPRESSION_SGILOG;
} else if (strcasecmp(compname, "tiff_sgilog24") == 0) {
compression = COMPRESSION_SGILOG24;
} else if (strcasecmp(compname, "tiff_raw_16") == 0) {
compression = COMPRESSION_CCITTRLEW;
} else {
PyErr_SetString(PyExc_ValueError, "unknown compession");
return NULL;
}
decoder = PyImaging_DecoderNew(sizeof(TIFFSTATE));
if (decoder == NULL)
return NULL;
@ -482,7 +441,7 @@ PyImaging_LibTiffDecoderNew(PyObject* self, PyObject* args)
if (get_unpacker(decoder, mode, rawmode) < 0)
return NULL;
if (! ImagingLibTiffInit(&decoder->state, compression, fp)) {
if (! ImagingLibTiffInit(&decoder->state, fp)) {
Py_DECREF(decoder);
PyErr_SetString(PyExc_RuntimeError, "tiff codec initialization failed");
return NULL;

View File

@ -673,7 +673,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
char* rawmode;
char* compname;
char* filename;
int compression;
int fp;
PyObject *dir;
@ -706,47 +705,6 @@ PyImaging_LibTiffEncoderNew(PyObject* self, PyObject* args)
TRACE(("new tiff encoder %s fp: %d, filename: %s \n", compname, fp, filename));
/* UNDONE -- we can probably do almost any arbitrary compression here,
* so long as we're doing row/stripe based actions and not tiles.
*/
if (strcasecmp(compname, "tiff_ccitt") == 0) {
compression = COMPRESSION_CCITTRLE;
} else if (strcasecmp(compname, "group3") == 0) {
compression = COMPRESSION_CCITTFAX3;
} else if (strcasecmp(compname, "group4") == 0) {
compression = COMPRESSION_CCITTFAX4;
} else if (strcasecmp(compname, "tiff_jpeg") == 0) {
compression = COMPRESSION_OJPEG;
} else if (strcasecmp(compname, "tiff_adobe_deflate") == 0) {
compression = COMPRESSION_ADOBE_DEFLATE;
} else if (strcasecmp(compname, "tiff_thunderscan") == 0) {
compression = COMPRESSION_THUNDERSCAN;
} else if (strcasecmp(compname, "tiff_deflate") == 0) {
compression = COMPRESSION_DEFLATE;
} else if (strcasecmp(compname, "tiff_sgilog") == 0) {
compression = COMPRESSION_SGILOG;
} else if (strcasecmp(compname, "tiff_sgilog24") == 0) {
compression = COMPRESSION_SGILOG24;
} else if (strcasecmp(compname, "tiff_raw_16") == 0) {
compression = COMPRESSION_CCITTRLEW;
} else {
PyErr_SetString(PyExc_ValueError, "unknown compession");
return NULL;
}
TRACE(("Found compression: %d\n", compression));
encoder = PyImaging_EncoderNew(sizeof(TIFFSTATE));
if (encoder == NULL)
return NULL;

View File

@ -142,11 +142,11 @@ void _tiffUnmapProc(thandle_t hdata, tdata_t base, toff_t size) {
(void) hdata; (void) base; (void) size;
}
int ImagingLibTiffInit(ImagingCodecState state, int compression, int fp) {
int ImagingLibTiffInit(ImagingCodecState state, int fp) {
TIFFSTATE *clientstate = (TIFFSTATE *)state->context;
TRACE(("initing libtiff\n"));
TRACE(("Compression: %d, filepointer: %d \n", compression, fp));
TRACE(("filepointer: %d \n", fp));
TRACE(("State: count %d, state %d, x %d, y %d, ystep %d\n", state->count, state->state,
state->x, state->y, state->ystep));
TRACE(("State: xsize %d, ysize %d, xoff %d, yoff %d \n", state->xsize, state->ysize,

View File

@ -38,7 +38,7 @@ typedef struct {
extern int ImagingLibTiffInit(ImagingCodecState state, int compression, int fp);
extern int ImagingLibTiffInit(ImagingCodecState state, int fp);
extern int ImagingLibTiffEncodeInit(ImagingCodecState state, char *filename, int fp);
extern int ImagingLibTiffSetField(ImagingCodecState state, ttag_t tag, ...);

View File

@ -795,6 +795,60 @@ unpackI16N_I16(UINT8* out, const UINT8* in, int pixels){
}
}
static void
unpackI12_I16(UINT8* out, const UINT8* in, int pixels){
/* Fillorder 1/MSB -> LittleEndian, for 12bit integer greyscale tiffs.
According to the TIFF spec:
FillOrder = 2 should be used only when BitsPerSample = 1 and
the data is either uncompressed or compressed using CCITT 1D
or 2D compression, to avoid potentially ambigous situations.
Yeah. I thought so. We'll see how well people read the spec.
We've got several fillorder=2 modes in TiffImagePlugin.py
There's no spec I can find. It appears that the in storage
layout is: 00 80 00 ... -> (128 , 0 ...). The samples are
stored in a single big bitian 12bit block, but need to be
pulled out to little endian format to be stored in a 2 byte
int.
*/
int i;
UINT16 pixel;
#ifdef WORDS_BIGENDIAN
UINT8* tmp = (UINT8 *)&pixel;
#endif
UINT16* out16 = (UINT16 *)out;
for (i = 0; i < pixels-1; i+=2) {
pixel = (((UINT16) in[0]) << 4 ) + (in[1] >>4);
#ifdef WORDS_BIGENDIAN
out[0] = tmp[1]; out[1] = tmp[0];
#else
out16[0] = pixel;
#endif
pixel = (((UINT16) (in[1] & 0x0F)) << 8) + in[2];
#ifdef WORDS_BIGENDIAN
out[2] = tmp[1]; out[3] = tmp[0];
#else
out16[1] = pixel;
#endif
in += 3; out16 += 2; out+=4;
}
if (i == pixels-1) {
pixel = (((UINT16) in[0]) << 4 ) + (in[1] >>4);
#ifdef WORDS_BIGENDIAN
out[0] = tmp[1]; out[1] = tmp[0];
#else
out16[0] = pixel;
#endif
}
}
static void
copy1(UINT8* out, const UINT8* in, int pixels)
{
@ -1163,6 +1217,8 @@ static struct {
{"I;16L", "I;16N", 16, unpackI16N_I16}, // LibTiff native->image endian.
{"I;16B", "I;16N", 16, unpackI16N_I16B},
{"I;16", "I;12", 12, unpackI12_I16}, // 12 bit Tiffs stored in 16bits.
{NULL} /* sentinel */
};