piuspatch  v0.1
USB addition to PIE backend
only-pie/sanei/sanei_ir.c
Go to the documentation of this file.
00001 
00030 #include <stdlib.h>
00031 #include <string.h>
00032 #include <values.h>
00033 #include <math.h>
00034 
00035 #define BACKEND_NAME sanei_ir   /* name of this module for debugging */
00036 
00037 #include "../include/sane/sane.h"
00038 #include "../include/sane/sanei_debug.h"
00039 #include "../include/sane/sanei_ir.h"
00040 #include "../include/sane/sanei_magic.h"
00041 
00042 
00043 double *
00044 sanei_ir_create_norm_histo (const SANE_Parameters * params,
00045                        const SANEI_IR_bufptr img_data);
00046 double * sanei_ir_accumulate_norm_histo (double * histo_data);
00047 
00048 
00049 /* Initialize sanei_ir
00050  */
00051 void
00052 sanei_ir_init (void)
00053 {
00054   DBG_INIT ();
00055 }
00056 
00057 
00058 /* Create a normalized histogram of a grayscale image, internal
00059  */
00060 double *
00061 sanei_ir_create_norm_histo (const SANE_Parameters * params,
00062                        const SANEI_IR_bufptr img_data)
00063 {
00064   uint8_t *img_data8;
00065   uint16_t *img_data16;
00066   int is, i;
00067   int num_pixels;
00068   int *histo_data = NULL;
00069   double *histo = NULL;
00070   double term;
00071 
00072   DBG (10, "sanei_ir_create_histo\n");
00073 
00074   if ((params->format != SANE_FRAME_GRAY)
00075       && (params->format != SANE_FRAME_RED)
00076       && (params->format != SANE_FRAME_GREEN)
00077       && (params->format != SANE_FRAME_BLUE))
00078     {
00079       DBG (5, "sanei_ir_create_norm_histo: invalid format\n");
00080       return NULL;
00081     }
00082 
00083   /* Allocate storage for the histogram */
00084   histo_data = calloc (HISTOGRAM_SIZE, sizeof (int));
00085   histo = malloc (HISTOGRAM_SIZE * sizeof (double));
00086   if ((histo == NULL) || (histo_data == NULL))
00087     {
00088       DBG (5, "sanei_ir_create_norm_histo: no buffers\n");
00089       free (histo);
00090       free (histo_data);
00091       return NULL;
00092     }
00093 
00094   num_pixels = params->pixels_per_line * params->lines;
00095 
00096   /* Populate the histogram */
00097   is = params->depth - HISTOGRAM_SHIFT;
00098   if (params->depth > 8)
00099     {
00100       img_data16 = img_data.b16;
00101       if (is == 8)
00102         for (i = num_pixels; i > 0; i--)
00103           histo_data[*img_data16++ >> 8]++;
00104       else
00105         for (i = num_pixels; i > 0; i--)
00106           histo_data[*img_data16++ >> is]++;
00107     }
00108   else
00109     {
00110       img_data8 = img_data.b8;
00111       if (is == 0)
00112         for (i = num_pixels; i > 0; i--)
00113           histo_data[*img_data8++]++;
00114       else
00115         for (i = num_pixels; i > 0; i--)
00116           histo_data[*img_data8++ >> is]++;
00117     }
00118 
00119   /* Calculate the normalized histogram */
00120   term = 1.0 / (double) num_pixels;
00121   for (i = 0; i < HISTOGRAM_SIZE; i++)
00122     histo[i] = term * (double) histo_data[i];
00123 
00124   free (histo_data);
00125   return histo;
00126 }
00127 
00128 
00129 /* Create the normalized histogram of a grayscale image
00130  */
00131 SANE_Status
00132 sanei_ir_create_norm_histogram (const SANE_Parameters * params,
00133                                 const SANEI_IR_bufptr img_data,
00134                                 double ** histogram)
00135 {
00136   double *histo;
00137 
00138   DBG (10, "sanei_ir_create_histogram\n");
00139 
00140   histo = sanei_ir_create_norm_histo (params, img_data);
00141   if (!histo)
00142     return SANE_STATUS_NO_MEM;
00143 
00144   *histogram = histo;
00145   return SANE_STATUS_GOOD;
00146 }
00147 
00148 /* Accumulate a normalized histogram, internal
00149  */
00150 double *
00151 sanei_ir_accumulate_norm_histo (double * histo_data)
00152 {
00153   int i;
00154   double *accum_data;
00155 
00156   accum_data = malloc (HISTOGRAM_SIZE * sizeof (double));
00157   if (accum_data == NULL)
00158     {
00159       DBG (5, "sanei_ir_accumulate_norm_histo: Insufficient memory !\n");
00160       return NULL;
00161     }
00162 
00163   accum_data[0] = histo_data[0];
00164   for (i = 1; i < HISTOGRAM_SIZE; i++)
00165     accum_data[i] = accum_data[i - 1] + histo_data[i];
00166 
00167   return accum_data;
00168 }
00169 
00170 /* Implements Yen's thresholding method
00171  */
00172 SANE_Status
00173 sanei_ir_threshold_yen (const SANE_Parameters * params,
00174                          double * norm_histo, int *thresh)
00175 {
00176   double *P1 = NULL;            /* cumulative normalized histogram */
00177   double *P1_sq = NULL;         /* cumulative normalized histogram */
00178   double *P2_sq = NULL;
00179   double crit, max_crit;
00180   int threshold, i;
00181   SANE_Status ret = SANE_STATUS_NO_MEM;
00182 
00183   DBG (10, "sanei_ir_threshold_yen\n");
00184 
00185   P1 = sanei_ir_accumulate_norm_histo (norm_histo);
00186   P1_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
00187   P2_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
00188   if (!P1 || !P1_sq || !P2_sq)
00189     {
00190       DBG (5, "sanei_ir_threshold_yen: no buffers\n");
00191       goto cleanup;
00192     }
00193 
00194   /* calculate cumulative squares */
00195   P1_sq[0] = norm_histo[0] * norm_histo[0];
00196   for (i = 1; i < HISTOGRAM_SIZE; i++)
00197       P1_sq[i] = P1_sq[i - 1] + norm_histo[i] * norm_histo[i];
00198   P2_sq[HISTOGRAM_SIZE - 1] = 0.0;
00199   for (i = HISTOGRAM_SIZE - 2; i >= 0; i--)
00200       P2_sq[i] = P2_sq[i + 1] + norm_histo[i + 1] * norm_histo[i + 1];
00201 
00202   /* Find the threshold that maximizes the criterion */
00203   threshold = INT_MIN;
00204   max_crit = DBL_MIN;
00205   for (i = 0; i < HISTOGRAM_SIZE; i++)
00206     {
00207       crit =
00208         -1.0 * SAFE_LOG (P1_sq[i] * P2_sq[i]) +
00209         2 * SAFE_LOG (P1[i] * (1.0 - P1[i]));
00210       if (crit > max_crit)
00211         {
00212           max_crit = crit;
00213           threshold = i;
00214         }
00215     }
00216 
00217   if (threshold == INT_MIN)
00218     {
00219       DBG (5, "sanei_ir_threshold_yen: no threshold found\n");
00220       ret = SANE_STATUS_INVAL;
00221     }
00222   else
00223     {
00224       ret = SANE_STATUS_GOOD;
00225       if (params->depth > 8)
00226         {
00227           i = 1 << (params->depth - HISTOGRAM_SHIFT);
00228           *thresh = threshold * i + i / 2;
00229         }
00230       else
00231         *thresh = threshold;
00232       DBG (10, "sanei_ir_threshold_yen: threshold %d\n", *thresh);
00233     }
00234 
00235   cleanup:
00236     if (P1)
00237       free (P1);
00238     if (P1_sq)
00239       free (P1_sq);
00240     if (P2_sq)
00241       free (P2_sq);
00242     return ret;
00243 }
00244 
00245 
00246 /* Implements Otsu's thresholding method
00247  */
00248 SANE_Status
00249 sanei_ir_threshold_otsu (const SANE_Parameters * params,
00250                           double * norm_histo, int *thresh)
00251 {
00252   double *cnh = NULL;
00253   double *mean = NULL;
00254   double total_mean;
00255   double bcv, max_bcv;
00256   int first_bin, last_bin;
00257   int threshold, i;
00258   SANE_Status ret = SANE_STATUS_NO_MEM;
00259 
00260   DBG (10, "sanei_ir_threshold_otsu\n");
00261 
00262   cnh = sanei_ir_accumulate_norm_histo (norm_histo);
00263   mean = malloc (HISTOGRAM_SIZE * sizeof (double));
00264   if (!cnh || !mean)
00265     {
00266       DBG (5, "sanei_ir_threshold_otsu: no buffers\n");
00267       goto cleanup;
00268     }
00269 
00270   mean[0] = 0.0;
00271   for (i = 1; i < HISTOGRAM_SIZE; i++)
00272       mean[i] = mean[i - 1] + i * norm_histo[i];
00273   total_mean = mean[HISTOGRAM_SIZE - 1];
00274 
00275   first_bin = 0;
00276   for (i = 0; i < HISTOGRAM_SIZE; i++)
00277     if (cnh[i] != 0)
00278       {
00279         first_bin = i;
00280         break;
00281       }
00282   last_bin = HISTOGRAM_SIZE - 1;
00283   for (i = HISTOGRAM_SIZE - 1; i >= first_bin; i--)
00284     if (1.0 - cnh[i] != 0)
00285       {
00286         last_bin = i;
00287         break;
00288       }
00289 
00290   threshold = INT_MIN;
00291   max_bcv = 0.0;
00292   for (i = first_bin; i <= last_bin; i++)
00293     {
00294       bcv = total_mean * cnh[i] - mean[i];
00295       bcv *= bcv / (cnh[i] * (1.0 - cnh[i]));
00296       if (max_bcv < bcv)
00297         {
00298           max_bcv = bcv;
00299           threshold = i;
00300         }
00301     }
00302 
00303   if (threshold == INT_MIN)
00304     {
00305       DBG (5, "sanei_ir_threshold_otsu: no threshold found\n");
00306       ret = SANE_STATUS_INVAL;
00307     }
00308   else
00309     {
00310       ret = SANE_STATUS_GOOD;
00311       if (params->depth > 8)
00312         {
00313           i = 1 << (params->depth - HISTOGRAM_SHIFT);
00314           *thresh = threshold * i + i / 2;
00315         }
00316       else
00317         *thresh = threshold;
00318       DBG (10, "sanei_ir_threshold_otsu: threshold %d\n", *thresh);
00319     }
00320   cleanup:
00321     if (cnh)
00322       free (cnh);
00323     if (mean)
00324       free (mean);
00325     return ret;
00326 }
00327 
00328 
00329 /* Implements a Maximum Entropy thresholding method
00330  */
00331 SANE_Status
00332 sanei_ir_threshold_maxentropy (const SANE_Parameters * params,
00333                                double * norm_histo, int *thresh)
00334 {
00335  int ih, it;
00336  int threshold;
00337  int first_bin;
00338  int last_bin;
00339  double tot_ent, max_ent;       /* entropies */
00340  double ent_back, ent_obj;
00341  double *P1;                    /* cumulative normalized histogram */
00342  double *P2;
00343  SANE_Status ret = SANE_STATUS_NO_MEM;
00344 
00345  DBG (10, "sanei_ir_threshold_maxentropy\n");
00346 
00347  /* Calculate the cumulative normalized histogram */
00348  P1 = sanei_ir_accumulate_norm_histo (norm_histo);
00349  P2 = malloc (HISTOGRAM_SIZE * sizeof (double));
00350  if (!P1 || !P2)
00351    {
00352      DBG (5, "sanei_ir_threshold_maxentropy: no buffers\n");
00353      goto cleanup;
00354    }
00355 
00356  for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
00357    P2[ih] = 1.0 - P1[ih];
00358 
00359  first_bin = 0;
00360  for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
00361    if (P1[ih] != 0)
00362     {
00363      first_bin = ih;
00364      break;
00365     }
00366  last_bin = HISTOGRAM_SIZE - 1;
00367  for ( ih = HISTOGRAM_SIZE - 1; ih >= first_bin; ih-- )
00368    if (P2[ih] != 0)
00369     {
00370      last_bin = ih;
00371      break;
00372     }
00373 
00374  /* Calculate the total entropy each gray-level
00375   * and find the threshold that maximizes it
00376   */
00377  threshold = INT_MIN;
00378  max_ent = DBL_MIN;
00379  for ( it = first_bin; it <= last_bin; it++ )
00380   {
00381    /* Entropy of the background pixels */
00382    ent_back = 0.0;
00383    for ( ih = 0; ih <= it; ih++ )
00384      if (norm_histo[ih] != 0)
00385        ent_back -= ( norm_histo[ih] / P1[it] ) * log ( norm_histo[ih] / P1[it] );
00386 
00387    /* Entropy of the object pixels */
00388    ent_obj = 0.0;
00389    for ( ih = it + 1; ih < HISTOGRAM_SIZE; ih++ )
00390      if (norm_histo[ih] != 0)
00391        ent_obj -= ( norm_histo[ih] / P2[it] ) * log ( norm_histo[ih] / P2[it] );
00392 
00393    /* Total entropy */
00394    tot_ent = ent_back + ent_obj;
00395 
00396    if ( max_ent < tot_ent )
00397     {
00398      max_ent = tot_ent;
00399      threshold = it;
00400     }
00401   }
00402 
00403  if (threshold == INT_MIN)
00404    {
00405      DBG (5, "sanei_ir_threshold_maxentropy: no threshold found\n");
00406      ret = SANE_STATUS_INVAL;
00407    }
00408  else
00409    {
00410      ret = SANE_STATUS_GOOD;
00411      if (params->depth > 8)
00412        {
00413          it = 1 << (params->depth - HISTOGRAM_SHIFT);
00414          *thresh = threshold * it + it / 2;
00415        }
00416      else
00417        *thresh = threshold;
00418      DBG (10, "sanei_ir_threshold_maxentropy: threshold %d\n", *thresh);
00419  }
00420 
00421  cleanup:
00422    if (P1)
00423      free (P1);
00424    if (P2)
00425      free (P2);
00426    return ret;
00427 }
00428 
00429 /* Generate gray scale luminance image from separate R, G, B images
00430  */
00431 SANE_Status
00432 sane_ir_RGB_luminance (SANE_Parameters * params, const SANEI_IR_bufptr * in_img,
00433                       SANEI_IR_bufptr * out_img, SANE_Bool scale8)
00434 {
00435   SANEI_IR_bufptr cplane[3];    /* R, G, B gray scale planes */
00436   SANEI_IR_bufptr outi, dest;
00437   size_t ssize;
00438   int itop, is, i;
00439 
00440   if ((params->depth < 8) || (params->depth > 16) ||
00441       (params->format != SANE_FRAME_GRAY))
00442     {
00443       DBG (5, "sane_ir_RGB_luminance: invalid format\n");
00444       return SANE_STATUS_UNSUPPORTED;
00445     }
00446 
00447   for (i = 0; i < 3; i++)
00448     cplane[i] = in_img[i];
00449   itop = params->pixels_per_line * params->lines;
00450   ssize = itop;
00451   if ((params->depth > 8) && !scale8)
00452     ssize *= 2;
00453   outi.b8 = malloc (ssize);
00454   if (!outi.b8)
00455     {
00456       DBG (5, "sane_ir_to_8bit: can not allocate out_img\n");
00457       return SANE_STATUS_NO_MEM;
00458     }
00459   dest = outi;
00460 
00461   if (params->depth > 8)
00462     {
00463       if (scale8)
00464         {
00465           is = params->depth + 2;
00466           for (i = ssize; i > 0; i--)
00467             *(dest.b8)++ = (218 * (int) *(cplane[0].b16++) +
00468                             732 * (int) *(cplane[1].b16++) +
00469                             74 * (int) *(cplane[2].b16++)) >> is;
00470           params->depth = 8;
00471           params->bytes_per_line = params->pixels_per_line;
00472         }
00473       else
00474         for (i = ssize; i > 0; i--)
00475           *(dest.b16++) = (218 * (int) *(cplane[0].b16++) +
00476                            732 * (int) *(cplane[1].b16++) +
00477                            74 * (int) *(cplane[2].b16++)) >> 10;
00478     }
00479   else
00480     {
00481       for (i = ssize; i > 0; i--)
00482         *(dest.b8++) = (218 * (int) *(cplane[0].b8++) +
00483                         732 * (int) *(cplane[1].b8++) +
00484                         74 * (int) *(cplane[2].b8++)) >> 10;
00485     }
00486 
00487   *out_img = outi;
00488   return SANE_STATUS_GOOD;
00489 }
00490 
00491 /* Convert image from >8 bit depth to an 8 bit image
00492  */
00493 SANE_Status
00494 sane_ir_to_8bit (SANE_Parameters * params, const SANEI_IR_bufptr in_img,
00495                  SANE_Parameters * out_params, SANEI_IR_bufptr * out_img)
00496 {
00497   SANEI_IR_bufptr outi, src, dest;
00498   size_t ssize;
00499   int i, is;
00500 
00501   if ((params->depth < 8) || (params->depth > 16))
00502     {
00503       DBG (5, "sane_ir_to_8bit: invalid format\n");
00504       return SANE_STATUS_UNSUPPORTED;
00505     }
00506   ssize = params->pixels_per_line * params->lines;
00507   if (params->format == SANE_FRAME_RGB)
00508     ssize *= 3;
00509   outi.b8 = malloc (ssize);
00510   if (!outi.b8)
00511     {
00512       DBG (5, "sane_ir_to_8bit: can not allocate out_img\n");
00513       return SANE_STATUS_NO_MEM;
00514     }
00515 
00516   if (out_params)
00517     {
00518       memmove (out_params, params, sizeof(SANE_Parameters));
00519       out_params->bytes_per_line = out_params->pixels_per_line;
00520       if (params->format == SANE_FRAME_RGB)
00521         out_params->bytes_per_line *= 3;
00522       out_params->depth = 8;
00523     }
00524   if (params->depth == 8)
00525     memmove (outi.b8, in_img.b8, ssize);
00526   else
00527     {
00528       dest.b8 = outi.b8;
00529       src.b16 = in_img.b16;
00530       if (params->depth == 16)
00531         {
00532           for (i = ssize; i > 0; i--)
00533             *dest.b8++ = *src.b16++ >> 8;
00534         }
00535       else
00536         {
00537           is = params->depth - 8;
00538           for (i = ssize; i > 0; i--)
00539             *dest.b8++ = *src.b16++ >> is;
00540         }
00541     }
00542 
00543   *out_img = outi;
00544   return SANE_STATUS_GOOD;
00545 }
00546 
00547 /* allocate and initialize logarithmic lookup table
00548  */
00549 SANE_Status
00550 sane_ir_ln_table (int len, double **lut_ln)
00551 {
00552   double *llut;
00553   int i;
00554 
00555   DBG (10, "sane_ir_ln_table\n");
00556 
00557   llut = malloc (len * sizeof (double));
00558   if (!llut)
00559     {
00560       DBG (5, "sane_ir_ln_table: no table\n");
00561       return SANE_STATUS_NO_MEM;
00562     }
00563   llut[0] = 0;
00564   llut[1] = 0;
00565   for (i = 2; i < len; i++)
00566     llut[i] = log ((double) i);
00567 
00568   *lut_ln = llut;
00569   return SANE_STATUS_GOOD;
00570 }
00571 
00572 
00573 /* Reduce red spectral overlap from an infrared image plane
00574  */
00575 SANE_Status
00576 sane_ir_spectral_clean (const SANE_Parameters * params, double *lut_ln,
00577                         const SANEI_IR_bufptr red_data,
00578                         SANEI_IR_bufptr ir_data)
00579 {
00580   SANEI_IR_bufptr rptr, iptr;
00581   SANE_Int depth;
00582   double *llut;
00583   double rval, rsum, rrsum;
00584   double risum, rfac, radd;
00585   double *norm_histo;
00586   int64_t isum;
00587   int *calc_buf, *calc_ptr;
00588   int ival, imin, imax;
00589   int itop, len, ssize;
00590   int thresh_low, thresh;
00591   int irand, i;
00592   SANE_Status status;
00593 
00594   DBG (10, "sane_ir_spectral_clean\n");
00595 
00596   itop = params->pixels_per_line * params->lines;
00597   calc_buf = malloc (itop * sizeof (int));              /* could save this */
00598   if (!calc_buf)
00599     {
00600       DBG (5, "sane_ir_spectral_clean: no buffer\n");
00601       return SANE_STATUS_NO_MEM;
00602     }
00603 
00604   depth = params->depth;
00605   len = 1 << depth;
00606   if (lut_ln)
00607     llut = lut_ln;
00608   else
00609     {
00610       status = sane_ir_ln_table (len, &llut);
00611       if (status != SANE_STATUS_GOOD)
00612         return status;
00613     }
00614 
00615   /* determine not transparent areas to exclude them later
00616    * TODO: this has not been tested for negatives
00617    */
00618   thresh_low = INT_MAX;
00619   status =
00620       sanei_ir_create_norm_histogram (params, ir_data, &norm_histo);
00621   if (status != SANE_STATUS_GOOD)
00622     {
00623       DBG (5, "sane_ir_spectral_clean: no buffer\n");
00624       return SANE_STATUS_NO_MEM;
00625     }
00626 
00627   /* TODO: remember only needed if cropping is not ok */
00628   status = sanei_ir_threshold_maxentropy (params, norm_histo, &thresh);
00629   if (status == SANE_STATUS_GOOD)
00630     thresh_low = thresh;
00631   status = sanei_ir_threshold_otsu (params, norm_histo, &thresh);
00632   if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
00633     thresh_low = thresh;
00634   status = sanei_ir_threshold_yen (params, norm_histo, &thresh);
00635   if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
00636     thresh_low = thresh;
00637   if (thresh_low == INT_MAX)
00638     thresh_low = 0;
00639   else
00640     thresh_low /= 2;
00641   DBG (10, "sane_ir_spectral_clean: low threshold %d\n", thresh_low);
00642 
00643   /* calculate linear regression ired (red) from randomly chosen points */
00644   ssize = itop / 2;
00645   if (SAMPLE_SIZE < ssize)
00646     ssize = SAMPLE_SIZE;
00647   isum = 0;
00648   rsum = rrsum = risum = 0.0;
00649   i = ssize;
00650   while (i > 0)
00651     {
00652       irand = rand () % itop;
00653       if (depth > 8)
00654         {
00655           rval = llut[red_data.b16[irand]];
00656           ival = ir_data.b16[irand];
00657         }
00658       else
00659         {
00660           rval = llut[red_data.b8[irand]];
00661           ival = ir_data.b8[irand];
00662         }
00663       if (ival > thresh_low)
00664         {
00665           isum += ival;
00666           rsum += rval;
00667           rrsum += rval * rval;
00668           risum += rval * (double) ival;
00669           i--;
00670         }
00671     }
00672 
00673   /* "a" in ired = b + a * ln (red) */
00674   rfac =
00675     ((double) ssize * risum -
00676     rsum * (double) isum) / ((double) ssize * rrsum - rsum * rsum);
00677     radd = ((double) isum - rfac * rsum) / (double) ssize;      /* "b" unused */
00678 
00679   DBG (10, "sane_ir_spectral_clean: n = %d, ired(red) = %f * ln(red) + %f\n",
00680             ssize, rfac, radd);
00681 
00682   /* now calculate ired' = ired - a  * ln (red) */
00683   imin = INT_MAX;
00684   imax = INT_MIN;
00685   rptr = red_data;
00686   iptr = ir_data;
00687   calc_ptr = calc_buf;
00688   if (depth > 8)
00689     for (i = itop; i > 0; i--)
00690       {
00691         ival = *iptr.b16++ - (int) (rfac * llut[*rptr.b16++] + 0.5);
00692         if (ival > imax)
00693           imax = ival;
00694         if (ival < imin)
00695           imin = ival;
00696         *calc_ptr++ = ival;
00697       }
00698   else
00699     for (i = itop; i > 0; i--)
00700       {
00701         ival = *iptr.b8++ - (int) (rfac * llut[*rptr.b8++] + 0.5);
00702         if (ival > imax)
00703           imax = ival;
00704         if (ival < imin)
00705           imin = ival;
00706         *calc_ptr++ = ival;
00707       }
00708 
00709   /* scale the result back into the ired image */
00710   calc_ptr = calc_buf;
00711   iptr = ir_data;
00712   rfac = (double) (len - 1) / (double) (imax - imin);
00713   if (depth > 8)
00714     for (i = itop; i > 0; i--)
00715       *iptr.b16++ = (double) (*calc_ptr++ - imin) * rfac;
00716   else
00717     for (i = itop; i > 0; i--)
00718       *iptr.b8++ = (double) (*calc_ptr++ - imin) * rfac;
00719 
00720   if (!lut_ln)
00721     free (llut);
00722   free (calc_buf);
00723   free (norm_histo);
00724   return SANE_STATUS_GOOD;
00725 }
00726 
00727 
00728 /* Hopefully fast mean filter
00729  */
00730 SANE_Status
00731 sanei_ir_filter_mean (const SANE_Parameters * params,
00732                       const SANEI_IR_bufptr in_img, SANEI_IR_bufptr out_img,
00733                       int win_rows, int win_cols)
00734 {
00735   SANEI_IR_bufptr src, dest;
00736   int num_cols, num_rows;
00737   int itop, iadd, isub;
00738   int ndiv, the_sum;
00739   int nrow, ncol;
00740   int hwr, hwc;
00741   int depth;
00742   int *sum;
00743   int i, j;
00744 
00745   DBG (10, "sanei_ir_filter_mean, window: %d x%d\n", win_rows, win_cols);
00746 
00747   if (((win_rows & 1) == 0) || ((win_cols & 1) == 0))
00748     {
00749       DBG (5, "sanei_ir_filter_mean: window even sized\n");
00750       return SANE_STATUS_INVAL;
00751     }
00752 
00753   num_cols = params->pixels_per_line;
00754   num_rows = params->lines;
00755   depth = params->depth;
00756 
00757   sum = malloc (num_cols * sizeof (int));
00758   if (!sum)
00759     {
00760       DBG (5, "sanei_ir_filter_mean: no buffer for sums\n");
00761       return SANE_STATUS_NO_MEM;
00762     }
00763   dest = out_img;
00764 
00765   hwr = win_rows / 2;           /* half window sizes */
00766   hwc = win_cols / 2;
00767 
00768   if (depth > 8)                /* pre-pre calculation */
00769     for (j = 0; j < num_cols; j++)
00770       {
00771         sum[j] = 0;
00772         src.b16 = in_img.b16 + j;
00773         for (i = 0; i < hwr; i++)
00774           {
00775             sum[j] += *src.b16;
00776             src.b16 += num_cols;
00777           }
00778       }
00779   else
00780     for (j = 0; j < num_cols; j++)
00781       {
00782         sum[j] = 0;
00783         src.b8 = in_img.b8 + j;
00784         for (i = 0; i < hwr; i++)
00785           {
00786             sum[j] += *src.b8;
00787             src.b8 += num_cols;
00788           }
00789       }
00790 
00791   itop = num_rows * num_cols;
00792   iadd = hwr * num_cols;
00793   isub = (hwr - win_rows) * num_cols;
00794   nrow = hwr;
00795 
00796   if (depth > 8)
00797     {
00798       for (i = 0; i < num_rows; i++)
00799         {
00800           /* update row sums if possible */
00801           if (isub >= 0)        /* subtract old row */
00802             {
00803               nrow--;
00804               src.b16 = in_img.b16 + isub;
00805               for (j = 0; j < num_cols; j++)
00806                 sum[j] -= *src.b16++;
00807             }
00808           isub += num_cols;
00809 
00810           if (iadd < itop)      /* add new row */
00811             {
00812               nrow++;
00813               src.b16 = in_img.b16 + iadd;
00814               for (j = 0; j < num_cols; j++)
00815                 sum[j] += *src.b16++;
00816             }
00817           iadd += num_cols;
00818 
00819           /* now we do the image columns using only the precalculated sums */
00820 
00821           the_sum = 0;          /* precalculation */
00822           for (j = 0; j < hwc; j++)
00823             the_sum += sum[j];
00824           ncol = hwc;
00825 
00826           /* at the left margin, real index hwc lower */
00827           for (j = hwc; j < win_cols; j++)
00828             {
00829               ncol++;
00830               the_sum += sum[j];
00831               *dest.b16++ = the_sum / (ncol * nrow);
00832             }
00833 
00834           ndiv = ncol * nrow;
00835           /* in the middle, real index hwc + 1 higher */
00836           for (j = 0; j < num_cols - win_cols; j++)
00837             {
00838               the_sum -= sum[j];
00839               the_sum += sum[j + win_cols];
00840               *dest.b16++ = the_sum / ndiv;
00841             }
00842 
00843           /* at the right margin, real index hwc + 1 higher */
00844           for (j = num_cols - win_cols; j < num_cols - hwc - 1; j++)
00845             {
00846               ncol--;
00847               the_sum -= sum[j];        /* j - hwc - 1 */
00848               *dest.b16++ = the_sum / (ncol * nrow);
00849             }
00850         }
00851     }
00852   else
00853     {
00854       for (i = 0; i < num_rows; i++)
00855         {
00856           /* update row sums if possible */
00857           if (isub >= 0)        /* subtract old row */
00858             {
00859               nrow--;
00860               src.b8 = in_img.b8 + isub;
00861               for (j = 0; j < num_cols; j++)
00862                 sum[j] -= *src.b8++;
00863             }
00864           isub += num_cols;
00865 
00866           if (iadd < itop)      /* add new row */
00867             {
00868               nrow++;
00869               src.b8 = in_img.b8 + iadd;
00870               for (j = 0; j < num_cols; j++)
00871                 sum[j] += *src.b8++;
00872             }
00873           iadd += num_cols;
00874 
00875           /* now we do the image columns using only the precalculated sums */
00876 
00877           the_sum = 0;          /* precalculation */
00878           for (j = 0; j < hwc; j++)
00879             the_sum += sum[j];
00880           ncol = hwc;
00881 
00882           /* at the left margin, real index hwc lower */
00883           for (j = hwc; j < win_cols; j++)
00884             {
00885               ncol++;
00886               the_sum += sum[j];
00887               *dest.b8++ = the_sum / (ncol * nrow);
00888             }
00889 
00890           ndiv = ncol * nrow;
00891           /* in the middle, real index hwc + 1 higher */
00892           for (j = 0; j < num_cols - win_cols; j++)
00893             {
00894               the_sum -= sum[j];
00895               the_sum += sum[j + win_cols];
00896               *dest.b8++ = the_sum / ndiv;
00897             }
00898 
00899           /* at the right margin, real index hwc + 1 higher */
00900           for (j = num_cols - win_cols; j < num_cols - hwc - 1; j++)
00901             {
00902               ncol--;
00903               the_sum -= sum[j];        /* j - hwc - 1 */
00904               *dest.b8++ = the_sum / (ncol * nrow);
00905             }
00906         }
00907     }
00908   return SANE_STATUS_GOOD;
00909 }
00910 
00911 
00912 /* Find noise by adaptive thresholding
00913  */
00914 SANE_Status
00915 sanei_ir_filter_madmean (const SANE_Parameters * params,
00916                          const SANEI_IR_bufptr in_img,
00917                          SANE_Byte ** out_img, int win_size,
00918                          int a_val, int b_val)
00919 {
00920   SANEI_IR_bufptr delta_ij, delta_ptr;
00921   SANEI_IR_bufptr mad_ij, mad_ptr;
00922   SANE_Byte *out_ij, *dest8;
00923   double ab_term;
00924   int num_rows, num_cols;
00925   int threshold, itop;
00926   size_t size;
00927   int ival, i;
00928   int depth;
00929   SANE_Status ret = SANE_STATUS_NO_MEM;
00930 
00931   DBG (10, "sanei_ir_filter_madmean\n");
00932 
00933   depth = params->depth;
00934   if (depth != 8)
00935     {
00936       a_val = a_val << (depth - 8);
00937       b_val = b_val << (depth - 8);
00938     }
00939   num_cols = params->pixels_per_line;
00940   num_rows = params->lines;
00941   itop = num_rows * num_cols;
00942 
00943   size = itop * sizeof (uint8_t);
00944   out_ij = malloc (size);
00945   if (depth > 8)
00946     size = itop * sizeof (uint16_t);
00947   delta_ij.b8 = malloc (size);
00948   mad_ij.b8 = malloc (size);
00949 
00950   if (out_ij && delta_ij.b8 && mad_ij.b8)
00951     {
00952       /* get the differences to the local mean */
00953       mad_ptr = in_img;
00954       if (sanei_ir_filter_mean (params, mad_ptr, delta_ij, win_size, win_size)
00955           == SANE_STATUS_GOOD)
00956         {
00957           delta_ptr = delta_ij;
00958           if (depth > 8)
00959             for (i = 0; i < itop; i++)
00960               {
00961                 ival = *mad_ptr.b16++ - *delta_ptr.b16;
00962                 *delta_ptr.b16++ = abs (ival);
00963               }
00964           else
00965             for (i = 0; i < itop; i++)
00966               {
00967                 ival = *mad_ptr.b8++ - *delta_ptr.b8;
00968                 *delta_ptr.b8++ = abs (ival);
00969               }
00970           /* make the second filtering window a bit larger */
00971           win_size = MAD_WIN2_SIZE(win_size);
00972           /* and get the local mean differences */
00973           if (sanei_ir_filter_mean
00974               (params, delta_ij, mad_ij, win_size,
00975                win_size) == SANE_STATUS_GOOD)
00976             {
00977               mad_ptr = mad_ij;
00978               delta_ptr = delta_ij;
00979               dest8 = out_ij;
00980               /* construct the noise map */
00981               ab_term = (b_val - a_val) / (double) b_val;
00982               if (depth > 8)
00983                 for (i = 0; i < itop; i++)
00984                   {
00985                     /* by calculating the threshold */
00986                     ival = *mad_ptr.b16++;
00987                     if (ival >= b_val)  /* outlier */
00988                       threshold = a_val;
00989                     else
00990                       threshold = a_val + (double) ival *ab_term;
00991                     /* above threshold is noise, indicated by 0 */
00992                     if (*delta_ptr.b16++ >= threshold)
00993                       *dest8++ = 0;
00994                     else
00995                       *dest8++ = 255;
00996                   }
00997               else
00998                 for (i = 0; i < itop; i++)
00999                   {
01000                     /* by calculating the threshold */
01001                     ival = *mad_ptr.b8++;
01002                     if (ival >= b_val)  /* outlier */
01003                       threshold = a_val;
01004                     else
01005                       threshold = a_val + (double) ival *ab_term;
01006                     /* above threshold is noise, indicated by 0 */
01007                     if (*delta_ptr.b8++ >= threshold)
01008                       *dest8++ = 0;
01009                     else
01010                       *dest8++ = 255;
01011                   }
01012               *out_img = out_ij;
01013               ret = SANE_STATUS_GOOD;
01014             }
01015         }
01016     }
01017   else
01018     DBG (5, "sanei_ir_filter_madmean: Cannot allocate buffers\n");
01019 
01020   free (mad_ij.b8);
01021   free (delta_ij.b8);
01022   return ret;
01023 }
01024 
01025 
01026 /* Add dark pixels to mask from static threshold
01027  */
01028 void
01029 sanei_ir_add_threshold (const SANE_Parameters * params,
01030                         const SANEI_IR_bufptr in_img,
01031                         SANE_Byte * mask_img, int threshold)
01032 {
01033   SANEI_IR_bufptr in_ptr;
01034   SANE_Byte *mask_ptr;
01035   int itop, i;
01036 
01037   DBG (10, "sanei_ir_add_threshold\n");
01038 
01039   itop = params->pixels_per_line * params->lines;
01040   in_ptr = in_img;
01041   mask_ptr = mask_img;
01042 
01043   if (params->depth > 8)
01044     for (i = itop; i > 0; i--)
01045       {
01046         if (*in_ptr.b16++ <= threshold)
01047           *mask_ptr = 0;
01048         mask_ptr++;
01049       }
01050   else
01051     for (i = itop; i > 0; i--)
01052       {
01053         if (*in_ptr.b8++ <= threshold)
01054           *mask_ptr = 0;
01055         mask_ptr++;
01056       }
01057 }
01058 
01059 
01060 /* Calculate minimal Manhattan distances for an image mask
01061  */
01062 void
01063 sane_ir_manhattan_dist (const SANE_Parameters * params,
01064                         const SANE_Byte * mask_img, unsigned int *dist_map,
01065                         unsigned int *idx_map, unsigned int erode)
01066 {
01067   const SANE_Byte *mask;
01068   unsigned int *index, *manhattan;
01069   int rows, cols, itop;
01070   int i, j;
01071 
01072   DBG (10, "sane_ir_manhattan_dist\n");
01073 
01074   if (erode != 0)
01075     erode = 255;
01076 
01077   /* initialize maps */
01078   cols = params->pixels_per_line;
01079   rows = params->lines;
01080   itop = rows * cols;
01081   mask = mask_img;
01082   manhattan = dist_map;
01083   index = idx_map;
01084   for (i = 0; i < itop; i++)
01085     {
01086       *manhattan++ = *mask++;
01087       *index++ = i;
01088     }
01089 
01090   /* traverse from top left to bottom right */
01091   manhattan = dist_map;
01092   index = idx_map;
01093   for (i = 0; i < rows; i++)
01094     for (j = 0; j < cols; j++)
01095       {
01096         if (*manhattan == erode)
01097           {
01098             /* take original, distance = 0, index stays the same */
01099             *manhattan = 0;
01100           }
01101         else
01102           {
01103             /* assume maximal distance to clean pixel */
01104             *manhattan = cols + rows;
01105             /* or one further away than pixel to the top */
01106             if (i > 0)
01107               if (manhattan[-cols] + 1 < *manhattan)
01108                 {
01109                   *manhattan = manhattan[-cols] + 1;
01110                   *index = index[-cols];        /* index follows */
01111                 }
01112             /* or one further away than pixel to the left */
01113             if (j > 0)
01114               {
01115                 if (manhattan[-1] + 1 < *manhattan)
01116                   {
01117                     *manhattan = manhattan[-1] + 1;
01118                     *index = index[-1]; /* index follows */
01119                   }
01120                 if (manhattan[-1] + 1 == *manhattan)
01121                   if (rand () % 2 == 0) /* chose index */
01122                     *index = index[-1];
01123               }
01124           }
01125         manhattan++;
01126         index++;
01127       }
01128 
01129   /* traverse from bottom right to top left */
01130   manhattan = dist_map + itop - 1;
01131   index = idx_map + itop - 1;
01132   for (i = rows - 1; i >= 0; i--)
01133     for (j = cols - 1; j >= 0; j--)
01134       {
01135         if (i < rows - 1)
01136           {
01137             /* either what we had on the first pass
01138                or one more than the pixel to the bottm */
01139             if (manhattan[+cols] + 1 < *manhattan)
01140               {
01141                 *manhattan = manhattan[+cols] + 1;
01142                 *index = index[+cols];  /* index follows */
01143               }
01144             if (manhattan[+cols] + 1 == *manhattan)
01145               if (rand () % 2 == 0)     /* chose index */
01146                 *index = index[+cols];
01147           }
01148         if (j < cols - 1)
01149           {
01150             /* or one more than pixel to the right */
01151             if (manhattan[1] + 1 < *manhattan)
01152               {
01153                 *manhattan = manhattan[1] + 1;
01154                 *index = index[1];      /* index follows */
01155               }
01156             if (manhattan[1] + 1 == *manhattan)
01157               if (rand () % 2 == 0)     /* chose index */
01158                 *index = index[1];
01159           }
01160         manhattan--;
01161         index--;
01162       }
01163 }
01164 
01165 
01166 /* dilate or erode a mask image */
01167 
01168 void
01169 sane_ir_dilate (const SANE_Parameters * params, SANE_Byte * mask_img,
01170                 unsigned int *dist_map, unsigned int *idx_map, int by)
01171 {
01172   SANE_Byte *mask;
01173   unsigned int *manhattan;
01174   unsigned int erode;
01175   unsigned int thresh;
01176   int i, itop;
01177 
01178   DBG (10, "sane_ir_dilate\n");
01179 
01180   if (by == 0)
01181     return;
01182   if (by > 0)
01183     {
01184       erode = 0;
01185       thresh = by;
01186     }
01187   else
01188     {
01189       erode = 1;
01190       thresh = -by;
01191     }
01192 
01193   itop = params->pixels_per_line * params->lines;
01194   mask = mask_img;
01195   sane_ir_manhattan_dist (params, mask_img, dist_map, idx_map, erode);
01196 
01197   manhattan = dist_map;
01198   for (i = 0; i < itop; i++)
01199     {
01200       if (*manhattan++ <= thresh)
01201         *mask++ = 0;
01202       else
01203         *mask++ = 255;
01204     }
01205 
01206   return;
01207 }
01208 
01209 
01210 /* Suggest cropping for dark margins of positive film
01211  */
01212 void
01213 sanei_ir_find_crop (const SANE_Parameters * params,
01214                     unsigned int * dist_map, int inner, int * edges)
01215 {
01216   int width = params->pixels_per_line;
01217   int height = params->lines;
01218   uint64_t sum_x, sum_y, n;
01219   int64_t sum_xx, sum_xy;
01220   double a, b, mami;
01221   unsigned int *src;
01222   int off1, off2, inc, wh, i, j;
01223 
01224   DBG (10, "sanei_ir_find_crop\n");
01225 
01226   /* loop through top, bottom, left, right */
01227   for (j = 0; j < 4; j++)
01228     {
01229       if (j < 2)        /* top, bottom */
01230         {
01231           off1 = width / 8;     /* only middle 3/4 */
01232           off2 = width - off1;
01233           n = width - 2 * off1;
01234           src = dist_map + off1;        /* first row */
01235           inc = 1;
01236           wh = width;
01237           if (j == 1)                   /* last row */
01238             src += (height - 1) * width;
01239         }
01240       else              /* left, right */
01241         {
01242           off1 = height / 8;     /* only middle 3/4 */
01243           off2 = height - off1;
01244           n = height - 2 * off1;
01245           src = dist_map + (off1 * width);      /* first column */
01246           inc = width;
01247           wh = height;
01248           if (j == 3)
01249             src += width - 1;                   /* last column */
01250         }
01251 
01252       /* calculate linear regression */
01253       sum_x = 0; sum_y = 0;
01254       sum_xx = 0; sum_xy = 0;
01255       for (i = off1; i < off2; i++)
01256         {
01257           sum_x += i;
01258           sum_y += *src;
01259           sum_xx += i * i;
01260           sum_xy += i * (*src);
01261           src += inc;
01262         }
01263       b = ((double) n * (double) sum_xy - (double) sum_x * (double) sum_y)
01264           / ((double) n * (double) sum_xx - (double) sum_x * (double) sum_x);
01265       a = ((double) sum_y - b * (double) sum_x) / (double) n;
01266 
01267       DBG (10, "sanei_ir_find_crop: y = %f + %f * x\n", a, b);
01268 
01269       /* take maximal/minimal value from either side */
01270       mami = a + b * (wh - 1);
01271       if (inner)
01272         {
01273           if (a > mami)
01274             mami = a;
01275         }
01276       else
01277         {
01278           if (a < mami)
01279             mami = a;
01280         }
01281       edges[j] = mami + 0.5;
01282     }
01283   edges[1] = height - edges[1];
01284   edges[3] = width - edges[3];
01285 
01286   DBG (10, "sanei_ir_find_crop: would crop at top: %d, bot: %d, left %d, right %d\n",
01287       edges[0], edges[1], edges[2], edges[3]);
01288 
01289   return;
01290 }
01291 
01292 
01293 /* Dilate clean image parts into dirty ones and smooth
01294  */
01295 SANE_Status
01296 sanei_ir_dilate_mean (const SANE_Parameters * params,
01297                       SANEI_IR_bufptr * in_img,
01298                       SANE_Byte * mask_img,
01299                       int dist_max, int expand, int win_size,
01300                       SANE_Bool smooth, int inner,
01301                       int *crop)
01302 {
01303   SANEI_IR_bufptr color;
01304   SANEI_IR_bufptr plane;
01305   unsigned int *dist_map, *manhattan;
01306   unsigned int *idx_map, *index;
01307   int depth, dist;
01308   int rows, cols;
01309   int k, i, itop;
01310   SANE_Status ret = SANE_STATUS_NO_MEM;
01311 
01312   DBG (10, "sanei_ir_dilate_mean\n");
01313 
01314   depth = params->depth;
01315   cols = params->pixels_per_line;
01316   rows = params->lines;
01317   itop = rows * cols;
01318   idx_map = malloc (itop * sizeof (unsigned int));
01319   dist_map = malloc (itop * sizeof (unsigned int));
01320   if (depth > 8)
01321     plane.b16 = malloc (itop * sizeof (uint16_t));
01322   else
01323     plane.b8 = malloc (itop * sizeof (uint8_t));
01324 
01325   if (!idx_map || !dist_map || !plane.b8)
01326     DBG (5, "sanei_ir_dilate_mean: Cannot allocate buffers\n");
01327   else
01328     {
01329       /* expand dirty regions into their half dirty surround*/
01330       if (expand > 0)
01331         sane_ir_dilate (params, mask_img, dist_map, idx_map, expand);
01332       /* for dirty pixels determine the closest clean ones */
01333       sane_ir_manhattan_dist (params, mask_img, dist_map, idx_map, 1);
01334 
01335       /* use the distance map to find how to crop dark edges */
01336       if (crop)
01337         sanei_ir_find_crop (params, dist_map, inner, crop);
01338 
01339       /* replace dirty pixels */
01340       for (k = 0; k < 3; k++)
01341         {
01342           manhattan = dist_map;
01343           index = idx_map;
01344           color = in_img[k];
01345           /* first replacement */
01346           if (depth > 8)
01347             for (i = 0; i < itop; i++)
01348               {
01349                 dist = *manhattan++;
01350                 if ((dist != 0) && (dist <= dist_max))
01351                   color.b16[i] = color.b16[index[i]];
01352               }
01353           else
01354             for (i = 0; i < itop; i++)
01355               {
01356                 dist = *manhattan++;
01357                 if ((dist != 0) && (dist <= dist_max))
01358                   color.b8[i] = color.b8[index[i]];
01359               }
01360           /* adapt pixels to their new surround and
01361            * smooth the whole image or the replaced pixels only */
01362           ret =
01363             sanei_ir_filter_mean (params, color, plane, win_size, win_size);
01364           if (ret != SANE_STATUS_GOOD)
01365             break;
01366           else
01367             if (smooth)
01368               {
01369                 /* a second mean results in triangular blur */
01370                 ret =
01371                   sanei_ir_filter_mean (params, plane, color, win_size,
01372                                         win_size);
01373                 if (ret != SANE_STATUS_GOOD)
01374                   break;
01375               }
01376             else
01377               {
01378                 /* replace with smoothened pixels only */
01379                 manhattan = dist_map;
01380                 if (depth > 8)
01381                   for (i = 0; i < itop; i++)
01382                     {
01383                       dist = *manhattan++;
01384                       if ((dist != 0) && (dist <= dist_max))
01385                         color.b16[i] = plane.b16[i];
01386                     }
01387                 else
01388                   for (i = 0; i < itop; i++)
01389                     {
01390                       dist = *manhattan++;
01391                       if ((dist != 0) && (dist <= dist_max))
01392                         color.b8[i] = plane.b8[i];
01393                     }
01394               }
01395       }
01396     }
01397   free (plane.b8);
01398   free (dist_map);
01399   free (idx_map);
01400 
01401   return ret;
01402 }
01403 
 All Data Structures Files Functions Variables Typedefs Enumerations Enumerator Defines