add "whole_slide" toggle

openslideload now crops to image bounds (if set) ... use @whole_slide to
stop this autocrop
This commit is contained in:
John Cupitt 2014-07-30 12:27:19 +01:00
parent 0bb8a218bb
commit 639c22bf53
5 changed files with 95 additions and 17 deletions

View File

@ -2,6 +2,7 @@
- fix a race in im_maxpos_avg() - fix a race in im_maxpos_avg()
- limit n_thr on tiny images - limit n_thr on tiny images
- don't exit() on memleak detected, just warn - don't exit() on memleak detected, just warn
- add "whole_slide" option to openslide load
4/7/14 started 7.40.4 4/7/14 started 7.40.4
- fix vips_rawsave_fd(), thanks aferrero2707 - fix vips_rawsave_fd(), thanks aferrero2707

2
TODO
View File

@ -1,7 +1,5 @@
- affine makes black lines with interp window_offset > 1, see nohalo - affine makes black lines with interp window_offset > 1, see nohalo
- get rid of libexif, try exiv2
- experiment with size down to two sizes above in vipsthumbnail - experiment with size down to two sizes above in vipsthumbnail
how does this affect speed and sharpness? how does this affect speed and sharpness?

View File

@ -41,6 +41,8 @@
* - always output solid (not transparent) pixels * - always output solid (not transparent) pixels
* 25/1/14 * 25/1/14
* - use openslide_detect_vendor() on >= 3.4.0 * - use openslide_detect_vendor() on >= 3.4.0
* 30/7/14
* - add whole_slide toggle
*/ */
/* /*
@ -92,6 +94,12 @@ typedef struct {
char *associated; char *associated;
/* Normally we crop to image bounds, if set. @whole_slide means, get the
* whole image.
*/
gboolean whole_slide;
VipsRect bounds;
/* Only valid if associated == NULL. /* Only valid if associated == NULL.
*/ */
int32_t level; int32_t level;
@ -178,9 +186,39 @@ check_associated_image( openslide_t *osr, const char *name )
return( -1 ); return( -1 );
} }
static gboolean
get_bounds( openslide_t *osr, VipsRect *rect )
{
static const char *openslide_names[] = {
"openslide.bounds-x",
"openslide.bounds-y",
"openslide.bounds-width",
"openslide.bounds-height"
};
static int vips_offsets[] = {
G_STRUCT_OFFSET( VipsRect, left ),
G_STRUCT_OFFSET( VipsRect, top ),
G_STRUCT_OFFSET( VipsRect, width ),
G_STRUCT_OFFSET( VipsRect, height )
};
const char *value;
int i;
for( i = 0; i < 4; i++ ) {
if( !(value = openslide_get_property_value( osr,
openslide_names[i] )) )
return( FALSE );
G_STRUCT_MEMBER( int, rect, vips_offsets[i] ) =
atoi( value );
}
return( TRUE );
}
static ReadSlide * static ReadSlide *
readslide_new( const char *filename, VipsImage *out, readslide_new( const char *filename, VipsImage *out,
int level, const char *associated ) int level, gboolean whole_slide, const char *associated )
{ {
ReadSlide *rslide; ReadSlide *rslide;
int64_t w, h; int64_t w, h;
@ -188,7 +226,8 @@ readslide_new( const char *filename, VipsImage *out,
const char *background; const char *background;
const char * const *properties; const char * const *properties;
if( level && associated ) { if( level &&
associated ) {
vips_error( "openslide2vips", vips_error( "openslide2vips",
"%s", _( "specify only one of level or associated " "%s", _( "specify only one of level or associated "
"image" ) ); "image" ) );
@ -201,6 +240,7 @@ readslide_new( const char *filename, VipsImage *out,
rslide ); rslide );
rslide->level = level; rslide->level = level;
rslide->whole_slide = whole_slide;
rslide->associated = g_strdup( associated ); rslide->associated = g_strdup( associated );
/* Non-crazy defaults, override below if we can. /* Non-crazy defaults, override below if we can.
@ -264,6 +304,22 @@ readslide_new( const char *filename, VipsImage *out,
rslide->tile_height = atoi( value ); rslide->tile_height = atoi( value );
if( value ) if( value )
VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" ); VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" );
/* Some images have a bounds in the header. Try to crop to
* that, unless whole_slide is set.
*/
if( !rslide->whole_slide )
if( !get_bounds( rslide->osr, &rslide->bounds ) )
rslide->whole_slide = TRUE;
if( !rslide->whole_slide ) {
rslide->bounds.left /= rslide->downsample;
rslide->bounds.top /= rslide->downsample;
rslide->bounds.width /= rslide->downsample;
rslide->bounds.height /= rslide->downsample;
w = rslide->bounds.width;
h = rslide->bounds.height;
}
} }
rslide->bg = 0xffffff; rslide->bg = 0xffffff;
@ -271,7 +327,9 @@ readslide_new( const char *filename, VipsImage *out,
OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) ) OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) )
rslide->bg = strtoul( background, NULL, 16 ); rslide->bg = strtoul( background, NULL, 16 );
if( w < 0 || h < 0 || rslide->downsample < 0 ) { if( w <= 0 ||
h <= 0 ||
rslide->downsample < 0 ) {
vips_error( "openslide2vips", _( "getting dimensions: %s" ), vips_error( "openslide2vips", _( "getting dimensions: %s" ),
openslide_get_error( rslide->osr ) ); openslide_get_error( rslide->osr ) );
return( NULL ); return( NULL );
@ -283,6 +341,13 @@ readslide_new( const char *filename, VipsImage *out,
return( NULL ); return( NULL );
} }
if( rslide->whole_slide ) {
rslide->bounds.left = 0;
rslide->bounds.top = 0;
rslide->bounds.width = w;
rslide->bounds.height = h;
}
vips_image_init_fields( out, w, h, 4, VIPS_FORMAT_UCHAR, vips_image_init_fields( out, w, 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 );
@ -301,9 +366,9 @@ readslide_new( const char *filename, VipsImage *out,
int int
vips__openslide_read_header( const char *filename, VipsImage *out, vips__openslide_read_header( const char *filename, VipsImage *out,
int level, char *associated ) int level, gboolean whole_slide, char *associated )
{ {
if( !readslide_new( filename, out, level, associated ) ) if( !readslide_new( filename, out, level, whole_slide, associated ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );
@ -340,8 +405,8 @@ vips__openslide_generate( VipsRegion *out,
openslide_read_region( rslide->osr, openslide_read_region( rslide->osr,
buf, buf,
r->left * rslide->downsample, (r->left + rslide->bounds.left) * rslide->downsample,
r->top * rslide->downsample, (r->top + rslide->bounds.top) * rslide->downsample,
rslide->level, rslide->level,
r->width, r->height ); r->width, r->height );
@ -391,7 +456,8 @@ vips__openslide_generate( VipsRegion *out,
} }
int int
vips__openslide_read( const char *filename, VipsImage *out, int level ) vips__openslide_read( const char *filename, VipsImage *out,
int level, gboolean whole_slide )
{ {
ReadSlide *rslide; ReadSlide *rslide;
VipsImage *raw; VipsImage *raw;
@ -403,7 +469,8 @@ vips__openslide_read( const char *filename, VipsImage *out, int level )
raw = vips_image_new(); raw = vips_image_new();
vips_object_local( out, raw ); vips_object_local( out, raw );
if( !(rslide = readslide_new( filename, raw, level, NULL )) ) if( !(rslide = readslide_new( filename, raw,
level, whole_slide, NULL )) )
return( -1 ); return( -1 );
if( vips_image_generate( raw, if( vips_image_generate( raw,
@ -446,7 +513,7 @@ vips__openslide_read_associated( const char *filename, VipsImage *out,
raw = vips_image_new_memory(); raw = vips_image_new_memory();
vips_object_local( out, raw ); vips_object_local( out, raw );
if( !(rslide = readslide_new( filename, raw, 0, associated )) || if( !(rslide = readslide_new( filename, raw, 0, FALSE, associated )) ||
vips_image_write_prepare( raw ) ) vips_image_write_prepare( raw ) )
return( -1 ); return( -1 );
openslide_read_associated_image( rslide->osr, rslide->associated, openslide_read_associated_image( rslide->osr, rslide->associated,

View File

@ -37,9 +37,9 @@ extern "C" {
int vips__openslide_isslide( const char *filename ); int vips__openslide_isslide( const char *filename );
int vips__openslide_read_header( const char *filename, VipsImage *out, int vips__openslide_read_header( const char *filename, VipsImage *out,
int level, char *associated ); int level, gboolean whole_slide, char *associated );
int vips__openslide_read( const char *filename, VipsImage *out, int vips__openslide_read( const char *filename, VipsImage *out,
int level ); int level, gboolean whole_slide );
int vips__openslide_read_associated( const char *filename, VipsImage *out, int vips__openslide_read_associated( const char *filename, VipsImage *out,
const char *associated ); const char *associated );

View File

@ -70,6 +70,10 @@ typedef struct _VipsForeignLoadOpenslide {
*/ */
int level; int level;
/* Don't crop to image bounds.
*/
gboolean whole_slide;
/* Load this associated image. /* Load this associated image.
*/ */
char *associated; char *associated;
@ -109,7 +113,8 @@ vips_foreign_load_openslide_header( VipsForeignLoad *load )
VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load; VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load;
if( vips__openslide_read_header( openslide->filename, load->out, if( vips__openslide_read_header( openslide->filename, load->out,
openslide->level, openslide->associated ) ) openslide->level, openslide->whole_slide,
openslide->associated ) )
return( -1 ); return( -1 );
VIPS_SETSTR( load->out->filename, openslide->filename ); VIPS_SETSTR( load->out->filename, openslide->filename );
@ -124,7 +129,7 @@ vips_foreign_load_openslide_load( VipsForeignLoad *load )
if( !openslide->associated ) { if( !openslide->associated ) {
if( vips__openslide_read( openslide->filename, load->real, if( vips__openslide_read( openslide->filename, load->real,
openslide->level ) ) openslide->level, openslide->whole_slide ) )
return( -1 ); return( -1 );
} }
else { else {
@ -190,7 +195,14 @@ vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class )
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, level ), G_STRUCT_OFFSET( VipsForeignLoadOpenslide, level ),
0, 100000, 0 ); 0, 100000, 0 );
VIPS_ARG_STRING( class, "associated", 11, VIPS_ARG_BOOL( class, "whole_slide", 11,
_( "Whole slide" ),
_( "Output entire side area" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsForeignLoadOpenslide, whole_slide ),
FALSE );
VIPS_ARG_STRING( class, "associated", 12,
_( "Associated" ), _( "Associated" ),
_( "Load this associated image" ), _( "Load this associated image" ),
VIPS_ARGUMENT_OPTIONAL_INPUT, VIPS_ARGUMENT_OPTIONAL_INPUT,