redo im_convsep() as a class
and im_convsep_f()
This commit is contained in:
parent
edbbc5fe2b
commit
68c5f1909a
@ -1,7 +1,7 @@
|
||||
19/10/13 started 7.37.0
|
||||
- redone im_rotate_*mask45(), im_gauss_*mask*(), im_log_*mask(), im_dilate(),
|
||||
im_erode(), im_rank_image(), im_compass(), im_linedet(), im_gradient()
|
||||
as classes
|
||||
im_erode(), im_rank_image(), im_compass(), im_linedet(), im_gradient(),
|
||||
im_convsep(), im_convsep_f() as classes
|
||||
- vips_init() now does some ABI compat checking, though this change requires
|
||||
an ABI break
|
||||
- add "interlace" option to vips_jpegsave()
|
||||
|
10
TODO
10
TODO
@ -1,4 +1,12 @@
|
||||
- need to do conv sep
|
||||
- we have aconv and aconvsep
|
||||
|
||||
test timing, make sure it;s worth having a separate aconvsep version
|
||||
|
||||
if it is, make im_aconvsep an optimisation: call im_aconvsep_raw() from
|
||||
vips_conv() if mask width or height == 1 and prec == APPROX
|
||||
|
||||
now we can get rid of im_aconvsep() since it's just vips_convsep() with prec
|
||||
set to approx
|
||||
|
||||
- do conv and morph quickly as simple wrappers over the vips7 operations
|
||||
|
||||
|
@ -4,6 +4,7 @@ libconvolution_la_SOURCES = \
|
||||
convolution.c \
|
||||
pconvolution.h \
|
||||
conv.c \
|
||||
convsep.c \
|
||||
compass.c \
|
||||
morph.c \
|
||||
convol_dispatch.c \
|
||||
|
@ -78,8 +78,8 @@ G_DEFINE_ABSTRACT_TYPE( VipsConvolution, vips_convolution,
|
||||
static int
|
||||
vips_convolution_build( VipsObject *object )
|
||||
{
|
||||
VipsConvolution *convolution = VIPS_CONVOLUTION( object );
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsConvolution *convolution = VIPS_CONVOLUTION( object );
|
||||
VipsImage **t = (VipsImage **) vips_object_local_array( object, 2 );
|
||||
|
||||
#ifdef DEBUG
|
||||
@ -149,8 +149,10 @@ vips_convolution_operation_init( void )
|
||||
extern int vips_conv_get_type( void );
|
||||
extern int vips_morph_get_type( void );
|
||||
extern int vips_compass_get_type( void );
|
||||
extern int vips_convsep_get_type( void );
|
||||
|
||||
vips_conv_get_type();
|
||||
vips_morph_get_type();
|
||||
vips_compass_get_type();
|
||||
vips_convsep_get_type();
|
||||
}
|
||||
|
173
libvips/convolution/convsep.c
Normal file
173
libvips/convolution/convsep.c
Normal file
@ -0,0 +1,173 @@
|
||||
/* convolve twice, rotating the mask
|
||||
*
|
||||
* 23/10/13
|
||||
* - from vips_convsep()
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
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 <vips/intl.h>
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#include <vips/vips.h>
|
||||
|
||||
#include "pconvolution.h"
|
||||
|
||||
typedef struct {
|
||||
VipsConvolution parent_instance;
|
||||
|
||||
VipsPrecision precision;
|
||||
int layers;
|
||||
int cluster;
|
||||
} VipsConvsep;
|
||||
|
||||
typedef VipsConvolutionClass VipsConvsepClass;
|
||||
|
||||
G_DEFINE_TYPE( VipsConvsep, vips_convsep, VIPS_TYPE_CONVOLUTION );
|
||||
|
||||
static int
|
||||
vips_convsep_build( VipsObject *object )
|
||||
{
|
||||
VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object );
|
||||
VipsConvolution *convolution = (VipsConvolution *) object;
|
||||
VipsConvsep *convsep = (VipsConvsep *) object;
|
||||
VipsImage **t = (VipsImage **)
|
||||
vips_object_local_array( object, 3 );
|
||||
|
||||
g_object_set( convsep, "out", vips_image_new(), NULL );
|
||||
|
||||
if( VIPS_OBJECT_CLASS( vips_convsep_parent_class )->build( object ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_check_separable( class->nickname, convolution->M ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_rot( convolution->M, &t[0], VIPS_ANGLE_90, NULL ) ||
|
||||
vips_conv( convolution->in, &t[1], convolution->M,
|
||||
"precision", convsep->precision,
|
||||
"layers", convsep->layers,
|
||||
"cluster", convsep->cluster,
|
||||
NULL ) ||
|
||||
vips_conv( t[1], &t[2], t[0],
|
||||
"precision", convsep->precision,
|
||||
"layers", convsep->layers,
|
||||
"cluster", convsep->cluster,
|
||||
NULL ) )
|
||||
return( -1 );
|
||||
|
||||
if( vips_image_write( t[2], convolution->out ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static void
|
||||
vips_convsep_class_init( VipsConvsepClass *class )
|
||||
{
|
||||
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
|
||||
VipsObjectClass *object_class = (VipsObjectClass *) class;
|
||||
|
||||
gobject_class->set_property = vips_object_set_property;
|
||||
gobject_class->get_property = vips_object_get_property;
|
||||
|
||||
object_class->nickname = "convsep";
|
||||
object_class->description = _( "convolution operation" );
|
||||
object_class->build = vips_convsep_build;
|
||||
|
||||
VIPS_ARG_ENUM( class, "precision", 203,
|
||||
_( "Precision" ),
|
||||
_( "Convolve with this precision" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsConvsep, precision ),
|
||||
VIPS_TYPE_PRECISION, VIPS_PRECISION_INTEGER );
|
||||
|
||||
VIPS_ARG_INT( class, "layers", 204,
|
||||
_( "Layers" ),
|
||||
_( "Use this many layers in approximation" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsConvsep, layers ),
|
||||
1, 1000, 5 );
|
||||
|
||||
VIPS_ARG_INT( class, "cluster", 205,
|
||||
_( "Cluster" ),
|
||||
_( "Cluster lines closer than this in approximation" ),
|
||||
VIPS_ARGUMENT_OPTIONAL_INPUT,
|
||||
G_STRUCT_OFFSET( VipsConvsep, cluster ),
|
||||
1, 100, 1 );
|
||||
|
||||
}
|
||||
|
||||
static void
|
||||
vips_convsep_init( VipsConvsep *convsep )
|
||||
{
|
||||
convsep->precision = VIPS_PRECISION_INTEGER;
|
||||
convsep->layers = 5;
|
||||
convsep->cluster = 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_convsep:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolution mask
|
||||
*
|
||||
* Optional arguments:
|
||||
*
|
||||
* @precision: calculation accuracy
|
||||
* @layers: number of layers for approximation
|
||||
* @cluster: cluster lines closer than this distance
|
||||
*
|
||||
* Perform a separable convolution of @in with @mask.
|
||||
* See vips_conv() for a detailed description.
|
||||
*
|
||||
* The mask must be 1xn or nx1 elements.
|
||||
*
|
||||
* The image is convolved twice: once with @mask and then again with @mask
|
||||
* rotated by 90 degrees. This is much faster for certain types of mask
|
||||
* (gaussian blur, for example) than doing a full 2D convolution.
|
||||
*
|
||||
* See also: vips_conv(), vips_gaussmat().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
vips_convsep( VipsImage *in, VipsImage **out, VipsImage *mask, ... )
|
||||
{
|
||||
va_list ap;
|
||||
int result;
|
||||
|
||||
va_start( ap, mask );
|
||||
result = vips_call_split( "convsep", ap, in, out, mask );
|
||||
va_end( ap );
|
||||
|
||||
return( result );
|
||||
}
|
@ -1075,28 +1075,6 @@ im_conv_raw( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* im_conv:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolution mask
|
||||
*
|
||||
* Convolve @in with @mask using integer arithmetic. The output image
|
||||
* always has the same #VipsBandFmt as the input image.
|
||||
*
|
||||
* Each output pixel is
|
||||
* calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale
|
||||
* and offset are part of @mask. For integer @in, the division by scale
|
||||
* includes round-to-nearest.
|
||||
*
|
||||
* Convolutions on unsigned 8-bit images are calculated with the
|
||||
* processor's vector unit,
|
||||
* if possible. Disable this with --vips-novector or IM_NOVECTOR.
|
||||
*
|
||||
* See also: im_conv_f(), im_convsep(), im_create_imaskv().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
im_conv( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
{
|
||||
@ -1114,72 +1092,3 @@ im_conv( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
{
|
||||
IMAGE *t;
|
||||
INTMASK *rmask;
|
||||
|
||||
if( mask->xsize != 1 && mask->ysize != 1 ) {
|
||||
im_error( "im_convsep",
|
||||
"%s", _( "expect 1xN or Nx1 input mask" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( !(t = im_open_local( out, "im_convsep", "p" )) ||
|
||||
!(rmask = (INTMASK *) im_local( out,
|
||||
(im_construct_fn) im_dup_imask,
|
||||
(im_callback_fn) im_free_imask, mask, mask->filename, NULL )) )
|
||||
return( -1 );
|
||||
|
||||
rmask->xsize = mask->ysize;
|
||||
rmask->ysize = mask->xsize;
|
||||
rmask->offset = 0.;
|
||||
|
||||
if( im_conv_raw( in, t, rmask ) ||
|
||||
im_conv_raw( t, out, mask ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* im_convsep:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolution mask
|
||||
*
|
||||
* Perform a separable convolution of @in with @mask using integer arithmetic.
|
||||
* See im_conv() for a detailed description.
|
||||
*
|
||||
* The mask must be 1xn or nx1 elements.
|
||||
* The output image
|
||||
* always has the same #VipsBandFmt as the input image.
|
||||
*
|
||||
* The image is convolved twice: once with @mask and then again with @mask
|
||||
* rotated by 90 degrees. This is much faster for certain types of mask
|
||||
* (gaussian blur, for example) than doing a full 2D convolution.
|
||||
*
|
||||
* See also: im_convsep_f(), im_conv(), im_create_imaskv().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
{
|
||||
IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" );
|
||||
int n_mask = mask->xsize * mask->ysize;
|
||||
|
||||
if( !t1 ||
|
||||
im_embed( in, t1, 1, n_mask / 2, n_mask / 2,
|
||||
in->Xsize + n_mask - 1,
|
||||
in->Ysize + n_mask - 1 ) ||
|
||||
im_convsep_raw( t1, out, mask ) )
|
||||
return( -1 );
|
||||
|
||||
out->Xoffset = 0;
|
||||
out->Yoffset = 0;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
@ -392,77 +392,3 @@ im_conv_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
{
|
||||
IMAGE *t;
|
||||
DOUBLEMASK *rmask;
|
||||
|
||||
if( mask->xsize != 1 && mask->ysize != 1 ) {
|
||||
im_error( "im_convsep_f",
|
||||
"%s", _( "expect 1xN or Nx1 input mask" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
if( !(t = im_open_local( out, "im_convsep_f", "p" )) ||
|
||||
!(rmask = (DOUBLEMASK *) im_local( out,
|
||||
(im_construct_fn) im_dup_dmask,
|
||||
(im_callback_fn) im_free_dmask, mask, mask->filename, NULL )) )
|
||||
return( -1 );
|
||||
|
||||
rmask->xsize = mask->ysize;
|
||||
rmask->ysize = mask->xsize;
|
||||
rmask->offset = 0.;
|
||||
|
||||
if( im_conv_f_raw( in, t, rmask ) ||
|
||||
im_conv_f_raw( t, out, mask ) )
|
||||
return( -1 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* im_convsep_f:
|
||||
* @in: input image
|
||||
* @out: output image
|
||||
* @mask: convolution mask
|
||||
*
|
||||
* Perform a separable convolution of @in with @mask using floating-point
|
||||
* arithmetic.
|
||||
*
|
||||
* The mask must be 1xn or nx1 elements.
|
||||
* The output image
|
||||
* is always %IM_BANDFMT_FLOAT unless @in is %IM_BANDFMT_DOUBLE, in which case
|
||||
* @out is also %IM_BANDFMT_DOUBLE.
|
||||
*
|
||||
* The image is convolved twice: once with @mask and then again with @mask
|
||||
* rotated by 90 degrees. This is much faster for certain types of mask
|
||||
* (gaussian blur, for example) than doing a full 2D convolution.
|
||||
*
|
||||
* Each output pixel is
|
||||
* calculated as sigma[i]{pixel[i] * mask[i]} / scale + offset, where scale
|
||||
* and offset are part of @mask.
|
||||
*
|
||||
* See also: im_convsep(), im_conv(), im_create_dmaskv().
|
||||
*
|
||||
* Returns: 0 on success, -1 on error
|
||||
*/
|
||||
int
|
||||
im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
{
|
||||
IMAGE *t1 = im_open_local( out, "im_convsep intermediate", "p" );
|
||||
int size = mask->xsize * mask->ysize;
|
||||
|
||||
if( !t1 ||
|
||||
im_embed( in, t1, 1, size / 2, size / 2,
|
||||
in->Xsize + size - 1,
|
||||
in->Ysize + size - 1 ) ||
|
||||
im_convsep_f_raw( t1, out, mask ) )
|
||||
return( -1 );
|
||||
|
||||
out->Xoffset = 0;
|
||||
out->Yoffset = 0;
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
@ -2162,6 +2162,65 @@ im_gradient( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_convsep_raw( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
{
|
||||
im_error( "im_convsep_raw", "no compat function" );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
int
|
||||
im_convsep( IMAGE *in, IMAGE *out, INTMASK *mask )
|
||||
{
|
||||
VipsImage *t1, *t2;
|
||||
|
||||
if( !(t1 = vips_image_new()) ||
|
||||
im_imask2vips( mask, t1 ) )
|
||||
return( -1 );
|
||||
if( vips_convsep( in, &t2, t1,
|
||||
NULL ) ) {
|
||||
g_object_unref( t1 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t1 );
|
||||
if( vips_image_write( t2, out ) ) {
|
||||
g_object_unref( t2 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t2 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
int
|
||||
im_convsep_f_raw( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
im_convsep_f( IMAGE *in, IMAGE *out, DOUBLEMASK *mask )
|
||||
{
|
||||
VipsImage *t1, *t2;
|
||||
|
||||
if( !(t1 = vips_image_new()) ||
|
||||
im_imask2vips( mask, t1 ) )
|
||||
return( -1 );
|
||||
if( vips_convsep( in, &t2, t1,
|
||||
"precision", VIPS_PRECISION_FLOAT,
|
||||
NULL ) ) {
|
||||
g_object_unref( t1 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t1 );
|
||||
if( vips_image_write( t2, out ) ) {
|
||||
g_object_unref( t2 );
|
||||
return( -1 );
|
||||
}
|
||||
g_object_unref( t2 );
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
static int
|
||||
vips__round( VipsImage *in, VipsImage *out, VipsOperationRound round )
|
||||
{
|
||||
|
@ -71,6 +71,8 @@ int vips_conv( VipsImage *in, VipsImage **out, VipsImage *mask, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_compass( VipsImage *in, VipsImage **out, VipsImage *mask, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_convsep( VipsImage *in, VipsImage **out, VipsImage *mask, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
int vips_morph( VipsImage *in, VipsImage **out, VipsImage *mask,
|
||||
VipsOperationMorphology morph, ... )
|
||||
@ -81,12 +83,6 @@ void vips_convolution_operation_init( void );
|
||||
|
||||
|
||||
|
||||
int im_aconvsep( VipsImage *in, VipsImage *out,
|
||||
DOUBLEMASK *mask, int n_layers );
|
||||
|
||||
int im_convsep( VipsImage *in, VipsImage *out, INTMASK *mask );
|
||||
int im_convsep_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask );
|
||||
|
||||
int im_sharpen( VipsImage *in, VipsImage *out,
|
||||
int mask_size,
|
||||
double x1, double y2, double y3,
|
||||
|
@ -88,6 +88,7 @@ int vips_check_vector_length( const char *domain, int n, int len );
|
||||
int vips_check_vector( const char *domain, int n, VipsImage *im );
|
||||
int vips_check_hist( const char *domain, VipsImage *im );
|
||||
int vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out );
|
||||
int vips_check_separable( const char *domain, VipsImage *im );
|
||||
|
||||
int vips_check_imask( const char *domain, INTMASK *mask );
|
||||
int vips_check_dmask( const char *domain, DOUBLEMASK *mask );
|
||||
|
@ -906,6 +906,12 @@ int im_aconv( VipsImage *in, VipsImage *out,
|
||||
int im_conv( VipsImage *in, VipsImage *out, INTMASK *mask );
|
||||
int im_conv_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask );
|
||||
|
||||
int im_aconvsep( VipsImage *in, VipsImage *out,
|
||||
DOUBLEMASK *mask, int n_layers );
|
||||
|
||||
int im_convsep( VipsImage *in, VipsImage *out, INTMASK *mask );
|
||||
int im_convsep_f( VipsImage *in, VipsImage *out, DOUBLEMASK *mask );
|
||||
|
||||
int im_compass( VipsImage *in, VipsImage *out, INTMASK *mask );
|
||||
int im_gradient( VipsImage *in, VipsImage *out, INTMASK *mask );
|
||||
int im_lindetect( VipsImage *in, VipsImage *out, INTMASK *mask );
|
||||
|
@ -1266,6 +1266,33 @@ vips_check_matrix( const char *domain, VipsImage *im, VipsImage **out )
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_check_separable:
|
||||
* @domain: the originating domain for the error message
|
||||
* @im: image to check
|
||||
*
|
||||
* Separable matrix images must have width or height 1.
|
||||
* Return 0 if the image will pass, or -1 and
|
||||
* set an error message otherwise.
|
||||
*
|
||||
* See also: vips_error().
|
||||
*
|
||||
* Returns: 0 if OK, -1 otherwise.
|
||||
*/
|
||||
int
|
||||
vips_check_separable( const char *domain, VipsImage *im )
|
||||
{
|
||||
if( im->Xsize != 1 &&
|
||||
im->Ysize != 1 ) {
|
||||
vips_error( domain,
|
||||
"%s", _( "separable matrix images must have "
|
||||
"width or height 1" ) );
|
||||
return( -1 );
|
||||
}
|
||||
|
||||
return( 0 );
|
||||
}
|
||||
|
||||
/**
|
||||
* vips_check_imask: (skip)
|
||||
* @domain: the originating domain for the error message
|
||||
|
Loading…
Reference in New Issue
Block a user