From: Tony Cook Date: Sat, 12 Apr 2014 03:11:27 +0000 (+1000) Subject: rt #94413: autolevels no longer color-skews X-Git-Tag: v0.99~9 X-Git-Url: http://git.imager.perl.org/imager.git/commitdiff_plain/ac00f58dab33380ec5299f66f97e0a216bb9a47a rt #94413: autolevels no longer color-skews The old autolevels filter has been renamed to "autolevels_skew", and autolevels is now a filter that does histogram equalization over the luminosity of the image, instead of per channel --- diff --git a/Imager.pm b/Imager.pm index 1008f60e..9671b049 100644 --- a/Imager.pm +++ b/Imager.pm @@ -204,12 +204,18 @@ BEGIN { callsub => sub { my %hsh=@_; i_hardinvertall($hsh{image}); } }; - $filters{autolevels} ={ + $filters{autolevels_skew} ={ callseq => ['image','lsat','usat','skew'], defaults => { lsat=>0.1,usat=>0.1,skew=>0.0 }, callsub => sub { my %hsh=@_; i_autolevels($hsh{image},$hsh{lsat},$hsh{usat},$hsh{skew}); } }; + $filters{autolevels} ={ + callseq => ['image','lsat','usat'], + defaults => { lsat=>0.1,usat=>0.1 }, + callsub => sub { my %hsh=@_; i_autolevels_mono($hsh{image},$hsh{lsat},$hsh{usat}); } + }; + $filters{turbnoise} ={ callseq => ['image'], defaults => { xo=>0.0,yo=>0.0,scale=>10.0 }, diff --git a/Imager.xs b/Imager.xs index 2b757fed..66d3c444 100644 --- a/Imager.xs +++ b/Imager.xs @@ -2703,6 +2703,12 @@ i_autolevels(im,lsat,usat,skew) float usat float skew +void +i_autolevels_mono(im,lsat,usat) + Imager::ImgRaw im + float lsat + float usat + void i_radnoise(im,xo,yo,rscale,ascale) Imager::ImgRaw im diff --git a/filters.im b/filters.im index dda6fd9c..d980fcb6 100644 --- a/filters.im +++ b/filters.im @@ -648,6 +648,89 @@ i_watermark(i_img *im, i_img *wmark, i_img_dim tx, i_img_dim ty, int pixdiff) { } } +/* +=item i_autolevels_mono(im, lsat, usat) + +Do autolevels, but monochromatically. + +=cut +*/ + +void +i_autolevels_mono(i_img *im, float lsat, float usat) { + i_color val; + i_img_dim i, x, y, hist[256]; + i_img_dim sum_lum, min_lum, max_lum; + i_img_dim upper_accum, lower_accum; + i_color *row; + dIMCTXim(im); + int adapt_channels = im->channels == 4 ? 2 : 1; + int color_channels = i_img_color_channels(im); + i_img_dim color_samples = im->xsize * color_channels; + + + im_log((aIMCTX, 1,"i_autolevels_mono(im %p, lsat %f,usat %f)\n", im, lsat,usat)); + + /* build the histogram in 8-bits, unless the image has a very small + range it should make little difference to the result */ + sum_lum = 0; + for (i = 0; i < 256; i++) + hist[i] = 0; + + row = mymalloc(im->xsize * sizeof(i_color)); + /* create histogram for each channel */ + for (y = 0; y < im->ysize; y++) { + i_color *p = row; + i_glin(im, 0, im->xsize, y, row); + if (im->channels > 2) + i_adapt_colors(adapt_channels, im->channels, row, im->xsize); + for (x = 0; x < im->xsize; x++) { + hist[p->channel[0]]++; + ++p; + } + } + myfree(row); + + for(i = 0; i < 256; i++) { + sum_lum += hist[i]; + } + + min_lum = 0; + max_lum = 255; + + lower_accum = upper_accum = 0; + + for(i=0; i<256; i++) { + lower_accum += hist[i]; + if (lower_accum < sum_lum * lsat) + min_lum = i; + + upper_accum += hist[255-i]; + if (upper_accum < sum_lum * usat) + max_lum = 255-i; + } + +#code im->bits <= 8 + IM_SAMPLE_T *srow = mymalloc(color_samples * sizeof(IM_SAMPLE_T)); +#ifdef IM_EIGHT_BIT + IM_WORK_T low = min_lum; +#else + IM_WORK_T low = min_lum / 255.0 * IM_SAMPLE_MAX; +#endif + IM_WORK_T scale = 255.0 / (max_lum - min_lum); + + for(y = 0; y < im->ysize; y++) { + IM_GSAMP(im, 0, im->xsize, y, srow, NULL, color_channels); + for(i = 0; i < color_samples; ++i) { + IM_WORK_T tmp = (srow[i] - low) * scale; + srow[i] = IM_LIMIT(tmp); + } + IM_PSAMP(im, 0, im->xsize, y, srow, NULL, color_channels); + } + myfree(srow); +#/code +} + /* =item i_autolevels(im, lsat, usat, skew) @@ -661,6 +744,9 @@ occur when changing the contrast. usat - fraction of pixels that will be truncated at the higher end of the spectrum skew - not used yet +Note: this code calculates levels and adjusts each channel separately, +which will typically cause a color shift. + =cut */ diff --git a/imager.h b/imager.h index 087a0e9a..e114bca0 100644 --- a/imager.h +++ b/imager.h @@ -271,6 +271,7 @@ void i_postlevels(i_img *im,int levels); void i_mosaic(i_img *im,i_img_dim size); void i_watermark(i_img *im,i_img *wmark,i_img_dim tx,i_img_dim ty,int pixdiff); void i_autolevels(i_img *im,float lsat,float usat,float skew); +void i_autolevels_mono(i_img *im,float lsat,float usat); void i_radnoise(i_img *im,i_img_dim xo,i_img_dim yo,double rscale,double ascale); void i_turbnoise(i_img *im,double xo,double yo,double scale); void i_gradgen(i_img *im, int num, i_img_dim *xo, i_img_dim *yo, i_color *ival, int dmeasure); diff --git a/lib/Imager/Filters.pod b/lib/Imager/Filters.pod index 6e14c8a8..f3d9e5e4 100644 --- a/lib/Imager/Filters.pod +++ b/lib/Imager/Filters.pod @@ -64,6 +64,9 @@ that comes with the module source. Filter Arguments Default value autolevels lsat 0.1 usat 0.1 + + autolevels_skew lsat 0.1 + usat 0.1 skew 0 bumpmap bump lightx lighty @@ -148,19 +151,36 @@ A reference of the filters follows: =item autolevels -scales the value of each channel so that the values in the image will +Scales the luminosity of the image so that the luminosity will cover +the possible range for the image. C and C truncate the +range by the specified fraction at the top and bottom of the range +respectively. + + # increase contrast, losing little detail + $img->filter(type=>"autolevels") + or die $img->errstr; + +The method used here is typically called L. + +=item autolevels_skew + +Scales the value of each channel so that the values in the image will cover the whole possible range for the channel. C and C truncate the range by the specified fraction at the top and bottom of the range respectively. # increase contrast per channel, losing little detail - $img->filter(type=>"autolevels") + $img->filter(type=>"autolevels_skew") or die $img->errstr; # increase contrast, losing 20% of highlight at top and bottom range $img->filter(type=>"autolevels", lsat=>0.2, usat=>0.2) or die $img->errstr; +This filter was the original C filter, but it's typically +useless due to the significant color skew it can produce. + =item bumpmap uses the channel C image C as a bump map on your diff --git a/t/400-filter/010-filters.t b/t/400-filter/010-filters.t index 0fe334e3..1be79b13 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 => 122; +use Test::More tests => 124; -d "testout" or mkdir "testout"; @@ -16,6 +16,8 @@ $im_other->box(xmin=>30, ymin=>60, xmax=>120, ymax=>90, filled=>1); test($imbase, {type=>'autolevels'}, 'testout/t61_autolev.ppm'); +test($imbase, {type=>'autolevels_skew'}, 'testout/t61_autoskew.ppm'); + test($imbase, {type=>'contrast', intensity=>0.5}, 'testout/t61_contrast.ppm');