Merge branch 'add-canny' of github.com:jcupitt/libvips into add-canny

This commit is contained in:
John Cupitt 2018-02-27 12:08:15 +00:00
commit d5aabaa23e
9 changed files with 254 additions and 78 deletions

View File

@ -5,6 +5,10 @@
- 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
5/1/18 started 8.6.2
- vips_sink_screen() keeps a ref to the input image ... stops a rare race

View File

@ -1034,23 +1034,41 @@ if test x"$with_png" != "xno"; then
)
fi
FIND_JPEG(
[with_jpeg=yes
# look for libjpeg with pkg-config ... fall back to our tester
AC_ARG_WITH([jpeg],
AS_HELP_STRING([--without-jpeg], [build without libjpeg (default: test)]))
if test x"$with_jpeg" != x"no"; then
PKG_CHECK_MODULES(JPEG, libjpeg,
[AC_DEFINE(HAVE_JPEG,1,[define if you have libjpeg installed.])
with_jpeg="yes (pkg-config)"
PACKAGES_USED="$PACKAGES_USED libjpeg"
],
[FIND_JPEG(
[with_jpeg="yes (found by search)"
EXTRA_LIBS_USED="$EXTRA_LIBS_USED -ljpeg"
],
[AC_MSG_WARN([libjpeg not found; disabling JPEG support])
with_jpeg=no
]
)
]
)
fi
# JPEG extension parameters available in libjpeg-turbo >=1.5.0, mozjpeg >=3.0
# features like trellis quant are exposed as extension parameters ...
# mozjpeg 3.2 and later have #define JPEG_C_PARAM_SUPPORTED, but we must
# work with earlier versions
if test x"$with_jpeg" != "xno"; then
save_LIBS="$LIBS"
LIBS="$LIBS $JPEG_LIBS"
save_CFLAGS="$CFLAGS"
LIBS="$JPEG_LIBS $LIBS"
CFLAGS="$JPEG_INCLUDES $CFLAGS"
AC_CHECK_FUNCS(jpeg_c_bool_param_supported,
AC_DEFINE(HAVE_JPEG_EXT_PARAMS,1,
[define if your libjpeg has extension parameters.]))
LIBS="$save_LIBS"
CFLAGS="$save_CFLAGS"
fi
# libexif
@ -1262,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"
@ -1295,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:

View File

@ -10,18 +10,18 @@
</refnamediv>
There are full libvips bindings for quite a few environments now: C, C++,
command-line, Ruby, PHP, Python and JavaScript (node).
command-line, Ruby, PHP, Lua, Python and JavaScript (node).
This chapter runs through the four main styles that have been found to work
well. If you want to write a new binding, one of these should be close
to what you need.
# C API
# Don't bind the top-level C API
The libvips C API (vips_add() and so on) is very inconvenient to use from other
languages due to its heavy use of varargs.
The libvips C API (vips_add() and so on) is very inconvenient and dangerous
to use from other languages due to its heavy use of varargs.
It's much better to use the layer below. This lower layer is structured as:
It's much better to use the layer below. This lower layer is structured as
create operator, set parameters, execute, extract results. For example, you can
execute vips_invert() like this:
@ -113,10 +113,10 @@ main( int argc, char **argv )
}
```
libvips has a couple of extra things to let you fetch the arguments and types
of an operator. Use vips_lib.vips_argument_map() to loop over all the arguments
of an operator, and vips_object_get_argument() to fetch the type and flags
of a specific argument.
libvips has a couple of extra things to let you examine the arguments and
types of an operator at runtime. Use vips_lib.vips_argument_map() to loop
over all the arguments of an operator, and vips_object_get_argument()
to fetch the type and flags of a specific argument.
Use vips_operation_get_flags() to get general information about an operator.
@ -142,7 +142,7 @@ VImage VImage::invert( VOption *options )
}
```
So from C++ you can call any libvips operator, though without type-safety, with
So from C++ you can call any libvips operator (though without type-safety) with
`VImage::call()`, or use the member functions on `VImage` to get type-safe
calls for at least the required operator arguments.
@ -167,7 +167,7 @@ PHP does not have FFI, unfortunately, so for this language a small native
module implements the general `vips_call()` function for PHP language types,
and a larger pure PHP layer makes it convenient to use.
# `gobject-introspection`
# gobject-introspection
The C source code to libvips has been marked up with special comments
describing the interface in a standard way. These comments are read by

View File

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

View File

@ -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

View File

@ -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();
}

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, "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, ... )
__attribute__((sentinel));
int vips_sobel( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));
int vips_canny( VipsImage *in, VipsImage **out, ... )
__attribute__((sentinel));