Handle mirroring in autorot (#8)

* handle mirroring in autorot
* added tests for autorot
This commit is contained in:
Elad Laufer 2020-05-12 13:01:48 +03:00 committed by GitHub
parent b9eddecca3
commit b05cf39df8
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 140 additions and 52 deletions

View File

@ -6,6 +6,8 @@
* - test and remove orientation from every ifd
* 6/10/18
* - don't remove orientation if it's one of the cases we don't handle
* 10/5/20
* - handle mirrored images
*/
/*
@ -55,6 +57,8 @@ typedef struct _VipsAutorot {
VipsAngle angle;
gboolean flip;
} VipsAutorot;
typedef VipsConversionClass VipsAutorotClass;
@ -106,34 +110,6 @@ vips_autorot_get_angle( VipsImage *im )
return( angle );
}
/* TRUE if this is one of the cases we handle.
*/
static gboolean
vips_autorot_handled( VipsImage *im )
{
int orientation;
gboolean handled;
if( !vips_image_get_typeof( im, VIPS_META_ORIENTATION ) ||
vips_image_get_int( im, VIPS_META_ORIENTATION, &orientation ) )
orientation = 1;
switch( orientation ) {
case 1:
case 3:
case 6:
case 8:
handled = TRUE;
break;
default:
handled = FALSE;
break;
}
return( handled );
}
static void *
vips_autorot_remove_angle_sub( VipsImage *image,
const char *field, GValue *value, void *my_data )
@ -170,27 +146,93 @@ vips_autorot_build( VipsObject *object )
{
VipsConversion *conversion = VIPS_CONVERSION( object );
VipsAutorot *autorot = (VipsAutorot *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
VipsImage **t = (VipsImage **) vips_object_local_array( object, 3 );
if( VIPS_OBJECT_CLASS( vips_autorot_parent_class )->build( object ) )
return( -1 );
int orientation = 0;
VipsAngle angle;
gboolean flip = FALSE;
if( !vips_image_get_typeof( autorot->in, VIPS_META_ORIENTATION ) ||
vips_image_get_int( autorot->in, VIPS_META_ORIENTATION, &orientation ) )
orientation = 1;
g_object_set( object,
"angle", vips_autorot_get_angle( autorot->in ),
NULL );
switch( orientation ) {
if( vips_autorot_handled( autorot->in ) ) {
if( vips_rot( autorot->in, &t[0], autorot->angle, NULL ) ||
vips_copy( t[0], &t[1], NULL ) )
return( -1 );
vips_autorot_remove_angle( t[1] );
}
else {
if( vips_copy( autorot->in, &t[1], NULL ) )
return( -1 );
}
case 2:
angle = VIPS_ANGLE_D0;
flip = TRUE;
break;
if( vips_image_write( t[1], conversion->out ) )
case 4:
flip = TRUE;
case 3:
angle = VIPS_ANGLE_D180;
break;
case 5:
flip = TRUE;
case 6:
angle = VIPS_ANGLE_D90;
break;
case 7:
flip = TRUE;
case 8:
angle = VIPS_ANGLE_D270;
break;
default:
angle = VIPS_ANGLE_D0;
flip = FALSE;
break;
}
g_object_set( object,
"angle", angle,
NULL );
g_object_set( object,
"flip", flip,
NULL );
if( angle != VIPS_ANGLE_D0 && flip) {
if( vips_rot( autorot->in, &t[0], angle, NULL ) )
return( -1 );
if( vips_flip( t[0], &t[1], VIPS_DIRECTION_HORIZONTAL, NULL ) )
return ( -1 );
if( vips_copy( t[1], &t[2], NULL ) )
return( -1 );
}
else if( angle != VIPS_ANGLE_D0 ) {
if( vips_rot( autorot->in, &t[0], angle, NULL))
return ( -1 );
if( vips_copy( t[0], &t[2], NULL))
return ( -1 );
}
else if( flip ) {
if( vips_flip( autorot->in, &t[0], VIPS_DIRECTION_HORIZONTAL, NULL ) )
return ( -1 );
if( vips_copy( t[0], &t[2], NULL ) )
return( -1 );
} else {
if( vips_copy( autorot->in, &t[2], NULL ) )
return( -1 );
}
vips_autorot_remove_angle( t[2] );
if( vips_image_write( t[2], conversion->out ) )
return( -1 );
return( 0 );
@ -220,13 +262,21 @@ vips_autorot_class_init( VipsAutorotClass *class )
_( "Angle image was rotated by" ),
VIPS_ARGUMENT_OPTIONAL_OUTPUT,
G_STRUCT_OFFSET( VipsAutorot, angle ),
VIPS_TYPE_ANGLE, VIPS_ANGLE_D0 );
VIPS_TYPE_ANGLE, VIPS_ANGLE_D0 );
VIPS_ARG_BOOL( class, "flip", 7,
_( "Flip" ),
_( "Whether the image was flipped or not" ),
VIPS_ARGUMENT_OPTIONAL_OUTPUT,
G_STRUCT_OFFSET( VipsAutorot, flip ),
FALSE);
}
static void
vips_autorot_init( VipsAutorot *autorot )
{
autorot->angle = VIPS_ANGLE_D0;
autorot->flip = FALSE;
}
/**
@ -238,16 +288,13 @@ vips_autorot_init( VipsAutorot *autorot )
* Optional arguments:
*
* * @angle: output #VipsAngle the image was rotated by
* * @flip: output whether the image was flipped
*
* Look at the image metadata and rotate the image to make it upright. The
* #VIPS_META_ORIENTATION tag is removed from @out to prevent accidental
* double rotation.
*
* Read @angle to find the amount the image was rotated by.
*
* vips only supports the four simple rotations, it does not support the
* various mirror modes. If the image is using one of these mirror modes, the
* image is not rotated and the #VIPS_META_ORIENTATION tag is not removed.
* Read @angle to find the amount the image was rotated by.
*
* See also: vips_autorot_get_angle(), vips_autorot_remove_angle(), vips_rot().
*
@ -265,4 +312,3 @@ vips_autorot( VipsImage *in, VipsImage **out, ... )
return( result );
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 661 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 55 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 33 KiB

View File

@ -1,17 +1,24 @@
# vim: set fileencoding=utf-8 :
import filecmp
from functools import reduce
import os
import pytest
import tempfile
import shutil
import pyvips
from helpers import JPEG_FILE, unsigned_formats, \
from helpers import IMAGES, JPEG_FILE, unsigned_formats, \
signed_formats, float_formats, int_formats, \
noncomplex_formats, all_formats, max_value, \
sizeof_format, rot45_angles, rot45_angle_bonds, \
rot_angles, rot_angle_bonds, run_cmp, run_cmp2, \
assert_almost_equal_objects
assert_almost_equal_objects, temp_filename
class TestConversion:
tempdir = None
# run a function on an image,
# 50,50 and 10,10 should have different values on the test image
# don't loop over band elements
@ -37,6 +44,7 @@ class TestConversion:
@classmethod
def setup_class(cls):
cls.tempdir = tempfile.mkdtemp()
im = pyvips.Image.mask_ideal(100, 100, 0.5,
reject=True, optical=True)
cls.colour = (im * [1, 2, 3] + [2, 3, 4]).copy(interpretation="srgb")
@ -46,6 +54,7 @@ class TestConversion:
@classmethod
def teardown_class(cls):
shutil.rmtree(cls.tempdir, ignore_errors=True)
cls.colour = None
cls.mono = None
cls.image = None
@ -738,6 +747,39 @@ class TestConversion:
diff = (after - im).abs().max()
assert diff == 0
def test_autorot(self):
rotation_images = os.path.join(IMAGES, 'rotation')
files = os.listdir(rotation_images)
files.sort()
meta = {
0: {'w': 290, 'h': 442},
1: {'w': 308, 'h': 410},
2: {'w': 308, 'h': 410},
3: {'w': 308, 'h': 410},
4: {'w': 308, 'h': 410},
5: {'w': 231, 'h': 308},
6: {'w': 231, 'h': 308},
7: {'w': 231, 'h': 308},
8: {'w': 231, 'h': 308},
}
i = 0
for f in files:
if '.autorot.' not in f and not f.startswith('.'):
source_filename = os.path.join(rotation_images, f)
actual_filename = temp_filename(self.tempdir, '.png')
pyvips.Image.new_from_file(source_filename).autorot().write_to_file(actual_filename)
actual = pyvips.Image.new_from_file(actual_filename)
assert actual.width == meta[i]['w']
assert actual.height == meta[i]['h']
assert actual.get('orientation') if actual.get_typeof('orientation') else None is None
i = i + 1
def test_scaleimage(self):
for fmt in noncomplex_formats:
test = self.colour.cast(fmt)