deprecate heifload autorotate

heifload autorotate is now always on, and we always delete EXIF
orientation tags. If a HEIC image fails to rotate upright, at least it's
someone else's fault.

see https://github.com/libvips/libvips/pull/1680
This commit is contained in:
John Cupitt 2020-06-10 19:57:50 +01:00
parent f2688ee6c1
commit 1e015654c3
7 changed files with 42 additions and 107 deletions

View File

@ -24,8 +24,9 @@
behaviour with alpha channels behaviour with alpha channels
- improve bioformats support with read and write of tiff subifd pyramids - improve bioformats support with read and write of tiff subifd pyramids
- thumbnail exploits subifd pyramids - thumbnail exploits subifd pyramids
- handle all EXIF orientation cases, deprecate - handle all EXIF orientation cases, deprecate vips_autorot_get_angle()
vips_autorot_get_angle() [Elad-Laufer] [Elad-Laufer]
- deprecate heifload autorotate -- it's now always on
24/4/20 started 8.9.3 24/4/20 started 8.9.3
- better iiif tile naming [IllyaMoskvin] - better iiif tile naming [IllyaMoskvin]

View File

@ -928,18 +928,6 @@ if test x"$with_heif" = x"yes"; then
LIBS="$save_LIBS" LIBS="$save_LIBS"
fi fi
# fetch untransformed width/height added in 1.3.4
if test x"$with_heif" = x"yes"; then
save_LIBS="$LIBS"
LIBS="$LIBS $HEIF_LIBS"
AC_CHECK_FUNCS(heif_image_handle_get_ispe_width,[
AC_DEFINE(HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH,1,
[define if you have heif_image_handle_get_ispe_width.])
],[]
)
LIBS="$save_LIBS"
fi
# heif_decoding_options.convert_hdr_to_8bit added in 1.7.0 # heif_decoding_options.convert_hdr_to_8bit added in 1.7.0
if test x"$with_heif" = x"yes"; then if test x"$with_heif" = x"yes"; then
save_CFLAGS="$CFLAGS" save_CFLAGS="$CFLAGS"

View File

@ -16,6 +16,8 @@
* - restart after minimise * - restart after minimise
* 15/3/20 * 15/3/20
* - revise for new VipsSource API * - revise for new VipsSource API
* 10/5/20
* - deprecate autorotate -- it's too difficult to support properly
*/ */
/* /*
@ -99,6 +101,11 @@ typedef struct _VipsForeignLoadHeif {
gboolean thumbnail; gboolean thumbnail;
/* Apply any orientation tags in the header. /* Apply any orientation tags in the header.
*
* This is deprecated and does nothing. Non-autorotated reads from
* libheif are surprisingly hard to support well, since orientation can
* be represented in several different ways in HEIC files and devices
* vary in how they do this.
*/ */
gboolean autorotate; gboolean autorotate;
@ -364,7 +371,7 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
#endif /*DEBUG*/ #endif /*DEBUG*/
bands = heif->has_alpha ? 4 : 3; bands = heif->has_alpha ? 4 : 3;
/* FIXME .. need to test XMP and IPCT. /* FIXME .. IPTC as well?
*/ */
n_metadata = heif_image_handle_get_list_of_metadata_block_IDs( n_metadata = heif_image_handle_get_list_of_metadata_block_IDs(
heif->handle, NULL, id, VIPS_NUMBER( id ) ); heif->handle, NULL, id, VIPS_NUMBER( id ) );
@ -414,10 +421,20 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
vips_image_set_blob( out, name, vips_image_set_blob( out, name,
(VipsCallbackFn) NULL, data, length ); (VipsCallbackFn) NULL, data, length );
if( g_ascii_strcasecmp( type, "exif" ) == 0 ) /* image_set will automatically parse EXIF, if necessary.
(void) vips__exif_parse( out ); */
} }
/* We use libheif's autorotate, so we need to remove any EXIF
* orientaion tags.
*
* According to the HEIF standard, EXIF orientation tags are only
* informational and images should not be rotated because of them.
* Unless we strip these tags, there's a danger downstream processing
* could double-rotate.
*/
vips_autorot_remove_angle( out );
#ifdef HAVE_HEIF_COLOR_PROFILE #ifdef HAVE_HEIF_COLOR_PROFILE
enum heif_color_profile_type profile_type = enum heif_color_profile_type profile_type =
heif_image_handle_get_color_profile_type( heif->handle ); heif_image_handle_get_color_profile_type( heif->handle );
@ -480,13 +497,6 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
} }
#endif /*HAVE_HEIF_COLOR_PROFILE*/ #endif /*HAVE_HEIF_COLOR_PROFILE*/
/* If we are using libheif's autorotate, remove the exif one.
*/
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
if( heif->autorotate )
vips_autorot_remove_angle( out );
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
vips_image_set_int( out, "heif-primary", heif->primary_page ); vips_image_set_int( out, "heif-primary", heif->primary_page );
vips_image_set_int( out, "n-pages", heif->n_top ); vips_image_set_int( out, "n-pages", heif->n_top );
if( vips_object_argument_isset( VIPS_OBJECT( heif ), "n" ) ) if( vips_object_argument_isset( VIPS_OBJECT( heif ), "n" ) )
@ -508,40 +518,6 @@ vips_foreign_load_heif_set_header( VipsForeignLoadHeif *heif, VipsImage *out )
return( 0 ); return( 0 );
} }
static int
vips_foreign_load_heif_get_width( VipsForeignLoadHeif *heif,
struct heif_image_handle *handle )
{
int width;
/* _get_ipse_width() fetches the untransformed dimension, but was only
* added in 1.3.4. Without it, we just use the transformed dimension
* and have to autorotate.
*/
width = heif_image_handle_get_width( handle );
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
if( !heif->autorotate )
width = heif_image_handle_get_ispe_width( handle );
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
return( width );
}
static int
vips_foreign_load_heif_get_height( VipsForeignLoadHeif *heif,
struct heif_image_handle *handle )
{
int height;
height = heif_image_handle_get_height( handle );
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
if( !heif->autorotate )
height = heif_image_handle_get_ispe_height( handle );
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
return( height );
}
static int static int
vips_foreign_load_heif_header( VipsForeignLoad *load ) vips_foreign_load_heif_header( VipsForeignLoad *load )
{ {
@ -585,10 +561,6 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
} }
#ifdef DEBUG #ifdef DEBUG
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
if( !heif->autorotate )
printf( "using _get_ispe_width() / _height()\n" );
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
for( i = heif->page; i < heif->page + heif->n; i++ ) { for( i = heif->page; i < heif->page + heif->n; i++ ) {
heif_item_id thumb_ids[1]; heif_item_id thumb_ids[1];
int n_items; int n_items;
@ -619,11 +591,9 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
printf( " thumb %d\n", j ); printf( " thumb %d\n", j );
printf( " width = %d\n", printf( " width = %d\n",
vips_foreign_load_heif_get_width( heif, heif_image_handle_get_width( thumb_handle ) );
thumb_handle ) );
printf( " height = %d\n", printf( " height = %d\n",
vips_foreign_load_heif_get_height( heif, heif_image_handle_get_height( thumb_handle ) );
thumb_handle ) );
} }
} }
#endif /*DEBUG*/ #endif /*DEBUG*/
@ -633,18 +603,16 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
if( vips_foreign_load_heif_set_page( heif, if( vips_foreign_load_heif_set_page( heif,
heif->page, heif->thumbnail ) ) heif->page, heif->thumbnail ) )
return( -1 ); return( -1 );
heif->page_width = vips_foreign_load_heif_get_width( heif, heif->page_width = heif_image_handle_get_width( heif->handle );
heif->handle ); heif->page_height = heif_image_handle_get_height( heif->handle );
heif->page_height = vips_foreign_load_heif_get_height( heif,
heif->handle );
for( i = heif->page + 1; i < heif->page + heif->n; i++ ) { for( i = heif->page + 1; i < heif->page + heif->n; i++ ) {
if( vips_foreign_load_heif_set_page( heif, if( vips_foreign_load_heif_set_page( heif,
i, heif->thumbnail ) ) i, heif->thumbnail ) )
return( -1 ); return( -1 );
if( vips_foreign_load_heif_get_width( heif, if( heif_image_handle_get_width( heif->handle )
heif->handle ) != heif->page_width || != heif->page_width ||
vips_foreign_load_heif_get_height( heif, heif_image_handle_get_height( heif->handle )
heif->handle ) != heif->page_height ) { != heif->page_height ) {
vips_error( class->nickname, "%s", vips_error( class->nickname, "%s",
_( "not all pages are the same size" ) ); _( "not all pages are the same size" ) );
return( -1 ); return( -1 );
@ -658,11 +626,9 @@ vips_foreign_load_heif_header( VipsForeignLoad *load )
if( vips_foreign_load_heif_set_page( heif, i, FALSE ) ) if( vips_foreign_load_heif_set_page( heif, i, FALSE ) )
return( -1 ); return( -1 );
printf( " width = %d\n", printf( " width = %d\n",
vips_foreign_load_heif_get_width( heif, heif_image_handle_get_width( heif->handle ) );
heif->handle ) );
printf( " height = %d\n", printf( " height = %d\n",
vips_foreign_load_heif_get_height( heif, heif_image_handle_get_height( heif->handle ) );
heif->handle ) );
printf( " has_depth = %d\n", printf( " has_depth = %d\n",
heif_image_handle_has_depth_image( heif->handle ) ); heif_image_handle_has_depth_image( heif->handle ) );
printf( " has_alpha = %d\n", printf( " has_alpha = %d\n",
@ -713,13 +679,7 @@ vips_foreign_load_heif_generate( VipsRegion *or,
heif_chroma_interleaved_RGBA : heif_chroma_interleaved_RGBA :
heif_chroma_interleaved_RGB; heif_chroma_interleaved_RGB;
/* Only disable transforms if we have been able to fetch the
* untransformed dimensions.
*/
options = heif_decoding_options_alloc(); options = heif_decoding_options_alloc();
#ifdef HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH
options->ignore_transformations = !heif->autorotate;
#endif /*HAVE_HEIF_IMAGE_HANDLE_GET_ISPE_WIDTH*/
#ifdef HAVE_HEIF_DECODING_OPTIONS_CONVERT_HDR_TO_8BIT #ifdef HAVE_HEIF_DECODING_OPTIONS_CONVERT_HDR_TO_8BIT
/* VIPS_FORMAT_UCHAR is assumed so downsample HDR to 8bpc /* VIPS_FORMAT_UCHAR is assumed so downsample HDR to 8bpc
*/ */
@ -891,7 +851,7 @@ vips_foreign_load_heif_class_init( VipsForeignLoadHeifClass *class )
VIPS_ARG_BOOL( class, "autorotate", 21, VIPS_ARG_BOOL( class, "autorotate", 21,
_( "Autorotate" ), _( "Autorotate" ),
_( "Rotate image using exif orientation" ), _( "Rotate image using exif orientation" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT | VIPS_ARGUMENT_DEPRECATED,
G_STRUCT_OFFSET( VipsForeignLoadHeif, autorotate ), G_STRUCT_OFFSET( VipsForeignLoadHeif, autorotate ),
FALSE ); FALSE );
@ -913,9 +873,12 @@ vips_foreign_load_heif_read( void *data, size_t size, void *userdata )
gint64 result; gint64 result;
result = vips_source_read( heif->source, data, size ); result = vips_source_read( heif->source, data, size );
/* On EOF, make a note of the file length.
*/
if( result == 0 && if( result == 0 &&
heif->length == -1 ) heif->length == -1 )
heif->length = vips_source_seek( heif->source, 0L, SEEK_CUR ); result = heif->length =
vips_source_seek( heif->source, 0L, SEEK_CUR );
if( result < 0 ) if( result < 0 )
return( -1 ); return( -1 );
@ -1211,7 +1174,6 @@ vips_foreign_load_heif_source_init( VipsForeignLoadHeifSource *source )
* * @page: %gint, page (top-level image number) to read * * @page: %gint, page (top-level image number) to read
* * @n: %gint, load this many pages * * @n: %gint, load this many pages
* * @thumbnail: %gboolean, fetch thumbnail instead of image * * @thumbnail: %gboolean, fetch thumbnail instead of image
* * @autorotate: %gboolean, rotate image upright during load
* *
* Read a HEIF image file into a VIPS image. * Read a HEIF image file into a VIPS image.
* *
@ -1228,17 +1190,6 @@ vips_foreign_load_heif_source_init( VipsForeignLoadHeifSource *source )
* If @thumbnail is %TRUE, then fetch a stored thumbnail rather than the * If @thumbnail is %TRUE, then fetch a stored thumbnail rather than the
* image. * image.
* *
* Setting @autorotate to %TRUE will make the loader interpret the
* orientation tag and automatically rotate the image appropriately during
* load.
*
* If @autorotate is %FALSE, the metadata field #VIPS_META_ORIENTATION is set
* to the value of the orientation tag. Applications may read and interpret
* this field
* as they wish later in processing. See vips_autorot(). Save
* operations will use #VIPS_META_ORIENTATION, if present, to set the
* orientation of output images.
*
* See also: vips_image_new_from_file(). * See also: vips_image_new_from_file().
* *
* Returns: 0 on success, -1 on error. * Returns: 0 on success, -1 on error.
@ -1268,7 +1219,6 @@ vips_heifload( const char *filename, VipsImage **out, ... )
* * @page: %gint, page (top-level image number) to read * * @page: %gint, page (top-level image number) to read
* * @n: %gint, load this many pages * * @n: %gint, load this many pages
* * @thumbnail: %gboolean, fetch thumbnail instead of image * * @thumbnail: %gboolean, fetch thumbnail instead of image
* * @autorotate: %gboolean, rotate image upright during load
* *
* Read a HEIF image file into a VIPS image. * Read a HEIF image file into a VIPS image.
* Exactly as vips_heifload(), but read from a memory buffer. * Exactly as vips_heifload(), but read from a memory buffer.
@ -1311,7 +1261,6 @@ vips_heifload_buffer( void *buf, size_t len, VipsImage **out, ... )
* * @page: %gint, page (top-level image number) to read * * @page: %gint, page (top-level image number) to read
* * @n: %gint, load this many pages * * @n: %gint, load this many pages
* * @thumbnail: %gboolean, fetch thumbnail instead of image * * @thumbnail: %gboolean, fetch thumbnail instead of image
* * @autorotate: %gboolean, rotate image upright during load
* *
* Exactly as vips_heifload(), but read from a source. * Exactly as vips_heifload(), but read from a source.
* *

View File

@ -36,7 +36,7 @@ DICOM_FILE = os.path.join(IMAGES, "dicom_test_image.dcm")
BMP_FILE = os.path.join(IMAGES, "MARBLES.BMP") BMP_FILE = os.path.join(IMAGES, "MARBLES.BMP")
NIFTI_FILE = os.path.join(IMAGES, "avg152T1_LR_nifti.nii.gz") NIFTI_FILE = os.path.join(IMAGES, "avg152T1_LR_nifti.nii.gz")
ICO_FILE = os.path.join(IMAGES, "favicon.ico") ICO_FILE = os.path.join(IMAGES, "favicon.ico")
HEIC_FILE = os.path.join(IMAGES, "Example1.heic") HEIC_FILE = os.path.join(IMAGES, "heic-orientation-6.heic")
unsigned_formats = [pyvips.BandFormat.UCHAR, unsigned_formats = [pyvips.BandFormat.UCHAR,
pyvips.BandFormat.USHORT, pyvips.BandFormat.USHORT,

Binary file not shown.

View File

@ -968,9 +968,9 @@ class TestForeign:
a = im(10, 10) a = im(10, 10)
# different versions of HEIC decode have slightly different # different versions of HEIC decode have slightly different
# rounding # rounding
assert_almost_equal_objects(a, [75.0, 86.0, 81.0], threshold=2) assert_almost_equal_objects(a, [197.0, 181.0, 158.0], threshold=2)
assert im.width == 4032 assert im.width == 3024
assert im.height == 3024 assert im.height == 4032
assert im.bands == 3 assert im.bands == 3
self.file_loader("heifload", HEIC_FILE, heif_valid) self.file_loader("heifload", HEIC_FILE, heif_valid)

View File

@ -171,9 +171,6 @@ class TestResample:
im = pyvips.Image.new_from_file(HEIC_FILE) im = pyvips.Image.new_from_file(HEIC_FILE)
thumb = pyvips.Image.thumbnail(HEIC_FILE, 100) thumb = pyvips.Image.thumbnail(HEIC_FILE, 100)
# original is landscape
assert im.width > im.height
# thumb should be portrait # thumb should be portrait
assert thumb.width < thumb.height assert thumb.width < thumb.height
assert thumb.height == 100 assert thumb.height == 100