From 7752eb89e0c527dbf81b90052bd4d73e3d235eef Mon Sep 17 00:00:00 2001 From: John Cupitt Date: Wed, 26 Mar 2014 20:33:31 +0000 Subject: [PATCH] add vips_draw_add() and start vips_hough_circle() --- ChangeLog | 1 + TODO | 5 +- libvips/arithmetic/hough.c | 2 +- libvips/arithmetic/hough_circle.c | 272 ++++++++++++++++++++++++++++++ libvips/arithmetic/hough_line.c | 2 +- libvips/draw/Makefile.am | 1 + libvips/draw/draw.c | 2 + libvips/draw/draw_add.c | 238 ++++++++++++++++++++++++++ libvips/draw/draw_image.c | 2 +- libvips/include/vips/draw.h | 2 + 10 files changed, 523 insertions(+), 4 deletions(-) create mode 100644 libvips/arithmetic/hough_circle.c create mode 100644 libvips/draw/draw_add.c diff --git a/ChangeLog b/ChangeLog index 26fe7fef..21053e8d 100644 --- a/ChangeLog +++ b/ChangeLog @@ -15,6 +15,7 @@ - fix to vipsthumbnail --crop, thanks Alessandro - add vips_sum() - add vips_hough base class and vips_hough_line() +- add vips_draw_add() 6/3/14 started 7.38.6 - grey ramp minimum was wrong diff --git a/TODO b/TODO index 2ed0e5e3..29d1fcbe 100644 --- a/TODO +++ b/TODO @@ -1,4 +1,7 @@ -- vipsthumbnail needs to know about more exif orientation tags +- need call operation along a circle, see line draw + +- use with vips_draw_add() to make amask image for each radius for + vips_hough_circle() diff --git a/libvips/arithmetic/hough.c b/libvips/arithmetic/hough.c index 2ef17e64..a435fc59 100644 --- a/libvips/arithmetic/hough.c +++ b/libvips/arithmetic/hough.c @@ -204,7 +204,7 @@ vips_hough_class_init( VipsHoughClass *class ) G_STRUCT_OFFSET( VipsHough, width ), 1, 100000, 256 ); - VIPS_ARG_INT( class, "height", 110, + VIPS_ARG_INT( class, "height", 111, _( "Height" ), _( "Vertical size of parameter space" ), VIPS_ARGUMENT_OPTIONAL_INPUT, diff --git a/libvips/arithmetic/hough_circle.c b/libvips/arithmetic/hough_circle.c new file mode 100644 index 00000000..5a32fa53 --- /dev/null +++ b/libvips/arithmetic/hough_circle.c @@ -0,0 +1,272 @@ +/* hough transform for circles + * + * 7/3/14 + * - from hough_line.c + */ + +/* + + 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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include + +#include + +#include "statistic.h" +#include "hough.h" + +typedef struct _VipsHoughCircle { + VipsHough parent_instance; + + int min_radius; + int max_radius; + int bands; + +} VipsHoughCircle; + +typedef VipsHoughClass VipsHoughCircleClass; + +G_DEFINE_TYPE( VipsHoughCircle, vips_hough_circle, VIPS_TYPE_HOUGH ); + +static int +vips_hough_circle_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsHough *hough = (VipsHough *) object; + VipsHoughCircle *hough_circle = (VipsHoughCircle *) object; + + if( !vips_object_argument_isset( object, "bands" ) ) + hough_circle->bands = 1 + hough_circle->max_radius - + hough_circle->min_radius; + + if( hough_circle->min_radius > hough_circle->max_radius ) { + vips_error( class->nickname, + "%s", _( "parameters out of range" ) ); + return( NULL ); + } + + if( VIPS_OBJECT_CLASS( vips_hough_circle_parent_class )-> + build( object ) ) + return( -1 ); + + return( 0 ); +} + +static int +vips_hough_circle_init_accumulator( VipsHough *hough, VipsImage *accumulator ) +{ + VipsHoughCircle *hough_circle = (VipsHoughCircle *) hough; + + vips_image_init_fields( accumulator, + hough->width, hough->height, hough_circle->bands, + VIPS_FORMAT_UINT, VIPS_CODING_NONE, + VIPS_INTERPRETATION_MATRIX, + 1.0, 1.0 ); + + return( 0 ); +} + +static inline void +vips_hough_circle_pel( VipsImage *accumulator, int band, int x, int y ) +{ + VIPS_IMAGE_ADDR( accumulator, x, y )[band] += 1; +} + +static inline void +vips_hough_circle_pel_clip( VipsImage *accumulator, int band, int x, int y ) +{ + if( x >= 0 && + x < accumulator->Xsize && + y >= 0 && + y < accumulator->Ysize ) + VIPS_IMAGE_ADDR( accumulator, x, y )[band] += 1; +} + +static void +vips_hough_circle_octants( VipsImage *accumulator, + gboolean noclip, int band, + int cx, int cy, int x, int y ) +{ + if( noclip ) { + vips_hough_circle_pel( accumulator, band, cx + y, cy - x ); + vips_hough_circle_pel( accumulator, band, cx + y, cy + x ); + vips_hough_circle_pel( accumulator, band, cx - y, cy - x ); + vips_hough_circle_pel( accumulator, band, cx - y, cy + x ); + vips_hough_circle_pel( accumulator, band, cx + x, cy - y ); + vips_hough_circle_pel( accumulator, band, cx + x, cy + y ); + vips_hough_circle_pel( accumulator, band, cx - x, cy - y ); + vips_hough_circle_pel( accumulator, band, cx - x, cy + y ); + } + else { + vips_hough_circle_pel_clip( accumulator, band, cx + y, cy - x ); + vips_hough_circle_pel_clip( accumulator, band, cx + y, cy + x ); + vips_hough_circle_pel_clip( accumulator, band, cx - y, cy - x ); + vips_hough_circle_pel_clip( accumulator, band, cx - y, cy + x ); + vips_hough_circle_pel_clip( accumulator, band, cx + x, cy - y ); + vips_hough_circle_pel_clip( accumulator, band, cx + x, cy + y ); + vips_hough_circle_pel_clip( accumulator, band, cx - x, cy - y ); + vips_hough_circle_pel_clip( accumulator, band, cx - x, cy + y ); + } +} + +static void +vips_hough_circle_draw( VipsHoughCircle *hough_circle, + VipsImage *accumulator, int cx, int cy, int radius ) +{ + int bands = hough_circle->bands; + int band = bands * (radius - hough_circle->min_band) / bands; + + gboolean noclip; + int x, y, d; + + noclip = cx - radius >= 0 && + cx + radius < accumulator->Xsize && + cy - radius >= 0 && + cy + radius < accumulator->Ysize; + + y = radius; + d = 3 - 2 * radius; + + for( x = 0; x < y; x++ ) { + vips_draw_circle_octants( accumulator, noclip, band, x, y ); + + if( d < 0 ) + d += 4 * x + 6; + else { + d += 4 * (x - y) + 10; + y--; + } + } + + if( x == y ) + vips_draw_circle_octants( accumulator, noclip, band, x, y ); +} + +/* Cast votes for all possible circles passing through x, y. + */ +static void +vips_hough_circle_vote( VipsHough *hough, VipsImage *accumulator, int x, int y ) +{ + VipsHoughCircle *hough_circle = (VipsHoughCircle *) hough; + VipsStatistic *statistic = (VipsStatistic *) hough; + double xd = (double) x / statistic->ready->Xsize; + double yd = (double) y / statistic->ready->Ysize; + + int r; + + for( i = 0; i < hough->width; i++ ) { + int i90 = (i + hough->width / 4) % hough->width; + double r = xd * hough_circle->sin[i90] + yd * hough_circle->sin[i]; + int ri = hough->height * r; + + if( ri >= 0 && + ri < hough->height ) + *VIPS_IMAGE_ADDR( accumulator, i, ri ) += 1; + } +} + +static void +vips_hough_circle_class_init( VipsHoughClass *class ) +{ + GObjectClass *gobject_class = (GObjectClass *) class; + VipsObjectClass *object_class = (VipsObjectClass *) class; + VipsHoughClass *hclass = (VipsHoughClass *) class; + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + object_class->nickname = "hough_circle"; + object_class->description = _( "find hough circle transform" ); + object_class->build = vips_hough_circle_build; + + hclass->init_accumulator = vips_hough_circle_init_accumulator; + hclass->vote = vips_hough_circle_vote; + + VIPS_ARG_INT( class, "bands", 119, + _( "Bands" ), + _( "Number of bands in parameter space" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsHoughCircle, bands ), + 1, 100000, 10 ); + + VIPS_ARG_INT( class, "min_radius", 120, + _( "Min radius" ), + _( "Smallest radius to search for" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsHoughCircle, min_radius ), + 1, 100000, 10 ); + + VIPS_ARG_INT( class, "max_radius", 121, + _( "Max radius" ), + _( "Largest radius to search for" ), + VIPS_ARGUMENT_OPTIONAL_INPUT, + G_STRUCT_OFFSET( VipsHoughCircle, max_radius ), + 1, 100000, 20 ); + +} + +static void +vips_hough_circle_init( VipsHoughCircle *hough_circle ) +{ + hough_circle->min_radius = 10; + hough_circle->max_radius = 20; + hough_circle->bands = 10; +} + +/** + * vips_hough_circle: + * @in: input image + * @out: output image + * @...: %NULL-terminated list of optional named arguments + * + * Optional arguments: + * + * @width: horizontal size of parameter space + * @height: vertical size of parameter space + * @min_radius: smallest radius to search for + * @max_radius: largest radius to search for + * + * See also: + * + * Returns: 0 on success, -1 on error + */ +int +vips_hough_circle( VipsImage *in, VipsImage **out, ... ) +{ + va_list ap; + int result; + + va_start( ap, out ); + result = vips_call_split( "hough_circle", ap, in, out ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/arithmetic/hough_line.c b/libvips/arithmetic/hough_line.c index 8fce2da0..07c2a20c 100644 --- a/libvips/arithmetic/hough_line.c +++ b/libvips/arithmetic/hough_line.c @@ -1,4 +1,4 @@ -/* hough transform +/* hough transform for lines * * 7/3/14 * - from hist_find.c diff --git a/libvips/draw/Makefile.am b/libvips/draw/Makefile.am index 20f8ece2..fe68356f 100644 --- a/libvips/draw/Makefile.am +++ b/libvips/draw/Makefile.am @@ -9,6 +9,7 @@ libdraw_la_SOURCES = \ draw_flood.c \ draw_mask.c \ draw_image.c \ + draw_add.c \ draw_rect.c \ draw_line.h \ draw_line.c \ diff --git a/libvips/draw/draw.c b/libvips/draw/draw.c index e127cdf6..3b828ea8 100644 --- a/libvips/draw/draw.c +++ b/libvips/draw/draw.c @@ -117,6 +117,7 @@ vips_draw_operation_init( void ) { extern GType vips_draw_rect_get_type( void ); extern GType vips_draw_image_get_type( void ); + extern GType vips_draw_add_get_type( void ); extern GType vips_draw_mask_get_type( void ); extern GType vips_draw_line_get_type( void ); extern GType vips_draw_line_mask_get_type( void ); @@ -127,6 +128,7 @@ vips_draw_operation_init( void ) vips_draw_rect_get_type(); vips_draw_image_get_type(); + vips_draw_add_get_type(); vips_draw_mask_get_type(); vips_draw_line_get_type(); vips_draw_line_mask_get_type(); diff --git a/libvips/draw/draw_add.c b/libvips/draw/draw_add.c new file mode 100644 index 00000000..1c8769b3 --- /dev/null +++ b/libvips/draw/draw_add.c @@ -0,0 +1,238 @@ +/* in-place add, ie. a = a + b + * + * 26/3/14 + * - from draw_add.c + */ + +/* + + 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 +#endif /*HAVE_CONFIG_H*/ +#include + +#include +#include +#include + +#include +#include + +#include "pdraw.h" + +typedef struct _VipsDrawAdd { + VipsDraw parent_object; + + /* Parameters. + */ + VipsImage *sub; + int x; + int y; + +} VipsDrawAdd; + +typedef struct _VipsDrawAddClass { + VipsDrawClass parent_class; + +} VipsDrawAddClass; + +G_DEFINE_TYPE( VipsDrawAdd, vips_draw_add, VIPS_TYPE_DRAW ); + +#define LOOP( TYPE ) { \ + TYPE * restrict pt = (TYPE *) p; \ + TYPE * restrict qt = (TYPE *) q; \ + \ + for( x = 0; x < sz; x++ ) \ + qt[x] += pt[x]; \ +} + +static int +vips_draw_add_build( VipsObject *object ) +{ + VipsObjectClass *class = VIPS_OBJECT_GET_CLASS( object ); + VipsDraw *draw = VIPS_DRAW( object ); + VipsDrawAdd *image = (VipsDrawAdd *) object; + VipsImage **t = (VipsImage **) vips_object_local_array( object, 3 ); + + VipsImage *im; + VipsRect image_rect; + VipsRect sub_rect; + VipsRect clip_rect; + + if( VIPS_OBJECT_CLASS( vips_draw_add_parent_class )->build( object ) ) + return( -1 ); + + if( vips_check_uncoded( class->nickname, draw->image ) || + vips_check_coding_same( class->nickname, + draw->image, image->sub ) || + vips_check_bands_1orn_unary( class->nickname, + image->sub, draw->image->Bands ) ) + return( -1 ); + + /* Cast sub to match main in bands and format. + */ + im = image->sub; + if( vips__bandup( class->nickname, + im, &t[0], draw->image->Bands ) || + vips_cast( t[0], &t[1], draw->image->BandFmt, NULL ) ) + return( -1 ); + im = t[1]; + + /* Make rects for main and sub and clip. + */ + image_rect.left = 0; + image_rect.top = 0; + image_rect.width = draw->image->Xsize; + image_rect.height = draw->image->Ysize; + sub_rect.left = image->x; + sub_rect.top = image->y; + sub_rect.width = im->Xsize; + sub_rect.height = im->Ysize; + vips_rect_intersectrect( &image_rect, &sub_rect, &clip_rect ); + + if( !vips_rect_isempty( &clip_rect ) ) { + /* Complex just doubles the size. + */ + const int sz = clip_rect.width * im->Bands * + (vips_band_format_iscomplex( im->BandFmt ) ? + 2 : 1); + + VipsPel *p, *q; + int x, y; + + if( vips_image_wio_input( im ) ) + return( -1 ); + + p = VIPS_IMAGE_ADDR( im, + clip_rect.left - image->x, clip_rect.top - image->y ); + q = VIPS_IMAGE_ADDR( draw->image, + clip_rect.left, clip_rect.top ); + + for( y = 0; y < clip_rect.height; y++ ) { + switch( im->BandFmt ) { + case VIPS_FORMAT_UCHAR: + LOOP( unsigned char ); break; + case VIPS_FORMAT_CHAR: + LOOP( signed char ); break; + case VIPS_FORMAT_USHORT: + LOOP( unsigned short ); break; + case VIPS_FORMAT_SHORT: + LOOP( signed short ); break; + case VIPS_FORMAT_UINT: + LOOP( unsigned int ); break; + case VIPS_FORMAT_INT: + LOOP( signed int ); break; + + case VIPS_FORMAT_FLOAT: + case VIPS_FORMAT_COMPLEX: + LOOP( float ); break; + + case VIPS_FORMAT_DOUBLE: + case VIPS_FORMAT_DPCOMPLEX: + LOOP( double ); break; + + default: + g_assert( 0 ); + } + + p += VIPS_IMAGE_SIZEOF_LINE( im ); + q += VIPS_IMAGE_SIZEOF_LINE( draw->image ); + } + } + + return( 0 ); +} + +static void +vips_draw_add_class_init( VipsDrawAddClass *class ) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS( class ); + VipsObjectClass *vobject_class = VIPS_OBJECT_CLASS( class ); + + gobject_class->set_property = vips_object_set_property; + gobject_class->get_property = vips_object_get_property; + + vobject_class->nickname = "draw_add"; + vobject_class->description = _( "add an image into another image" ); + vobject_class->build = vips_draw_add_build; + + VIPS_ARG_IMAGE( class, "sub", 5, + _( "Sub-image" ), + _( "Sub-image to add to main image" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsDrawAdd, sub ) ); + + VIPS_ARG_INT( class, "x", 6, + _( "x" ), + _( "Add image here" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsDrawAdd, x ), + -1000000000, 1000000000, 0 ); + + VIPS_ARG_INT( class, "y", 7, + _( "y" ), + _( "Add image here" ), + VIPS_ARGUMENT_REQUIRED_INPUT, + G_STRUCT_OFFSET( VipsDrawAdd, y ), + -1000000000, 1000000000, 0 ); + +} + +static void +vips_draw_add_init( VipsDrawAdd *draw_add ) +{ +} + +/** + * vips_draw_add: + * @image: image to add to + * @sub: image to add from + * @x: add @sub here + * @y: add @sub here + * + * Add @sub to @image at position @x, @y. The two images must be uncoded. + * If @sub has 1 band, the bands will be duplicated to match the + * number of bands in @image. @sub will be converted to @image's format, see + * vips_cast(). + * + * See also: vips_draw_mask(), vips_draw_image(), vips_insert(). + * + * Returns: 0 on success, or -1 on error. + */ +int +vips_draw_add( VipsImage *image, VipsImage *sub, int x, int y, ... ) +{ + va_list ap; + int result; + + va_start( ap, y ); + result = vips_call_split( "draw_add", ap, image, sub, x, y ); + va_end( ap ); + + return( result ); +} diff --git a/libvips/draw/draw_image.c b/libvips/draw/draw_image.c index d095e03b..192fa91d 100644 --- a/libvips/draw/draw_image.c +++ b/libvips/draw/draw_image.c @@ -197,7 +197,7 @@ vips_draw_image_init( VipsDrawImage *draw_image ) } /** - * vips_draw_image1: + * vips_draw_image: * @image: image to draw on * @sub: image to paint * @x: draw @sub here diff --git a/libvips/include/vips/draw.h b/libvips/include/vips/draw.h index 4c595d16..1892ea8f 100644 --- a/libvips/include/vips/draw.h +++ b/libvips/include/vips/draw.h @@ -51,6 +51,8 @@ int vips_draw_point1( VipsImage *image, double ink, int x, int y, ... ) int vips_draw_image( VipsImage *image, VipsImage *sub, int x, int y, ... ) __attribute__((sentinel)); +int vips_draw_add( VipsImage *image, VipsImage *sub, int x, int y, ... ) + __attribute__((sentinel)); int vips_draw_mask( VipsImage *image, double *ink, int n, VipsImage *mask, int x, int y, ... )