option to attach associated images as metadata

you now see:

```
$ vipsheader -a CMU-1.svs[attach_associated] | grep ass
openslide.associated.label: 387x463 uchar, 4 bands, rgb
openslide.associated.macro: 1280x431 uchar, 4 bands, rgb
openslide.associated.thumbnail: 1024x732 uchar, 4 bands, rgb
slide-associated-images: label, macro, thumbnail
```
This commit is contained in:
John Cupitt 2018-01-27 16:43:58 +00:00
parent de2e3e3299
commit 28391dbfc7
4 changed files with 149 additions and 55 deletions

View File

@ -3,6 +3,7 @@
- dzsave to szi sets suffix correctly [martinweihrauch]
- dzsave szi writes "scan-properties.xml"
- add vips_image_(get|set)_image()
- add openslideload option to attach all associated images as metadata
5/1/18 started 8.6.2
- vips_sink_screen() keeps a ref to the input image ... stops a rare race

View File

@ -49,6 +49,8 @@
* - unpremultiplication speedups for fully opaque/transparent pixels
* 18/1/17
* - reorganise to support invalidate on read error
* 27/1/18
* - option to attach associated images as metadata
*/
/*
@ -103,6 +105,7 @@ typedef struct {
int32_t level;
gboolean autocrop;
char *associated;
gboolean attach_associated;
openslide_t *osr;
@ -229,15 +232,24 @@ get_bounds( openslide_t *osr, VipsRect *rect )
static ReadSlide *
readslide_new( const char *filename, VipsImage *out,
int level, gboolean autocrop, const char *associated )
int level, gboolean autocrop,
const char *associated, gboolean attach_associated )
{
ReadSlide *rslide;
if( level &&
associated ) {
vips_error( "openslide2vips",
"%s", _( "specify only one of level or associated "
"image" ) );
"%s", _( "specify only one of level and "
"associated image" ) );
return( NULL );
}
if( attach_associated &&
associated ) {
vips_error( "openslide2vips",
"%s", _( "specify only one of attach_assicated and "
"associated image" ) );
return( NULL );
}
@ -251,6 +263,7 @@ readslide_new( const char *filename, VipsImage *out,
rslide->level = level;
rslide->autocrop = autocrop;
rslide->associated = g_strdup( associated );
rslide->attach_associated = attach_associated;
/* Non-crazy defaults, override in _parse() if we can.
*/
@ -260,6 +273,94 @@ readslide_new( const char *filename, VipsImage *out,
return( rslide );
}
/* Convert from ARGB to RGBA and undo premultiplication.
*
* We throw away transparency. Formats like Mirax use transparent + bg
* colour for areas with no useful pixels. But if we output
* transparent pixels and then convert to RGB for jpeg write later, we
* would have to pass the bg colour down the pipe somehow. The
* structure of dzsave makes this tricky.
*
* We could output plain RGB instead, but that would break
* compatibility with older vipses.
*/
static void
argb2rgba( uint32_t * restrict buf, int n, uint32_t bg )
{
const uint32_t pbg = GUINT32_TO_BE( (bg << 8) | 255 );
int i;
for( i = 0; i < n; i++ ) {
uint32_t * restrict p = buf + i;
uint32_t x = *p;
uint8_t a = x >> 24;
VipsPel * restrict out = (VipsPel *) p;
if( a == 255 )
*p = GUINT32_TO_BE( (x << 8) | 255 );
else if( a == 0 )
/* Use background color.
*/
*p = pbg;
else {
/* Undo premultiplication.
*/
out[0] = 255 * ((x >> 16) & 255) / a;
out[1] = 255 * ((x >> 8) & 255) / a;
out[2] = 255 * (x & 255) / a;
out[3] = 255;
}
}
}
static int
readslide_attach_associated( ReadSlide *rslide, VipsImage *image )
{
const char * const *associated_name;
for( associated_name =
openslide_get_associated_image_names( rslide->osr );
*associated_name != NULL; associated_name++ ) {
int64_t w, h;
VipsImage *associated;
uint32_t *p;
const char *error;
char buf[256];
associated = vips_image_new_memory();
openslide_get_associated_image_dimensions( rslide->osr,
*associated_name, &w, &h );
vips_image_init_fields( associated, w, h, 4, VIPS_FORMAT_UCHAR,
VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 );
vips_image_pipelinev( associated,
VIPS_DEMAND_STYLE_THINSTRIP, NULL );
if( vips_image_write_prepare( associated ) ) {
g_object_unref( associated );
return( -1 );
}
p = (uint32_t *) VIPS_IMAGE_ADDR( associated, 0, 0 );
openslide_read_associated_image( rslide->osr,
*associated_name, p );
error = openslide_get_error( rslide->osr );
if( error ) {
vips_error( "openslide2vips",
_( "reading associated image: %s" ), error );
g_object_unref( associated );
return( -1 );
}
argb2rgba( p, w * h, rslide->bg );
vips_snprintf( buf, 256,
"openslide.associated.%s", *associated_name );
vips_image_set_image( image, buf, associated );
g_object_unref( associated );
}
return( 0 );
}
static int
readslide_parse( ReadSlide *rslide, VipsImage *image )
{
@ -358,6 +459,12 @@ readslide_parse( ReadSlide *rslide, VipsImage *image )
w = rslide->bounds.width;
h = rslide->bounds.height;
}
/* Attach all associated images.
*/
if( rslide->attach_associated &&
readslide_attach_associated( rslide, image ) )
return( -1 );
}
rslide->bg = 0xffffff;
@ -406,57 +513,19 @@ readslide_parse( ReadSlide *rslide, VipsImage *image )
int
vips__openslide_read_header( const char *filename, VipsImage *out,
int level, gboolean autocrop, char *associated )
int level, gboolean autocrop,
char *associated, gboolean attach_associated )
{
ReadSlide *rslide;
if( !(rslide = readslide_new( filename,
out, level, autocrop, associated )) ||
out, level, autocrop, associated, attach_associated )) ||
readslide_parse( rslide, out ) )
return( -1 );
return( 0 );
}
/* Convert from ARGB to RGBA and undo premultiplication.
*
* We throw away transparency. Formats like Mirax use transparent + bg
* colour for areas with no useful pixels. But if we output
* transparent pixels and then convert to RGB for jpeg write later, we
* would have to pass the bg colour down the pipe somehow. The
* structure of dzsave makes this tricky.
*
* We could output plain RGB instead, but that would break
* compatibility with older vipses.
*/
static void
argb2rgba( uint32_t * restrict buf, int n, uint32_t bg )
{
int i;
for( i = 0; i < n; i++ ) {
uint32_t * restrict p = buf + i;
uint32_t x = *p;
uint8_t a = x >> 24;
VipsPel * restrict out = (VipsPel *) p;
if( a == 255 )
*p = GUINT32_TO_BE( (x << 8) | 255 );
else if( a == 0 )
/* Use background color.
*/
*p = GUINT32_TO_BE( (bg << 8) | 255 );
else {
/* Undo premultiplication.
*/
out[0] = 255 * ((x >> 16) & 255) / a;
out[1] = 255 * ((x >> 8) & 255) / a;
out[2] = 255 * (x & 255) / a;
out[3] = 255;
}
}
}
static int
vips__openslide_generate( VipsRegion *out,
void *_seq, void *_rslide, void *unused, gboolean *stop )
@ -515,7 +584,7 @@ vips__openslide_generate( VipsRegion *out,
int
vips__openslide_read( const char *filename, VipsImage *out,
int level, gboolean autocrop )
int level, gboolean autocrop, gboolean attach_associated )
{
ReadSlide *rslide;
VipsImage *raw;
@ -524,7 +593,8 @@ vips__openslide_read( const char *filename, VipsImage *out,
VIPS_DEBUG_MSG( "vips__openslide_read: %s %d\n",
filename, level );
if( !(rslide = readslide_new( filename, out, level, autocrop, NULL )) )
if( !(rslide = readslide_new( filename, out, level, autocrop,
NULL, attach_associated )) )
return( -1 );
raw = vips_image_new();
@ -567,7 +637,8 @@ vips__openslide_read_associated( const char *filename, VipsImage *out,
VIPS_DEBUG_MSG( "vips__openslide_read_associated: %s %s\n",
filename, associated );
if( !(rslide = readslide_new( filename, out, 0, FALSE, associated )) )
if( !(rslide = readslide_new( filename, out, 0, FALSE,
associated, FALSE )) )
return( -1 );
/* Memory buffer. Get associated directly to this, then copy to out.

View File

@ -9,6 +9,8 @@
* 20/9/12
* - add Leica filename suffix
* - drop glib log handler (unneeded with >= 3.3.0)
* 27/1/18
* - option to attach associated images as metadata
*/
/*
@ -74,10 +76,14 @@ typedef struct _VipsForeignLoadOpenslide {
*/
gboolean autocrop;
/* Load this associated image.
/* Load just this associated image.
*/
char *associated;
/* Attach all associated images as metadata items.
*/
gboolean attach_associated;
} VipsForeignLoadOpenslide;
typedef VipsForeignLoadClass VipsForeignLoadOpenslideClass;
@ -114,7 +120,7 @@ vips_foreign_load_openslide_header( VipsForeignLoad *load )
if( vips__openslide_read_header( openslide->filename, load->out,
openslide->level, openslide->autocrop,
openslide->associated ) )
openslide->associated, openslide->attach_associated ) )
return( -1 );
VIPS_SETSTR( load->out->filename, openslide->filename );
@ -129,7 +135,8 @@ vips_foreign_load_openslide_load( VipsForeignLoad *load )
if( !openslide->associated ) {
if( vips__openslide_read( openslide->filename, load->real,
openslide->level, openslide->autocrop ) )
openslide->level, openslide->autocrop,
openslide->attach_associated ) )
return( -1 );
}
else {
@ -210,6 +217,14 @@ vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class )
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, associated ),
NULL );
VIPS_ARG_BOOL( class, "attach-associated", 13,
_( "Attach associated" ),
_( "Attach all asssociated images" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, attach_associated ),
FALSE );
}
static void
@ -227,9 +242,10 @@ vips_foreign_load_openslide_init( VipsForeignLoadOpenslide *openslide )
*
* Optional arguments:
*
* * @level: load this level
* * @associated: load this associated image
* * @autocrop: crop to image bounds
* * @level: %gint, load this level
* * @associated: %gchararray, load this associated image
* * @attach_associated: %gboolean, attach all associated images as metadata
* * @autocrop: %gboolean, crop to image bounds
*
* Read a virtual slide supported by the OpenSlide library into a VIPS image.
* OpenSlide supports images in Aperio, Hamamatsu, MIRAX, Sakura, Trestle,
@ -247,6 +263,11 @@ vips_foreign_load_openslide_init( VipsForeignLoadOpenslide *openslide )
* A slide's associated images are listed in the
* "slide-associated-images" metadata item.
*
* If you set @attach_associated, then all associated images are attached as
* metadata items. Use vips_image_get_image() on @out to retrieve them. Images
* are attached as "openslide-associated-XXXXX", where XXXXX is the name of the
* associated image.
*
* The output of this operator is always RGBA.
*
* See also: vips_image_new_from_file().

View File

@ -233,9 +233,10 @@ int vips__webp_write_buffer( VipsImage *out, void **buf, size_t *len,
int vips__openslide_isslide( const char *filename );
int vips__openslide_read_header( const char *filename, VipsImage *out,
int level, gboolean autocrop, char *associated );
int level, gboolean autocrop,
char *associated, gboolean attach_associated );
int vips__openslide_read( const char *filename, VipsImage *out,
int level, gboolean autocrop );
int level, gboolean autocrop, gboolean attach_associated );
int vips__openslide_read_associated( const char *filename, VipsImage *out,
const char *associated );