Skip Menu |

This queue is for tickets about the Imager CPAN distribution.

Report information
The Basics
Id: 129769
Status: patched
Priority: 0/
Queue: Imager

People
Owner: Nobody in particular
Requestors: tony [...] develop-help.com
Cc:
AdminCc:

Bug Information
Severity: (no value)
Broken in: (no value)
Fixed in: (no value)



Subject: separate vertical and horizontal blur amounts for guassian blur
Date: Sat, 8 Jun 2019 08:25:36 +1000
To: bug-Imager [...] rt.cpan.org
From: Tony Cook <tony [...] develop-help.com>
per subject
Here's a patch that adds i_gaussian2, with tests and doco. It redefines i_gaussian to use i_gaussian2.
Subject: Imager-gaussian2-filter.PG01.patch
diff -ubr Imager-1.011-ORIG/Imager.pm Imager-1.011-PG/Imager.pm --- Imager-1.011-ORIG/Imager.pm 2019-03-06 22:15:05.000000000 -0500 +++ Imager-1.011-PG/Imager.pm 2020-03-12 13:29:09.224962719 -0400 @@ -49,6 +49,7 @@ i_count_colors i_gaussian + i_gaussian2 i_conv i_convert @@ -279,6 +280,11 @@ 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) ], Only in Imager-1.011-PG/: Imager.pm~ diff -ubr Imager-1.011-ORIG/Imager.xs Imager-1.011-PG/Imager.xs --- Imager-1.011-ORIG/Imager.xs 2019-03-06 19:03:57.000000000 -0500 +++ Imager-1.011-PG/Imager.xs 2020-03-12 13:07:13.526887687 -0400 @@ -2251,9 +2251,15 @@ 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 -ubr Imager-1.011-ORIG/gaussian.im Imager-1.011-PG/gaussian.im --- Imager-1.011-ORIG/gaussian.im 2014-01-10 04:46:40.000000000 -0500 +++ Imager-1.011-PG/gaussian.im 2020-03-12 15:16:11.036095579 -0400 @@ -19,28 +19,22 @@ 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 @@ 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;ch<im->channels;ch++) res[ch]=0; - for(c = 0;c < diameter; c++) - if (IM_GPIX(im,x+c-radius,y,&rcolor)!=-1) { + for(c = 0;c < co->diameter; c++) + if (IM_GPIX(im,x+c-co->radius,y,&rcolor)!=-1) { for(ch=0;ch<im->channels;ch++) - res[ch]+= rcolor.channel[ch] * coeff[c]; - pc+=coeff[c]; + res[ch]+= rcolor.channel[ch] * co->coeff[c]; + pc+=co->coeff[c]; } for(ch=0;ch<im->channels;ch++) { double value = res[ch] / pc; @@ -80,27 +147,73 @@ 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; ch<im->channels; ch++) res[ch]=0; - for(c=0; c < diameter; c++) - if (IM_GPIX(timg, x, y+c-radius, &rcolor)!=-1) { - for(ch=0;ch<im->channels;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;ch<yin->channels;ch++) + res[ch]+= rcolor.channel[ch] * co->coeff[c]; + pc+=co->coeff[c]; } - for(ch=0;ch<im->channels;ch++) { + for(ch=0;ch<yin->channels;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 -ubr Imager-1.011-ORIG/imager.h Imager-1.011-PG/imager.h --- Imager-1.011-ORIG/imager.h 2019-03-06 19:03:57.000000000 -0500 +++ Imager-1.011-PG/imager.h 2020-03-12 13:08:14.292872226 -0400 @@ -178,7 +178,8 @@ /* 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 -ubr Imager-1.011-ORIG/lib/Imager/Filters.pod Imager-1.011-PG/lib/Imager/Filters.pod --- Imager-1.011-ORIG/lib/Imager/Filters.pod 2014-04-12 18:12:11.000000000 -0400 +++ Imager-1.011-PG/lib/Imager/Filters.pod 2020-03-12 14:13:09.642294725 -0400 @@ -101,6 +101,9 @@ gaussian stddev + gaussian2 stddevX + stddevY + gradgen xo yo colors dist 0 @@ -456,6 +459,26 @@ $img->filter(type=>"gaussian", stddev=>5) or die $img->errstr; +=item gaussian2 + +performs a Gaussian blur of the image, using C<stddevX>, C<stddevY> 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<stddevX> or C<stddevY> 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<colors> at the corresponding diff -ubr Imager-1.011-ORIG/t/400-filter/010-filters.t Imager-1.011-PG/t/400-filter/010-filters.t --- Imager-1.011-ORIG/t/400-filter/010-filters.t 2014-04-12 18:12:11.000000000 -0400 +++ Imager-1.011-PG/t/400-filter/010-filters.t 2020-03-12 15:11:52.293117482 -0400 @@ -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 @@ 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 ],
On Thu Mar 12 15:57:17 2020, GWYN wrote: Show quoted text
> Here's a patch that adds i_gaussian2, with tests and doco. It > redefines i_gaussian to use i_gaussian2.
Thanks, applied as http://git.imager.perl.org/imager.git/commit/3d3b6bed7c6ca5efb92cc8b04c867b73890f3cfa Tony