1139 lines
28 KiB
C
1139 lines
28 KiB
C
/* Merge two images left-right.
|
|
*
|
|
* Copyright: 1990, 1991 N. Dessipris
|
|
* Author: N. Dessipris
|
|
* Written on: 20/09/1990
|
|
* Updated on: 17/04/1991
|
|
* 1/6/92: JC
|
|
* - check for difference bug fixed
|
|
* - geometry calculations improved and simplified
|
|
* - small speedups
|
|
Kirk Martinez for Sys5 29/4/93
|
|
* 7/8/93 JC
|
|
* - ANSIfied
|
|
* - memory leaks fixed, ready for partial v2
|
|
* - now does IM_CODING_LABQ too
|
|
* 8/11/93 JC
|
|
* - now propogates both input histories
|
|
* - adds magic lines for global mosaic optimisation
|
|
*
|
|
*
|
|
*
|
|
May/1994 Ahmed Abbood
|
|
*
|
|
* - Modified to use partials on all IO
|
|
*
|
|
June/1995 Ahmed Abbood
|
|
*
|
|
* - Modified to work with different types of images.
|
|
*
|
|
* 16/6/95 JC
|
|
* - tidied up a little
|
|
* - added to VIPS!
|
|
* 7/9/95 JC
|
|
* - split into two parts: im_lrmerge() and im__lrmerge()
|
|
* - latter called by im_lrmosaic()
|
|
* - just the same as public im_lrmerge(), but adds no history
|
|
* - necessary for im_global_balance()
|
|
* - small bugs fixed
|
|
* 10/10/95 JC
|
|
* - better checks that parameters are sensible
|
|
* 11/10/95 JC
|
|
* - Kirk spotted what a load of rubbish Ahmed's code is
|
|
* - rewritten - many, many bugs fixed
|
|
* 24/1/97 JC
|
|
* - now outputs bounding area of input images, rather than clipping
|
|
* - ignores 0 pixels in blend
|
|
* - small tidies
|
|
* 7/2/97 JC
|
|
* - new blend, caching
|
|
* 25/2/97 JC
|
|
* - old blend back, much simpler
|
|
* - speed this up at some point if you think of an easy way to do it
|
|
* 29/7/97 JC
|
|
* - IM_CODING_LABQ blend now works, was bug in im_wrapone()
|
|
* - small tidies
|
|
* 10/1/98 JC
|
|
* - merge LUTs now shared between all running mergers
|
|
* - frees memory explicitly in im__stop_merge, for much better memory
|
|
* use in large mosaics, huge improvement!
|
|
* 18/2/98 JC
|
|
* - im_demand_hint() call added
|
|
* 19/2/98 JC
|
|
* - now works for any dx/dy by calling im_insert() for bizarre cases
|
|
* 26/9/99 JC
|
|
* - ooops, blend lut was wrong! wonder how long that's been broken,
|
|
* since feb97 I guess
|
|
* 2/2/01 JC
|
|
* - added tunable max blend width
|
|
* 8/3/01 JC
|
|
* - switched to integer arithmetic for integer blends
|
|
* 7/11/01 JC
|
|
* - more sophisticated transparency handling
|
|
* - tiny blend speed up
|
|
* 19/3/02 JC
|
|
* - move fl cache to main state for better sharing
|
|
* 15/8/02 JC
|
|
* - records Xoffset/Yoffset
|
|
* 20/6/05
|
|
* - now requires all bands == 0 for transparency (used to just check
|
|
* band 0)
|
|
* 24/1/11
|
|
* - gtk-doc
|
|
* - match formats and bands automatically
|
|
* 22/5/14
|
|
* - wrap as a class
|
|
* 18/6/20 kleisauke
|
|
* - convert to vips8
|
|
*/
|
|
|
|
/*
|
|
|
|
This file is part of VIPS.
|
|
|
|
VIPS is free software; you can redistribute it and/or modify
|
|
it under the terms of the GNU Lesser General Public License as published by
|
|
the Free Software Foundation; either version 2 of the License, or
|
|
(at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
|
|
02110-1301 USA
|
|
|
|
*/
|
|
|
|
/*
|
|
|
|
These files are distributed with VIPS - http://www.vips.ecs.soton.ac.uk
|
|
|
|
*/
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
#include <config.h>
|
|
#endif /*HAVE_CONFIG_H*/
|
|
#include <glib/gi18n-lib.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <math.h>
|
|
#include <limits.h>
|
|
|
|
/* Define for debug output.
|
|
#define DEBUG
|
|
*/
|
|
|
|
#include <vips/vips.h>
|
|
#include <vips/thread.h>
|
|
#include <vips/transform.h>
|
|
#include <vips/internal.h>
|
|
|
|
#include "pmosaicing.h"
|
|
|
|
/* Blend luts. Shared between all lr and tb blends.
|
|
*/
|
|
double *vips__coef1 = NULL;
|
|
double *vips__coef2 = NULL;
|
|
int *vips__icoef1 = NULL;
|
|
int *vips__icoef2 = NULL;
|
|
|
|
/* Create a lut for the merging area. Always BLEND_SIZE entries, we
|
|
* scale later when we index it.
|
|
*/
|
|
int
|
|
vips__make_blend_luts( void )
|
|
{
|
|
int x;
|
|
|
|
/* Already done?
|
|
*/
|
|
if( vips__coef1 && vips__coef2 )
|
|
return( 0 );
|
|
|
|
/* Allocate and fill.
|
|
*/
|
|
vips__coef1 = VIPS_ARRAY( NULL, BLEND_SIZE, double );
|
|
vips__coef2 = VIPS_ARRAY( NULL, BLEND_SIZE, double );
|
|
vips__icoef1 = VIPS_ARRAY( NULL, BLEND_SIZE, int );
|
|
vips__icoef2 = VIPS_ARRAY( NULL, BLEND_SIZE, int );
|
|
if( !vips__coef1 || !vips__coef2 || !vips__icoef1 || !vips__icoef2 )
|
|
return( -1 );
|
|
|
|
for( x = 0; x < BLEND_SIZE; x++ ) {
|
|
double a = VIPS_PI * x / (BLEND_SIZE - 1.0);
|
|
|
|
vips__coef1[x] = (cos( a ) + 1.0) / 2.0;
|
|
vips__coef2[x] = 1.0 - vips__coef1[x];
|
|
vips__icoef1[x] = vips__coef1[x] * BLEND_SCALE;
|
|
vips__icoef2[x] = vips__coef2[x] * BLEND_SCALE;
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Return the position of the first non-zero pel from the left.
|
|
*/
|
|
static int
|
|
find_first( VipsRegion *ir, int *pos, int x, int y, int w )
|
|
{
|
|
VipsPel *pr = VIPS_REGION_ADDR( ir, x, y );
|
|
VipsImage *im = ir->im;
|
|
int ne = w * im->Bands;
|
|
int i;
|
|
|
|
/* Double the number of bands in a complex.
|
|
*/
|
|
if( vips_band_format_iscomplex( im->BandFmt ) )
|
|
ne *= 2;
|
|
|
|
/* Search for the first non-zero band element from the left edge of the image.
|
|
*/
|
|
#define lsearch( TYPE ) { \
|
|
TYPE *p = (TYPE *) pr; \
|
|
\
|
|
for( i = 0; i < ne; i++ ) \
|
|
if( p[i] )\
|
|
break;\
|
|
}
|
|
|
|
switch( im->BandFmt ) {
|
|
case VIPS_FORMAT_UCHAR: lsearch( unsigned char ); break;
|
|
case VIPS_FORMAT_CHAR: lsearch( signed char ); break;
|
|
case VIPS_FORMAT_USHORT: lsearch( unsigned short ); break;
|
|
case VIPS_FORMAT_SHORT: lsearch( signed short ); break;
|
|
case VIPS_FORMAT_UINT: lsearch( unsigned int ); break;
|
|
case VIPS_FORMAT_INT: lsearch( signed int ); break;
|
|
case VIPS_FORMAT_FLOAT: lsearch( float ); break;
|
|
case VIPS_FORMAT_DOUBLE: lsearch( double ); break;
|
|
case VIPS_FORMAT_COMPLEX: lsearch( float ); break;
|
|
case VIPS_FORMAT_DPCOMPLEX: lsearch( double ); break;
|
|
|
|
default:
|
|
g_assert_not_reached();
|
|
return( -1 );
|
|
}
|
|
|
|
/* i is first non-zero band element, we want first non-zero pixel.
|
|
*/
|
|
*pos = x + i / im->Bands;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Return the position of the first non-zero pel from the right.
|
|
*/
|
|
static int
|
|
find_last( VipsRegion *ir, int *pos, int x, int y, int w )
|
|
{
|
|
VipsPel *pr = VIPS_REGION_ADDR( ir, x, y );
|
|
VipsImage *im = ir->im;
|
|
int ne = w * im->Bands;
|
|
int i;
|
|
|
|
/* Double the number of bands in a complex.
|
|
*/
|
|
if( vips_band_format_iscomplex( im->BandFmt ) )
|
|
ne *= 2;
|
|
|
|
/* Search for the first non-zero band element from the right.
|
|
*/
|
|
#define rsearch( TYPE ) { \
|
|
TYPE *p = (TYPE *) pr; \
|
|
\
|
|
for( i = ne - 1; i >= 0; i-- )\
|
|
if( p[i] )\
|
|
break;\
|
|
}
|
|
|
|
switch( im->BandFmt ) {
|
|
case VIPS_FORMAT_UCHAR: rsearch( unsigned char ); break;
|
|
case VIPS_FORMAT_CHAR: rsearch( signed char ); break;
|
|
case VIPS_FORMAT_USHORT: rsearch( unsigned short ); break;
|
|
case VIPS_FORMAT_SHORT: rsearch( signed short ); break;
|
|
case VIPS_FORMAT_UINT: rsearch( unsigned int ); break;
|
|
case VIPS_FORMAT_INT: rsearch( signed int ); break;
|
|
case VIPS_FORMAT_FLOAT: rsearch( float ); break;
|
|
case VIPS_FORMAT_DOUBLE: rsearch( double ); break;
|
|
case VIPS_FORMAT_COMPLEX: rsearch( float ); break;
|
|
case VIPS_FORMAT_DPCOMPLEX: rsearch( double ); break;
|
|
|
|
default:
|
|
vips_error( "lrmerge", "%s", _( "internal error" ) );
|
|
return( -1 );
|
|
}
|
|
|
|
/* i is first non-zero band element, we want first non-zero pixel.
|
|
*/
|
|
*pos = x + i / im->Bands;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Make sure we have first/last for this area.
|
|
*/
|
|
static int
|
|
make_firstlast( MergeInfo *inf, Overlapping *ovlap, VipsRect *oreg )
|
|
{
|
|
VipsRegion *rir = inf->rir;
|
|
VipsRegion *sir = inf->sir;
|
|
VipsRect rr, sr;
|
|
int y, yr, ys;
|
|
int missing;
|
|
|
|
/* We're going to build first/last ... lock it from other generate
|
|
* threads. In fact it's harmless if we do get two writers, but we may
|
|
* avoid duplicating work.
|
|
*/
|
|
g_mutex_lock( ovlap->fl_lock );
|
|
|
|
/* Do we already have first/last for this area? Bail out if we do.
|
|
*/
|
|
missing = 0;
|
|
for( y = oreg->top; y < VIPS_RECT_BOTTOM( oreg ); y++ ) {
|
|
const int j = y - ovlap->overlap.top;
|
|
const int first = ovlap->first[j];
|
|
|
|
if( first < 0 ) {
|
|
missing = 1;
|
|
break;
|
|
}
|
|
}
|
|
if( !missing ) {
|
|
/* No work to do!
|
|
*/
|
|
g_mutex_unlock( ovlap->fl_lock );
|
|
return( 0 );
|
|
}
|
|
|
|
/* Entire width of overlap in ref for scan-lines we want.
|
|
*/
|
|
rr.left = ovlap->overlap.left;
|
|
rr.top = oreg->top;
|
|
rr.width = ovlap->overlap.width;
|
|
rr.height = oreg->height;
|
|
rr.left -= ovlap->rarea.left;
|
|
rr.top -= ovlap->rarea.top;
|
|
|
|
/* Entire width of overlap in sec for scan-lines we want.
|
|
*/
|
|
sr.left = ovlap->overlap.left;
|
|
sr.top = oreg->top;
|
|
sr.width = ovlap->overlap.width;
|
|
sr.height = oreg->height;
|
|
sr.left -= ovlap->sarea.left;
|
|
sr.top -= ovlap->sarea.top;
|
|
|
|
#ifdef DEBUG
|
|
printf( "lrmerge: making first/last for areas:\n" );
|
|
printf( "ref: left = %d, top = %d, width = %d, height = %d\n",
|
|
rr.left, rr.top, rr.width, rr.height );
|
|
printf( "sec: left = %d, top = %d, width = %d, height = %d\n",
|
|
sr.left, sr.top, sr.width, sr.height );
|
|
#endif
|
|
|
|
/* Make pixels.
|
|
*/
|
|
if( vips_region_prepare( rir, &rr ) ||
|
|
vips_region_prepare( sir, &sr ) ) {
|
|
g_mutex_unlock( ovlap->fl_lock );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Make first/last cache.
|
|
*/
|
|
for( y = oreg->top, yr = rr.top, ys = sr.top;
|
|
y < VIPS_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) {
|
|
const int j = y - ovlap->overlap.top;
|
|
int *first = &ovlap->first[j];
|
|
int *last = &ovlap->last[j];
|
|
|
|
/* Done this line already?
|
|
*/
|
|
if( *first < 0 ) {
|
|
/* Search for start/end of overlap on this scan-line.
|
|
*/
|
|
if( find_first( sir, first,
|
|
sr.left, ys, sr.width ) ||
|
|
find_last( rir, last,
|
|
rr.left, yr, rr.width ) ) {
|
|
g_mutex_unlock( ovlap->fl_lock );
|
|
return( -1 );
|
|
}
|
|
|
|
/* Translate to output space.
|
|
*/
|
|
*first += ovlap->sarea.left;
|
|
*last += ovlap->rarea.left;
|
|
|
|
/* Clip to maximum blend width, if necessary.
|
|
*/
|
|
if( ovlap->mwidth >= 0 &&
|
|
*last - *first > ovlap->mwidth ) {
|
|
int shrinkby = (*last - *first) - ovlap->mwidth;
|
|
|
|
*first += shrinkby / 2;
|
|
*last -= shrinkby / 2;
|
|
}
|
|
}
|
|
}
|
|
|
|
g_mutex_unlock( ovlap->fl_lock );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Test pixel == 0.
|
|
*/
|
|
#define TEST_ZERO( TYPE, T, RESULT ) { \
|
|
TYPE *tt = (T); \
|
|
int ii; \
|
|
\
|
|
for( ii = 0; ii < cb; ii++ ) \
|
|
if( tt[i + ii] ) \
|
|
break; \
|
|
if( ii == cb ) \
|
|
(RESULT) = 1; \
|
|
}
|
|
|
|
/* Blend two integer images.
|
|
*/
|
|
#define iblend( TYPE, B, IN1, IN2, OUT ) { \
|
|
TYPE *tr = (TYPE *) (IN1); \
|
|
TYPE *ts = (TYPE *) (IN2); \
|
|
TYPE *tq = (TYPE *) (OUT); \
|
|
const int cb = (B); \
|
|
const int left = VIPS_CLIP( 0, first - oreg->left, oreg->width ); \
|
|
const int right = VIPS_CLIP( left, last - oreg->left, oreg->width ); \
|
|
int ref_zero; \
|
|
int sec_zero; \
|
|
int x, b; \
|
|
int i; \
|
|
\
|
|
/* Left of the blend area. \
|
|
*/ \
|
|
for( i = 0, x = 0; x < left; x++ ) { \
|
|
ref_zero = 0; \
|
|
TEST_ZERO( TYPE, tr, ref_zero ); \
|
|
if( !ref_zero ) \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = tr[i]; \
|
|
else \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = ts[i]; \
|
|
} \
|
|
\
|
|
/* In blend area. \
|
|
*/ \
|
|
for( x = left; x < right; x++ ) { \
|
|
ref_zero = 0; \
|
|
sec_zero = 0; \
|
|
TEST_ZERO( TYPE, tr, ref_zero ); \
|
|
TEST_ZERO( TYPE, ts, sec_zero ); \
|
|
\
|
|
if( !ref_zero && !sec_zero ) { \
|
|
int inx = ((x + oreg->left - first) << \
|
|
BLEND_SHIFT) / bwidth; \
|
|
int c1 = vips__icoef1[inx]; \
|
|
int c2 = vips__icoef2[inx]; \
|
|
\
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = c1 * tr[i] / BLEND_SCALE + \
|
|
c2 * ts[i] / BLEND_SCALE; \
|
|
} \
|
|
else if( !ref_zero ) \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = tr[i]; \
|
|
else \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = ts[i]; \
|
|
} \
|
|
\
|
|
/* Right of blend.
|
|
*/ \
|
|
for( x = right; x < oreg->width; x++ ) { \
|
|
sec_zero = 0; \
|
|
TEST_ZERO( TYPE, ts, sec_zero ); \
|
|
if( !sec_zero ) \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = ts[i]; \
|
|
else \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = tr[i]; \
|
|
} \
|
|
}
|
|
|
|
/* Blend two float images.
|
|
*/
|
|
#define fblend( TYPE, B, IN1, IN2, OUT ) { \
|
|
TYPE *tr = (TYPE *) (IN1); \
|
|
TYPE *ts = (TYPE *) (IN2); \
|
|
TYPE *tq = (TYPE *) (OUT); \
|
|
const int cb = (B); \
|
|
const int left = VIPS_CLIP( 0, first - oreg->left, oreg->width ); \
|
|
const int right = VIPS_CLIP( left, last - oreg->left, oreg->width ); \
|
|
int ref_zero; \
|
|
int sec_zero; \
|
|
int x, b; \
|
|
int i; \
|
|
\
|
|
/* Left of the blend area. \
|
|
*/ \
|
|
for( i = 0, x = 0; x < left; x++ ) { \
|
|
ref_zero = 0; \
|
|
TEST_ZERO( TYPE, tr, ref_zero ); \
|
|
if( !ref_zero ) \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = tr[i]; \
|
|
else \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = ts[i]; \
|
|
} \
|
|
\
|
|
/* In blend area. \
|
|
*/ \
|
|
for( x = left; x < right; x++ ) { \
|
|
ref_zero = 0; \
|
|
sec_zero = 0; \
|
|
TEST_ZERO( TYPE, tr, ref_zero ); \
|
|
TEST_ZERO( TYPE, ts, sec_zero ); \
|
|
\
|
|
if( !ref_zero && !sec_zero ) { \
|
|
int inx = ((x + oreg->left - first) << \
|
|
BLEND_SHIFT) / bwidth; \
|
|
double c1 = vips__coef1[inx]; \
|
|
double c2 = vips__coef2[inx]; \
|
|
\
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = c1 * tr[i] + c2 * ts[i]; \
|
|
} \
|
|
else if( !ref_zero ) \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = tr[i]; \
|
|
else \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = ts[i]; \
|
|
} \
|
|
\
|
|
/* Right of blend.
|
|
*/ \
|
|
for( x = right; x < oreg->width; x++ ) { \
|
|
sec_zero = 0; \
|
|
TEST_ZERO( TYPE, ts, sec_zero ); \
|
|
if( !sec_zero ) \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = ts[i]; \
|
|
else \
|
|
for( b = 0; b < cb; b++, i++ ) \
|
|
tq[i] = tr[i]; \
|
|
} \
|
|
}
|
|
|
|
/* Left-right blend function for non-labpack images.
|
|
*/
|
|
static int
|
|
lr_blend( VipsRegion *or, MergeInfo *inf, Overlapping *ovlap, VipsRect *oreg )
|
|
{
|
|
VipsRegion *rir = inf->rir;
|
|
VipsRegion *sir = inf->sir;
|
|
VipsImage *im = or->im;
|
|
|
|
VipsRect prr, psr;
|
|
int y, yr, ys;
|
|
|
|
/* Make sure we have a complete first/last set for this area.
|
|
*/
|
|
if( make_firstlast( inf, ovlap, oreg ) )
|
|
return( -1 );
|
|
|
|
/* Part of rr which we will output.
|
|
*/
|
|
prr = *oreg;
|
|
prr.left -= ovlap->rarea.left;
|
|
prr.top -= ovlap->rarea.top;
|
|
|
|
/* Part of sr which we will output.
|
|
*/
|
|
psr = *oreg;
|
|
psr.left -= ovlap->sarea.left;
|
|
psr.top -= ovlap->sarea.top;
|
|
|
|
/* Make pixels.
|
|
*/
|
|
if( vips_region_prepare( rir, &prr ) ||
|
|
vips_region_prepare( sir, &psr ) )
|
|
return( -1 );
|
|
|
|
/* Loop down overlap area.
|
|
*/
|
|
for( y = oreg->top, yr = prr.top, ys = psr.top;
|
|
y < VIPS_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) {
|
|
VipsPel *pr = VIPS_REGION_ADDR( rir, prr.left, yr );
|
|
VipsPel *ps = VIPS_REGION_ADDR( sir, psr.left, ys );
|
|
VipsPel *q = VIPS_REGION_ADDR( or, oreg->left, y );
|
|
|
|
const int j = y - ovlap->overlap.top;
|
|
const int first = ovlap->first[j];
|
|
const int last = ovlap->last[j];
|
|
const int bwidth = last - first;
|
|
|
|
switch( im->BandFmt ) {
|
|
case VIPS_FORMAT_UCHAR:
|
|
iblend( unsigned char, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_CHAR:
|
|
iblend( signed char, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_USHORT:
|
|
iblend( unsigned short, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_SHORT:
|
|
iblend( signed short, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_UINT:
|
|
iblend( unsigned int, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_INT:
|
|
iblend( signed int, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_FLOAT:
|
|
fblend( float, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_DOUBLE:
|
|
fblend( double, im->Bands, pr, ps, q ); break;
|
|
case VIPS_FORMAT_COMPLEX:
|
|
fblend( float, im->Bands * 2, pr, ps, q ); break;
|
|
case VIPS_FORMAT_DPCOMPLEX:
|
|
fblend( double, im->Bands * 2, pr, ps, q ); break;
|
|
|
|
default:
|
|
g_assert_not_reached();
|
|
return( -1 );
|
|
}
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Left-right blend function for VIPS_CODING_LABQ images.
|
|
*/
|
|
static int
|
|
lr_blend_labpack( VipsRegion *or, MergeInfo *inf, Overlapping *ovlap,
|
|
VipsRect *oreg )
|
|
{
|
|
VipsRegion *rir = inf->rir;
|
|
VipsRegion *sir = inf->sir;
|
|
VipsRect prr, psr;
|
|
int y, yr, ys;
|
|
|
|
/* Make sure we have a complete first/last set for this area. This
|
|
* will just look at the top 8 bits of L, not all 10, but should be OK.
|
|
*/
|
|
if( make_firstlast( inf, ovlap, oreg ) )
|
|
return( -1 );
|
|
|
|
/* Part of rr which we will output.
|
|
*/
|
|
prr = *oreg;
|
|
prr.left -= ovlap->rarea.left;
|
|
prr.top -= ovlap->rarea.top;
|
|
|
|
/* Part of sr which we will output.
|
|
*/
|
|
psr = *oreg;
|
|
psr.left -= ovlap->sarea.left;
|
|
psr.top -= ovlap->sarea.top;
|
|
|
|
/* Make pixels.
|
|
*/
|
|
if( vips_region_prepare( rir, &prr ) ||
|
|
vips_region_prepare( sir, &psr ) )
|
|
return( -1 );
|
|
|
|
/* Loop down overlap area.
|
|
*/
|
|
for( y = oreg->top, yr = prr.top, ys = psr.top;
|
|
y < VIPS_RECT_BOTTOM( oreg ); y++, yr++, ys++ ) {
|
|
VipsPel *pr = VIPS_REGION_ADDR( rir, prr.left, yr );
|
|
VipsPel *ps = VIPS_REGION_ADDR( sir, psr.left, ys );
|
|
VipsPel *q = VIPS_REGION_ADDR( or, oreg->left, y );
|
|
|
|
const int j = y - ovlap->overlap.top;
|
|
const int first = ovlap->first[j];
|
|
const int last = ovlap->last[j];
|
|
const int bwidth = last - first;
|
|
|
|
float *fq = inf->merge;
|
|
float *r = inf->from1;
|
|
float *s = inf->from2;
|
|
|
|
/* Unpack two bits we want.
|
|
*/
|
|
vips__LabQ2Lab_vec( r, pr, oreg->width );
|
|
vips__LabQ2Lab_vec( s, ps, oreg->width );
|
|
|
|
/* Blend as floats.
|
|
*/
|
|
fblend( float, 3, r, s, fq );
|
|
|
|
/* Re-pack to output buffer.
|
|
*/
|
|
vips__Lab2LabQ_vec( q, inf->merge, oreg->width );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
static void
|
|
lock_free( VipsImage *image, GMutex *lock )
|
|
{
|
|
VIPS_FREEF( vips_g_mutex_free, lock );
|
|
}
|
|
|
|
/* Build basic per-call state and do some geometry calculations. Shared with
|
|
* tbmerge, so not static.
|
|
*/
|
|
Overlapping *
|
|
vips__build_mergestate( const char *domain,
|
|
VipsImage *ref, VipsImage *sec, VipsImage *out,
|
|
int dx, int dy, int mwidth )
|
|
{
|
|
VipsImage **t = (VipsImage **)
|
|
vips_object_local_array( VIPS_OBJECT( out ), 4 );
|
|
|
|
VipsImage **arry;
|
|
Overlapping *ovlap;
|
|
int x;
|
|
|
|
/* TODO(kleisauke): Copied from vips_insert, perhaps we
|
|
* need a separate function for this?
|
|
* (just like im__insert_base)
|
|
*/
|
|
if( vips_image_pio_input( ref ) ||
|
|
vips_image_pio_input( sec ) ||
|
|
vips_check_bands_1orn( domain, ref, sec ) ||
|
|
vips_check_coding_known( domain, ref ) ||
|
|
vips_check_coding_same( domain, ref, sec ) )
|
|
return( NULL );
|
|
|
|
/* Cast our input images up to a common format and bands.
|
|
*/
|
|
if( vips__formatalike( ref, sec, &t[0], &t[1] ) ||
|
|
vips__bandalike( domain, t[0], t[1], &t[2], &t[3] ) )
|
|
return( NULL );
|
|
|
|
if( !(arry = vips_allocate_input_array( out,
|
|
t[2], t[3], NULL )) )
|
|
return( NULL );
|
|
|
|
if( vips_image_pipeline_array( out,
|
|
VIPS_DEMAND_STYLE_SMALLTILE, arry ) )
|
|
return( NULL );
|
|
|
|
if( mwidth < -1 ) {
|
|
vips_error( domain, "%s", _( "mwidth must be -1 or >= 0" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
if( !(ovlap = VIPS_NEW( out, Overlapping )) )
|
|
return( NULL );
|
|
|
|
ovlap->ref = arry[0];
|
|
ovlap->sec = arry[1];
|
|
ovlap->out = out;
|
|
ovlap->dx = dx;
|
|
ovlap->dy = dy;
|
|
ovlap->mwidth = mwidth;
|
|
|
|
/* Area occupied by ref image. Place at (0,0) to start with.
|
|
*/
|
|
ovlap->rarea.left = 0;
|
|
ovlap->rarea.top = 0;
|
|
ovlap->rarea.width = ovlap->ref->Xsize;
|
|
ovlap->rarea.height = ovlap->ref->Ysize;
|
|
|
|
/* Area occupied by sec image.
|
|
*/
|
|
ovlap->sarea.left = -dx;
|
|
ovlap->sarea.top = -dy;
|
|
ovlap->sarea.width = ovlap->sec->Xsize;
|
|
ovlap->sarea.height = ovlap->sec->Ysize;
|
|
|
|
/* Compute overlap.
|
|
*/
|
|
vips_rect_intersectrect( &ovlap->rarea, &ovlap->sarea, &ovlap->overlap );
|
|
if( vips_rect_isempty( &ovlap->overlap ) ) {
|
|
vips_error( domain, "%s", _( "no overlap" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Find position and size of output image.
|
|
*/
|
|
vips_rect_unionrect( &ovlap->rarea, &ovlap->sarea, &ovlap->oarea );
|
|
|
|
/* Now: translate everything, so that the output image, not the left
|
|
* image, is at (0,0).
|
|
*/
|
|
ovlap->rarea.left -= ovlap->oarea.left;
|
|
ovlap->rarea.top -= ovlap->oarea.top;
|
|
ovlap->sarea.left -= ovlap->oarea.left;
|
|
ovlap->sarea.top -= ovlap->oarea.top;
|
|
ovlap->overlap.left -= ovlap->oarea.left;
|
|
ovlap->overlap.top -= ovlap->oarea.top;
|
|
ovlap->oarea.left = 0;
|
|
ovlap->oarea.top = 0;
|
|
|
|
/* Make sure blend luts are built.
|
|
*/
|
|
vips__make_blend_luts();
|
|
|
|
/* Size of first/last cache. Could be either of these ... just pick
|
|
* the larger.
|
|
*/
|
|
ovlap->flsize = VIPS_MAX( ovlap->overlap.width, ovlap->overlap.height );
|
|
|
|
/* Build first/last cache.
|
|
*/
|
|
ovlap->first = VIPS_ARRAY( out, ovlap->flsize, int );
|
|
ovlap->last = VIPS_ARRAY( out, ovlap->flsize, int );
|
|
if( !ovlap->first || !ovlap->last )
|
|
return( NULL );
|
|
for( x = 0; x < ovlap->flsize; x++ )
|
|
ovlap->first[x] = -1;
|
|
|
|
ovlap->fl_lock = vips_g_mutex_new();
|
|
|
|
g_signal_connect( out, "close",
|
|
G_CALLBACK( lock_free ), ovlap->fl_lock );
|
|
|
|
return( ovlap );
|
|
}
|
|
|
|
/* Build per-call state.
|
|
*/
|
|
static Overlapping *
|
|
build_lrstate( VipsImage *ref, VipsImage *sec, VipsImage *out,
|
|
int dx, int dy, int mwidth )
|
|
{
|
|
Overlapping *ovlap;
|
|
|
|
if( !(ovlap = vips__build_mergestate( "lrmerge",
|
|
ref, sec, out, dx, dy, mwidth )) )
|
|
return( NULL );
|
|
|
|
/* Select blender.
|
|
*/
|
|
switch( ovlap->ref->Coding ) {
|
|
case VIPS_CODING_LABQ:
|
|
ovlap->blend = lr_blend_labpack;
|
|
break;
|
|
|
|
case VIPS_CODING_NONE:
|
|
ovlap->blend = lr_blend;
|
|
break;
|
|
|
|
default:
|
|
vips_error( "lrmerge", "%s", _( "unknown coding type" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Find the parts of output which come just from ref and just from sec.
|
|
*/
|
|
ovlap->rpart = ovlap->rarea;
|
|
ovlap->spart = ovlap->sarea;
|
|
ovlap->rpart.width -= ovlap->overlap.width;
|
|
ovlap->spart.left += ovlap->overlap.width;
|
|
ovlap->spart.width -= ovlap->overlap.width;
|
|
|
|
/* Is there too much overlap? ie. right edge of ref image is greater
|
|
* than right edge of sec image, or left > left.
|
|
*/
|
|
if( VIPS_RECT_RIGHT( &ovlap->rarea ) >
|
|
VIPS_RECT_RIGHT( &ovlap->sarea ) ||
|
|
ovlap->rarea.left > ovlap->sarea.left ) {
|
|
vips_error( "lrmerge", "%s", _( "too much overlap" ) );
|
|
return( NULL );
|
|
}
|
|
|
|
/* Max number of pixels we may have to blend over.
|
|
*/
|
|
ovlap->blsize = ovlap->overlap.width;
|
|
|
|
return( ovlap );
|
|
}
|
|
|
|
/* The area being demanded can be filled using only pels from either the ref
|
|
* or the sec images. Attach output to the appropriate part of the input image.
|
|
* area is the position that ir->im occupies in the output image.
|
|
*
|
|
* Shared with tbmerge, so not static.
|
|
*/
|
|
int
|
|
vips__attach_input( VipsRegion *or, VipsRegion *ir, VipsRect *area )
|
|
{
|
|
VipsRect r = or->valid;
|
|
|
|
/* Translate to source coordinate space.
|
|
*/
|
|
r.left -= area->left;
|
|
r.top -= area->top;
|
|
|
|
/* Demand input.
|
|
*/
|
|
if( vips_region_prepare( ir, &r ) )
|
|
return( -1 );
|
|
|
|
/* Attach or to ir.
|
|
*/
|
|
if( vips_region_region( or, ir, &or->valid, r.left, r.top ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* The area being demanded requires pixels from the ref and sec images. As
|
|
* above, but just do a sub-area of the output, and make sure we copy rather
|
|
* than just pointer-fiddling. reg is the sub-area of or->valid we should do.
|
|
*
|
|
* Shared with tbmerge, so not static.
|
|
*/
|
|
int
|
|
vips__copy_input( VipsRegion *or, VipsRegion *ir,
|
|
VipsRect *area, VipsRect *reg )
|
|
{
|
|
VipsRect r = *reg;
|
|
|
|
/* Translate to source coordinate space.
|
|
*/
|
|
r.left -= area->left;
|
|
r.top -= area->top;
|
|
|
|
/* Paint this area of ir into or.
|
|
*/
|
|
if( vips_region_prepare_to( ir, or, &r, reg->left, reg->top ) )
|
|
return( -1 );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Generate function for merge. This is shared between lrmerge and
|
|
* tbmerge.
|
|
*/
|
|
int
|
|
vips__merge_gen( VipsRegion *or, void *seq, void *a, void *b,
|
|
gboolean *stop )
|
|
{
|
|
MergeInfo *inf = (MergeInfo *) seq;
|
|
Overlapping *ovlap = (Overlapping *) a;
|
|
VipsRect *r = &or->valid;
|
|
VipsRect rreg, sreg, oreg;
|
|
|
|
/* Find intersection with overlap, ref and sec parts.
|
|
*/
|
|
vips_rect_intersectrect( r, &ovlap->rpart, &rreg );
|
|
vips_rect_intersectrect( r, &ovlap->spart, &sreg );
|
|
|
|
/* Do easy cases first: can we satisfy this demand with pixels just
|
|
* from ref, or just from sec.
|
|
*/
|
|
if( vips_rect_equalsrect( r, &rreg ) ) {
|
|
if( vips__attach_input( or, inf->rir, &ovlap->rarea ) )
|
|
return( -1 );
|
|
}
|
|
else if( vips_rect_equalsrect( r, &sreg ) ) {
|
|
if( vips__attach_input( or, inf->sir, &ovlap->sarea ) )
|
|
return( -1 );
|
|
}
|
|
else {
|
|
/* Difficult case - do in three stages: black out whole area,
|
|
* copy in parts of ref and sec we touch, write blend area.
|
|
* This could be sped up somewhat ... we will usually black
|
|
* out far too much, and write to the blend area three times.
|
|
* Upgrade in the future!
|
|
*/
|
|
|
|
/* Need intersections with whole of left & right, and overlap
|
|
* too.
|
|
*/
|
|
vips_rect_intersectrect( r, &ovlap->rarea, &rreg );
|
|
vips_rect_intersectrect( r, &ovlap->sarea, &sreg );
|
|
vips_rect_intersectrect( r, &ovlap->overlap, &oreg );
|
|
|
|
vips_region_black( or );
|
|
if( !vips_rect_isempty( &rreg ) )
|
|
if( vips__copy_input( or,
|
|
inf->rir, &ovlap->rarea, &rreg ) )
|
|
return( -1 );
|
|
if( !vips_rect_isempty( &sreg ) )
|
|
if( vips__copy_input( or,
|
|
inf->sir, &ovlap->sarea, &sreg ) )
|
|
return( -1 );
|
|
|
|
/* Nasty: inf->rir and inf->sir now point to the same bit of
|
|
* memory (part of or), and we've written twice. We need to
|
|
* make sure we get fresh pixels for the blend, so we must
|
|
* invalidate them both. Should maybe add a call to the API
|
|
* for this.
|
|
*/
|
|
inf->rir->valid.width = inf->sir->valid.width = 0;
|
|
|
|
/* Now blat in the blended area.
|
|
*/
|
|
if( !vips_rect_isempty( &oreg ) )
|
|
if( ovlap->blend( or, inf, ovlap, &oreg ) )
|
|
return( -1 );
|
|
}
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Stop function. Shared with tbmerge. Free explicitly to reduce mem
|
|
* requirements quickly for large mosaics.
|
|
*/
|
|
int
|
|
vips__stop_merge( void *seq, void *a, void *b )
|
|
{
|
|
MergeInfo *inf = (MergeInfo *) seq;
|
|
|
|
VIPS_UNREF( inf->rir );
|
|
VIPS_UNREF( inf->sir );
|
|
VIPS_FREE( inf->from1 );
|
|
VIPS_FREE( inf->from2 );
|
|
VIPS_FREE( inf->merge );
|
|
g_free( inf );
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
/* Start function. Shared with tbmerge.
|
|
*/
|
|
void *
|
|
vips__start_merge( VipsImage *out, void *a, void *b )
|
|
{
|
|
Overlapping *ovlap = (Overlapping *) a;
|
|
MergeInfo *inf;
|
|
|
|
if( !(inf = VIPS_NEW( NULL, MergeInfo )) )
|
|
return( NULL );
|
|
|
|
inf->rir = NULL;
|
|
inf->sir = NULL;
|
|
inf->from1 = NULL;
|
|
inf->from2 = NULL;
|
|
inf->merge = NULL;
|
|
|
|
/* If this is going to be a VIPS_CODING_LABQ, we need VIPS_CODING_LABQ
|
|
* blend buffers.
|
|
*/
|
|
if( out->Coding == VIPS_CODING_LABQ ) {
|
|
inf->from1 = VIPS_ARRAY( NULL, ovlap->blsize * 3, float );
|
|
inf->from2 = VIPS_ARRAY( NULL, ovlap->blsize * 3, float );
|
|
inf->merge = VIPS_ARRAY( NULL, ovlap->blsize * 3, float );
|
|
if( !inf->from1 || !inf->from2 || !inf->merge ) {
|
|
vips__stop_merge( inf, NULL, NULL );
|
|
return( NULL );
|
|
}
|
|
}
|
|
|
|
inf->rir = vips_region_new( ovlap->ref );
|
|
inf->sir = vips_region_new( ovlap->sec );
|
|
|
|
if( !inf->rir || !inf->sir ) {
|
|
vips__stop_merge( inf, NULL, NULL );
|
|
return( NULL );
|
|
}
|
|
|
|
return( inf );
|
|
}
|
|
|
|
int
|
|
vips__lrmerge( VipsImage *ref, VipsImage *sec, VipsImage *out,
|
|
int dx, int dy, int mwidth )
|
|
{
|
|
Overlapping *ovlap;
|
|
|
|
#ifdef DEBUG
|
|
printf( "lrmerge %s %s %s %d %d %d\n",
|
|
ref->filename, sec->filename, out->filename,
|
|
dx, dy, mwidth );
|
|
printf( "ref is %d x %d pixels\n", ref->Xsize, ref->Ysize );
|
|
printf( "sec is %d x %d pixels\n", sec->Xsize, sec->Ysize );
|
|
#endif
|
|
|
|
if( dx > 0 || dx < 1 - ref->Xsize ) {
|
|
VipsImage *x;
|
|
|
|
#ifdef DEBUG
|
|
printf( "lrmerge: no overlap, using insert\n" );
|
|
#endif
|
|
|
|
/* No overlap, use insert instead.
|
|
*/
|
|
if( vips_insert( ref, sec, &x, -dx, -dy,
|
|
"expand", TRUE,
|
|
NULL ) )
|
|
return( -1 );
|
|
if( vips_image_write( x, out ) ) {
|
|
g_object_unref( x );
|
|
return( -1 );
|
|
}
|
|
g_object_unref( x );
|
|
|
|
out->Xoffset = -dx;
|
|
out->Yoffset = -dy;
|
|
|
|
return( 0 );
|
|
}
|
|
|
|
if( !(ovlap = build_lrstate( ref, sec, out, dx, dy, mwidth )) )
|
|
return( -1 );
|
|
|
|
if( vips_image_pipelinev( out,
|
|
VIPS_DEMAND_STYLE_THINSTRIP, ovlap->ref, ovlap->sec, NULL ) )
|
|
return( -1 );
|
|
|
|
out->Xsize = ovlap->oarea.width;
|
|
out->Ysize = ovlap->oarea.height;
|
|
out->Xoffset = -dx;
|
|
out->Yoffset = -dy;
|
|
|
|
if( vips_image_generate( out,
|
|
vips__start_merge, vips__merge_gen, vips__stop_merge,
|
|
ovlap, NULL ) )
|
|
return( -1 );
|
|
|
|
return ( 0 );
|
|
}
|
|
|
|
const char *
|
|
vips__get_mosaic_name( VipsImage *image )
|
|
{
|
|
const char *name;
|
|
|
|
if( vips_image_get_typeof( image, "mosaic-name" ) ) {
|
|
if( vips_image_get_string( image, "mosaic-name", &name ) )
|
|
return( NULL );
|
|
}
|
|
else
|
|
name = image->filename;
|
|
|
|
return( name );
|
|
}
|
|
|
|
void
|
|
vips__add_mosaic_name( VipsImage *image )
|
|
{
|
|
static int global_serial = 0;
|
|
|
|
/* TODO(kleisauke): Could we call vips_image_temp_name instead?
|
|
*/
|
|
int serial = g_atomic_int_add( &global_serial, 1 );
|
|
|
|
char name[256];
|
|
|
|
/* We must override any inherited name, so don't test for doesn't
|
|
* exist before setting.
|
|
*/
|
|
vips_snprintf( name, 256, "mosaic-temp-%d", serial );
|
|
vips_image_set_string( image, "mosaic-name", name );
|
|
}
|
|
|