faster and more accurate sRGB <-> XYZ

just use one table, since all colour channels are the same now
have more points in the float -> int direction, fewer in int -> float
faster out of range detection
This commit is contained in:
John Cupitt 2012-11-01 08:53:54 +00:00
parent a2d4c15049
commit 63a06e5f81
3 changed files with 44 additions and 93 deletions

View File

@ -7,6 +7,7 @@
im_dECMC_fromLab(), im_dE00_from_Lab()
as classes
- added vips_colour_convert(), replaces all derived conversions
- faster and more accurate sRGB <-> XYZ conversion
- dzsave can write zoomify and google maps layout as well
- tilecache supports threaded access
- openslide2vips gets underlying tile size from openslide

9
TODO
View File

@ -1,11 +1,10 @@
- see vips_col_sRGB2XYZ()
t_r2Yr[] is a 1501 element array, but we just index with a uchar
trim this down!
- add something to dzsave to control pyramid depth ... can then use it for
straight tiling
- add mono as a colourspace? also rad?
- rename INTERPRETATION_UCS to _CMC
- something to test if an image is in a supported colourspace?
at the moment we only look at interpretation, but for things like labq

View File

@ -10,6 +10,8 @@
* 21/9/12
* - redone as a class
* - sRGB only, support for other RGBs is now via lcms
* 1/11/12
* - faster and more accurate sRGB <-> XYZ conversion
*/
/*
@ -51,6 +53,8 @@
#include "colour.h"
#define TABLE_SIZE (20000)
typedef VipsColourCode VipsLabQ2sRGB;
typedef VipsColourCodeClass VipsLabQ2sRGBClass;
@ -88,16 +92,11 @@ struct im_col_display {
*/
struct im_col_tab_disp {
/*< private >*/
float t_Yr2r[1501]; /* Conversion of Yr to r */
float t_Yg2g[1501]; /* Conversion of Yg to g */
float t_Yb2b[1501]; /* Conversion of Yb to b */
float t_r2Yr[1501]; /* Conversion of r to Yr */
float t_g2Yg[1501]; /* Conversion of g to Yg */
float t_b2Yb[1501]; /* Conversion of b to Yb */
float t_Y2v[TABLE_SIZE]; /* Conversion of Y to v */
float t_v2Y[256]; /* Conversion of v to Y */
float mat_XYZ2lum[3][3]; /* XYZ to Yr, Yg, Yb matrix */
float mat_lum2XYZ[3][3]; /* Yr, Yg, Yb to XYZ matrix */
float rstep, gstep, bstep;
float ristep, gistep, bistep;
float rstep; /* Scale Y by this to fit TABLE_SIZE */
};
/* Do our own indexing of the arrays below to make sure we get efficient mults.
@ -140,7 +139,6 @@ calcul_tables( void *client )
int i, j;
float a, ga_i, ga, c, f, yo, p;
float maxr, maxg, maxb;
double **temp;
c = (d->d_B - 100.0) / 500.0;
@ -153,54 +151,13 @@ calcul_tables( void *client )
p = d->d_P / 100.0;
f = d->d_Vrwr / p;
maxr = (float) d->d_Vrwr;
table->ristep = maxr / 1500.0;
table->rstep = a / 1500.0;
table->rstep = a / (TABLE_SIZE - 1);
for( i = 0; i < 1501; i++ )
table->t_Yr2r[i] = f * (pow( i * table->rstep / a, ga_i ) - c);
for( i = 0; i < TABLE_SIZE; i++ )
table->t_Y2v[i] = f * (pow( i * table->rstep / a, ga_i ) - c);
for( i = 0; i < 1501; i++ )
table->t_r2Yr[i] = yo +
a * pow( i * table->ristep / f + c, ga );
/**** Green ****/
yo = d->d_Y0G;
a = d->d_YCG - yo;
ga = d->d_gammaG;
ga_i = 1.0 / ga;
p = d->d_P / 100.0;
f = d->d_Vrwg / p;
maxg = (float)d->d_Vrwg;
table->gistep = maxg / 1500.0;
table->gstep = a / 1500.0;
for( i = 0; i < 1501; i++ )
table->t_Yg2g[i] = f * (pow( i * table->gstep / a, ga_i ) - c);
for( i = 0; i < 1501; i++ )
table->t_g2Yg[i] = yo +
a * pow( i * table->gistep / f + c, ga );
/**** Blue ****/
yo = d->d_Y0B;
a = d->d_YCB - yo;
ga = d->d_gammaB;
ga_i = 1.0 / ga;
p = d->d_P / 100.0;
f = d->d_Vrwb / p;
maxb = (float)d->d_Vrwb;
table->bistep = maxb / 1500.0;
table->bstep = a / 1500.0;
for( i = 0; i < 1501; i++ )
table->t_Yb2b[i] = f * (pow( i * table->bstep / a, ga_i ) - c);
for( i = 0; i < 1501; i++ )
table->t_b2Yb[i] = yo +
a * pow( i * table->bistep / f + c, ga );
for( i = 0; i < 256; i++ )
table->t_v2Y[i] = yo + a * pow( i / f + c, ga );
if( !(temp = im_dmat_alloc( 0, 2, 0, 2 )) )
return( NULL );
@ -256,18 +213,14 @@ vips_col_sRGB2XYZ( int r, int g, int b, float *X, float *Y, float *Z )
float Yr, Yg, Yb;
int i;
r = VIPS_CLIP( 0, r, 255 );
g = VIPS_CLIP( 0, g, 255 );
b = VIPS_CLIP( 0, b, 255 );
i = VIPS_CLIP( 0, r, 255 );
Yr = table->t_v2Y[i];
i = r / table->ristep;
Yr = table->t_r2Yr[i];
i = VIPS_CLIP( 0, g, 255 );
Yg = table->t_v2Y[i];
i = g / table->gistep;
Yg = table->t_g2Yg[i];
i = b / table->bistep;
Yb = table->t_b2Yb[i];
i = VIPS_CLIP( 0, b, 255 );
Yb = table->t_v2Y[i];
*X = mat[0] * Yr + mat[1] * Yg + mat[2] * Yb;
*Y = mat[3] * Yr + mat[4] * Yg + mat[5] * Yb;
@ -300,35 +253,33 @@ vips_col_XYZ2sRGB( float X, float Y, float Z,
Yg = mat[3] * X + mat[4] * Y + mat[5] * Z;
Yb = mat[6] * X + mat[7] * Y + mat[8] * Z;
/* Any negatives? If yes, set the out-of-range flag and bump up.
/* Clip range, set the out-of-range flag.
*/
if( Yr < d->d_Y0R ) {
or = 1;
Yr = d->d_Y0R;
}
if( Yg < d->d_Y0G ) {
or = 1;
Yg = d->d_Y0G;
}
if( Yb < d->d_Y0B ) {
or = 1;
Yb = d->d_Y0B;
}
#define CLIP( L, V, H ) { \
if( (V) < (L) ) { \
(V) = (L); \
or = 1; \
} \
if( (V) > (H) ) { \
(V) = (H); \
or = 1; \
} \
}
/* Work out colour value (0-Vrw) to feed the tube to get that
* luminosity.
*/
Yint = (Yr - d->d_Y0R) / table->rstep;
Yint = VIPS_CLIP( 0, Yint, 1500 );
r = IM_RINT( table->t_Yr2r[Yint] );
CLIP( 0, Yint, TABLE_SIZE - 1);
r = VIPS_RINT( table->t_Y2v[Yint] );
Yint = (Yg - d->d_Y0G) / table->gstep;
Yint = VIPS_CLIP( 0, Yint, 1500 );
g = IM_RINT( table->t_Yg2g[Yint] );
Yint = (Yg - d->d_Y0G) / table->rstep;
CLIP( 0, Yint, TABLE_SIZE - 1);
g = VIPS_RINT( table->t_Y2v[Yint] );
Yint = (Yb - d->d_Y0B) / table->bstep;
Yint = VIPS_CLIP( 0, Yint, 1500 );
b = IM_RINT( table->t_Yb2b[Yint] );
Yint = (Yb - d->d_Y0B) / table->rstep;
CLIP( 0, Yint, TABLE_SIZE - 1);
b = VIPS_RINT( table->t_Y2v[Yint] );
*r_ret = r;
*g_ret = g;
@ -468,9 +419,9 @@ vips_LabQ2sRGB_init( VipsLabQ2sRGB *LabQ2sRGB )
* @in: input image
* @out: output image
*
* Unpack a LabQ (#IM_CODING_LABQ) image to a three-band short image.
* Unpack a LabQ (#VIPS_CODING_LABQ) image to a three-band short image.
*
* See also: im_LabS2LabQ(), im_LabQ2sRGB(), im_rad2float().
* See also: vips_LabS2LabQ(), vips_LabQ2sRGB(), vips_rad2float().
*
* Returns: 0 on success, -1 on error.
*/