From 639c22bf5379da524144e40de576d5bd1907d57a Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 30 Jul 2014 12:27:19 +0100 Subject: [PATCH] add "whole_slide" toggle openslideload now crops to image bounds (if set) ... use @whole_slide to stop this autocrop --- ChangeLog | 1 + TODO | 2 - libvips/foreign/openslide2vips.c | 87 ++++++++++++++++++++++++++++---- libvips/foreign/openslide2vips.h | 4 +- libvips/foreign/openslideload.c | 18 +++++-- 5 files changed, 95 insertions(+), 17 deletions(-) diff --git a/ChangeLog b/ChangeLog index 6894bf84..13d5d057 100644 --- a/ChangeLog +++ b/ChangeLog @@ -2,6 +2,7 @@ - fix a race in im_maxpos_avg() - limit n_thr on tiny images - don't exit() on memleak detected, just warn +- add "whole_slide" option to openslide load 4/7/14 started 7.40.4 - fix vips_rawsave_fd(), thanks aferrero2707 diff --git a/TODO b/TODO index 7dbbc571..ca01dea0 100644 --- a/TODO +++ b/TODO @@ -1,7 +1,5 @@ - 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 how does this affect speed and sharpness? diff --git a/libvips/foreign/openslide2vips.c b/libvips/foreign/openslide2vips.c index b3e61918..22486467 100644 --- a/libvips/foreign/openslide2vips.c +++ b/libvips/foreign/openslide2vips.c @@ -41,6 +41,8 @@ * - always output solid (not transparent) pixels * 25/1/14 * - use openslide_detect_vendor() on >= 3.4.0 + * 30/7/14 + * - add whole_slide toggle */ /* @@ -92,6 +94,12 @@ typedef struct { 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. */ int32_t level; @@ -178,9 +186,39 @@ check_associated_image( openslide_t *osr, const char *name ) 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 * readslide_new( const char *filename, VipsImage *out, - int level, const char *associated ) + int level, gboolean whole_slide, const char *associated ) { ReadSlide *rslide; int64_t w, h; @@ -188,7 +226,8 @@ readslide_new( const char *filename, VipsImage *out, const char *background; const char * const *properties; - if( level && associated ) { + if( level && + associated ) { vips_error( "openslide2vips", "%s", _( "specify only one of level or associated " "image" ) ); @@ -201,6 +240,7 @@ readslide_new( const char *filename, VipsImage *out, rslide ); rslide->level = level; + rslide->whole_slide = whole_slide; rslide->associated = g_strdup( associated ); /* Non-crazy defaults, override below if we can. @@ -264,6 +304,22 @@ readslide_new( const char *filename, VipsImage *out, rslide->tile_height = atoi( value ); if( value ) 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; @@ -271,7 +327,9 @@ readslide_new( const char *filename, VipsImage *out, OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) ) 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" ), openslide_get_error( rslide->osr ) ); return( NULL ); @@ -283,6 +341,13 @@ readslide_new( const char *filename, VipsImage *out, 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_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 ); @@ -301,9 +366,9 @@ readslide_new( 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 ) { - if( !readslide_new( filename, out, level, associated ) ) + if( !readslide_new( filename, out, level, whole_slide, associated ) ) return( -1 ); return( 0 ); @@ -340,8 +405,8 @@ vips__openslide_generate( VipsRegion *out, openslide_read_region( rslide->osr, buf, - r->left * rslide->downsample, - r->top * rslide->downsample, + (r->left + rslide->bounds.left) * rslide->downsample, + (r->top + rslide->bounds.top) * rslide->downsample, rslide->level, r->width, r->height ); @@ -391,7 +456,8 @@ vips__openslide_generate( VipsRegion *out, } 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; VipsImage *raw; @@ -403,7 +469,8 @@ vips__openslide_read( const char *filename, VipsImage *out, int level ) raw = vips_image_new(); 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 ); if( vips_image_generate( raw, @@ -446,7 +513,7 @@ vips__openslide_read_associated( const char *filename, VipsImage *out, raw = vips_image_new_memory(); 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 ) ) return( -1 ); openslide_read_associated_image( rslide->osr, rslide->associated, diff --git a/libvips/foreign/openslide2vips.h b/libvips/foreign/openslide2vips.h index 48c26790..06a8aa68 100644 --- a/libvips/foreign/openslide2vips.h +++ b/libvips/foreign/openslide2vips.h @@ -37,9 +37,9 @@ extern "C" { int vips__openslide_isslide( const char *filename ); 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 level ); + int level, gboolean whole_slide ); int vips__openslide_read_associated( const char *filename, VipsImage *out, const char *associated ); diff --git a/libvips/foreign/openslideload.c b/libvips/foreign/openslideload.c index cfef1ccb..723f2692 100644 --- a/libvips/foreign/openslideload.c +++ b/libvips/foreign/openslideload.c @@ -70,6 +70,10 @@ typedef struct _VipsForeignLoadOpenslide { */ int level; + /* Don't crop to image bounds. + */ + gboolean whole_slide; + /* Load this associated image. */ char *associated; @@ -109,7 +113,8 @@ vips_foreign_load_openslide_header( VipsForeignLoad *load ) VipsForeignLoadOpenslide *openslide = (VipsForeignLoadOpenslide *) load; if( vips__openslide_read_header( openslide->filename, load->out, - openslide->level, openslide->associated ) ) + openslide->level, openslide->whole_slide, + openslide->associated ) ) return( -1 ); VIPS_SETSTR( load->out->filename, openslide->filename ); @@ -124,7 +129,7 @@ vips_foreign_load_openslide_load( VipsForeignLoad *load ) if( !openslide->associated ) { if( vips__openslide_read( openslide->filename, load->real, - openslide->level ) ) + openslide->level, openslide->whole_slide ) ) return( -1 ); } else { @@ -190,7 +195,14 @@ vips_foreign_load_openslide_class_init( VipsForeignLoadOpenslideClass *class ) G_STRUCT_OFFSET( VipsForeignLoadOpenslide, level ), 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" ), _( "Load this associated image" ), VIPS_ARGUMENT_OPTIONAL_INPUT,