add minimise to insert

This patch makes "insert" issue minimise signals for inputs in sequential
mode. This can drop memory use in some programs, for example:

```python

import sys
import random
import pyvips

image = pyvips.Image.black(20000, 20000)

for filename in sys.argv[2:]:
    tile = pyvips.Image.new_from_file(filename, access='sequential')
    x = random.randint(0, image.width - tile.width)
    y = random.randint(0, image.height - tile.height)
    image = image.insert(tile, x, y)

image.write_to_file(sys.argv[1])
```

Before this patch:

```
$ for i in {0..1000}; do cp ~/pics/k2.jpg $i.jpg; done
$ /usr/bin/time -f %M:%e ../manyjoin.py ../x.jpg *.jpg
5456256:4.34
```

With this patch:

```
$ /usr/bin/time -f %M:%e ../manyjoin.py ../x.jpg *.jpg
2475324:3.38
```
This commit is contained in:
John Cupitt 2021-11-05 12:14:50 +00:00
parent 41e92b9ada
commit 6f081de0a0
3 changed files with 72 additions and 61 deletions

View File

@ -19,6 +19,7 @@
- add fail-on to give better control over loader error sensitivity - add fail-on to give better control over loader error sensitivity
- add hyperbolic functions sinh, cosh, tanh, asinh, acosh, atanh [hroskes] - add hyperbolic functions sinh, cosh, tanh, asinh, acosh, atanh [hroskes]
- add untiled jp2k load - add untiled jp2k load
- "insert" will minimise in seq mode
16/8/21 started 8.11.4 16/8/21 started 8.11.4
- fix off-by-one error in new rank fast path - fix off-by-one error in new rank fast path

View File

@ -129,13 +129,15 @@ vips_arrayjoin_gen( VipsRegion *or, void *seq,
* *
* We don't lock for minimised[], but it's harmless. * We don't lock for minimised[], but it's harmless.
*/ */
for( i = 0; i < n; i++ ) for( i = 0; i < n; i++ ) {
int bottom_edge = VIPS_RECT_BOTTOM( &join->rects[i] );
if( !join->minimised[i] && if( !join->minimised[i] &&
r->top > VIPS_RECT_BOTTOM( &join->rects[i] ) + r->top > bottom_edge + 1024 ) {
1024 ) {
join->minimised[i] = TRUE; join->minimised[i] = TRUE;
vips_image_minimise_all( in[i] ); vips_image_minimise_all( in[i] );
} }
}
return( 0 ); return( 0 );
} }

View File

@ -28,6 +28,8 @@
* 29/9/11 * 29/9/11
* - rewrite as a class * - rewrite as a class
* - add expand, bg options * - add expand, bg options
* 5/11/21
* - add minimise for seq pipelines
*/ */
/* /*
@ -93,20 +95,19 @@ typedef struct _VipsInsert {
*/ */
VipsPel *ink; VipsPel *ink;
/* Inputs cast and banded up. /* Inputs cast and banded up, plus a NULL at the end.
*/ */
VipsImage *main_processed; VipsImage *processed[3];
VipsImage *sub_processed;
/* Geometry. /* Geometry.
*/ */
VipsRect rout; /* Output space */ VipsRect rout; /* Output space */
VipsRect rmain; /* Position of main in output */ VipsRect rimage[2]; /* Position of main in output */
VipsRect rsub; /* Position of sub in output */
/* TRUE if we've minimise sub. /* TRUE if we've minimised an input.
*/ */
gboolean sub_minimised; gboolean minimised[2];
} VipsInsert; } VipsInsert;
typedef VipsConversionClass VipsInsertClass; typedef VipsConversionClass VipsInsertClass;
@ -174,47 +175,58 @@ vips_insert_gen( VipsRegion *or, void *seq, void *a, void *b, gboolean *stop )
VipsRegion **ir = (VipsRegion **) seq; VipsRegion **ir = (VipsRegion **) seq;
VipsRect *r = &or->valid; VipsRect *r = &or->valid;
VipsInsert *insert = (VipsInsert *) b; VipsInsert *insert = (VipsInsert *) b;
VipsConversion *conversion = VIPS_CONVERSION( insert );
VipsRect ovl; int i;
/* The part of the subimage we will use. /* First, does this request fall entirely inside one of our inputs? If
* it does, we can just redirect the request there. Test sub first.
*/ */
vips_rect_intersectrect( &or->valid, &insert->rsub, &ovl ); for( i = 1; i >= 0; i-- ) {
VipsRect *rimage = &insert->rimage[i];
/* Three cases: we are generating entirely within the sub-image, if( vips_rect_includesrect( rimage, r ) ) {
* entirely within the main image, or a mixture. if( vips__insert_just_one( or, ir[i],
rimage->left, rimage->top ) )
return( -1 );
break;
}
}
/* Otherwise, it requires both (or neither) input.
*/ */
if( vips_rect_includesrect( &insert->rsub, &or->valid ) ) { if( i < 0 ) {
if( vips__insert_just_one( or, ir[1],
insert->rsub.left, insert->rsub.top ) )
return( -1 );
}
else if( vips_rect_includesrect( &insert->rmain, &or->valid ) &&
vips_rect_isempty( &ovl ) ) {
if( vips__insert_just_one( or, ir[0],
insert->rmain.left, insert->rmain.top ) )
return( -1 );
}
else {
/* Output requires both (or neither) input. If it is not /* Output requires both (or neither) input. If it is not
* entirely inside both the main and the sub, then there is * entirely inside both the main and the sub, then there is
* going to be some background. * going to be some background.
*/ */
if( !(vips_rect_includesrect( &insert->rsub, &or->valid ) && vips_region_paint_pel( or, r, insert->ink );
vips_rect_includesrect( &insert->rmain, &or->valid )) )
vips_region_paint_pel( or, r, insert->ink );
/* Paste from main. /* Paste the background first.
*/ */
if( vips__insert_paste_region( or, ir[0], &insert->rmain ) ) for( i = 0; i < 2; i++ )
return( -1 ); if( vips__insert_paste_region( or, ir[i],
&insert->rimage[i] ) )
/* Paste from sub. return( -1 );
*/
if( vips__insert_paste_region( or, ir[1], &insert->rsub ) )
return( -1 );
} }
/* See arrayjoin for almost this code again. Move into conversion.c?
*/
if( vips_image_is_sequential( conversion->out ) )
for( i = 0; i < 2; i++ ) {
int bottom_edge =
VIPS_RECT_BOTTOM( &insert->rimage[i] );
/* 1024 is a generous margin. 256 is too small.
*/
if( !insert->minimised[i] &&
r->top > bottom_edge + 1024 ) {
insert->minimised[i] = TRUE;
vips_image_minimise_all( insert->processed[i] );
}
}
return( 0 ); return( 0 );
} }
@ -351,8 +363,6 @@ vips_insert_build( VipsObject *object )
VipsInsert *insert = (VipsInsert *) object; VipsInsert *insert = (VipsInsert *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 6 ); VipsImage **t = (VipsImage **) vips_object_local_array( object, 6 );
VipsImage **arry;
if( VIPS_OBJECT_CLASS( vips_insert_parent_class )->build( object ) ) if( VIPS_OBJECT_CLASS( vips_insert_parent_class )->build( object ) )
return( -1 ); return( -1 );
@ -370,48 +380,46 @@ vips_insert_build( VipsObject *object )
if( vips__formatalike( insert->main, insert->sub, &t[0], &t[1] ) || if( vips__formatalike( insert->main, insert->sub, &t[0], &t[1] ) ||
vips__bandalike( class->nickname, t[0], t[1], &t[2], &t[3] ) ) vips__bandalike( class->nickname, t[0], t[1], &t[2], &t[3] ) )
return( -1 ); return( -1 );
insert->main_processed = t[2]; insert->processed[0] = t[2];
insert->sub_processed = t[3]; insert->processed[1] = t[3];
if( !(arry = vips_allocate_input_array( conversion->out,
insert->main_processed, insert->sub_processed, NULL )) )
return( -1 );
/* Joins can get very wide (eg. consider joining a set of tiles /* Joins can get very wide (eg. consider joining a set of tiles
* horizontally to make a large image), we don't want mem use to shoot * horizontally to make a large image), we don't want mem use to shoot
* up. SMALLTILE will guarantee we keep small and local. * up. SMALLTILE will guarantee we keep small and local.
*/ */
if( vips_image_pipeline_array( conversion->out, if( vips_image_pipeline_array( conversion->out,
VIPS_DEMAND_STYLE_SMALLTILE, arry ) ) VIPS_DEMAND_STYLE_SMALLTILE, insert->processed ) )
return( -1 ); return( -1 );
/* Calculate geometry. /* Calculate geometry.
*/ */
insert->rmain.left = 0; insert->rimage[0].left = 0;
insert->rmain.top = 0; insert->rimage[0].top = 0;
insert->rmain.width = insert->main_processed->Xsize; insert->rimage[0].width = insert->processed[0]->Xsize;
insert->rmain.height = insert->main_processed->Ysize; insert->rimage[0].height = insert->processed[0]->Ysize;
insert->rsub.left = insert->x;
insert->rsub.top = insert->y; insert->rimage[1].left = insert->x;
insert->rsub.width = insert->sub_processed->Xsize; insert->rimage[1].top = insert->y;
insert->rsub.height = insert->sub_processed->Ysize; insert->rimage[1].width = insert->processed[1]->Xsize;
insert->rimage[1].height = insert->processed[1]->Ysize;
if( insert->expand ) { if( insert->expand ) {
/* Expand output to bounding box of these two. /* Expand output to bounding box of these two.
*/ */
vips_rect_unionrect( &insert->rmain, &insert->rsub, vips_rect_unionrect( &insert->rimage[0], &insert->rimage[1],
&insert->rout ); &insert->rout );
/* Translate origin to top LH corner of rout. /* Translate origin to top LH corner of rout.
*/ */
insert->rmain.left -= insert->rout.left; insert->rimage[0].left -= insert->rout.left;
insert->rmain.top -= insert->rout.top; insert->rimage[0].top -= insert->rout.top;
insert->rsub.left -= insert->rout.left; insert->rimage[1].left -= insert->rout.left;
insert->rsub.top -= insert->rout.top; insert->rimage[1].top -= insert->rout.top;
insert->rout.left = 0; insert->rout.left = 0;
insert->rout.top = 0; insert->rout.top = 0;
} }
else else
insert->rout = insert->rmain; insert->rout = insert->rimage[0];
conversion->out->Xsize = insert->rout.width; conversion->out->Xsize = insert->rout.width;
conversion->out->Ysize = insert->rout.height; conversion->out->Ysize = insert->rout.height;
@ -424,7 +432,7 @@ vips_insert_build( VipsObject *object )
if( vips_image_generate( conversion->out, if( vips_image_generate( conversion->out,
vips_start_many, vips_insert_gen, vips_stop_many, vips_start_many, vips_insert_gen, vips_stop_many,
arry, insert ) ) insert->processed, insert ) )
return( -1 ); return( -1 );
return( 0 ); return( 0 );