change vips_flatten() alpha rules
now match vips_premultiply() add tests foreign knows about new logic
This commit is contained in:
parent
9dda68f8cd
commit
ec52d1b922
@ -1,5 +1,6 @@
|
|||||||
7/5/15 started 8.1.0
|
7/5/15 started 8.1.0
|
||||||
- add vips_premultiply(), vips_unpremultiply()
|
- add vips_premultiply(), vips_unpremultiply()
|
||||||
|
- change the alpha range rules for vips_flatten() to match vips_premultiply()
|
||||||
|
|
||||||
4/5/15 started 8.0.2
|
4/5/15 started 8.0.2
|
||||||
- fix a refcount error in C++ wrapper, thanks huskier
|
- fix a refcount error in C++ wrapper, thanks huskier
|
||||||
|
6
TODO
6
TODO
@ -1,9 +1,3 @@
|
|||||||
- looks like flatten has some confusion with flatten->in and in
|
|
||||||
|
|
||||||
change rules for flatten alpha max ... copy the premultiply thing of being
|
|
||||||
255 + a param, look at convert savveable
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
- are the mosaic functions calling vips_fastcor()? it must be very slow
|
- are the mosaic functions calling vips_fastcor()? it must be very slow
|
||||||
|
|
||||||
|
@ -5,6 +5,8 @@
|
|||||||
*
|
*
|
||||||
* 4/1/14
|
* 4/1/14
|
||||||
* - better rounding
|
* - better rounding
|
||||||
|
* 9/5/15
|
||||||
|
* - add max_alpha to match vips_premultiply() etc.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -67,6 +69,10 @@ typedef struct _VipsFlatten {
|
|||||||
*/
|
*/
|
||||||
VipsPel *ink;
|
VipsPel *ink;
|
||||||
|
|
||||||
|
/* Use this to scale alpha to 0 - 1.
|
||||||
|
*/
|
||||||
|
double max_alpha;
|
||||||
|
|
||||||
} VipsFlatten;
|
} VipsFlatten;
|
||||||
|
|
||||||
typedef VipsConversionClass VipsFlattenClass;
|
typedef VipsConversionClass VipsFlattenClass;
|
||||||
@ -75,7 +81,7 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
|
|
||||||
/* Flatten with black background.
|
/* Flatten with black background.
|
||||||
*/
|
*/
|
||||||
#define VIPS_FLATTEN_BLACK( TYPE, MAX ) { \
|
#define VIPS_FLATTEN_BLACK( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
@ -84,7 +90,7 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = (p[b] * alpha) / (MAX); \
|
q[b] = (p[b] * alpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
@ -93,19 +99,19 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
|
|
||||||
/* Flatten with any background.
|
/* Flatten with any background.
|
||||||
*/
|
*/
|
||||||
#define VIPS_FLATTEN( TYPE, MAX ) { \
|
#define VIPS_FLATTEN( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
for( x = 0; x < width; x++ ) { \
|
for( x = 0; x < width; x++ ) { \
|
||||||
TYPE alpha = p[bands - 1]; \
|
TYPE alpha = p[bands - 1]; \
|
||||||
TYPE nalpha = (MAX) - alpha; \
|
TYPE nalpha = max_alpha - alpha; \
|
||||||
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = (p[b] * alpha) / (MAX) + \
|
q[b] = (p[b] * alpha) / max_alpha + \
|
||||||
(bg[b] * nalpha) / (MAX); \
|
(bg[b] * nalpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
@ -115,7 +121,7 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
/* Same, but with float arithmetic. Necessary for int/uint to prevent
|
/* Same, but with float arithmetic. Necessary for int/uint to prevent
|
||||||
* overflow.
|
* overflow.
|
||||||
*/
|
*/
|
||||||
#define VIPS_FLATTEN_BLACK_FLOAT( TYPE, MAX ) { \
|
#define VIPS_FLATTEN_BLACK_FLOAT( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
@ -124,26 +130,26 @@ G_DEFINE_TYPE( VipsFlatten, vips_flatten, VIPS_TYPE_CONVERSION );
|
|||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = ((double) p[b] * alpha) / (MAX); \
|
q[b] = ((double) p[b] * alpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
} \
|
} \
|
||||||
}
|
}
|
||||||
|
|
||||||
#define VIPS_FLATTEN_FLOAT( TYPE, MAX ) { \
|
#define VIPS_FLATTEN_FLOAT( TYPE ) { \
|
||||||
TYPE * restrict p = (TYPE *) in; \
|
TYPE * restrict p = (TYPE *) in; \
|
||||||
TYPE * restrict q = (TYPE *) out; \
|
TYPE * restrict q = (TYPE *) out; \
|
||||||
\
|
\
|
||||||
for( x = 0; x < width; x++ ) { \
|
for( x = 0; x < width; x++ ) { \
|
||||||
TYPE alpha = p[bands - 1]; \
|
TYPE alpha = p[bands - 1]; \
|
||||||
TYPE nalpha = (MAX) - alpha; \
|
TYPE nalpha = max_alpha - alpha; \
|
||||||
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
TYPE * restrict bg = (TYPE *) flatten->ink; \
|
||||||
int b; \
|
int b; \
|
||||||
\
|
\
|
||||||
for( b = 0; b < bands - 1; b++ ) \
|
for( b = 0; b < bands - 1; b++ ) \
|
||||||
q[b] = ((double) p[b] * alpha) / (MAX) + \
|
q[b] = ((double) p[b] * alpha) / max_alpha + \
|
||||||
((double) bg[b] * nalpha) / (MAX); \
|
((double) bg[b] * nalpha) / max_alpha; \
|
||||||
\
|
\
|
||||||
p += bands; \
|
p += bands; \
|
||||||
q += bands - 1; \
|
q += bands - 1; \
|
||||||
@ -159,6 +165,7 @@ vips_flatten_black_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
VipsRect *r = &or->valid;
|
VipsRect *r = &or->valid;
|
||||||
int width = r->width;
|
int width = r->width;
|
||||||
int bands = ir->im->Bands;
|
int bands = ir->im->Bands;
|
||||||
|
double max_alpha = flatten->max_alpha;
|
||||||
|
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
@ -171,37 +178,35 @@ vips_flatten_black_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
|
|
||||||
switch( flatten->in->BandFmt ) {
|
switch( flatten->in->BandFmt ) {
|
||||||
case VIPS_FORMAT_UCHAR:
|
case VIPS_FORMAT_UCHAR:
|
||||||
VIPS_FLATTEN_BLACK( unsigned char, UCHAR_MAX );
|
VIPS_FLATTEN_BLACK( unsigned char );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_CHAR:
|
case VIPS_FORMAT_CHAR:
|
||||||
/* Alpha is 0 - 127? No idea, really.
|
VIPS_FLATTEN_BLACK( signed char );
|
||||||
*/
|
|
||||||
VIPS_FLATTEN_BLACK( signed char, CHAR_MAX );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_USHORT:
|
case VIPS_FORMAT_USHORT:
|
||||||
VIPS_FLATTEN_BLACK( unsigned short, USHRT_MAX );
|
VIPS_FLATTEN_BLACK( unsigned short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_SHORT:
|
case VIPS_FORMAT_SHORT:
|
||||||
VIPS_FLATTEN_BLACK( signed short, SHRT_MAX );
|
VIPS_FLATTEN_BLACK( signed short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_UINT:
|
case VIPS_FORMAT_UINT:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( unsigned int, UINT_MAX );
|
VIPS_FLATTEN_BLACK_FLOAT( unsigned int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_INT:
|
case VIPS_FORMAT_INT:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( signed int, INT_MAX );
|
VIPS_FLATTEN_BLACK_FLOAT( signed int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_FLOAT:
|
case VIPS_FORMAT_FLOAT:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( float, 1.0 );
|
VIPS_FLATTEN_BLACK_FLOAT( float );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_DOUBLE:
|
case VIPS_FORMAT_DOUBLE:
|
||||||
VIPS_FLATTEN_BLACK_FLOAT( double, 1.0 );
|
VIPS_FLATTEN_BLACK_FLOAT( double );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_COMPLEX:
|
case VIPS_FORMAT_COMPLEX:
|
||||||
@ -225,6 +230,7 @@ vips_flatten_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
VipsRect *r = &or->valid;
|
VipsRect *r = &or->valid;
|
||||||
int width = r->width;
|
int width = r->width;
|
||||||
int bands = ir->im->Bands;
|
int bands = ir->im->Bands;
|
||||||
|
double max_alpha = flatten->max_alpha;
|
||||||
|
|
||||||
int x, y;
|
int x, y;
|
||||||
|
|
||||||
@ -237,37 +243,35 @@ vips_flatten_gen( VipsRegion *or, void *vseq, void *a, void *b,
|
|||||||
|
|
||||||
switch( flatten->in->BandFmt ) {
|
switch( flatten->in->BandFmt ) {
|
||||||
case VIPS_FORMAT_UCHAR:
|
case VIPS_FORMAT_UCHAR:
|
||||||
VIPS_FLATTEN( unsigned char, UCHAR_MAX );
|
VIPS_FLATTEN( unsigned char );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_CHAR:
|
case VIPS_FORMAT_CHAR:
|
||||||
/* Alpha is 0 - 127? No idea, really.
|
VIPS_FLATTEN( signed char );
|
||||||
*/
|
|
||||||
VIPS_FLATTEN( signed char, CHAR_MAX );
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_USHORT:
|
case VIPS_FORMAT_USHORT:
|
||||||
VIPS_FLATTEN( unsigned short, USHRT_MAX );
|
VIPS_FLATTEN( unsigned short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_SHORT:
|
case VIPS_FORMAT_SHORT:
|
||||||
VIPS_FLATTEN( signed short, SHRT_MAX );
|
VIPS_FLATTEN( signed short );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_UINT:
|
case VIPS_FORMAT_UINT:
|
||||||
VIPS_FLATTEN_FLOAT( unsigned int, UINT_MAX );
|
VIPS_FLATTEN_FLOAT( unsigned int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_INT:
|
case VIPS_FORMAT_INT:
|
||||||
VIPS_FLATTEN_FLOAT( signed int, INT_MAX );
|
VIPS_FLATTEN_FLOAT( signed int );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_FLOAT:
|
case VIPS_FORMAT_FLOAT:
|
||||||
VIPS_FLATTEN_FLOAT( float, 1.0 );
|
VIPS_FLATTEN_FLOAT( float );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_DOUBLE:
|
case VIPS_FORMAT_DOUBLE:
|
||||||
VIPS_FLATTEN_FLOAT( double, 1.0 );
|
VIPS_FLATTEN_FLOAT( double );
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case VIPS_FORMAT_COMPLEX:
|
case VIPS_FORMAT_COMPLEX:
|
||||||
@ -304,7 +308,7 @@ vips_flatten_build( VipsObject *object )
|
|||||||
|
|
||||||
/* Trivial case: fall back to copy().
|
/* Trivial case: fall back to copy().
|
||||||
*/
|
*/
|
||||||
if( flatten->in->Bands == 1 )
|
if( in->Bands == 1 )
|
||||||
return( vips_image_write( in, conversion->out ) );
|
return( vips_image_write( in, conversion->out ) );
|
||||||
|
|
||||||
if( vips_check_noncomplex( class->nickname, in ) )
|
if( vips_check_noncomplex( class->nickname, in ) )
|
||||||
@ -380,12 +384,21 @@ vips_flatten_class_init( VipsFlattenClass *class )
|
|||||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
G_STRUCT_OFFSET( VipsFlatten, background ),
|
G_STRUCT_OFFSET( VipsFlatten, background ),
|
||||||
VIPS_TYPE_ARRAY_DOUBLE );
|
VIPS_TYPE_ARRAY_DOUBLE );
|
||||||
|
|
||||||
|
VIPS_ARG_DOUBLE( class, "max_alpha", 115,
|
||||||
|
_( "Maximum alpha" ),
|
||||||
|
_( "Maximum value of alpha channel" ),
|
||||||
|
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||||
|
G_STRUCT_OFFSET( VipsFlatten, max_alpha ),
|
||||||
|
0, 100000000, 255 );
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
static void
|
static void
|
||||||
vips_flatten_init( VipsFlatten *flatten )
|
vips_flatten_init( VipsFlatten *flatten )
|
||||||
{
|
{
|
||||||
flatten->background = vips_array_double_newv( 1, 0.0 );
|
flatten->background = vips_array_double_newv( 1, 0.0 );
|
||||||
|
flatten->max_alpha= 255.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -397,19 +410,24 @@ vips_flatten_init( VipsFlatten *flatten )
|
|||||||
* Optional arguments:
|
* Optional arguments:
|
||||||
*
|
*
|
||||||
* @background: #VipsArrayDouble colour for new pixels
|
* @background: #VipsArrayDouble colour for new pixels
|
||||||
|
* @max_alpha: %gdouble, maximum value for alpha
|
||||||
*
|
*
|
||||||
* Take the last band of @in as an alpha and use it to blend the
|
* Take the last band of @in as an alpha and use it to blend the
|
||||||
* remaining channels with @background.
|
* remaining channels with @background.
|
||||||
*
|
*
|
||||||
* The alpha channel is 0 - MAX for
|
* The alpha channel is 0 - @max_alpha,
|
||||||
* integer images and 0 - 1 for float images, where MAX means 100% image and 0
|
* where 1 means 100% image and 0
|
||||||
* means 100% background. Non-complex images only.
|
* means 100% background.
|
||||||
* @background defaults to zero (black). MAX is the largest possible positive
|
* Non-complex images only.
|
||||||
* value for that int type.
|
* @background defaults to zero (black).
|
||||||
|
*
|
||||||
|
* @max_alpha has the default value 255. You will need to set this to 65535
|
||||||
|
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||||
|
* alpha.
|
||||||
*
|
*
|
||||||
* Useful for flattening PNG images to RGB.
|
* Useful for flattening PNG images to RGB.
|
||||||
*
|
*
|
||||||
* See also: pngload().
|
* See also: vips_premultiply(), vips_pngload().
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, -1 on error
|
* Returns: 0 on success, -1 on error
|
||||||
*/
|
*/
|
||||||
|
@ -296,6 +296,8 @@ vips_premultiply_init( VipsPremultiply *premultiply )
|
|||||||
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||||
* alpha.
|
* alpha.
|
||||||
*
|
*
|
||||||
|
* Non-complex images only.
|
||||||
|
*
|
||||||
* See also: vips_unpremultiply(), vips_flatten().
|
* See also: vips_unpremultiply(), vips_flatten().
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, -1 on error
|
* Returns: 0 on success, -1 on error
|
||||||
|
@ -311,6 +311,8 @@ vips_unpremultiply_init( VipsUnpremultiply *unpremultiply )
|
|||||||
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
* for images with a 16-bit alpha, or perhaps 1.0 for images with a float
|
||||||
* alpha.
|
* alpha.
|
||||||
*
|
*
|
||||||
|
* Non-complex images only.
|
||||||
|
*
|
||||||
* See also: vips_premultiply(), vips_flatten().
|
* See also: vips_premultiply(), vips_flatten().
|
||||||
*
|
*
|
||||||
* Returns: 0 on success, -1 on error
|
* Returns: 0 on success, -1 on error
|
||||||
|
@ -1202,6 +1202,9 @@ vips_foreign_convert_saveable( VipsForeignSave *save )
|
|||||||
|
|
||||||
if( vips_flatten( in, &out,
|
if( vips_flatten( in, &out,
|
||||||
"background", save->background,
|
"background", save->background,
|
||||||
|
"max_alpha",
|
||||||
|
in->BandFmt == VIPS_FORMAT_USHORT ?
|
||||||
|
65535 : 255,
|
||||||
NULL ) ) {
|
NULL ) ) {
|
||||||
g_object_unref( in );
|
g_object_unref( in );
|
||||||
return( -1 );
|
return( -1 );
|
||||||
|
@ -348,23 +348,12 @@ class TestConversion(unittest.TestCase):
|
|||||||
self.assertAlmostEqualObjects(pixel, [20, 0, 41])
|
self.assertAlmostEqualObjects(pixel, [20, 0, 41])
|
||||||
|
|
||||||
def test_flatten(self):
|
def test_flatten(self):
|
||||||
max_value = {Vips.BandFormat.UCHAR: 0xff,
|
for fmt in unsigned_formats + [Vips.BandFormat.SHORT,
|
||||||
Vips.BandFormat.USHORT: 0xffff,
|
Vips.BandFormat.INT] + float_formats:
|
||||||
Vips.BandFormat.UINT: 0xffffffff,
|
mx = 255
|
||||||
Vips.BandFormat.CHAR: 0x7f,
|
|
||||||
Vips.BandFormat.SHORT: 0x7fff,
|
|
||||||
Vips.BandFormat.INT: 0x7fffffff,
|
|
||||||
Vips.BandFormat.FLOAT: 1.0,
|
|
||||||
Vips.BandFormat.DOUBLE: 1.0,
|
|
||||||
Vips.BandFormat.COMPLEX: 1.0,
|
|
||||||
Vips.BandFormat.DPCOMPLEX: 1.0}
|
|
||||||
black = self.mono * 0.0
|
|
||||||
|
|
||||||
for fmt in noncomplex_formats:
|
|
||||||
mx = max_value[fmt]
|
|
||||||
alpha = mx / 2.0
|
alpha = mx / 2.0
|
||||||
nalpha = mx - alpha
|
nalpha = mx - alpha
|
||||||
test = self.colour.bandjoin(black + alpha).cast(fmt)
|
test = self.colour.bandjoin(alpha).cast(fmt)
|
||||||
pixel = test(30, 30)
|
pixel = test(30, 30)
|
||||||
|
|
||||||
predict = [int(x) * alpha / mx for x in pixel[:-1]]
|
predict = [int(x) * alpha / mx for x in pixel[:-1]]
|
||||||
@ -389,6 +378,46 @@ class TestConversion(unittest.TestCase):
|
|||||||
for x, y in zip(pixel, predict):
|
for x, y in zip(pixel, predict):
|
||||||
self.assertLess(abs(x - y), 2)
|
self.assertLess(abs(x - y), 2)
|
||||||
|
|
||||||
|
def test_premultiply(self):
|
||||||
|
for fmt in unsigned_formats + [Vips.BandFormat.SHORT,
|
||||||
|
Vips.BandFormat.INT] + float_formats:
|
||||||
|
mx = 255
|
||||||
|
alpha = mx / 2.0
|
||||||
|
nalpha = mx - alpha
|
||||||
|
test = self.colour.bandjoin(alpha).cast(fmt)
|
||||||
|
pixel = test(30, 30)
|
||||||
|
|
||||||
|
predict = [int(x) * alpha / mx for x in pixel[:-1]] + [alpha]
|
||||||
|
|
||||||
|
im = test.premultiply()
|
||||||
|
|
||||||
|
self.assertEqual(im.bands, test.bands)
|
||||||
|
pixel = im(30, 30)
|
||||||
|
for x, y in zip(pixel, predict):
|
||||||
|
# we use float arithetic for int and uint, so the rounding
|
||||||
|
# differs ... don't require huge accuracy
|
||||||
|
self.assertLess(abs(x - y), 2)
|
||||||
|
|
||||||
|
def test_unpremultiply(self):
|
||||||
|
for fmt in unsigned_formats + [Vips.BandFormat.SHORT,
|
||||||
|
Vips.BandFormat.INT] + float_formats:
|
||||||
|
mx = 255
|
||||||
|
alpha = mx / 2.0
|
||||||
|
nalpha = mx - alpha
|
||||||
|
test = self.colour.bandjoin(alpha).cast(fmt)
|
||||||
|
pixel = test(30, 30)
|
||||||
|
|
||||||
|
predict = [int(x) / (alpha / mx) for x in pixel[:-1]] + [alpha]
|
||||||
|
|
||||||
|
im = test.unpremultiply()
|
||||||
|
|
||||||
|
self.assertEqual(im.bands, test.bands)
|
||||||
|
pixel = im(30, 30)
|
||||||
|
for x, y in zip(pixel, predict):
|
||||||
|
# we use float arithetic for int and uint, so the rounding
|
||||||
|
# differs ... don't require huge accuracy
|
||||||
|
self.assertLess(abs(x - y), 2)
|
||||||
|
|
||||||
def test_flip(self):
|
def test_flip(self):
|
||||||
for fmt in all_formats:
|
for fmt in all_formats:
|
||||||
test = self.colour.cast(fmt)
|
test = self.colour.cast(fmt)
|
||||||
|
Loading…
Reference in New Issue
Block a user