Example #1
0
int
vips__openslide_read_associated( const char *filename, VipsImage *out, 
	const char *associated )
{
	ReadSlide *rslide;
	VipsImage *raw;
	const char *error;

	VIPS_DEBUG_MSG( "vips__openslide_read_associated: %s %s\n", 
		filename, associated );

	/* Memory buffer. Get associated directly to this, then copy to out.
	 */
	raw = vips_image_new_buffer();
	vips_object_local( out, raw );

	if( !(rslide = readslide_new( filename, raw, 0, associated )) ||
		vips_image_write_prepare( raw ) )
		return( -1 );
	openslide_read_associated_image( rslide->osr, rslide->associated, 
		(uint32_t *) VIPS_IMAGE_ADDR( raw, 0, 0 ) );
	error = openslide_get_error( rslide->osr );
	if( error ) {
		vips_error( "openslide2vips",
			_( "reading associated image: %s" ), error );
		return( -1 );
	}

	if( vips_image_write( raw, out ) ) 
		return( -1 );

	return( 0 );
}
Example #2
0
static void test_image_fetch(openslide_t *osr,
			     int64_t x, int64_t y,
			     int64_t w, int64_t h) {
  uint32_t *buf = g_new(uint32_t, w * h);
  for (int32_t level = 0; level < openslide_get_level_count(osr); level++) {
    openslide_read_region(osr, buf, x, y, level, w, h);
  }
  g_free(buf);

  const char *err = openslide_get_error(osr);
  if (err) {
    fail("Read failed: %"G_GINT64_FORMAT" %"G_GINT64_FORMAT
         " %"G_GINT64_FORMAT" %"G_GINT64_FORMAT": %s", x, y, w, h, err);
  }
}
Example #3
0
static int
vips__openslide_generate( VipsRegion *out, 
	void *seq, void *_rslide, void *unused, gboolean *stop )
{
	ReadSlide *rslide = _rslide;
	VipsRect *r = &out->valid;

	const char *error;
	int x, y;

	VIPS_DEBUG_MSG( "vips__openslide_generate: %dx%d @ %dx%d\n",
		r->width, r->height, r->left, r->top );

	/* Fill in tile-sized chunks. Some versions of OpenSlide can fail for
	 * very large requests.
	 */
	for( y = 0; y < r->height; y += TILE_HEIGHT ) 
		for( x = 0; x < r->width; x += TILE_WIDTH ) {
			int w = VIPS_MIN( TILE_WIDTH, r->width - x );
			int h = VIPS_MIN( TILE_HEIGHT, r->height - y );

			openslide_read_region( rslide->osr, 
				(uint32_t *) VIPS_REGION_ADDR( out, 
					r->left + x, r->top + y ),
				(r->left + x) * rslide->downsample, 
				(r->top + y) * rslide->downsample, 
				rslide->layer,
				w, h ); 
		}

	error = openslide_get_error( rslide->osr );
	if( error ) {
		vips_error( "openslide2vips", 
			_( "reading region: %s" ), error );

		return( -1 );
	}

	return( 0 );
}
Example #4
0
static ReadSlide *
readslide_new( const char *filename, VipsImage *out, 
	int level, gboolean autocrop, const char *associated )
{
	ReadSlide *rslide;
	int64_t w, h;
	const char *error;
	const char *background;
	const char * const *properties;
	char *associated_names;

	if( level && 
		associated ) {
		vips_error( "openslide2vips",
			"%s", _( "specify only one of level or associated "
			"image" ) );
		return( NULL );
	}

	rslide = VIPS_NEW( out, ReadSlide );
	memset( rslide, 0, sizeof( *rslide ) );
	g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ),
		rslide );

	rslide->level = level;
	rslide->autocrop = autocrop;
	rslide->associated = g_strdup( associated );

	/* Non-crazy defaults, override below if we can.
	 */
	rslide->tile_width = 256;
	rslide->tile_height = 256;

	rslide->osr = openslide_open( filename );
	if( rslide->osr == NULL ) {
		vips_error( "openslide2vips", 
			"%s", _( "unsupported slide format" ) );
		return( NULL );
	}

	error = openslide_get_error( rslide->osr );
	if( error ) {
		vips_error( "openslide2vips",
			_( "opening slide: %s" ), error );
		return( NULL );
	}

	if( level < 0 || 
		level >= openslide_get_level_count( rslide->osr ) ) {
		vips_error( "openslide2vips",
			"%s", _( "invalid slide level" ) );
		return( NULL );
	}

	if( associated &&
		check_associated_image( rslide->osr, associated ) )
		return( NULL );

	if( associated ) {
		openslide_get_associated_image_dimensions( rslide->osr,
			associated, &w, &h );
		vips_image_set_string( out, "slide-associated-image",
			associated );
		vips_image_pipelinev( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
	} 
	else {
		char buf[256];
		const char *value;

		openslide_get_level_dimensions( rslide->osr,
			level, &w, &h );
		rslide->downsample = openslide_get_level_downsample(
			rslide->osr, level );
		vips_image_set_int( out, "slide-level", level );
		vips_image_pipelinev( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );

		/* Try to get tile width/height. An undocumented, experimental
		 * feature.
		 */
		vips_snprintf( buf, 256, 
			"openslide.level[%d].tile-width", level );
		if( (value = openslide_get_property_value( rslide->osr, buf )) )
			rslide->tile_width = atoi( value );
		vips_snprintf( buf, 256, 
			"openslide.level[%d].tile-height", level );
		if( (value = openslide_get_property_value( rslide->osr, buf )) )
			rslide->tile_height = atoi( value );
		if( value )
			VIPS_DEBUG_MSG( "readslide_new: found tile-size\n" );

		/* Some images have a bounds in the header. Crop to 
		 * that if autocrop is set. 
		 */
		if( rslide->autocrop ) 
			if( !get_bounds( rslide->osr, &rslide->bounds ) )
				rslide->autocrop = FALSE; 
		if( rslide->autocrop ) {
			VipsRect image;

			rslide->bounds.left /= rslide->downsample;
			rslide->bounds.top /= rslide->downsample;
			rslide->bounds.width /= rslide->downsample;
			rslide->bounds.height /= rslide->downsample;

			/* Clip against image size.
			 */
			image.left = 0;
			image.top = 0;
			image.width = w;
			image.height = h;
			vips_rect_intersectrect( &rslide->bounds, &image, 
				&rslide->bounds );

			/* If we've clipped to nothing, ignore bounds.
			 */
			if( vips_rect_isempty( &rslide->bounds ) )
				rslide->autocrop = FALSE;
		}
		if( rslide->autocrop ) {
			w = rslide->bounds.width;
			h = rslide->bounds.height;
		}
	}

	rslide->bg = 0xffffff;
	if( (background = openslide_get_property_value( rslide->osr,
		OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR )) )
		rslide->bg = strtoul( background, NULL, 16 );

	if( w <= 0 || 
		h <= 0 || 
		rslide->downsample < 0 ) {
		vips_error( "openslide2vips", _( "getting dimensions: %s" ),
			openslide_get_error( rslide->osr ) );
		return( NULL );
	}
	if( w > INT_MAX || 
		h > INT_MAX ) {
		vips_error( "openslide2vips",
			"%s", _( "image dimensions overflow int" ) );
		return( NULL );
	}

	if( !rslide->autocrop ) {
		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 );

	for( properties = openslide_get_property_names( rslide->osr );
		*properties != NULL; properties++ )
		vips_image_set_string( out, *properties,
			openslide_get_property_value( rslide->osr,
			*properties ) );

	associated_names = g_strjoinv( ", ", (char **)
		openslide_get_associated_image_names( rslide->osr ) );
	vips_image_set_string( out, 
		"slide-associated-images", associated_names );
	VIPS_FREE( associated_names );

	return( rslide );
}
Example #5
0
int main(int argc, char **argv) {
  if (!g_thread_supported()) {
    g_thread_init(NULL);
  }

  if (argc != 2) {
    fail("No file specified");
  }
  const char *path = argv[1];

  if (g_str_equal(path, "--leak-check--")) {
    child_check_open_fds();
    return 0;
  }

  openslide_get_version();

  if (!openslide_detect_vendor(path)) {
    fail("No vendor for %s", path);
  }

  openslide_t *osr = openslide_open(path);
  if (!osr) {
    fail("Couldn't open %s", path);
  }
  const char *err = openslide_get_error(osr);
  if (err) {
    fail("Open failed: %s", err);
  }
  openslide_close(osr);

  osr = openslide_open(path);
  if (!osr || openslide_get_error(osr)) {
    fail("Reopen failed");
  }

  int64_t w, h;
  openslide_get_level0_dimensions(osr, &w, &h);

  int32_t levels = openslide_get_level_count(osr);
  for (int32_t i = -1; i < levels + 1; i++) {
    int64_t ww, hh;
    openslide_get_level_dimensions(osr, i, &ww, &hh);
    openslide_get_level_downsample(osr, i);
  }

  openslide_get_best_level_for_downsample(osr, 0.8);
  openslide_get_best_level_for_downsample(osr, 1.0);
  openslide_get_best_level_for_downsample(osr, 1.5);
  openslide_get_best_level_for_downsample(osr, 2.0);
  openslide_get_best_level_for_downsample(osr, 3.0);
  openslide_get_best_level_for_downsample(osr, 3.1);
  openslide_get_best_level_for_downsample(osr, 10);
  openslide_get_best_level_for_downsample(osr, 20);
  openslide_get_best_level_for_downsample(osr, 25);
  openslide_get_best_level_for_downsample(osr, 100);
  openslide_get_best_level_for_downsample(osr, 1000);
  openslide_get_best_level_for_downsample(osr, 10000);

  // NULL buffer
  openslide_read_region(osr, NULL, 0, 0, 0, 1000, 1000);

  // empty region
  openslide_read_region(osr, NULL, 0, 0, 0, 0, 0);

  // read properties
  const char * const *property_names = openslide_get_property_names(osr);
  while (*property_names) {
    const char *name = *property_names;
    openslide_get_property_value(osr, name);
    property_names++;
  }

  // read associated images
  const char * const *associated_image_names =
    openslide_get_associated_image_names(osr);
  while (*associated_image_names) {
    int64_t w, h;
    const char *name = *associated_image_names;
    openslide_get_associated_image_dimensions(osr, name, &w, &h);

    uint32_t *buf = g_new(uint32_t, w * h);
    openslide_read_associated_image(osr, name, buf);
    g_free(buf);

    associated_image_names++;
  }

  test_image_fetch(osr, -10, -10, 200, 200);
  test_image_fetch(osr, w/2, h/2, 500, 500);
  test_image_fetch(osr, w - 200, h - 100, 500, 400);
  test_image_fetch(osr, w*2, h*2, 400, 400);
  test_image_fetch(osr, w - 20, 0, 40, 100);
  test_image_fetch(osr, 0, h - 20, 100, 40);

  // active region
  const char *bounds_x = openslide_get_property_value(osr, OPENSLIDE_PROPERTY_NAME_BOUNDS_X);
  const char *bounds_y = openslide_get_property_value(osr, OPENSLIDE_PROPERTY_NAME_BOUNDS_Y);
  int64_t bounds_xx = 0;
  int64_t bounds_yy = 0;
  if (bounds_x && bounds_y) {
    bounds_xx = g_ascii_strtoll(bounds_x, NULL, 10);
    bounds_yy = g_ascii_strtoll(bounds_y, NULL, 10);
    test_image_fetch(osr, bounds_xx, bounds_yy, 200, 200);
  }

  openslide_close(osr);

  check_cloexec_leaks(path, argv[0], bounds_xx, bounds_yy);

  return 0;
}
static void aperio_tiff_tilereader(openslide_t *osr,
				   TIFF *tiff,
				   uint32_t *dest,
				   int64_t x, int64_t y,
				   int32_t w, int32_t h) {
  // which compression?
  uint16_t compression_mode;
  TIFFGetField(tiff, TIFFTAG_COMPRESSION, &compression_mode);

  // not for us? fallback
  if ((compression_mode != APERIO_COMPRESSION_JP2K_YCBCR) &&
      (compression_mode != APERIO_COMPRESSION_JP2K_RGB)) {
    _openslide_generic_tiff_tilereader(osr, tiff, dest, x, y, w, h);
    return;
  }

  // else, JPEG 2000!
  opj_cio_t *stream = NULL;
  opj_dinfo_t *dinfo = NULL;
  opj_image_t *image = NULL;
  opj_image_comp_t *comps = NULL;

  // note: don't use info_handler, it outputs lots of junk
  opj_event_mgr_t event_callbacks = {
    .error_handler = error_callback,
    .warning_handler = warning_callback,
  };

  // get tile number
  ttile_t tile_no = TIFFComputeTile(tiff, x, y, 0, 0);

  //  g_debug("aperio reading tile_no: %d", tile_no);

  // get tile size
  toff_t *sizes;
  if (TIFFGetField(tiff, TIFFTAG_TILEBYTECOUNTS, &sizes) == 0) {
    _openslide_set_error(osr, "Cannot get tile size");
    return;  // ok, haven't allocated anything yet
  }
  tsize_t tile_size = sizes[tile_no];

  // a slide with zero-length tiles has been seen in the wild
  if (!tile_size) {
    // fill with transparent
    memset(dest, 0, w * h * 4);
    //g_debug("skipping tile %d", tile_no);
    return;  // ok, haven't allocated anything yet
  }

  // get raw tile
  tdata_t buf = g_slice_alloc(tile_size);
  tsize_t size = TIFFReadRawTile(tiff, tile_no, buf, tile_size);
  if (size == -1) {
    _openslide_set_error(osr, "Cannot get raw tile");
    goto DONE;
  }

  // init decompressor
  opj_dparameters_t parameters;
  dinfo = opj_create_decompress(CODEC_J2K);
  opj_set_default_decoder_parameters(&parameters);
  opj_setup_decoder(dinfo, &parameters);
  stream = opj_cio_open((opj_common_ptr) dinfo, buf, size);
  opj_set_event_mgr((opj_common_ptr) dinfo, &event_callbacks, osr);


  // decode
  image = opj_decode(dinfo, stream);

  // check error
  if (openslide_get_error(osr)) {
    goto DONE;
  }

  comps = image->comps;

  // sanity check
  if (image->numcomps != 3) {
    _openslide_set_error(osr, "image->numcomps != 3");
    goto DONE;
  }

  // TODO more checks?

  copy_aperio_tile(compression_mode, comps, dest,
		   w, h,
		   w / comps[0].w, h / comps[0].h,
		   w / comps[1].w, h / comps[1].h,
		   w / comps[2].w, h / comps[2].h);

 DONE:
  // erase
  g_slice_free1(tile_size, buf);
  if (image) opj_image_destroy(image);
  if (stream) opj_cio_close(stream);
  if (dinfo) opj_destroy_decompress(dinfo);
}
Example #7
0
static ReadSlide *
readslide_new( const char *filename, VipsImage *out, 
	int layer, const char *associated )
{
	ReadSlide *rslide;
	int64_t w, h;
	const char *background;
	const char * const *properties;

	rslide = VIPS_NEW( out, ReadSlide );
	memset( rslide, 0, sizeof( *rslide ) );
	g_signal_connect( out, "close", G_CALLBACK( readslide_destroy_cb ),
		rslide );

	rslide->layer = layer;
	rslide->associated = g_strdup( associated );

	rslide->osr = openslide_open( filename );
	if( rslide->osr == NULL ) {
		vips_error( "openslide2vips", 
			"%s", _( "failure opening slide" ) );
		return( NULL );
	}

	if( layer < 0 || 
		layer >= openslide_get_layer_count( rslide->osr ) ) {
		vips_error( "openslide2vips",
			"%s", _( "invalid slide level" ) );
		return( NULL );
	}

	if( associated &&
		check_associated_image( rslide->osr, associated ) )
		return( NULL );

	if( associated ) {
		openslide_get_associated_image_dimensions( rslide->osr,
			associated, &w, &h );
		vips_image_set_string( out, "slide-associated-image",
			associated );
		vips_demand_hint( out, VIPS_DEMAND_STYLE_THINSTRIP, NULL );
	} 
	else {
		openslide_get_layer_dimensions( rslide->osr, 
			layer, &w, &h );
		rslide->downsample = openslide_get_layer_downsample(
			rslide->osr, layer );
		vips_image_set_int( out, "slide-level", layer );
		vips_demand_hint( out, VIPS_DEMAND_STYLE_SMALLTILE, NULL );
	}

	/* This tag is used by argb2rgba() to paint fully-transparent pixels.
	 */
	background = openslide_get_property_value( rslide->osr,
		OPENSLIDE_PROPERTY_NAME_BACKGROUND_COLOR );
	if( background != NULL )
		vips_image_set_int( out, 
			VIPS_META_BACKGROUND_RGB, 
			strtoul( background, NULL, 16 ) );
	else
		vips_image_set_int( out, VIPS_META_BACKGROUND_RGB, 0xffffff );

	if( w < 0 || h < 0 || rslide->downsample < 0 ) {
		vips_error( "openslide2vips", _( "getting dimensions: %s" ),
			openslide_get_error( rslide->osr ) );
		return( NULL );
	}
	if( w > INT_MAX || 
		h > INT_MAX ) {
		vips_error( "openslide2vips",
			"%s", _( "image dimensions overflow int" ) );
		return( NULL );
	}

	vips_image_init_fields( out, w, h, 4, VIPS_FORMAT_UCHAR,
		VIPS_CODING_NONE, VIPS_INTERPRETATION_RGB, 1.0, 1.0 );

	for( properties = openslide_get_property_names( rslide->osr );
		*properties != NULL; properties++ )
		vips_image_set_string( out, *properties,
			openslide_get_property_value( rslide->osr,
			*properties ) );

	associated = g_strjoinv( ", ", (char **)
		openslide_get_associated_image_names( rslide->osr ) );
	vips_image_set_string( out, "slide-associated-images", associated );

	return( rslide );
}
void mexFunction(int nlhs, mxArray *plhs[], int nrhs, const mxArray *prhs[]) {

	/*variables*/
	char *buffer; /*array for input string*/
	int32_t levels; /*number of levels*/
	int64_t w, h; /*width, height of each level*/
	double *dims, *ds; /*pointers to outputs - for convenience*/
	const char *objective, *mppx, *mppy;
	openslide_t *slide; /*openslide struct*/
	int i; /*loop iterator*/
	
	/*check input arguments*/
	if(nrhs != 1) {
		mexErrMsgTxt("'openslide_check_levels.m' requires one input argument.");
	}
	if(nlhs > 5) {
		mexErrMsgTxt("'openslide_check_levels.m' produces two outputs.");
	}
	
	/*check input type*/
	if(!mxIsChar(prhs[0]) || (mxGetM(prhs[0]) != 1)) {
		mexErrMsgTxt("Input must be character array.");
	}
	
	/*copy*/
	buffer = mxArrayToString(prhs[0]);
	
	/*copy input*/
	if(openslide_can_open(buffer) == 1) {
			
		/*open*/	
		slide = openslide_open(buffer);

		/*check for error*/
		if(openslide_get_error(slide) != NULL) {
			mexErrMsgTxt("'openslide_check_levels.m' cannot open slide.");
		}
		else {
			
			/*get # of levels*/
			levels = openslide_get_layer_count(slide);
			
			/*allocate arrays*/
			plhs[0] = mxCreateDoubleMatrix((mwSize)levels, 2, mxREAL);
			plhs[1] = mxCreateDoubleMatrix((mwSize)levels, 1, mxREAL);
			
			/*get output addresses*/
			dims = mxGetPr(plhs[0]);
			ds = mxGetPr(plhs[1]);
			
			/*loop through levels, get dimensions/downsample factor of each*/
			for(i = 0; i < levels; i++) {
			
				/*get level info*/
				openslide_get_layer_dimensions(slide, (int32_t)i, &w, &h);
				*ds = openslide_get_layer_downsample(slide, (int32_t)i);
				ds = ds + 1;
				
				/*copy dimensions to outputs*/
				*dims = (double)h;
				*(dims+levels) = (double)w;
				dims = dims + 1;
				
			}

			/*get properties*/
			objective = openslide_get_property_value(slide, OPENSLIDE_PROPERTY_NAME_OBJECTIVE_POWER);
			mppx = openslide_get_property_value(slide, OPENSLIDE_PROPERTY_NAME_MPP_X);
			mppy = openslide_get_property_value(slide, OPENSLIDE_PROPERTY_NAME_MPP_Y);
			
			/*assign properties to outputs*/
			plhs[2] = mxCreateString(objective);
			plhs[3] = mxCreateString(mppx);
			plhs[4] = mxCreateString(mppy);
			
		}
	}
	else{ /*can't open slide*/
		mexErrMsgTxt("'openslide_check_levels.m' cannot open slide.");
	}
	
	/*free input buffer copy*/
	mxFree(buffer);
	
}