From 045678d438a0865462864079b19d91ecd0d051d8 Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Fri, 15 May 2015 14:24:53 +0100 Subject: [PATCH] vips_copy() can turn bands into width handy for slicing up very large 3D arrays --- ChangeLog | 1 + TODO | 26 +++------- libvips/conversion/copy.c | 99 +++++++++++++++++++++++++++++++++++++-- 3 files changed, 103 insertions(+), 23 deletions(-) diff --git a/ChangeLog b/ChangeLog index f114bb1a..ed4b46b9 100644 --- a/ChangeLog +++ b/ChangeLog @@ -3,6 +3,7 @@ - change the alpha range rules for vips_flatten() to match vips_premultiply() - vipsthumbnail uses vips_resize() rather than its own code - vipsthumbnail uses vips_premultiply() for better alpha quality +- vips_copy() can turn 1xN or Nx1 M-band images into MxN one-band images 4/5/15 started 8.0.2 - fix a refcount error in C++ wrapper, thanks huskier diff --git a/TODO b/TODO index fc2c79a5..1e1ac37e 100644 --- a/TODO +++ b/TODO @@ -1,24 +1,12 @@ -- try making a 1000 x 500 x 500 float image on disc, then +- how about something like vips_grid() which turns a tall thin one-band + image into a much smaller many-band image? - https://gist.github.com/jcupitt/93e7248bf9eddc34c4cb - - see: - - $ time ./slice.py ~/pics/1000x500x500.v - vips warning: VipsImage: /Users/john/pics/1000x500x500.v is longer than - expected - Traceback (most recent call last): - File "./slice.py", line 26, in row.falsecolour().write_to_file("row.v") - File "/usr/local/lib/python2.7/site-packages/gi/overrides/Vips.py", line 570, in write_to_file _call_base(saver, [filename], kwargs, self, option_string) - File "/usr/local/lib/python2.7/site-packages/gi/overrides/Vips.py", line 338, in _call_base - raise Error('Error calling operator %s.' % name) - gi.overrides.Vips.Error: Error calling operator - VipsForeignSaveVips. - VipsRegion: images do not match in pixel size - VipsRegion: valid clipped to nothing - - caching is breaking with copy() stuff, somehow? + at the moment making a 500-band image uses huge amounts of memory :-( since + we have at least 500 bytes per pixel, times 128x128 for each tile, times N + for the number of threads, times 500, since we need a 500-stage pipeline + much faster to make a very tall, thin image and fold it up in a single + operation - are the mosaic functions calling vips_fastcor()? it must be very slow diff --git a/libvips/conversion/copy.c b/libvips/conversion/copy.c index f927d849..4aff2a8a 100644 --- a/libvips/conversion/copy.c +++ b/libvips/conversion/copy.c @@ -47,6 +47,8 @@ * - rewrite as a class * 1/12/11 * - use glib byteswap macros + * 15/5/15 + * - support bands -> width conversion */ /* @@ -87,6 +89,7 @@ #include #include +#include #include #include @@ -185,6 +188,70 @@ static SwapFn vips_copy_swap_fn[] = { vips_copy_swap8 /* VIPS_FORMAT_DPCOMPLEX = 9, */ }; +/* Copy, turning bands into the x axis. + */ +static int +vips_copy_unbandize_gen( VipsRegion *or, + void *seq, void *a, void *b, gboolean *stop ) +{ + VipsRegion *ir = (VipsRegion *) seq; + VipsImage *in = ir->im; + VipsRect *r = &or->valid; + VipsCopy *copy = (VipsCopy *) b; + SwapFn swap = vips_copy_swap_fn[copy->in->BandFmt]; + + VipsRect need; + int y; + + /* Ask for input we need. + */ + if( in->Xsize == 1 ) { + need.left = 0; + need.top = r->top; + need.width = 1; + need.height = r->height; + } + else { + need.left = r->top; + need.top = 0; + need.width = r->height; + need.height = 1; + } + if( vips_region_prepare( ir, &need ) ) + return( -1 ); + + + for( y = 0; y < r->height; y++ ) { + VipsPel *p; + VipsPel *q; + + if( in->Xsize == 1 ) { + p = r->left * VIPS_IMAGE_SIZEOF_ELEMENT( in ) + + VIPS_REGION_ADDR( ir, 0, r->top + y ); + } + else { + p = r->left * VIPS_IMAGE_SIZEOF_ELEMENT( in ) + + VIPS_REGION_ADDR( ir, r->top + y, 0 ); + } + q = VIPS_REGION_ADDR( or, r->left, r->top + y ); + + if( copy->swap && + swap ) { + swap( p, q, r->width, copy->in ); + } + else + /* We can't use vips_region_region(), it doesn't do + * coordinate transforms. If we want to avoid the + * memcpy() we'd need to add another vips_region_ + * function. + */ + memcpy( q, p, + r->width * VIPS_IMAGE_SIZEOF_ELEMENT( in ) ); + } + + return( 0 ); +} + /* Copy a small area. */ static int @@ -246,6 +313,8 @@ vips_copy_build( VipsObject *object ) guint64 image_size_before; guint64 image_size_after; + VipsImage copy_of_fields; + VipsGenerateFn copy_generate_fn; int i; if( VIPS_OBJECT_CLASS( vips_copy_parent_class )->build( object ) ) @@ -258,10 +327,10 @@ vips_copy_build( VipsObject *object ) VIPS_DEMAND_STYLE_THINSTRIP, copy->in, NULL ) ) return( -1 ); - /* We try to stop the worst crashes by at least ensuring that we don't - * increase the number of pixels which might be addressed. + /* Take a copy of all the basic header fields. We use this for + * sanity-checking the changes our caller has made. */ - image_size_before = VIPS_IMAGE_SIZEOF_IMAGE( conversion->out ); + copy_of_fields = *conversion->out; /* Use props to adjust header fields. */ @@ -300,6 +369,10 @@ vips_copy_build( VipsObject *object ) } } + /* We try to stop the worst crashes by at least ensuring that we don't + * increase the number of pixels which might be addressed. + */ + image_size_before = VIPS_IMAGE_SIZEOF_IMAGE( ©_of_fields ); image_size_after = VIPS_IMAGE_SIZEOF_IMAGE( conversion->out ); if( image_size_after > image_size_before ) { vips_error( class->nickname, @@ -307,8 +380,23 @@ vips_copy_build( VipsObject *object ) return( -1 ); } + /* Pick a generate function. + */ + copy_generate_fn = vips_copy_gen; + + /* We let our caller change a 1xN or Nx1 image with M bands into a MxN + * image. In other words, bands becomes width. + */ + if( (copy_of_fields.Xsize == 1 || copy_of_fields.Ysize == 1) && + conversion->out->Bands == 1 && + conversion->out->Xsize == copy_of_fields.Bands && + conversion->out->Ysize == VIPS_MAX( + copy_of_fields.Xsize, copy_of_fields.Ysize ) ) { + copy_generate_fn = vips_copy_unbandize_gen; + } + if( vips_image_generate( conversion->out, - vips_start_one, vips_copy_gen, vips_stop_one, + vips_start_one, copy_generate_fn, vips_stop_one, copy->in, copy ) ) return( -1 ); @@ -461,6 +549,9 @@ vips_copy_init( VipsCopy *copy ) * Setting @swap to %TRUE will make vips_copy() swap the byte ordering of * pixels according to the image's format. * + * You can use this operation to turn 1xN or Nx1 images with M bands into MxN + * one band images. + * * Returns: 0 on success, -1 on error. */ int