revise tiff2vips 1/2/4 bit load
simpler, faster, smaller
This commit is contained in:
parent
99d7573ab6
commit
19077b53ac
@ -199,7 +199,7 @@
|
|||||||
* - better handling of aligned reads in multipage tiffs
|
* - better handling of aligned reads in multipage tiffs
|
||||||
* 28/5/20
|
* 28/5/20
|
||||||
* - add subifd
|
* - add subifd
|
||||||
* 06/6/20 MathemanFlo
|
* 6/6/20 MathemanFlo
|
||||||
* - support 2 and 4 bit greyscale load
|
* - support 2 and 4 bit greyscale load
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -267,6 +267,7 @@ typedef struct _RtiffHeader {
|
|||||||
int sample_format;
|
int sample_format;
|
||||||
gboolean separate;
|
gboolean separate;
|
||||||
int orientation;
|
int orientation;
|
||||||
|
|
||||||
/* If there's a premultiplied alpha, the band we need to
|
/* If there's a premultiplied alpha, the band we need to
|
||||||
* unpremultiply with. -1 for no unpremultiplication.
|
* unpremultiply with. -1 for no unpremultiplication.
|
||||||
*/
|
*/
|
||||||
@ -1019,122 +1020,61 @@ rtiff_parse_logluv( Rtiff *rtiff, VipsImage *out )
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Per-scanline process function for 1 bit images.
|
/* Make a N-bit scanline process function. We pass in the code to expand the
|
||||||
|
* bits down the byte since this does not generalize well.
|
||||||
*/
|
*/
|
||||||
static void
|
#define NBIT_LINE( N, EXPAND ) \
|
||||||
rtiff_onebit_line( Rtiff *rtiff, VipsPel *q, VipsPel *p, int n, void *flg )
|
static void \
|
||||||
{
|
rtiff_ ## N ## bit_line( Rtiff *rtiff, \
|
||||||
int photometric_interpretation =
|
VipsPel *q, VipsPel *p, int n, void *flg ) \
|
||||||
rtiff->header.photometric_interpretation;
|
{ \
|
||||||
int black = photometric_interpretation == PHOTOMETRIC_MINISBLACK ?
|
int photometric = rtiff->header.photometric_interpretation; \
|
||||||
0 : 255;
|
int mask = photometric == PHOTOMETRIC_MINISBLACK ? 0 : 0xff; \
|
||||||
int white = black ^ 0xff;
|
int bps = rtiff->header.bits_per_sample; \
|
||||||
|
int load = 8 / bps - 1; \
|
||||||
|
\
|
||||||
|
int x; \
|
||||||
|
VipsPel bits; \
|
||||||
|
\
|
||||||
|
/* Stop a compiler warning. \
|
||||||
|
*/ \
|
||||||
|
bits = 0; \
|
||||||
|
\
|
||||||
|
for( x = 0; x < n; x++ ) { \
|
||||||
|
if( (x & load) == 0 ) \
|
||||||
|
/* Flip the bits for miniswhite. \
|
||||||
|
*/ \
|
||||||
|
bits = *p++ ^ mask; \
|
||||||
|
\
|
||||||
|
EXPAND( q[x], bits ); \
|
||||||
|
\
|
||||||
|
bits <<= bps; \
|
||||||
|
} \
|
||||||
|
}
|
||||||
|
|
||||||
int x, i, z;
|
/* Expand the top bit down a byte. Use a sign-extending shift.
|
||||||
VipsPel bits;
|
|
||||||
|
|
||||||
/* (sigh) how many times have I written this?
|
|
||||||
*/
|
*/
|
||||||
x = 0;
|
#define EXPAND1( Q, BITS ) G_STMT_START { \
|
||||||
for( i = 0; i < (n >> 3); i++ ) {
|
(Q) = (((signed char) (BITS & 128)) >> 7); \
|
||||||
bits = (VipsPel) p[i];
|
} G_STMT_END
|
||||||
|
|
||||||
for( z = 0; z < 8; z++ ) {
|
/* Expand the top two bits down a byte. Shift down, then expand up.
|
||||||
q[x] = (bits & 128) ? white : black;
|
|
||||||
bits <<= 1;
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do last byte in line.
|
|
||||||
*/
|
*/
|
||||||
if( n & 7 ) {
|
#define EXPAND2( Q, BITS ) G_STMT_START { \
|
||||||
bits = p[i];
|
VipsPel twobits = BITS >> 6; \
|
||||||
|
VipsPel fourbits = twobits | (twobits << 2); \
|
||||||
|
Q = fourbits | (fourbits << 4); \
|
||||||
|
} G_STMT_END
|
||||||
|
|
||||||
for( z = 0; z < (n & 7); z++ ) {
|
/* Expand the top four bits down a byte.
|
||||||
q[x + z] = (bits & 128) ? white : black;
|
|
||||||
bits <<= 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Per-scanline process function for 2 bit greyscale images.
|
|
||||||
*/
|
*/
|
||||||
static void
|
#define EXPAND4( Q, BITS ) G_STMT_START { \
|
||||||
rtiff_twobit_line( Rtiff *rtiff, VipsPel *q, VipsPel *p, int n, void *flg )
|
Q = (BITS & 0xf0) | (BITS >> 4); \
|
||||||
{
|
} G_STMT_END
|
||||||
int photometric_interpretation =
|
|
||||||
rtiff->header.photometric_interpretation;
|
|
||||||
int minisblack = photometric_interpretation == PHOTOMETRIC_MINISBLACK;
|
|
||||||
|
|
||||||
int x, i, z;
|
|
||||||
VipsPel twobits, fourbits, bits;
|
|
||||||
|
|
||||||
x = 0;
|
|
||||||
for( i = 0; i < (n >> 2); i++ ) {
|
|
||||||
bits = (VipsPel) minisblack ? p[i] : ~p[i];
|
|
||||||
|
|
||||||
for( z = 0; z < 4; z++ ) {
|
|
||||||
/* The grey shade is the value four times concatenated.
|
|
||||||
*/
|
|
||||||
twobits = bits >> 6;
|
|
||||||
fourbits = twobits | (twobits << 2);
|
|
||||||
q[x] = fourbits | (fourbits << 4);
|
|
||||||
bits <<= 2;
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do last byte in line.
|
|
||||||
*/
|
|
||||||
if( n & 3 ) {
|
|
||||||
bits = (VipsPel) minisblack ? p[i] : ~p[i];
|
|
||||||
|
|
||||||
for( z = 0; z < (n & 3); z++ ) {
|
|
||||||
twobits = bits >> 6;
|
|
||||||
fourbits = twobits | (twobits << 2);
|
|
||||||
q[x + z] = fourbits | (fourbits << 4);
|
|
||||||
bits <<= 2;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Per-scanline process function for 4 bit greyscale images.
|
|
||||||
*/
|
|
||||||
static void
|
|
||||||
rtiff_fourbit_line( Rtiff *rtiff, VipsPel *q, VipsPel *p, int n, void *flg )
|
|
||||||
{
|
|
||||||
int photometric_interpretation =
|
|
||||||
rtiff->header.photometric_interpretation;
|
|
||||||
int minisblack = photometric_interpretation == PHOTOMETRIC_MINISBLACK;
|
|
||||||
|
|
||||||
int x, i, z;
|
|
||||||
VipsPel bits;
|
|
||||||
|
|
||||||
x = 0;
|
|
||||||
for( i = 0; i < (n >> 1); i++ ) {
|
|
||||||
bits = (VipsPel) minisblack ? p[i] : ~p[i];
|
|
||||||
|
|
||||||
for( z = 0; z < 2; z++ ) {
|
|
||||||
/* The grey shade is the value two times concatenated.
|
|
||||||
*/
|
|
||||||
q[x] = (bits & 0xF0) | (bits >> 4);
|
|
||||||
bits <<= 4;
|
|
||||||
x += 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Do last byte in line.
|
|
||||||
*/
|
|
||||||
if( n & 1 ) {
|
|
||||||
bits = (VipsPel) minisblack ? p[i] : ~p[i];
|
|
||||||
for( z = 0; z < (n & 1); z++ ) {
|
|
||||||
q[x + z] = (bits & 0xF0) | (bits >> 4);
|
|
||||||
bits <<= 4;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
NBIT_LINE( 1, EXPAND1 )
|
||||||
|
NBIT_LINE( 2, EXPAND2 )
|
||||||
|
NBIT_LINE( 4, EXPAND4 )
|
||||||
|
|
||||||
/* Read a 1-bit TIFF image.
|
/* Read a 1-bit TIFF image.
|
||||||
*/
|
*/
|
||||||
@ -1150,7 +1090,7 @@ rtiff_parse_onebit( Rtiff *rtiff, VipsImage *out )
|
|||||||
out->Coding = VIPS_CODING_NONE;
|
out->Coding = VIPS_CODING_NONE;
|
||||||
out->Type = VIPS_INTERPRETATION_B_W;
|
out->Type = VIPS_INTERPRETATION_B_W;
|
||||||
|
|
||||||
rtiff->sfn = rtiff_onebit_line;
|
rtiff->sfn = rtiff_1bit_line;
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
@ -1169,7 +1109,7 @@ rtiff_parse_twobit( Rtiff *rtiff, VipsImage *out )
|
|||||||
out->Coding = VIPS_CODING_NONE;
|
out->Coding = VIPS_CODING_NONE;
|
||||||
out->Type = VIPS_INTERPRETATION_B_W;
|
out->Type = VIPS_INTERPRETATION_B_W;
|
||||||
|
|
||||||
rtiff->sfn = rtiff_twobit_line;
|
rtiff->sfn = rtiff_2bit_line;
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
@ -1188,7 +1128,7 @@ rtiff_parse_fourbit( Rtiff *rtiff, VipsImage *out )
|
|||||||
out->Coding = VIPS_CODING_NONE;
|
out->Coding = VIPS_CODING_NONE;
|
||||||
out->Type = VIPS_INTERPRETATION_B_W;
|
out->Type = VIPS_INTERPRETATION_B_W;
|
||||||
|
|
||||||
rtiff->sfn = rtiff_fourbit_line;
|
rtiff->sfn = rtiff_4bit_line;
|
||||||
|
|
||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
@ -195,7 +195,7 @@
|
|||||||
* - add PAGENUMBER support
|
* - add PAGENUMBER support
|
||||||
* 23/5/20
|
* 23/5/20
|
||||||
* - add support for subifd pyramid layers
|
* - add support for subifd pyramid layers
|
||||||
* 06/6/20 MathemanFlo
|
* 6/6/20 MathemanFlo
|
||||||
* - add bitdepth support for 2 and 4 bit greyscale images
|
* - add bitdepth support for 2 and 4 bit greyscale images
|
||||||
*/
|
*/
|
||||||
|
|
||||||
@ -1301,7 +1301,7 @@ eightbit2onebit( Wtiff *wtiff, VipsPel *q, VipsPel *p, int n )
|
|||||||
bits = 0;
|
bits = 0;
|
||||||
for( x = 0; x < n; x++ ) {
|
for( x = 0; x < n; x++ ) {
|
||||||
bits <<= 1;
|
bits <<= 1;
|
||||||
if( p[x] > 128 )
|
if( p[x] >= 128 )
|
||||||
bits |= white;
|
bits |= white;
|
||||||
else
|
else
|
||||||
bits |= black;
|
bits |= black;
|
||||||
|
@ -14,8 +14,9 @@ SRGB_FILE = os.path.join(IMAGES, "sRGB.icm")
|
|||||||
MATLAB_FILE = os.path.join(IMAGES, "sample.mat")
|
MATLAB_FILE = os.path.join(IMAGES, "sample.mat")
|
||||||
PNG_FILE = os.path.join(IMAGES, "sample.png")
|
PNG_FILE = os.path.join(IMAGES, "sample.png")
|
||||||
TIF_FILE = os.path.join(IMAGES, "sample.tif")
|
TIF_FILE = os.path.join(IMAGES, "sample.tif")
|
||||||
TIF2_FILE = os.path.join(IMAGES, "result2Bit.tif")
|
TIF1_FILE = os.path.join(IMAGES, "1bit.tif")
|
||||||
TIF4_FILE = os.path.join(IMAGES, "result4Bit.tif")
|
TIF2_FILE = os.path.join(IMAGES, "2bit.tif")
|
||||||
|
TIF4_FILE = os.path.join(IMAGES, "4bit.tif")
|
||||||
OME_FILE = os.path.join(IMAGES, "multi-channel-z-series.ome.tif")
|
OME_FILE = os.path.join(IMAGES, "multi-channel-z-series.ome.tif")
|
||||||
ANALYZE_FILE = os.path.join(IMAGES, "t00740_tr1_segm.hdr")
|
ANALYZE_FILE = os.path.join(IMAGES, "t00740_tr1_segm.hdr")
|
||||||
GIF_FILE = os.path.join(IMAGES, "cramps.gif")
|
GIF_FILE = os.path.join(IMAGES, "cramps.gif")
|
||||||
|
BIN
test/test-suite/images/1bit.tif
Normal file
BIN
test/test-suite/images/1bit.tif
Normal file
Binary file not shown.
BIN
test/test-suite/images/2bit.tif
Normal file
BIN
test/test-suite/images/2bit.tif
Normal file
Binary file not shown.
Binary file not shown.
@ -17,7 +17,7 @@ from helpers import \
|
|||||||
GIF_ANIM_DISPOSE_PREVIOUS_FILE, \
|
GIF_ANIM_DISPOSE_PREVIOUS_FILE, \
|
||||||
GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE, \
|
GIF_ANIM_DISPOSE_PREVIOUS_EXPECTED_PNG_FILE, \
|
||||||
temp_filename, assert_almost_equal_objects, have, skip_if_no, \
|
temp_filename, assert_almost_equal_objects, have, skip_if_no, \
|
||||||
TIF2_FILE, TIF4_FILE
|
TIF1_FILE, TIF2_FILE, TIF4_FILE
|
||||||
|
|
||||||
|
|
||||||
class TestForeign:
|
class TestForeign:
|
||||||
@ -325,9 +325,22 @@ class TestForeign:
|
|||||||
self.file_loader("tiffload", TIF_FILE, tiff_valid)
|
self.file_loader("tiffload", TIF_FILE, tiff_valid)
|
||||||
self.buffer_loader("tiffload_buffer", TIF_FILE, tiff_valid)
|
self.buffer_loader("tiffload_buffer", TIF_FILE, tiff_valid)
|
||||||
|
|
||||||
|
def tiff1_valid(im):
|
||||||
|
a = im(127, 0)
|
||||||
|
assert_almost_equal_objects(a, [0.0])
|
||||||
|
a = im(128, 0)
|
||||||
|
assert_almost_equal_objects(a, [255.0])
|
||||||
|
assert im.width == 256
|
||||||
|
assert im.height == 4
|
||||||
|
assert im.bands == 1
|
||||||
|
|
||||||
|
self.file_loader("tiffload", TIF1_FILE, tiff1_valid)
|
||||||
|
|
||||||
def tiff2_valid(im):
|
def tiff2_valid(im):
|
||||||
a = im(80, 0)
|
a = im(127, 0)
|
||||||
assert_almost_equal_objects(a, [85.0])
|
assert_almost_equal_objects(a, [85.0])
|
||||||
|
a = im(128, 0)
|
||||||
|
assert_almost_equal_objects(a, [170.0])
|
||||||
assert im.width == 256
|
assert im.width == 256
|
||||||
assert im.height == 4
|
assert im.height == 4
|
||||||
assert im.bands == 1
|
assert im.bands == 1
|
||||||
@ -335,8 +348,10 @@ class TestForeign:
|
|||||||
self.file_loader("tiffload", TIF2_FILE, tiff2_valid)
|
self.file_loader("tiffload", TIF2_FILE, tiff2_valid)
|
||||||
|
|
||||||
def tiff4_valid(im):
|
def tiff4_valid(im):
|
||||||
a = im(109, 0)
|
a = im(127, 0)
|
||||||
assert_almost_equal_objects(a, [102.0])
|
assert_almost_equal_objects(a, [119.0])
|
||||||
|
a = im(128, 0)
|
||||||
|
assert_almost_equal_objects(a, [136.0])
|
||||||
assert im.width == 256
|
assert im.width == 256
|
||||||
assert im.height == 4
|
assert im.height == 4
|
||||||
assert im.bands == 1
|
assert im.bands == 1
|
||||||
@ -372,10 +387,10 @@ class TestForeign:
|
|||||||
self.save_load_file(".tif",
|
self.save_load_file(".tif",
|
||||||
"[tile,tile-width=256]", self.colour, 10)
|
"[tile,tile-width=256]", self.colour, 10)
|
||||||
|
|
||||||
im = pyvips.Image.new_from_file(TIF4_FILE)
|
|
||||||
self.save_load_file(".tif", "[bitdepth=4]", im, 0)
|
|
||||||
im = pyvips.Image.new_from_file(TIF2_FILE)
|
im = pyvips.Image.new_from_file(TIF2_FILE)
|
||||||
self.save_load_file(".tif", "[bitdepth=2]", im, 0)
|
self.save_load_file(".tif", "[bitdepth=2]", im, 0)
|
||||||
|
im = pyvips.Image.new_from_file(TIF4_FILE)
|
||||||
|
self.save_load_file(".tif", "[bitdepth=4]", im, 0)
|
||||||
|
|
||||||
filename = temp_filename(self.tempdir, '.tif')
|
filename = temp_filename(self.tempdir, '.tif')
|
||||||
x = pyvips.Image.new_from_file(TIF_FILE)
|
x = pyvips.Image.new_from_file(TIF_FILE)
|
||||||
|
Loading…
Reference in New Issue
Block a user