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:
parent
de2e3e3299
commit
28391dbfc7
@ -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
|
||||
|
@ -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.
|
||||
|
@ -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().
|
||||
|
@ -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 );
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user