[rt #111871] re-work autolevels
authorTony Cook <tony@develop-help.com>
Sun, 21 Feb 2016 00:45:43 +0000 (11:45 +1100)
committerTony Cook <tony@develop-help.com>
Sun, 21 Feb 2016 00:45:43 +0000 (11:45 +1100)
- avoid an off-by-one error when calculating the minimum and maximum
  levels from the histogram

- optimize 8-bit images by using a lookup table instead of doing
  a floating point multiply for each sample

MANIFEST
filters.im
t/400-filter/020-autolevels.t [new file with mode: 0644]

index e8dd256..41825da 100644 (file)
--- a/MANIFEST
+++ b/MANIFEST
@@ -360,6 +360,7 @@ t/350-font/030-ttoo.t               OO level FT1 tests
 t/350-font/040-ttstd.t         Standard font tests for TT
 t/350-font/100-texttools.t     Test text wrapping
 t/400-filter/010-filters.t     Consolidated filter tests (needs to split)
+t/400-filter/020-autolevels.t  Test the autolevels filter
 t/450-api/100-inline.t         Inline::C integration and API
 t/450-api/110-inlinectx.t      context APIs
 t/850-thread/010-base.t                Test wrt to perl threads
index 081072f..ee4f5d8 100644 (file)
@@ -696,34 +696,47 @@ i_autolevels_mono(i_img *im, float lsat, float usat) {
   }
   
   min_lum = 0;
-  max_lum = 255;
-
-  lower_accum = upper_accum = 0;
-  
-  for(i=0; i<256; i++) { 
-    lower_accum += hist[i];
+  lower_accum = 0;
+  for (i = 0; i < 256; ++i) {
     if (lower_accum < sum_lum * lsat)
       min_lum = i;
+    lower_accum += hist[i];
+  }
 
-    upper_accum  += hist[255-i];
+  max_lum = 255;
+  upper_accum = 0;
+  for(i = 255; i >= 0; i--) {
     if (upper_accum < sum_lum * usat)
-      max_lum = 255-i;
+      max_lum = i;
+    upper_accum  += hist[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;
+  i_sample_t lookup[256];
 #else
   IM_WORK_T low = min_lum / 255.0 * IM_SAMPLE_MAX;
 #endif
   double scale = 255.0 / (max_lum - min_lum);
 
+#ifdef IM_EIGHT_BIT
+  for (i = 0; i < 256; ++i) {
+    IM_WORK_T tmp = (i - low) * scale;
+    lookup[i] = IM_LIMIT(tmp);
+  }
+#endif
+
   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) {
+#ifdef IM_EIGHT_BIT
+      srow[i] = lookup[srow[i]];
+#else
       IM_WORK_T tmp = (srow[i] - low) * scale;
       srow[i] = IM_LIMIT(tmp);
+#endif
     }
     IM_PSAMP(im, 0, im->xsize, y, srow, NULL, color_channels);
   }
diff --git a/t/400-filter/020-autolevels.t b/t/400-filter/020-autolevels.t
new file mode 100644 (file)
index 0000000..c8f3f95
--- /dev/null
@@ -0,0 +1,38 @@
+#!perl -w
+use strict;
+use Imager;
+use Test::More tests => 4;
+use Imager::Test qw(is_image);
+
+-d "testout" or mkdir "testout";
+
+Imager->open_log(log => "testout/filters-autolev.log");
+
+my $base = Imager->new(xsize => 10, ysize => 10);
+$base->box(filled => 1, xmax => 4, color => "404040");
+$base->box(filled => 1, xmin => 5, color => "C0C0C0");
+
+{
+  my $cmp = Imager->new(xsize => 10, ysize => 10);
+  $cmp->box(filled => 1, xmax => 4, color => "#000");
+  $cmp->box(filled => 1, xmin => 5, color => "#FFF");
+
+  {
+    my $work = $base->copy;
+    #$work->write(file => "testout/autolevel-base.ppm");
+    ok($work->filter(type => "autolevels"), "default autolevels");
+    #$work->write(file => "testout/autolevel-filtered.ppm");
+    #$cmp->write(file => "testout/autolevel-cmp.ppm");
+    is_image($work, $cmp, "check we got expected image");
+  }
+
+  {
+    my $work = $base->to_rgb_double;
+    ok($work->filter(type => "autolevels"), "default autolevels (double)");
+    is_image($work, $cmp, "check we got expected image");
+    $work->write(file => "testout/autolevel-filtered.ppm", pnm_write_wide_data => 1);
+  }
+}
+
+
+Imager->close_log;