parent
b07a7c60b7
commit
4a59fa652c
|
@ -5,6 +5,7 @@
|
|||
- hough_line scales width to 0 - 180, not 0 - 360
|
||||
- hough_line is 4x faster
|
||||
- hough_circle is 2x faster
|
||||
- add vips_sobel() and vips_canny() edge detectors
|
||||
|
||||
12/2/18 started 8.6.3
|
||||
- use pkg-config to find libjpeg, if we can
|
||||
|
|
|
@ -1280,8 +1280,8 @@ if test x"$found_introspection" = xyes -a "$VIPS_LIBDIR/girepository-1.0" != "$I
|
|||
;; # ignore for homebrew
|
||||
*)
|
||||
AC_MSG_RESULT([dnl
|
||||
Vips-8.0.typelib will install to $VIPS_LIBDIR/girepository-1.0, but your
|
||||
system repository seems to be $INTROSPECTION_TYPELIBDIR.
|
||||
Vips-8.0.typelib will be installed to $VIPS_LIBDIR/girepository-1.0, but
|
||||
your system repository seems to be $INTROSPECTION_TYPELIBDIR.
|
||||
You may need to add this directory to your typelib path, for example:
|
||||
|
||||
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
|
||||
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
|
||||
system gi overrides seem to be $syspygipath.
|
||||
You may need to copy this file, for example:
|
||||
|
|
|
@ -2,6 +2,7 @@ noinst_LTLIBRARIES = libconvolution.la
|
|||
|
||||
libconvolution_la_SOURCES = \
|
||||
canny.c \
|
||||
sobel.c \
|
||||
convolution.c \
|
||||
pconvolution.h \
|
||||
correlation.c \
|
||||
|
|
|
@ -35,6 +35,17 @@
|
|||
#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
|
||||
#include <config.h>
|
||||
#endif /*HAVE_CONFIG_H*/
|
||||
|
@ -53,6 +64,7 @@ typedef struct _VipsCanny {
|
|||
VipsImage *out;
|
||||
|
||||
double sigma;
|
||||
gboolean interpolate;
|
||||
double low;
|
||||
double high;
|
||||
|
||||
|
@ -65,50 +77,7 @@ typedef VipsOperationClass VipsCannyClass;
|
|||
|
||||
G_DEFINE_TYPE( VipsCanny, vips_canny, VIPS_TYPE_OPERATION );
|
||||
|
||||
static int
|
||||
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.
|
||||
/* Simple 2x2 -1/+1 difference.
|
||||
*/
|
||||
static int
|
||||
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 a = VIPS_DEG( atan2( x, y ) ) + 360;
|
||||
|
||||
/* Faster than hypot() for int args. Scale down
|
||||
* or we'll clip on very hard edges.
|
||||
/* We should calculate
|
||||
* 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 += 2;
|
||||
|
@ -428,6 +401,8 @@ vips_canny_thresh_generate( VipsRegion *or,
|
|||
VipsCanny *canny = (VipsCanny *) b;
|
||||
VipsRect *r = &or->valid;
|
||||
int sz = r->width * in->im->Bands;
|
||||
VipsPel low = canny->low;
|
||||
VipsPel high = canny->high;
|
||||
|
||||
int x, y;
|
||||
|
||||
|
@ -444,9 +419,9 @@ vips_canny_thresh_generate( VipsRegion *or,
|
|||
int v;
|
||||
|
||||
v = p[x];
|
||||
if( v <= canny->low )
|
||||
if( v <= low )
|
||||
v = 0;
|
||||
else if( v <= canny->high )
|
||||
else if( v <= high )
|
||||
v = 128;
|
||||
else
|
||||
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,
|
||||
"extend", VIPS_EXTEND_COPY,
|
||||
NULL ) ||
|
||||
vips_canny_nonmax( t[10], &t[11] ) )
|
||||
//vips_canny_nonmaxi( t[10], &t[11] ) )
|
||||
NULL ) )
|
||||
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];
|
||||
|
||||
/* Double threshold.
|
||||
|
@ -578,6 +559,13 @@ vips_canny_class_init( VipsCannyClass *class )
|
|||
G_STRUCT_OFFSET( VipsCanny, high ),
|
||||
-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
|
||||
|
|
|
@ -168,6 +168,7 @@ vips_convolution_operation_init( void )
|
|||
extern int vips_spcor_get_type( void );
|
||||
extern int vips_sharpen_get_type( void );
|
||||
extern int vips_gaussblur_get_type( void );
|
||||
extern int vips_sobel_get_type( void );
|
||||
extern int vips_canny_get_type( void );
|
||||
|
||||
vips_conv_get_type();
|
||||
|
@ -182,4 +183,5 @@ vips_convolution_operation_init( void )
|
|||
vips_sharpen_get_type();
|
||||
vips_gaussblur_get_type();
|
||||
vips_canny_get_type();
|
||||
vips_sobel_get_type();
|
||||
}
|
||||
|
|
|
@ -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 );
|
||||
}
|
|
@ -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, "offset", 0.0 );
|
||||
|
||||
|
|
|
@ -70,6 +70,8 @@ int vips_spcor( VipsImage *in, VipsImage *ref, VipsImage **out, ... )
|
|||
int vips_fastcor( VipsImage *in, VipsImage *ref, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
int vips_sobel( VipsImage *in, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
int vips_canny( VipsImage *in, VipsImage **out, ... )
|
||||
__attribute__((sentinel));
|
||||
|
||||
|
|
Loading…
Reference in New Issue