make vips_sobel()

cut out of vips_canny()
This commit is contained in:
John Cupitt 2018-02-26 18:33:20 +00:00
parent b07a7c60b7
commit 4a59fa652c
8 changed files with 210 additions and 55 deletions

View File

@ -5,6 +5,7 @@
- hough_line scales width to 0 - 180, not 0 - 360 - hough_line scales width to 0 - 180, not 0 - 360
- hough_line is 4x faster - hough_line is 4x faster
- hough_circle is 2x faster - hough_circle is 2x faster
- add vips_sobel() and vips_canny() edge detectors
12/2/18 started 8.6.3 12/2/18 started 8.6.3
- use pkg-config to find libjpeg, if we can - use pkg-config to find libjpeg, if we can

View File

@ -1280,8 +1280,8 @@ if test x"$found_introspection" = xyes -a "$VIPS_LIBDIR/girepository-1.0" != "$I
;; # ignore for homebrew ;; # ignore for homebrew
*) *)
AC_MSG_RESULT([dnl AC_MSG_RESULT([dnl
Vips-8.0.typelib will install to $VIPS_LIBDIR/girepository-1.0, but your Vips-8.0.typelib will be installed to $VIPS_LIBDIR/girepository-1.0, but
system repository seems to be $INTROSPECTION_TYPELIBDIR. your system repository seems to be $INTROSPECTION_TYPELIBDIR.
You may need to add this directory to your typelib path, for example: You may need to add this directory to your typelib path, for example:
export GI_TYPELIB_PATH="$VIPS_LIBDIR/girepository-1.0" export GI_TYPELIB_PATH="$VIPS_LIBDIR/girepository-1.0"
@ -1313,7 +1313,7 @@ overrides are installed in the correct location.
]) ])
elif test x"$VIPS_PYEXECDIR/gi/overrides" != x"$syspygipath"; then elif test x"$VIPS_PYEXECDIR/gi/overrides" != x"$syspygipath"; then
AC_MSG_RESULT([dnl AC_MSG_RESULT([dnl
The vips Python overrides file will install to The vips Python overrides file will be installed to
$VIPS_PYEXECDIR/gi/overrides/Vips.py, but your $VIPS_PYEXECDIR/gi/overrides/Vips.py, but your
system gi overrides seem to be $syspygipath. system gi overrides seem to be $syspygipath.
You may need to copy this file, for example: You may need to copy this file, for example:

View File

@ -2,6 +2,7 @@ noinst_LTLIBRARIES = libconvolution.la
libconvolution_la_SOURCES = \ libconvolution_la_SOURCES = \
canny.c \ canny.c \
sobel.c \
convolution.c \ convolution.c \
pconvolution.h \ pconvolution.h \
correlation.c \ correlation.c \

View File

@ -35,6 +35,17 @@
#define DEBUG #define DEBUG
*/ */
/* TODO
* - verify that our interpolating max edge works
* - does it actually help much?
* - can skip the sqrt()
* - support other image types
* - swap atan2 for a LUT with perhaps +/- 2 or 4 bits
* - check sobel speed with separated and non-sep masks
* - add autothreshold with otsu's method
* - leave blob analysis to a separate pass
*/
#ifdef HAVE_CONFIG_H #ifdef HAVE_CONFIG_H
#include <config.h> #include <config.h>
#endif /*HAVE_CONFIG_H*/ #endif /*HAVE_CONFIG_H*/
@ -53,6 +64,7 @@ typedef struct _VipsCanny {
VipsImage *out; VipsImage *out;
double sigma; double sigma;
gboolean interpolate;
double low; double low;
double high; double high;
@ -65,50 +77,7 @@ typedef VipsOperationClass VipsCannyClass;
G_DEFINE_TYPE( VipsCanny, vips_canny, VIPS_TYPE_OPERATION ); G_DEFINE_TYPE( VipsCanny, vips_canny, VIPS_TYPE_OPERATION );
static int /* Simple 2x2 -1/+1 difference.
vips_canny_gradient_sobel( VipsImage *in, VipsImage **Gx, VipsImage **Gy )
{
VipsImage *scope;
VipsImage **t;
scope = vips_image_new();
t = (VipsImage **) vips_object_local_array( (VipsObject *) scope, 20 );
/* Separated Sobel gives Gx / Gy.
*/
t[1] = vips_image_new_matrixv( 1, 3, 1.0, 2.0, 1.0 );
t[2] = vips_image_new_matrixv( 3, 1, 1.0, 0.0, -1.0 );
vips_image_set_double( t[2], "offset", 128.0 );
if( vips_conv( in, &t[3], t[1],
"precision", VIPS_PRECISION_INTEGER,
NULL ) ||
vips_conv( t[3], Gx, t[2],
"precision", VIPS_PRECISION_INTEGER,
NULL ) ) {
g_object_unref( scope );
return( -1 );
}
t[5] = vips_image_new_matrixv( 3, 1, 1.0, 2.0, 1.0 );
t[6] = vips_image_new_matrixv( 1, 3, 1.0, 0.0, -1.0 );
vips_image_set_double( t[6], "offset", 128.0 );
if( vips_conv( in, &t[7], t[5],
"precision", VIPS_PRECISION_INTEGER,
NULL ) ||
vips_conv( t[7], Gy, t[6],
"precision", VIPS_PRECISION_INTEGER,
NULL ) ) {
g_object_unref( scope );
return( -1 );
}
g_object_unref( scope );
return( 0 );
}
/* Simple -1/+1 difference. The sobel version above does an edge
* detect as well.
*/ */
static int static int
vips_canny_gradient_simple( VipsImage *in, VipsImage **Gx, VipsImage **Gy ) vips_canny_gradient_simple( VipsImage *in, VipsImage **Gx, VipsImage **Gy )
@ -173,10 +142,14 @@ vips_canny_polar_generate( VipsRegion *or,
int y = p2[band] - 128; int y = p2[band] - 128;
int a = VIPS_DEG( atan2( x, y ) ) + 360; int a = VIPS_DEG( atan2( x, y ) ) + 360;
/* Faster than hypot() for int args. Scale down /* We should calculate
* or we'll clip on very hard edges. * 0.5 * sqrt( x * x + y * y )
* ie. length of hypot, scaled down to avoid
* clipping. We are only interested in relative
* magnitude, so we can skip the sqrt and just
* shift down 9 bits.
*/ */
q[0] = 0.5 * sqrt( x * x + y * y ); q[0] = (x * x + y * y + 256) >> 9;
q[1] = 256 * a / 360; q[1] = 256 * a / 360;
q += 2; q += 2;
@ -428,6 +401,8 @@ vips_canny_thresh_generate( VipsRegion *or,
VipsCanny *canny = (VipsCanny *) b; VipsCanny *canny = (VipsCanny *) b;
VipsRect *r = &or->valid; VipsRect *r = &or->valid;
int sz = r->width * in->im->Bands; int sz = r->width * in->im->Bands;
VipsPel low = canny->low;
VipsPel high = canny->high;
int x, y; int x, y;
@ -444,9 +419,9 @@ vips_canny_thresh_generate( VipsRegion *or,
int v; int v;
v = p[x]; v = p[x];
if( v <= canny->low ) if( v <= low )
v = 0; v = 0;
else if( v <= canny->high ) else if( v <= high )
v = 128; v = 128;
else else
v = 255; v = 255;
@ -509,10 +484,16 @@ vips_canny_build( VipsObject *object )
*/ */
if( vips_embed( in, &t[10], 1, 1, in->Xsize + 2, in->Ysize + 2, if( vips_embed( in, &t[10], 1, 1, in->Xsize + 2, in->Ysize + 2,
"extend", VIPS_EXTEND_COPY, "extend", VIPS_EXTEND_COPY,
NULL ) || NULL ) )
vips_canny_nonmax( t[10], &t[11] ) )
//vips_canny_nonmaxi( t[10], &t[11] ) )
return( -1 ); return( -1 );
if( canny->interpolate ) {
if( vips_canny_nonmaxi( t[10], &t[11] ) )
return( -1 );
}
else {
if( vips_canny_nonmax( t[10], &t[11] ) )
return( -1 );
}
in = t[11]; in = t[11];
/* Double threshold. /* Double threshold.
@ -578,6 +559,13 @@ vips_canny_class_init( VipsCannyClass *class )
G_STRUCT_OFFSET( VipsCanny, high ), G_STRUCT_OFFSET( VipsCanny, high ),
-INFINITY, INFINITY, 7.0 ); -INFINITY, INFINITY, 7.0 );
VIPS_ARG_BOOL( class, "interpolate", 13,
_( "Interpolate" ),
_( "Interpolate gradient angles" ),
VIPS_ARGUMENT_OPTIONAL_INPUT,
G_STRUCT_OFFSET( VipsCanny, interpolate ),
FALSE );
} }
static void static void

View File

@ -168,6 +168,7 @@ vips_convolution_operation_init( void )
extern int vips_spcor_get_type( void ); extern int vips_spcor_get_type( void );
extern int vips_sharpen_get_type( void ); extern int vips_sharpen_get_type( void );
extern int vips_gaussblur_get_type( void ); extern int vips_gaussblur_get_type( void );
extern int vips_sobel_get_type( void );
extern int vips_canny_get_type( void ); extern int vips_canny_get_type( void );
vips_conv_get_type(); vips_conv_get_type();
@ -182,4 +183,5 @@ vips_convolution_operation_init( void )
vips_sharpen_get_type(); vips_sharpen_get_type();
vips_gaussblur_get_type(); vips_gaussblur_get_type();
vips_canny_get_type(); vips_canny_get_type();
vips_sobel_get_type();
} }

156
libvips/convolution/sobel.c Normal file
View File

@ -0,0 +1,156 @@
/* Sobel edge detector
*
* 2/2/18
* - from vips_sobel()
*/
/*
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
*/
/*
#define DEBUG
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif /*HAVE_CONFIG_H*/
#include <vips/intl.h>
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <vips/vips.h>
/* TODO
* - check sobel speed with separated and non-sep masks
* - add an 8-bit sobel path, with offset 128 and code for abs() + abs()
*/
typedef struct _VipsSobel {
VipsOperation parent_instance;
VipsImage *in;
VipsImage *out;
} VipsSobel;
typedef VipsOperationClass VipsSobelClass;
G_DEFINE_TYPE( VipsSobel, vips_sobel, VIPS_TYPE_OPERATION );
static int
vips_sobel_build( VipsObject *object )
{
VipsSobel *sobel = (VipsSobel *) object;
VipsImage **t = (VipsImage **) vips_object_local_array( object, 20 );
t[1] = vips_image_new_matrixv( 1, 3, 1.0, 2.0, 1.0 );
t[2] = vips_image_new_matrixv( 3, 1, 1.0, 0.0, -1.0 );
if( vips_conv( sobel->in, &t[3], t[1], NULL ) ||
vips_conv( t[3], &t[4], t[2], NULL ) )
return( -1 );
t[5] = vips_image_new_matrixv( 3, 1, 1.0, 2.0, 1.0 );
t[6] = vips_image_new_matrixv( 1, 3, 1.0, 0.0, -1.0 );
if( vips_conv( sobel->in, &t[7], t[5], NULL ) ||
vips_conv( t[7], &t[8], t[6], NULL ) )
return( -1 );
if( vips_abs( t[4], &t[9], NULL ) ||
vips_abs( t[8], &t[10], NULL ) ||
vips_add( t[9], t[10], &t[11], NULL ) ||
vips_cast( t[11], &t[12], sobel->in->BandFmt, NULL ) )
return( -1 );
g_object_set( object, "out", vips_image_new(), NULL );
if( vips_image_write( t[12], sobel->out ) )
return( -1 );
return( 0 );
}
static void
vips_sobel_class_init( VipsSobelClass *class )
{
GObjectClass *gobject_class = G_OBJECT_CLASS( class );
VipsObjectClass *object_class = (VipsObjectClass *) class;
VipsOperationClass *operation_class = VIPS_OPERATION_CLASS( class );
gobject_class->set_property = vips_object_set_property;
gobject_class->get_property = vips_object_get_property;
object_class->nickname = "sobel";
object_class->description = _( "Sobel edge detector" );
object_class->build = vips_sobel_build;
operation_class->flags = VIPS_OPERATION_SEQUENTIAL;
VIPS_ARG_IMAGE( class, "in", 1,
_( "Input" ),
_( "Input image" ),
VIPS_ARGUMENT_REQUIRED_INPUT,
G_STRUCT_OFFSET( VipsSobel, in ) );
VIPS_ARG_IMAGE( class, "out", 2,
_( "Output" ),
_( "Output image" ),
VIPS_ARGUMENT_REQUIRED_OUTPUT,
G_STRUCT_OFFSET( VipsSobel, out ) );
}
static void
vips_sobel_init( VipsSobel *sobel )
{
}
/**
* vips_sobel: (method)
* @in: input image
* @out: (out): output image
* @...: %NULL-terminated list of optional named arguments
*
* Simple Sobel edge detector.
*
* See also: vips_canny().
*
* Returns: 0 on success, -1 on error.
*/
int
vips_sobel( VipsImage *in, VipsImage **out, ... )
{
va_list ap;
int result;
va_start( ap, out );
result = vips_call_split( "sobel", ap, in, out );
va_end( ap );
return( result );
}

View File

@ -157,6 +157,11 @@ vips_gaussmat_build( VipsObject *object )
} }
} }
/* Make sure we can't make sum == 0: it'd certainly cause /0 later.
*/
if( sum == 0 )
sum = 1;
vips_image_set_double( create->out, "scale", sum ); vips_image_set_double( create->out, "scale", sum );
vips_image_set_double( create->out, "offset", 0.0 ); vips_image_set_double( create->out, "offset", 0.0 );

View File

@ -70,6 +70,8 @@ int vips_spcor( VipsImage *in, VipsImage *ref, VipsImage **out, ... )
int vips_fastcor( VipsImage *in, VipsImage *ref, VipsImage **out, ... ) int vips_fastcor( VipsImage *in, VipsImage *ref, VipsImage **out, ... )
__attribute__((sentinel)); __attribute__((sentinel));
int vips_sobel( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
int vips_canny( VipsImage *in, VipsImage **out, ... ) int vips_canny( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel)); __attribute__((sentinel));