needs testing etc.
This commit is contained in:
John Cupitt 2016-05-26 14:58:04 +01:00
parent ac2ce4228b
commit 5a9f2c787b
7 changed files with 245 additions and 77 deletions

73
TODO
View File

@ -1,3 +1,44 @@
- set tiff orientation tag on write, also copy orientation between tiff iamges
- switch jpeg read/write over to new orientation tag
- support orientation tag in tiff images
see
http://www.awaresystems.be/imaging/tiff/tifftags/orientation.html
seems to be the same values used for exif
jpg uses "exif-ifd0-Orientation", see conversion/autorot.c
instead, standardize on "orientation" and give it an int value
VIPS_META_ORIENTATION
jpg load and save need to use this in preference to the exif value
tiff load and save need to set and recover this value
tiff load needs an autorot flag
don't touch parse_header()
on header-only read, just swap width/height and delete the tag
on full image read, use read_jpeg_rotate(), broken out into a separate
func
see https://github.com/jcupitt/libvips/issues/243
- tiff write does not support [strip]
- add more webp tests to py suite
@ -62,39 +103,9 @@
- support orientation tag in tiff images
see
http://www.awaresystems.be/imaging/tiff/tifftags/orientation.html
seems to be the same values used for exif
jpg uses "exif-ifd0-Orientation", see conversion/autorot.c
instead, standardize on "orientation" and give it an int value
jpg load and save need to use this in preference to the exif value
tiff load and save need to set and recover this value
tiff load needs an autorot flag
don't touch parse_header()
on header-only read, just swap width/height and delete the tag
on full image read, use read_jpeg_rotate(), broken out into a separate
func
see https://github.com/jcupitt/libvips/issues/243
- why can't we do
im = Vips.Image.new_from_file("/data/john/pics/k2.jpg", access = "sequential")

View File

@ -94,11 +94,11 @@ tiff2vips( const char *name, IMAGE *out, gboolean header_only )
}
if( header_only ) {
if( vips__tiff_read_header( filename, out, page ) )
if( vips__tiff_read_header( filename, out, page, FALSE ) )
return( -1 );
}
else {
if( vips__tiff_read( filename, out, page, TRUE ) )
if( vips__tiff_read( filename, out, page, FALSE, TRUE ) )
return( -1 );
}
#else

View File

@ -157,6 +157,7 @@ vips_foreign_load_jpeg_class_init( VipsForeignLoadJpegClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadJpeg, autorotate ),
FALSE );
}
static void
@ -354,7 +355,8 @@ vips_foreign_load_jpeg_buffer_init( VipsForeignLoadJpegBuffer *buffer )
*
* * @shrink: %gint, shrink by this much on load
* * @fail: %gboolean, fail on warnings
* * @autorotate: %gboolean, use exif Orientation tag to rotate the image during load
* * @autorotate: %gboolean, use exif Orientation tag to rotate the image
* during load
*
* Read a JPEG file into a VIPS image. It can read most 8-bit JPEG images,
* including CMYK and YCbCr.

View File

@ -52,17 +52,18 @@ int vips__tiff_write( VipsImage *in, const char *filename,
gboolean rgbjpeg,
gboolean properties );
int vips__tiff_read_header( const char *filename, VipsImage *out, int page );
int vips__tiff_read_header( const char *filename, VipsImage *out,
int page, gboolean autorotate );
int vips__tiff_read( const char *filename, VipsImage *out,
int page, gboolean readbehind );
int page, gboolean autorotate, gboolean readbehind );
gboolean vips__istifftiled( const char *filename );
gboolean vips__istiff_buffer( const void *buf, size_t len );
gboolean vips__istiff( const char *filename );
int vips__tiff_read_header_buffer( const void *buf, size_t len, VipsImage *out,
int page );
int page, gboolean autorotate );
int vips__tiff_read_buffer( const void *buf, size_t len, VipsImage *out,
int page, gboolean readbehind );
int page, gboolean autorotate, gboolean readbehind );
#ifdef __cplusplus
}

View File

@ -165,6 +165,8 @@
* 11/4/16
* - non-int RGB images are tagged as scRGB ... matches photoshop
* convention
* 26/5/16
* - add autorotate support
*/
/*
@ -195,8 +197,8 @@
*/
/*
#define DEBUG
*/
#define DEBUG
#ifdef HAVE_CONFIG_H
#include <config.h>
@ -233,6 +235,7 @@ typedef struct _ReadTiff {
size_t len;
VipsImage *out;
int page;
gboolean autorotate;
gboolean readbehind;
/* The TIFF we read.
@ -263,6 +266,7 @@ typedef struct _ReadTiff {
int bits_per_sample;
int photometric_interpretation;
int sample_format;
int orientation;
/* Turn on separate plane reading.
*/
@ -1134,6 +1138,17 @@ parse_header( ReadTiff *rtiff, VipsImage *out )
}
}
{
uint16 v;
rtiff->orientation = ORIENTATION_TOPLEFT;
if( TIFFGetFieldDefaulted( rtiff->tiff, TIFFTAG_ORIENTATION, &v ) )
/* Can have mad values.
*/
rtiff->orientation = VIPS_CLIP( 1, v, 8 );
}
/* Arbitrary sanity-checking limits.
*/
@ -1171,6 +1186,8 @@ parse_header( ReadTiff *rtiff, VipsImage *out )
rtiff->bits_per_sample );
printf( "parse_header: sample_format = %d\n",
rtiff->sample_format );
printf( "parse_header: orientation = %d\n",
rtiff->orientation );
#endif /*DEBUG*/
/* We have a range of output paths. Look at the tiff header and try to
@ -1246,6 +1263,11 @@ parse_header( ReadTiff *rtiff, VipsImage *out )
VIPS_META_IMAGEDESCRIPTION, (char *) data );
}
/* Set the "orientation" tag. This is picked up later by autorot, if
* requested.
*/
vips_image_set_int( out, VIPS_META_ORIENTATION, rtiff->orientation );
return( 0 );
}
@ -1409,14 +1431,86 @@ tiff_seq_stop( void *seq, void *a, void *b )
return( 0 );
}
/* Auto-rotate handling.
*/
static VipsAngle
vips_tiff_get_angle( VipsImage *im )
{
int orientation;
VipsAngle angle;
if( vips_image_get_int( im, VIPS_META_ORIENTATION, &orientation ) )
orientation = 1;
switch( orientation ) {
case 6:
angle = VIPS_ANGLE_D90;
break;
case 8:
angle = VIPS_ANGLE_D270;
break;
case 3:
angle = VIPS_ANGLE_D180;
break;
default:
angle = VIPS_ANGLE_D0;
break;
}
return( angle );
}
static int
vips_tiff_autorotate( ReadTiff *rtiff, VipsImage *in, VipsImage **out )
{
VipsAngle angle = vips_tiff_get_angle( in );
if( rtiff->autorotate &&
angle != VIPS_ANGLE_D0 ) {
/* Need to copy to memory or disc, we have to stay seq.
*/
const guint64 image_size = VIPS_IMAGE_SIZEOF_IMAGE( in );
const guint64 disc_threshold = vips_get_disc_threshold();
VipsImage *x;
if( image_size > disc_threshold )
x = vips_image_new_temp_file( "%s.v" );
else
x = vips_image_new_memory();
if( vips_image_write( in, x ) ||
vips_rot( x, out, angle, NULL ) ) {
g_object_unref( x );
return( -1 );
}
g_object_unref( x );
/* We must remove VIPS_META_ORIENTATION to prevent accidental
* double rotations.
*/
(void) vips_image_remove( *out, VIPS_META_ORIENTATION );
}
else {
*out = in;
g_object_ref( in );
}
return( 0 );
}
/* Tile-type TIFF reader core - pass in a per-tile transform. Generate into
* the im and do it all partially.
*/
static int
read_tilewise( ReadTiff *rtiff, VipsImage *out )
{
VipsImage *raw;
VipsImage *t;
VipsImage **t = (VipsImage **)
vips_object_local_array( VIPS_OBJECT( out ), 3 );
#ifdef DEBUG
printf( "tiff2vips: read_tilewise\n" );
@ -1438,12 +1532,11 @@ read_tilewise( ReadTiff *rtiff, VipsImage *out )
/* Read to this image, then cache to out, see below.
*/
raw = vips_image_new();
vips_object_local( out, raw );
t[0] = vips_image_new();
/* Parse the TIFF header and set up raw.
/* Parse the TIFF header and set up.
*/
if( parse_header( rtiff, raw ) )
if( parse_header( rtiff, t[0] ) )
return( -1 );
/* Double check: in memcpy mode, the vips tilesize should exactly
@ -1452,7 +1545,7 @@ read_tilewise( ReadTiff *rtiff, VipsImage *out )
if( rtiff->memcpy ) {
size_t vips_tile_size;
vips_tile_size = VIPS_IMAGE_SIZEOF_PEL( raw ) *
vips_tile_size = VIPS_IMAGE_SIZEOF_PEL( t[0] ) *
rtiff->twidth * rtiff->theight;
if( tiff_tile_size( rtiff ) != vips_tile_size ) {
@ -1466,26 +1559,25 @@ read_tilewise( ReadTiff *rtiff, VipsImage *out )
* the cache we are quite happy serving that if anything downstream
* would like it.
*/
vips_image_pipelinev( raw, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
vips_image_pipelinev( t[0], VIPS_DEMAND_STYLE_THINSTRIP, NULL );
if( vips_image_generate( raw,
if( vips_image_generate( t[0],
tiff_seq_start, tiff_fill_region, tiff_seq_stop,
rtiff, NULL ) )
return( -1 );
/* Copy to out, adding a cache. Enough tiles for two complete rows.
*/
if( vips_tilecache( raw, &t,
if( vips_tilecache( t[0], &t[1],
"tile_width", rtiff->twidth,
"tile_height", rtiff->theight,
"max_tiles", 2 * (1 + raw->Xsize / rtiff->twidth),
"max_tiles", 2 * (1 + t[0]->Xsize / rtiff->twidth),
NULL ) )
return( -1 );
if( vips_image_write( t, out ) ) {
g_object_unref( t );
if( vips_tiff_autorotate( rtiff, t[1], &t[2] ) )
return( -1 );
if( vips_image_write( t[2], out ) )
return( -1 );
}
g_object_unref( t );
return( 0 );
}
@ -1645,7 +1737,6 @@ read_stripwise( ReadTiff *rtiff, VipsImage *out )
#endif /*DEBUG*/
t[0] = vips_image_new();
if( parse_header( rtiff, t[0] ) )
return( -1 );
@ -1738,7 +1829,8 @@ read_stripwise( ReadTiff *rtiff, VipsImage *out )
VIPS_ACCESS_SEQUENTIAL :
VIPS_ACCESS_SEQUENTIAL_UNBUFFERED,
NULL ) ||
vips_image_write( t[1], out ) )
vips_tiff_autorotate( rtiff, t[1], &t[2] ) ||
vips_image_write( t[2], out ) )
return( -1 );
return( 0 );
@ -1759,7 +1851,8 @@ readtiff_close( VipsObject *object, ReadTiff *rtiff )
}
static ReadTiff *
readtiff_new( VipsImage *out, int page, gboolean readbehind )
readtiff_new( VipsImage *out,
int page, gboolean autorotate, gboolean readbehind )
{
ReadTiff *rtiff;
@ -1771,6 +1864,7 @@ readtiff_new( VipsImage *out, int page, gboolean readbehind )
rtiff->len = 0;
rtiff->out = out;
rtiff->page = page;
rtiff->autorotate = autorotate;
rtiff->readbehind = readbehind;
rtiff->tiff = NULL;
rtiff->sfn = NULL;
@ -1796,13 +1890,13 @@ readtiff_new( VipsImage *out, int page, gboolean readbehind )
}
static ReadTiff *
readtiff_new_filename( const char *filename, VipsImage *out, int page,
gboolean readbehind )
readtiff_new_filename( const char *filename, VipsImage *out,
int page, gboolean autorotate, gboolean readbehind )
{
ReadTiff *rtiff;
int i;
if( !(rtiff = readtiff_new( out, page, readbehind )) )
if( !(rtiff = readtiff_new( out, page, autorotate, readbehind )) )
return( NULL );
rtiff->filename = vips_strdup( VIPS_OBJECT( out ), filename );
@ -1897,13 +1991,13 @@ my_tiff_unmap( thandle_t st, tdata_t start, toff_t len )
}
static ReadTiff *
readtiff_new_buffer( const void *buf, size_t len, VipsImage *out, int page,
gboolean readbehind )
readtiff_new_buffer( const void *buf, size_t len, VipsImage *out,
int page, gboolean autorotate, gboolean readbehind )
{
ReadTiff *rtiff;
int i;
if( !(rtiff = readtiff_new( out, page, readbehind )) )
if( !(rtiff = readtiff_new( out, page, autorotate, readbehind )) )
return( NULL );
rtiff->buf = buf;
@ -1951,8 +2045,8 @@ istiffpyramid( const char *name )
*/
int
vips__tiff_read( const char *filename, VipsImage *out, int page,
gboolean readbehind )
vips__tiff_read( const char *filename, VipsImage *out,
int page, gboolean autorotate, gboolean readbehind )
{
ReadTiff *rtiff;
@ -1964,7 +2058,7 @@ vips__tiff_read( const char *filename, VipsImage *out, int page,
vips__tiff_init();
if( !(rtiff = readtiff_new_filename( filename,
out, page, readbehind )) )
out, page, autorotate, readbehind )) )
return( -1 );
if( TIFFIsTiled( rtiff->tiff ) ) {
@ -1979,19 +2073,45 @@ vips__tiff_read( const char *filename, VipsImage *out, int page,
return( 0 );
}
/* On a header-only read, we can just swap width/height if orientaion is 6 or
* 8.
*/
static void
vips__tiff_read_header_orientation( ReadTiff *rtiff, VipsImage *out )
{
int orientation;
if( rtiff->autorotate &&
!vips_image_get_int( out,
VIPS_META_ORIENTATION, &orientation ) ) {
if( orientation == 3 ||
orientation == 6 )
VIPS_SWAP( int, out->Xsize, out->Ysize );
/* We must remove VIPS_META_ORIENTATION to prevent accidental
* double rotations.
*/
vips_image_remove( out, VIPS_META_ORIENTATION );
}
}
int
vips__tiff_read_header( const char *filename, VipsImage *out, int page )
vips__tiff_read_header( const char *filename, VipsImage *out,
int page, gboolean autorotate )
{
ReadTiff *rtiff;
vips__tiff_init();
if( !(rtiff = readtiff_new_filename( filename, out, page, FALSE )) )
if( !(rtiff = readtiff_new_filename( filename, out,
page, autorotate, FALSE )) )
return( -1 );
if( parse_header( rtiff, out ) )
return( -1 );
vips__tiff_read_header_orientation( rtiff, out );
/* Just a header read: we can free the tiff read early and save an fd.
*/
readtiff_free( rtiff );
@ -2045,25 +2165,28 @@ vips__istiff( const char *filename )
}
int
vips__tiff_read_header_buffer( const void *buf, size_t len,
VipsImage *out, int page )
vips__tiff_read_header_buffer( const void *buf, size_t len, VipsImage *out,
int page, gboolean autorotate )
{
ReadTiff *rtiff;
vips__tiff_init();
if( !(rtiff = readtiff_new_buffer( buf, len, out, page, FALSE )) )
if( !(rtiff = readtiff_new_buffer( buf, len, out,
page, autorotate, FALSE )) )
return( -1 );
if( parse_header( rtiff, out ) )
return( -1 );
vips__tiff_read_header_orientation( rtiff, out );
return( 0 );
}
int
vips__tiff_read_buffer( const void *buf, size_t len,
VipsImage *out, int page, gboolean readbehind )
VipsImage *out, int page, gboolean autorotate, gboolean readbehind )
{
ReadTiff *rtiff;
@ -2074,7 +2197,8 @@ vips__tiff_read_buffer( const void *buf, size_t len,
vips__tiff_init();
if( !(rtiff = readtiff_new_buffer( buf, len, out, page, readbehind )) )
if( !(rtiff = readtiff_new_buffer( buf, len, out,
page, autorotate, readbehind )) )
return( -1 );
if( TIFFIsTiled( rtiff->tiff ) ) {

View File

@ -59,6 +59,10 @@ typedef struct _VipsForeignLoadTiff {
*/
int page;
/* Autorotate using orientation tag.
*/
gboolean autorotate;
} VipsForeignLoadTiff;
typedef VipsForeignLoadClass VipsForeignLoadTiffClass;
@ -91,6 +95,13 @@ vips_foreign_load_tiff_class_init( VipsForeignLoadTiffClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadTiff, page ),
0, 100000, 0 );
VIPS_ARG_BOOL( class, "autorotate", 11,
_( "Autorotate" ),
_( "Rotate image using orientation tag" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadTiff, autorotate ),
FALSE );
}
static void
@ -142,7 +153,8 @@ vips_foreign_load_tiff_file_header( VipsForeignLoad *load )
VipsForeignLoadTiff *tiff = (VipsForeignLoadTiff *) load;
VipsForeignLoadTiffFile *file = (VipsForeignLoadTiffFile *) load;
if( vips__tiff_read_header( file->filename, load->out, tiff->page ) )
if( vips__tiff_read_header( file->filename, load->out,
tiff->page, tiff->autorotate ) )
return( -1 );
VIPS_SETSTR( load->out->filename, file->filename );
@ -157,7 +169,7 @@ vips_foreign_load_tiff_file_load( VipsForeignLoad *load )
VipsForeignLoadTiffFile *file = (VipsForeignLoadTiffFile *) load;
if( vips__tiff_read( file->filename, load->real, tiff->page,
load->access == VIPS_ACCESS_SEQUENTIAL ) )
tiff->autorotate, load->access == VIPS_ACCESS_SEQUENTIAL ) )
return( -1 );
return( 0 );
@ -227,7 +239,7 @@ vips_foreign_load_tiff_buffer_header( VipsForeignLoad *load )
if( vips__tiff_read_header_buffer(
buffer->buf->data, buffer->buf->length, load->out,
tiff->page ) )
tiff->page, tiff->autorotate ) )
return( -1 );
return( 0 );
@ -241,7 +253,7 @@ vips_foreign_load_tiff_buffer_load( VipsForeignLoad *load )
if( vips__tiff_read_buffer(
buffer->buf->data, buffer->buf->length, load->real,
tiff->page,
tiff->page, tiff->autorotate,
load->access == VIPS_ACCESS_SEQUENTIAL ) )
return( -1 );
@ -290,6 +302,8 @@ vips_foreign_load_tiff_buffer_init( VipsForeignLoadTiffBuffer *buffer )
* Optional arguments:
*
* * @page: int, load this page
* * @autorotate: %gboolean, use Orientation tag to rotate the image
* during load
*
* Read a TIFF file into a VIPS image. It is a full baseline TIFF 6 reader,
* with extensions for tiled images, multipage images, LAB colour space,
@ -298,6 +312,14 @@ vips_foreign_load_tiff_buffer_init( VipsForeignLoadTiffBuffer *buffer )
* @page means load this page from the file. By default the first page (page
* 0) is read.
*
* Setting @autorotate to %TRUE will make the loader interpret the
* Orientation field and automatically rotate the image appropriately during
* load. After rotation, the Orientation tag will be removed to prevent
* accidental double-rotation.
*
* Using @autorotate can be much slower than doing the rotate later
* in processing. See vips_autorot().
*
* Any ICC profile is read and attached to the VIPS image. Any XMP metadata is
* read and attached to the image.
*

View File

@ -108,6 +108,14 @@ extern "C" {
*/
#define VIPS_META_LOADER "vips-loader"
/**
* VIPS_META_ORIENTATION:
*
* The orientation tag for this image. An int from 1 - 8 using the standard
* exif/tiff meanings.
*/
#define VIPS_META_ORIENTATION "orientation"
guint64 vips_format_sizeof( VipsBandFormat format );
int vips_image_get_width( const VipsImage *image );