Merge branch 'add-canny' of github.com:jcupitt/libvips into add-canny
This commit is contained in:
commit
d5aabaa23e
@ -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
|
||||
|
32
configure.ac
32
configure.ac
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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();
|
||||
}
|
||||
|
156
libvips/convolution/sobel.c
Normal file
156
libvips/convolution/sobel.c
Normal 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 );
|
||||
}
|
@ -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
Block a user