support reading OpenSlide associated images
This commit is contained in:
parent
395d4ef067
commit
04096821aa
@ -14,6 +14,7 @@
|
|||||||
* - support reading arbitrary layers
|
* - support reading arbitrary layers
|
||||||
* - use VIPS_ARRAY()
|
* - use VIPS_ARRAY()
|
||||||
* - add helper to copy a line of pixels
|
* - add helper to copy a line of pixels
|
||||||
|
* - support reading associated images
|
||||||
*/
|
*/
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -53,9 +54,13 @@
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
openslide_t *osr;
|
openslide_t *osr;
|
||||||
|
uint32_t background;
|
||||||
|
const char *associated;
|
||||||
|
|
||||||
|
/* Only valid if associated == NULL.
|
||||||
|
*/
|
||||||
int32_t layer;
|
int32_t layer;
|
||||||
double downsample;
|
double downsample;
|
||||||
uint32_t background;
|
|
||||||
} ReadSlide;
|
} ReadSlide;
|
||||||
|
|
||||||
static void
|
static void
|
||||||
@ -64,6 +69,19 @@ readslide_destroy_cb( VipsImage *image, ReadSlide *rslide )
|
|||||||
VIPS_FREEF( openslide_close, rslide->osr );
|
VIPS_FREEF( openslide_close, rslide->osr );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
check_associated_image( openslide_t *osr, const char *name )
|
||||||
|
{
|
||||||
|
const char * const *associated;
|
||||||
|
|
||||||
|
for( associated = openslide_get_associated_image_names( osr );
|
||||||
|
*associated != NULL; associated++ )
|
||||||
|
if( strcmp( *associated, name ) == 0 )
|
||||||
|
return( 0 );
|
||||||
|
vips_error( "im_openslide2vips", _( "invalid associated image name" ));
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
static ReadSlide *
|
static ReadSlide *
|
||||||
readslide_new( const char *filename, VipsImage *out )
|
readslide_new( const char *filename, VipsImage *out )
|
||||||
{
|
{
|
||||||
@ -74,6 +92,7 @@ readslide_new( const char *filename, VipsImage *out )
|
|||||||
char *endp;
|
char *endp;
|
||||||
int64_t w, h;
|
int64_t w, h;
|
||||||
const char * const *properties;
|
const char * const *properties;
|
||||||
|
char *associated;
|
||||||
|
|
||||||
rslide = VIPS_NEW( out, ReadSlide );
|
rslide = VIPS_NEW( out, ReadSlide );
|
||||||
memset( rslide, 0, sizeof( *rslide ));
|
memset( rslide, 0, sizeof( *rslide ));
|
||||||
@ -94,9 +113,10 @@ readslide_new( const char *filename, VipsImage *out )
|
|||||||
else
|
else
|
||||||
rslide->background = 0xffffff;
|
rslide->background = 0xffffff;
|
||||||
|
|
||||||
if( mode[0] != 0 ) {
|
/* Parse optional mode.
|
||||||
|
*/
|
||||||
rslide->layer = strtol( mode, &endp, 10 );
|
rslide->layer = strtol( mode, &endp, 10 );
|
||||||
if( *endp == 0 ) {
|
if( *mode != 0 && *endp == 0 ) {
|
||||||
/* Mode specifies slide layer.
|
/* Mode specifies slide layer.
|
||||||
*/
|
*/
|
||||||
if( rslide->layer < 0 || rslide->layer >=
|
if( rslide->layer < 0 || rslide->layer >=
|
||||||
@ -105,15 +125,27 @@ readslide_new( const char *filename, VipsImage *out )
|
|||||||
_( "invalid slide layer" ));
|
_( "invalid slide layer" ));
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
} else {
|
} else if( *mode != 0 ) {
|
||||||
vips_error( "im_openslide2vips",
|
/* Mode specifies associated image.
|
||||||
_( "invalid file mode" ));
|
*/
|
||||||
|
if ( check_associated_image( rslide->osr, mode ))
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
rslide->associated = vips_strdup( VIPS_OBJECT( out ), mode );
|
||||||
}
|
}
|
||||||
|
|
||||||
openslide_get_layer_dimensions( rslide->osr, rslide->layer, &w, &h );
|
if( rslide->associated ) {
|
||||||
if( w < 0 || h < 0 ) {
|
openslide_get_associated_image_dimensions( rslide->osr,
|
||||||
|
rslide->associated, &w, &h );
|
||||||
|
vips_image_set_string( out, "slide-associated-image",
|
||||||
|
rslide->associated );
|
||||||
|
} else {
|
||||||
|
openslide_get_layer_dimensions( rslide->osr, rslide->layer,
|
||||||
|
&w, &h );
|
||||||
|
rslide->downsample = openslide_get_layer_downsample(
|
||||||
|
rslide->osr, rslide->layer );
|
||||||
|
vips_image_set_int( out, "slide-layer", rslide->layer );
|
||||||
|
}
|
||||||
|
if( w < 0 || h < 0 || rslide->downsample < 0 ) {
|
||||||
vips_error( "im_openslide2vips", _( "getting dimensions: %s" ),
|
vips_error( "im_openslide2vips", _( "getting dimensions: %s" ),
|
||||||
openslide_get_error( rslide->osr ));
|
openslide_get_error( rslide->osr ));
|
||||||
return( NULL );
|
return( NULL );
|
||||||
@ -123,8 +155,6 @@ readslide_new( const char *filename, VipsImage *out )
|
|||||||
_( "image dimensions overflow int" ));
|
_( "image dimensions overflow int" ));
|
||||||
return( NULL );
|
return( NULL );
|
||||||
}
|
}
|
||||||
rslide->downsample = openslide_get_layer_downsample( rslide->osr,
|
|
||||||
rslide->layer );
|
|
||||||
|
|
||||||
vips_image_init_fields( out, (int) w, (int) h, 4, VIPS_FORMAT_UCHAR,
|
vips_image_init_fields( out, (int) w, (int) h, 4, VIPS_FORMAT_UCHAR,
|
||||||
VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 );
|
VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 );
|
||||||
@ -135,7 +165,10 @@ readslide_new( const char *filename, VipsImage *out )
|
|||||||
openslide_get_property_value( rslide->osr,
|
openslide_get_property_value( rslide->osr,
|
||||||
*properties ));
|
*properties ));
|
||||||
|
|
||||||
vips_image_set_int( out, "slide-layer", rslide->layer );
|
associated = g_strjoinv( ", ", (char **)
|
||||||
|
openslide_get_associated_image_names( rslide->osr ));
|
||||||
|
vips_image_set_string( out, "slide-associated-images", associated );
|
||||||
|
g_free( associated );
|
||||||
|
|
||||||
return( rslide );
|
return( rslide );
|
||||||
}
|
}
|
||||||
@ -195,6 +228,47 @@ fill_region( VipsRegion *out, void *seq, void *_rslide, void *unused,
|
|||||||
return( 0 );
|
return( 0 );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int
|
||||||
|
fill_associated( VipsImage *out, ReadSlide *rslide )
|
||||||
|
{
|
||||||
|
uint32_t *buf;
|
||||||
|
PEL *line;
|
||||||
|
int64_t w, h;
|
||||||
|
int y;
|
||||||
|
const char *error;
|
||||||
|
|
||||||
|
openslide_get_associated_image_dimensions( rslide->osr,
|
||||||
|
rslide->associated, &w, &h );
|
||||||
|
if( w == -1 || h == -1 ) {
|
||||||
|
vips_error( "im_openslide2vips", _( "getting dimensions: %s" ),
|
||||||
|
openslide_get_error( rslide->osr ));
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
|
||||||
|
buf = VIPS_ARRAY( NULL, w * h, uint32_t );
|
||||||
|
line = VIPS_ARRAY( NULL, VIPS_IMAGE_SIZEOF_LINE( out ), PEL );
|
||||||
|
openslide_read_associated_image( rslide->osr, rslide->associated,
|
||||||
|
buf );
|
||||||
|
for( y = 0; y < h; y++ ) {
|
||||||
|
copy_line( rslide, buf + y * w, w, line );
|
||||||
|
if( vips_image_write_line( out, y, line )) {
|
||||||
|
vips_free( line );
|
||||||
|
vips_free( buf );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
vips_free( line );
|
||||||
|
vips_free( buf );
|
||||||
|
|
||||||
|
error = openslide_get_error( rslide->osr );
|
||||||
|
if( error ) {
|
||||||
|
vips_error( "im_openslide2vips",
|
||||||
|
_( "reading associated image: %s" ), error );
|
||||||
|
return( -1 );
|
||||||
|
}
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
openslide2vips_header( const char *filename, VipsImage *out )
|
openslide2vips_header( const char *filename, VipsImage *out )
|
||||||
{
|
{
|
||||||
@ -218,7 +292,9 @@ openslide2vips_header( const char *filename, VipsImage *out )
|
|||||||
*
|
*
|
||||||
* By default, read the highest-resolution layer (layer 0). To read a
|
* By default, read the highest-resolution layer (layer 0). To read a
|
||||||
* different layer, specify the layer number as part of the filename
|
* different layer, specify the layer number as part of the filename
|
||||||
* (for example, "CMU-1.mrxs:3").
|
* (for example, "CMU-1.mrxs:3"). To read an associated image attached
|
||||||
|
* to the slide, specify the image's name as part of the filename (for
|
||||||
|
* example, "CMU-1.mrxs:label").
|
||||||
*
|
*
|
||||||
* See also: #VipsFormat
|
* See also: #VipsFormat
|
||||||
*
|
*
|
||||||
@ -230,11 +306,19 @@ im_openslide2vips( const char *filename, VipsImage *out )
|
|||||||
ReadSlide *rslide;
|
ReadSlide *rslide;
|
||||||
|
|
||||||
rslide = readslide_new( filename, out );
|
rslide = readslide_new( filename, out );
|
||||||
if( rslide == NULL || vips_image_pio_output( out ))
|
if( rslide == NULL )
|
||||||
|
return( -1 );
|
||||||
|
if( rslide->associated ) {
|
||||||
|
if( vips_image_wio_output( out ))
|
||||||
|
return( -1 );
|
||||||
|
return fill_associated( out, rslide );
|
||||||
|
} else {
|
||||||
|
if( vips_image_pio_output( out ))
|
||||||
return( -1 );
|
return( -1 );
|
||||||
vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
|
vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
|
||||||
return vips_image_generate( out, NULL, fill_region, NULL, rslide,
|
return vips_image_generate( out, NULL, fill_region, NULL,
|
||||||
NULL );
|
rslide, NULL );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
static int
|
static int
|
||||||
@ -265,7 +349,21 @@ isslide( const char *filename )
|
|||||||
static VipsFormatFlags
|
static VipsFormatFlags
|
||||||
slide_flags( const char *filename )
|
slide_flags( const char *filename )
|
||||||
{
|
{
|
||||||
|
char name[FILENAME_MAX];
|
||||||
|
char mode[FILENAME_MAX];
|
||||||
|
char *endp;
|
||||||
|
|
||||||
|
vips_filename_split( filename, name, mode );
|
||||||
|
strtol( mode, &endp, 10 );
|
||||||
|
if( *mode == 0 || *endp == 0 ) {
|
||||||
|
/* Slide layer or no mode specified.
|
||||||
|
*/
|
||||||
return( VIPS_FORMAT_PARTIAL );
|
return( VIPS_FORMAT_PARTIAL );
|
||||||
|
} else {
|
||||||
|
/* Associated image specified.
|
||||||
|
*/
|
||||||
|
return( 0 );
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/* openslide format adds no new members.
|
/* openslide format adds no new members.
|
||||||
|
Loading…
Reference in New Issue
Block a user