From: Tony Cook Date: Sat, 21 Mar 2020 00:47:31 +0000 (+1100) Subject: Leolo's guassian2 patch X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/3d3b6bed7c6ca5efb92cc8b04c867b73890f3cfa?hp=bcb589bc1d41ba474422eb114d605a7202de66e5 Leolo's guassian2 patch https://rt.cpan.org/Ticket/Display.html?id=129769 --- diff --git a/Imager.pm b/Imager.pm index 343cc5e1..e5edb916 100644 --- a/Imager.pm +++ b/Imager.pm @@ -279,6 +279,11 @@ BEGIN { defaults => { }, callsub => sub { my %hsh = @_; i_gaussian($hsh{image}, $hsh{stddev}); }, }; + $filters{gaussian2} = { + callseq => [ 'image', 'stddevX', 'stddevY' ], + defaults => { }, + callsub => sub { my %hsh = @_; i_gaussian2($hsh{image}, $hsh{stddevX}, $hsh{stddevY}); }, + }; $filters{mosaic} = { callseq => [ qw(image size) ], diff --git a/Imager.xs b/Imager.xs index 7b6433df..4bd58b61 100644 --- a/Imager.xs +++ b/Imager.xs @@ -2251,9 +2251,15 @@ i_matrix_transform(im, xsize, ysize, matrix_av, ...) RETVAL undef_int -i_gaussian(im,stdev) +i_gaussian(im,stddev) Imager::ImgRaw im - im_double stdev + im_double stddev + +undef_int +i_gaussian2(im,stddevX,stddevY) + Imager::ImgRaw im + im_double stddevX + im_double stddevY void i_unsharp_mask(im,stdev,scale) diff --git a/gaussian.im b/gaussian.im index 7e937692..6ab7eca7 100644 --- a/gaussian.im +++ b/gaussian.im @@ -19,28 +19,22 @@ gauss(int x, double std) { int i_gaussian(i_img *im, double stddev) { - int i, c, ch; - i_img_dim x, y; - double pc; - double *coeff; - double res[MAXCHANNELS]; - i_img *timg; - int radius, diameter; - dIMCTXim(im); + return i_gaussian2( im, stddev, stddev ); +} - im_log((aIMCTX, 1,"i_gaussian(im %p, stdev %.2f)\n",im,stddev)); - i_clear_error(); +typedef struct s_gauss_coeff { + int diameter; + int radius; + double *coeff; +} t_gauss_coeff; - if (stddev <= 0) { - i_push_error(0, "stddev must be positive"); - return 0; - } - /* totally silly cutoff */ - if (stddev > 1000) { - stddev = 1000; - } - timg = i_sametype(im, im->xsize, im->ysize); +static t_gauss_coeff *build_coeff( i_img *im, double stddev ) { + double *coeff = NULL; + double pc; + int radius, diameter, i; + t_gauss_coeff *ret = mymalloc(sizeof(struct s_gauss_coeff)); + ret->coeff = NULL; if (im->bits <= 8) radius = ceil(2 * stddev); @@ -53,25 +47,98 @@ i_gaussian(i_img *im, double stddev) { for(i=0;i <= radius;i++) coeff[radius + i]=coeff[radius - i]=gauss(i, stddev); - pc=0; + pc=0.0; for(i=0; i < diameter; i++) pc+=coeff[i]; - for(i=0;i < diameter;i++) + for(i=0;i < diameter;i++) { coeff[i] /= pc; + // im_log((aIMCTX, 1, "i_gaussian2 Y i=%i coeff=%.2f\n", i, coeff[i] )); + } + + ret->diameter = diameter; + ret->radius = radius; + ret->coeff = coeff; + return ret; +} + +static void free_coeff(t_gauss_coeff *co ) { + + if( co->coeff != NULL ) + myfree( co->coeff ); + myfree( co ); +} + +#define img_copy(dest, src) i_copyto( (dest), (src), 0,0, (src)->xsize,(src)->ysize, 0,0); + + + +int +i_gaussian2(i_img *im, double stddevX, double stddevY) { + int c, ch; + i_img_dim x, y; + double pc; + t_gauss_coeff *co = NULL; + double res[MAXCHANNELS]; + i_img *timg; + dIMCTXim(im); + + im_log((aIMCTX, 1,"i_gaussian2(im %p, stddev %.2f,%.2f)\n",im,stddevX,stddevY)); + i_clear_error(); + + if (stddevX < 0) { + i_push_error(0, "stddevX must be positive"); + return 0; + } + if (stddevY < 0) { + i_push_error(0, "stddevY must be positive"); + return 0; + } + if( stddevX == stddevY && stddevY == 0 ) { + i_push_error(0, "stddevX or stddevY must be positive"); + return 0; + } + + + /* totally silly cutoff */ + if (stddevX > 1000) { + stddevX = 1000; + } + if (stddevY > 1000) { + stddevY = 1000; + } + + timg = i_sametype(im, im->xsize, im->ysize); + + if( stddevX > 0 ) { + /* Build Y coefficient matrix */ + co = build_coeff( im, stddevX ); + im_log((aIMCTX, 1, "i_gaussian2 X coeff radius=%i diamter=%i coeff=%p\n", co->radius, co->diameter, co->coeff)); + } + else { + im_log((aIMCTX, 1, "i_gaussian2 X coeff is unity\n")); + } #code im->bits <= 8 IM_COLOR rcolor; + i_img *yin; + i_img *yout; + + if( stddevX > 0 ) { + /******************/ + /* Process X blur */ + im_log((aIMCTX, 1, "i_gaussian2 X blur from im=%p to timg=%p\n", im, timg)); + for(y = 0; y < im->ysize; y++) { for(x = 0; x < im->xsize; x++) { pc=0.0; for(ch=0;chchannels;ch++) res[ch]=0; - for(c = 0;c < diameter; c++) - if (IM_GPIX(im,x+c-radius,y,&rcolor)!=-1) { - for(ch=0;chchannels;ch++) - res[ch]+= rcolor.channel[ch] * coeff[c]; - pc+=coeff[c]; + for(c = 0;c < co->diameter; c++) + if (IM_GPIX(im,x+c-co->radius,y,&rcolor)!=-1) { + for(ch=0;chchannels;ch++) + res[ch]+= rcolor.channel[ch] * co->coeff[c]; + pc+=co->coeff[c]; } for(ch=0;chchannels;ch++) { double value = res[ch] / pc; @@ -80,27 +147,73 @@ i_gaussian(i_img *im, double stddev) { IM_PPIX(timg, x, y, &rcolor); } } + /* processing is im -> timg=yin -> im=yout */ + yin = timg; + yout = im; + } + else { + /* processing is im=yin -> timg=yout -> im */ + yin = im; + yout = timg; + } + if( stddevY > 0 ) { + if( stddevX != stddevY ) { + if( co != NULL ) { + free_coeff(co); + co = NULL; + } + + /* Build Y coefficient matrix */ + co = build_coeff( im, stddevY ); + im_log((aIMCTX, 1, "i_gaussian2 Y coeff radius=%i diamter=%i coeff=%p\n", co->radius, co->diameter, co->coeff)); + } + + /******************/ + /* Process Y blur */ + im_log((aIMCTX, 1, "i_gaussian2 Y blur from yin=%p to yout=%p\n", yin, yout)); for(x = 0;x < im->xsize; x++) { for(y = 0; y < im->ysize; y++) { pc=0.0; for(ch=0; chchannels; ch++) res[ch]=0; - for(c=0; c < diameter; c++) - if (IM_GPIX(timg, x, y+c-radius, &rcolor)!=-1) { - for(ch=0;chchannels;ch++) - res[ch]+= rcolor.channel[ch] * coeff[c]; - pc+=coeff[c]; + for(c=0; c < co->diameter; c++) + if (IM_GPIX(yin, x, y+c-co->radius, &rcolor)!=-1) { + for(ch=0;chchannels;ch++) + res[ch]+= rcolor.channel[ch] * co->coeff[c]; + pc+=co->coeff[c]; } - for(ch=0;chchannels;ch++) { + for(ch=0;chchannels;ch++) { double value = res[ch]/pc; rcolor.channel[ch] = value > IM_SAMPLE_MAX ? IM_SAMPLE_MAX : IM_ROUND(value); } - IM_PPIX(im, x, y, &rcolor); + IM_PPIX(yout, x, y, &rcolor); + } } + if( im != yout ) { + im_log((aIMCTX, 1, "i_gaussian2 copying yout=%p to im=%p\n", yout, im)); + img_copy( im, yout ); } + } + else { + im_log((aIMCTX, 1, "i_gaussian2 Y coeff is unity\n")); + if( yin==timg ) { + im_log((aIMCTX, 1, "i_gaussian2 copying timg=%p to im=%p\n", timg, im)); + img_copy( im, timg ); + } + } + + im_log((aIMCTX, 1, "i_gaussian2 im=%p\n", im)); + im_log((aIMCTX, 1, "i_gaussian2 timg=%p\n", timg)); + im_log((aIMCTX, 1, "i_gaussian2 yin=%p\n", yin)); + im_log((aIMCTX, 1, "i_gaussian2 yout=%p\n", yout)); + + + #/code - myfree(coeff); + if( co != NULL ) + free_coeff(co); + i_img_destroy(timg); return 1; diff --git a/imager.h b/imager.h index f87fc2ca..6a217657 100644 --- a/imager.h +++ b/imager.h @@ -178,7 +178,8 @@ undef_int i_flood_cfill_border(i_img *im, i_img_dim seedx, i_img_dim seedy, i_fi /* image processing functions */ -int i_gaussian (i_img *im, double stdev); +int i_gaussian (i_img *im, double stddev); +int i_gaussian2 (i_img *im, double stddevX, double stddevY); int i_conv (i_img *im,const double *coeff,int len); void i_unsharp_mask(i_img *im, double stddev, double scale); diff --git a/lib/Imager/Filters.pod b/lib/Imager/Filters.pod index f3d9e5e4..d308e482 100644 --- a/lib/Imager/Filters.pod +++ b/lib/Imager/Filters.pod @@ -101,6 +101,9 @@ that comes with the module source. gaussian stddev + gaussian2 stddevX + stddevY + gradgen xo yo colors dist 0 @@ -436,7 +439,7 @@ Really. It even loads GIMP gradient files. or die $radial->errstr; =for stopwords Gaussian - + =item gaussian performs a Gaussian blur of the image, using C as the standard @@ -456,6 +459,26 @@ values around 5 provide a very strong blur. $img->filter(type=>"gaussian", stddev=>5) or die $img->errstr; +=item gaussian2 + +performs a Gaussian blur of the image, using C, C as the +standard deviation of the curve used to combine pixels on the X and Y axis, +respectively. Larger values give bigger blurs. For a definition of Gaussian +Blur, see: + + http://www.maths.abdn.ac.uk/~igc/tch/mx4002/notes/node99.html + +Values of C or C around 0.5 provide a barely noticeable blur, +values around 5 provide a very strong blur. + + # only slightly blurred + $img->filter(type=>"gaussian2", stddevX=>0.5, stddevY=>0.5) + or die $img->errstr; + + # blur an image in the Y axis + $img->filter(type=>"gaussian", stddevX=>0, stddevY=>5 ) + or die $img->errstr; + =item gradgen renders a gradient, with the given I at the corresponding diff --git a/t/400-filter/010-filters.t b/t/400-filter/010-filters.t index 1be79b13..1ec6e199 100644 --- a/t/400-filter/010-filters.t +++ b/t/400-filter/010-filters.t @@ -1,7 +1,7 @@ #!perl -w use strict; use Imager qw(:handy); -use Test::More tests => 124; +use Test::More tests => 136; -d "testout" or mkdir "testout"; @@ -67,6 +67,34 @@ test($imbase, {type=>'conv', coef=>[ 0.3, 1, 0.3, ], }, is_image_similar($gauss, $gauss16, 250000, "8 and 16 gaussian match"); } +{ + my $imbase = Imager->new( xsize=>150, ysize=>150 ); + $imbase->box( filled=>1, color=>'white', box=>[ 70, 24, 80, 124 ] ); + $imbase->box( filled=>1, color=>'red', box=>[ 70, 24, 124, 30 ] ); + $imbase->write( file=>"testout/t61_gaussian2-base.ppm" ); + + my $gauss = test($imbase, {type=>'gaussian2', stddevY=>5, stddevX=>0 }, + 'testout/t61_gaussianY.ppm'); + + my $imbase16 = $imbase->to_rgb16; + my $gauss16 = test($imbase16, {type=>'gaussian2', stddevY=>5, stddevX=>0.1 }, + 'testout/t61_gaussianY-16.ppm'); + is_image_similar($gauss, $gauss16, 250000, "8 and 16 gaussian match"); + + + test($imbase, {type=>'gaussian2', stddevX=>5, stddevY=>5 }, + 'testout/t61_gaussian_both.ppm'); + + + $gauss = test($imbase, {type=>'gaussian2', stddevX=>5, stddevY=>0 }, + 'testout/t61_gaussianX.ppm'); + + $imbase16 = $imbase->to_rgb16; + $gauss16 = test($imbase16, {type=>'gaussian2', stddevX=>5, stddevY=>0.1 }, + 'testout/t61_gaussianX-16.ppm'); + is_image_similar($gauss, $gauss16, 250000, "8 and 16 gaussian match"); +} + test($imbase, { type=>'gradgen', dist=>1, xo=>[ 10, 10, 120 ],