kopia lustrzana https://gitlab.com/sane-project/backends
				
				
				
			
		
			
				
	
	
		
			1207 wiersze
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
			
		
		
	
	
			1207 wiersze
		
	
	
		
			30 KiB
		
	
	
	
		
			C
		
	
	
| /** @file sanei_ir.c
 | |
|  *
 | |
|  * sanei_ir - functions for utilizing the infrared plane
 | |
|  *
 | |
|  * Copyright (C) 2012 Michael Rickmann <mrickma@gwdg.de>
 | |
|  *
 | |
|  * This file is part of the SANE package.
 | |
|  *
 | |
|  * This program is free software; you can redistribute it and/or
 | |
|  * modify it under the terms of the GNU General Public License as
 | |
|  * published by the Free Software Foundation; either version 2 of the
 | |
|  * License, or (at your option) any later version.
 | |
|  *
 | |
|  * This program is distributed in the hope that it will be useful, but
 | |
|  * WITHOUT ANY WARRANTY; without even the implied warranty of
 | |
|  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 | |
|  * General Public License for more details.
 | |
|  *
 | |
|  * You should have received a copy of the GNU General Public License
 | |
|  * along with this program; if not, write to the Free Software
 | |
|  * Foundation, Inc., 59 Temple Place - Suite 330, Boston,
 | |
|  * MA 02111-1307, USA.
 | |
|  *
 | |
|  * The threshold yen, otsu and max_entropy routines have been
 | |
|  * adapted from the FOURIER 0.8 library by M. Emre Celebi,
 | |
|  * http://sourceforge.net/projects/fourier-ipal/ which is
 | |
|  * licensed under the GNU General Public License version 2 or later.
 | |
| */
 | |
| 
 | |
| #include <stdlib.h>
 | |
| #include <string.h>
 | |
| #include <float.h>
 | |
| #include <limits.h>
 | |
| #include <math.h>
 | |
| 
 | |
| #define BACKEND_NAME sanei_ir	/* name of this module for debugging */
 | |
| 
 | |
| #include "../include/sane/sane.h"
 | |
| #include "../include/sane/sanei_debug.h"
 | |
| #include "../include/sane/sanei_ir.h"
 | |
| #include "../include/sane/sanei_magic.h"
 | |
| 
 | |
| 
 | |
| double *
 | |
| sanei_ir_create_norm_histo (const SANE_Parameters * params, const SANE_Uint *img_data);
 | |
| double * sanei_ir_accumulate_norm_histo (double * histo_data);
 | |
| 
 | |
| 
 | |
| /* Initialize sanei_ir
 | |
|  */
 | |
| void
 | |
| sanei_ir_init (void)
 | |
| {
 | |
|   DBG_INIT ();
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Create a normalized histogram of a grayscale image, internal
 | |
|  */
 | |
| double *
 | |
| sanei_ir_create_norm_histo (const SANE_Parameters * params,
 | |
|                        const SANE_Uint *img_data)
 | |
| {
 | |
|   int is, i;
 | |
|   int num_pixels;
 | |
|   int *histo_data;
 | |
|   double *histo;
 | |
|   double term;
 | |
| 
 | |
|   DBG (10, "sanei_ir_create_norm_histo\n");
 | |
| 
 | |
|   if ((params->format != SANE_FRAME_GRAY)
 | |
|       && (params->format != SANE_FRAME_RED)
 | |
|       && (params->format != SANE_FRAME_GREEN)
 | |
|       && (params->format != SANE_FRAME_BLUE))
 | |
|     {
 | |
|       DBG (5, "sanei_ir_create_norm_histo: invalid format\n");
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   /* Allocate storage for the histogram */
 | |
|   histo_data = calloc (HISTOGRAM_SIZE, sizeof (int));
 | |
|   histo = malloc (HISTOGRAM_SIZE * sizeof (double));
 | |
|   if ((histo == NULL) || (histo_data == NULL))
 | |
|     {
 | |
|       DBG (5, "sanei_ir_create_norm_histo: no buffers\n");
 | |
|       if (histo) free (histo);
 | |
|       if (histo_data) free (histo_data);
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   num_pixels = params->pixels_per_line * params->lines;
 | |
| 
 | |
|   DBG (1, "sanei_ir_create_norm_histo: %d pixels_per_line, %d lines => %d num_pixels\n", params->pixels_per_line, params->lines, num_pixels);
 | |
|   DBG (1, "sanei_ir_create_norm_histo: histo_data[] with %d x %ld bytes\n", HISTOGRAM_SIZE, sizeof(int));
 | |
|   /* Populate the histogram */
 | |
|   is = 16 - HISTOGRAM_SHIFT; /* Number of data bits to ignore */
 | |
|   DBG (1, "sanei_ir_create_norm_histo: depth %d, HISTOGRAM_SHIFT %d => ignore %d bits\n", params->depth, HISTOGRAM_SHIFT, is);
 | |
|   for (i = num_pixels; i > 0; i--) {
 | |
|       histo_data[*img_data++ >> is]++;
 | |
|   }
 | |
| 
 | |
|   /* Calculate the normalized histogram */
 | |
|   term = 1.0 / (double) num_pixels;
 | |
|   for (i = 0; i < HISTOGRAM_SIZE; i++)
 | |
|     histo[i] = term * (double) histo_data[i];
 | |
| 
 | |
|   free (histo_data);
 | |
|   return histo;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Create the normalized histogram of a grayscale image
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_create_norm_histogram (const SANE_Parameters * params,
 | |
|                                 const SANE_Uint *img_data,
 | |
|                                 double ** histogram)
 | |
| {
 | |
|   double *histo;
 | |
| 
 | |
|   DBG (10, "sanei_ir_create_norm_histogram\n");
 | |
| 
 | |
|   histo = sanei_ir_create_norm_histo (params, img_data);
 | |
|   if (!histo)
 | |
|     return SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   *histogram = histo;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /* Accumulate a normalized histogram, internal
 | |
|  */
 | |
| double *
 | |
| sanei_ir_accumulate_norm_histo (double * histo_data)
 | |
| {
 | |
|   int i;
 | |
|   double *accum_data;
 | |
| 
 | |
|   accum_data = malloc (HISTOGRAM_SIZE * sizeof (double));
 | |
|   if (accum_data == NULL)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_accumulate_norm_histo: Insufficient memory !\n");
 | |
|       return NULL;
 | |
|     }
 | |
| 
 | |
|   accum_data[0] = histo_data[0];
 | |
|   for (i = 1; i < HISTOGRAM_SIZE; i++)
 | |
|     accum_data[i] = accum_data[i - 1] + histo_data[i];
 | |
| 
 | |
|   return accum_data;
 | |
| }
 | |
| 
 | |
| /* Implements Yen's thresholding method
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_threshold_yen (const SANE_Parameters * params,
 | |
|                          double * norm_histo, int *thresh)
 | |
| {
 | |
|   double *P1 = NULL;            /* cumulative normalized histogram */
 | |
|   double *P1_sq = NULL;         /* cumulative normalized histogram */
 | |
|   double *P2_sq = NULL;
 | |
|   double crit, max_crit;
 | |
|   int threshold, i;
 | |
|   SANE_Status ret = SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   DBG (10, "sanei_ir_threshold_yen\n");
 | |
| 
 | |
|   P1 = sanei_ir_accumulate_norm_histo (norm_histo);
 | |
|   P1_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
 | |
|   P2_sq = malloc (HISTOGRAM_SIZE * sizeof (double));
 | |
|   if (!P1 || !P1_sq || !P2_sq)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_threshold_yen: no buffers\n");
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   /* calculate cumulative squares */
 | |
|   P1_sq[0] = norm_histo[0] * norm_histo[0];
 | |
|   for (i = 1; i < HISTOGRAM_SIZE; i++)
 | |
|       P1_sq[i] = P1_sq[i - 1] + norm_histo[i] * norm_histo[i];
 | |
|   P2_sq[HISTOGRAM_SIZE - 1] = 0.0;
 | |
|   for (i = HISTOGRAM_SIZE - 2; i >= 0; i--)
 | |
|       P2_sq[i] = P2_sq[i + 1] + norm_histo[i + 1] * norm_histo[i + 1];
 | |
| 
 | |
|   /* Find the threshold that maximizes the criterion */
 | |
|   threshold = INT_MIN;
 | |
|   max_crit = DBL_MIN;
 | |
|   for (i = 0; i < HISTOGRAM_SIZE; i++)
 | |
|     {
 | |
|       crit =
 | |
|         -1.0 * SAFE_LOG (P1_sq[i] * P2_sq[i]) +
 | |
|         2 * SAFE_LOG (P1[i] * (1.0 - P1[i]));
 | |
|       if (crit > max_crit)
 | |
|         {
 | |
|           max_crit = crit;
 | |
|           threshold = i;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (threshold == INT_MIN)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_threshold_yen: no threshold found\n");
 | |
|       ret = SANE_STATUS_INVAL;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       ret = SANE_STATUS_GOOD;
 | |
|       if (params->depth > 8)
 | |
|         {
 | |
|           i = 1 << (params->depth - HISTOGRAM_SHIFT);
 | |
|           *thresh = threshold * i + i / 2;
 | |
|         }
 | |
|       else
 | |
|         *thresh = threshold;
 | |
|       DBG (10, "sanei_ir_threshold_yen: threshold %d\n", *thresh);
 | |
|     }
 | |
| 
 | |
|   cleanup:
 | |
|     if (P1)
 | |
|       free (P1);
 | |
|     if (P1_sq)
 | |
|       free (P1_sq);
 | |
|     if (P2_sq)
 | |
|       free (P2_sq);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Implements Otsu's thresholding method
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_threshold_otsu (const SANE_Parameters * params,
 | |
|                           double * norm_histo, int *thresh)
 | |
| {
 | |
|   double *cnh = NULL;
 | |
|   double *mean = NULL;
 | |
|   double total_mean;
 | |
|   double bcv, max_bcv;
 | |
|   int first_bin, last_bin;
 | |
|   int threshold, i;
 | |
|   SANE_Status ret = SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   DBG (10, "sanei_ir_threshold_otsu\n");
 | |
| 
 | |
|   cnh = sanei_ir_accumulate_norm_histo (norm_histo);
 | |
|   mean = malloc (HISTOGRAM_SIZE * sizeof (double));
 | |
|   if (!cnh || !mean)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_threshold_otsu: no buffers\n");
 | |
|       goto cleanup;
 | |
|     }
 | |
| 
 | |
|   mean[0] = 0.0;
 | |
|   for (i = 1; i < HISTOGRAM_SIZE; i++)
 | |
|       mean[i] = mean[i - 1] + i * norm_histo[i];
 | |
|   total_mean = mean[HISTOGRAM_SIZE - 1];
 | |
| 
 | |
|   first_bin = 0;
 | |
|   for (i = 0; i < HISTOGRAM_SIZE; i++)
 | |
|     if (cnh[i] != 0)
 | |
|       {
 | |
|         first_bin = i;
 | |
|         break;
 | |
|       }
 | |
|   last_bin = HISTOGRAM_SIZE - 1;
 | |
|   for (i = HISTOGRAM_SIZE - 1; i >= first_bin; i--)
 | |
|     if (1.0 - cnh[i] != 0)
 | |
|       {
 | |
|         last_bin = i;
 | |
|         break;
 | |
|       }
 | |
| 
 | |
|   threshold = INT_MIN;
 | |
|   max_bcv = 0.0;
 | |
|   for (i = first_bin; i <= last_bin; i++)
 | |
|     {
 | |
|       bcv = total_mean * cnh[i] - mean[i];
 | |
|       bcv *= bcv / (cnh[i] * (1.0 - cnh[i]));
 | |
|       if (max_bcv < bcv)
 | |
|         {
 | |
|           max_bcv = bcv;
 | |
|           threshold = i;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   if (threshold == INT_MIN)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_threshold_otsu: no threshold found\n");
 | |
|       ret = SANE_STATUS_INVAL;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       ret = SANE_STATUS_GOOD;
 | |
|       if (params->depth > 8)
 | |
|         {
 | |
|           i = 1 << (params->depth - HISTOGRAM_SHIFT);
 | |
|           *thresh = threshold * i + i / 2;
 | |
|         }
 | |
|       else
 | |
|         *thresh = threshold;
 | |
|       DBG (10, "sanei_ir_threshold_otsu: threshold %d\n", *thresh);
 | |
|     }
 | |
|   cleanup:
 | |
|     if (cnh)
 | |
|       free (cnh);
 | |
|     if (mean)
 | |
|       free (mean);
 | |
|     return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Implements a Maximum Entropy thresholding method
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_threshold_maxentropy (const SANE_Parameters * params,
 | |
|                                double * norm_histo, int *thresh)
 | |
| {
 | |
|  int ih, it;
 | |
|  int threshold;
 | |
|  int first_bin;
 | |
|  int last_bin;
 | |
|  double tot_ent, max_ent;       /* entropies */
 | |
|  double ent_back, ent_obj;
 | |
|  double *P1;                    /* cumulative normalized histogram */
 | |
|  double *P2;
 | |
|  SANE_Status ret = SANE_STATUS_NO_MEM;
 | |
| 
 | |
|  DBG (10, "sanei_ir_threshold_maxentropy\n");
 | |
| 
 | |
|  /* Calculate the cumulative normalized histogram */
 | |
|  P1 = sanei_ir_accumulate_norm_histo (norm_histo);
 | |
|  P2 = malloc (HISTOGRAM_SIZE * sizeof (double));
 | |
|  if (!P1 || !P2)
 | |
|    {
 | |
|      DBG (5, "sanei_ir_threshold_maxentropy: no buffers\n");
 | |
|      goto cleanup;
 | |
|    }
 | |
| 
 | |
|  for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
 | |
|    P2[ih] = 1.0 - P1[ih];
 | |
| 
 | |
|  first_bin = 0;
 | |
|  for ( ih = 0; ih < HISTOGRAM_SIZE; ih++ )
 | |
|    if (P1[ih] != 0)
 | |
|     {
 | |
|      first_bin = ih;
 | |
|      break;
 | |
|     }
 | |
|  last_bin = HISTOGRAM_SIZE - 1;
 | |
|  for ( ih = HISTOGRAM_SIZE - 1; ih >= first_bin; ih-- )
 | |
|    if (P2[ih] != 0)
 | |
|     {
 | |
|      last_bin = ih;
 | |
|      break;
 | |
|     }
 | |
| 
 | |
|  /* Calculate the total entropy each gray-level
 | |
|   * and find the threshold that maximizes it
 | |
|   */
 | |
|  threshold = INT_MIN;
 | |
|  max_ent = DBL_MIN;
 | |
|  for ( it = first_bin; it <= last_bin; it++ )
 | |
|   {
 | |
|    /* Entropy of the background pixels */
 | |
|    ent_back = 0.0;
 | |
|    for ( ih = 0; ih <= it; ih++ )
 | |
|      if (norm_histo[ih] != 0)
 | |
|        ent_back -= ( norm_histo[ih] / P1[it] ) * log ( norm_histo[ih] / P1[it] );
 | |
| 
 | |
|    /* Entropy of the object pixels */
 | |
|    ent_obj = 0.0;
 | |
|    for ( ih = it + 1; ih < HISTOGRAM_SIZE; ih++ )
 | |
|      if (norm_histo[ih] != 0)
 | |
|        ent_obj -= ( norm_histo[ih] / P2[it] ) * log ( norm_histo[ih] / P2[it] );
 | |
| 
 | |
|    /* Total entropy */
 | |
|    tot_ent = ent_back + ent_obj;
 | |
| 
 | |
|    if ( max_ent < tot_ent )
 | |
|     {
 | |
|      max_ent = tot_ent;
 | |
|      threshold = it;
 | |
|     }
 | |
|   }
 | |
| 
 | |
|  if (threshold == INT_MIN)
 | |
|    {
 | |
|      DBG (5, "sanei_ir_threshold_maxentropy: no threshold found\n");
 | |
|      ret = SANE_STATUS_INVAL;
 | |
|    }
 | |
|  else
 | |
|    {
 | |
|      ret = SANE_STATUS_GOOD;
 | |
|      if (params->depth > 8)
 | |
|        {
 | |
|          it = 1 << (params->depth - HISTOGRAM_SHIFT);
 | |
|          *thresh = threshold * it + it / 2;
 | |
|        }
 | |
|      else
 | |
|        *thresh = threshold;
 | |
|      DBG (10, "sanei_ir_threshold_maxentropy: threshold %d\n", *thresh);
 | |
|  }
 | |
| 
 | |
|  cleanup:
 | |
|    if (P1)
 | |
|      free (P1);
 | |
|    if (P2)
 | |
|      free (P2);
 | |
|    return ret;
 | |
| }
 | |
| 
 | |
| /* Generate gray scale luminance image from separate R, G, B images
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_RGB_luminance (SANE_Parameters * params, const SANE_Uint **in_img,
 | |
|                       SANE_Uint **out_img)
 | |
| {
 | |
|   SANE_Uint *outi;
 | |
|   int itop, i;
 | |
| 
 | |
|   if ((params->depth < 8) || (params->depth > 16) ||
 | |
|       (params->format != SANE_FRAME_GRAY))
 | |
|     {
 | |
|       DBG (5, "sanei_ir_RGB_luminance: invalid format\n");
 | |
|       return SANE_STATUS_UNSUPPORTED;
 | |
|     }
 | |
| 
 | |
|   itop = params->pixels_per_line * params->lines;
 | |
|   outi = malloc (itop * sizeof(SANE_Uint));
 | |
|   if (!outi)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_RGB_luminance: can not allocate out_img\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   for (i = itop; i > 0; i--)
 | |
|       *(outi++) = (218 * (int) *(in_img[0]++) +
 | |
|                    732 * (int) *(in_img[1]++) +
 | |
|                    74 * (int) *(in_img[2]++)) >> 10;
 | |
|   *out_img = outi;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /* Convert image from >8 bit depth to an 8 bit image
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_to_8bit (SANE_Parameters * params, const SANE_Uint *in_img,
 | |
|                  SANE_Parameters * out_params, SANE_Uint **out_img)
 | |
| {
 | |
|   SANE_Uint *outi;
 | |
|   size_t ssize;
 | |
|   int i, is;
 | |
| 
 | |
|   if ((params->depth < 8) || (params->depth > 16))
 | |
|     {
 | |
|       DBG (5, "sanei_ir_to_8bit: invalid format\n");
 | |
|       return SANE_STATUS_UNSUPPORTED;
 | |
|     }
 | |
|   ssize = params->pixels_per_line * params->lines;
 | |
|   if (params->format == SANE_FRAME_RGB)
 | |
|     ssize *= 3;
 | |
|   outi = malloc (ssize * sizeof(SANE_Uint));
 | |
|   if (!outi)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_to_8bit: can not allocate out_img\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   if (out_params)
 | |
|     {
 | |
|       memmove (out_params, params, sizeof(SANE_Parameters));
 | |
|       out_params->bytes_per_line = out_params->pixels_per_line;
 | |
|       if (params->format == SANE_FRAME_RGB)
 | |
|         out_params->bytes_per_line *= 3;
 | |
|       out_params->depth = 8;
 | |
|     }
 | |
| 
 | |
|   memmove (outi, in_img, ssize * sizeof(SANE_Uint));
 | |
|   is = params->depth - 8;
 | |
|   for (i = ssize; i > 0; i--) {
 | |
|     *outi = *outi >> is, outi += 2;
 | |
|   }
 | |
| 
 | |
|   *out_img = outi;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| /* allocate and initialize logarithmic lookup table
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_ln_table (int len, double **lut_ln)
 | |
| {
 | |
|   double *llut;
 | |
|   int i;
 | |
| 
 | |
|   DBG (10, "sanei_ir_ln_table\n");
 | |
| 
 | |
|   llut = malloc (len * sizeof (double));
 | |
|   if (!llut)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_ln_table: no table\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
|   llut[0] = 0;
 | |
|   llut[1] = 0;
 | |
|   for (i = 2; i < len; i++)
 | |
|     llut[i] = log ((double) i);
 | |
| 
 | |
|   *lut_ln = llut;
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Reduce red spectral overlap from an infrared image plane
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_spectral_clean (const SANE_Parameters * params, double *lut_ln,
 | |
| 			const SANE_Uint *red_data,
 | |
| 			SANE_Uint *ir_data)
 | |
| {
 | |
|   const SANE_Uint *rptr;
 | |
|   SANE_Uint *iptr;
 | |
|   SANE_Int depth;
 | |
|   double *llut;
 | |
|   double rval, rsum, rrsum;
 | |
|   double risum, rfac, radd;
 | |
|   double *norm_histo;
 | |
|   int64_t isum;
 | |
|   int *calc_buf, *calc_ptr;
 | |
|   int ival, imin, imax;
 | |
|   int itop, len, ssize;
 | |
|   int thresh_low, thresh;
 | |
|   int irand, i;
 | |
|   SANE_Status status;
 | |
| 
 | |
|   DBG (10, "sanei_ir_spectral_clean\n");
 | |
| 
 | |
|   itop = params->pixels_per_line * params->lines;
 | |
|   calc_buf = malloc (itop * sizeof (int));		/* could save this */
 | |
|   if (!calc_buf)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_spectral_clean: no buffer\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   depth = params->depth;
 | |
|   len = 1 << depth;
 | |
|   if (lut_ln)
 | |
|     llut = lut_ln;
 | |
|   else
 | |
|     {
 | |
|       status = sanei_ir_ln_table (len, &llut);
 | |
|       if (status != SANE_STATUS_GOOD) {
 | |
|         free (calc_buf);
 | |
|         return status;
 | |
|       }
 | |
|     }
 | |
| 
 | |
|   /* determine not transparent areas to exclude them later
 | |
|    * TODO: this has not been tested for negatives
 | |
|    */
 | |
|   thresh_low = INT_MAX;
 | |
|   status =
 | |
|       sanei_ir_create_norm_histogram (params, ir_data, &norm_histo);
 | |
|   if (status != SANE_STATUS_GOOD)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_spectral_clean: no buffer\n");
 | |
|       free (calc_buf);
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
| 
 | |
|   /* TODO: remember only needed if cropping is not ok */
 | |
|   status = sanei_ir_threshold_maxentropy (params, norm_histo, &thresh);
 | |
|   if (status == SANE_STATUS_GOOD)
 | |
|     thresh_low = thresh;
 | |
|   status = sanei_ir_threshold_otsu (params, norm_histo, &thresh);
 | |
|   if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
 | |
|     thresh_low = thresh;
 | |
|   status = sanei_ir_threshold_yen (params, norm_histo, &thresh);
 | |
|   if ((status == SANE_STATUS_GOOD) && (thresh < thresh_low))
 | |
|     thresh_low = thresh;
 | |
|   if (thresh_low == INT_MAX)
 | |
|     thresh_low = 0;
 | |
|   else
 | |
|     thresh_low /= 2;
 | |
|   DBG (10, "sanei_ir_spectral_clean: low threshold %d\n", thresh_low);
 | |
| 
 | |
|   /* calculate linear regression ired (red) from randomly chosen points */
 | |
|   ssize = itop / 2;
 | |
|   if (SAMPLE_SIZE < ssize)
 | |
|     ssize = SAMPLE_SIZE;
 | |
|   isum = 0;
 | |
|   rsum = rrsum = risum = 0.0;
 | |
|   i = ssize;
 | |
|   while (i > 0)
 | |
|     {
 | |
|       irand = rand () % itop;
 | |
|       rval = llut[red_data[irand]];
 | |
|       ival = ir_data[irand];
 | |
|       if (ival > thresh_low)
 | |
|         {
 | |
|           isum += ival;
 | |
|           rsum += rval;
 | |
|           rrsum += rval * rval;
 | |
|           risum += rval * (double) ival;
 | |
|           i--;
 | |
|         }
 | |
|     }
 | |
| 
 | |
|   /* "a" in ired = b + a * ln (red) */
 | |
|   rfac =
 | |
|     ((double) ssize * risum -
 | |
|     rsum * (double) isum) / ((double) ssize * rrsum - rsum * rsum);
 | |
|     radd = ((double) isum - rfac * rsum) / (double) ssize;      /* "b" unused */
 | |
| 
 | |
|   DBG (10, "sanei_ir_spectral_clean: n = %d, ired(red) = %f * ln(red) + %f\n",
 | |
|             ssize, rfac, radd);
 | |
| 
 | |
|   /* now calculate ired' = ired - a  * ln (red) */
 | |
|   imin = INT_MAX;
 | |
|   imax = INT_MIN;
 | |
|   rptr = red_data;
 | |
|   iptr = ir_data;
 | |
|   calc_ptr = calc_buf;
 | |
|     for (i = itop; i > 0; i--)
 | |
|       {
 | |
| 	ival = *iptr++ - (int) (rfac * llut[*rptr++] + 0.5);
 | |
| 	if (ival > imax)
 | |
| 	  imax = ival;
 | |
| 	if (ival < imin)
 | |
| 	  imin = ival;
 | |
| 	*calc_ptr++ = ival;
 | |
|       }
 | |
| 
 | |
|   /* scale the result back into the ired image */
 | |
|   calc_ptr = calc_buf;
 | |
|   iptr = ir_data;
 | |
|   rfac = (double) (len - 1) / (double) (imax - imin);
 | |
|     for (i = itop; i > 0; i--)
 | |
|       *iptr++ = (double) (*calc_ptr++ - imin) * rfac;
 | |
| 
 | |
|   if (!lut_ln)
 | |
|     free (llut);
 | |
|   free (calc_buf);
 | |
|   free (norm_histo);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Hopefully fast mean filter
 | |
|  * JV: what does this do? Remove local mean?
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_filter_mean (const SANE_Parameters * params,
 | |
| 		      const SANE_Uint *in_img, SANE_Uint *out_img,
 | |
| 		      int win_rows, int win_cols)
 | |
| {
 | |
|   const SANE_Uint *src;
 | |
|   SANE_Uint *dest;
 | |
|   int num_cols, num_rows;
 | |
|   int itop, iadd, isub;
 | |
|   int ndiv, the_sum;
 | |
|   int nrow, ncol;
 | |
|   int hwr, hwc;
 | |
|   int *sum;
 | |
|   int i, j;
 | |
| 
 | |
|   DBG (10, "sanei_ir_filter_mean, window: %d x%d\n", win_rows, win_cols);
 | |
| 
 | |
|   if (((win_rows & 1) == 0) || ((win_cols & 1) == 0))
 | |
|     {
 | |
|       DBG (5, "sanei_ir_filter_mean: window even sized\n");
 | |
|       return SANE_STATUS_INVAL;
 | |
|     }
 | |
| 
 | |
|   num_cols = params->pixels_per_line;
 | |
|   num_rows = params->lines;
 | |
| 
 | |
|   sum = malloc (num_cols * sizeof (int));
 | |
|   if (!sum)
 | |
|     {
 | |
|       DBG (5, "sanei_ir_filter_mean: no buffer for sums\n");
 | |
|       return SANE_STATUS_NO_MEM;
 | |
|     }
 | |
|   dest = out_img;
 | |
| 
 | |
|   hwr = win_rows / 2;		/* half window sizes */
 | |
|   hwc = win_cols / 2;
 | |
| 
 | |
|   /* pre-pre calculation */
 | |
|   for (j = 0; j < num_cols; j++)
 | |
|     {
 | |
|         sum[j] = 0;
 | |
| 	src = in_img + j;
 | |
| 	for (i = 0; i < hwr; i++)
 | |
| 	  {
 | |
| 	    sum[j] += *src;
 | |
| 	    src += num_cols;
 | |
| 	  }
 | |
|     }
 | |
| 
 | |
|   itop = num_rows * num_cols;
 | |
|   iadd = hwr * num_cols;
 | |
|   isub = (hwr - win_rows) * num_cols;
 | |
|   nrow = hwr;
 | |
| 
 | |
|       for (i = 0; i < num_rows; i++)
 | |
| 	{
 | |
| 	  /* update row sums if possible */
 | |
| 	  if (isub >= 0)	/* subtract old row */
 | |
| 	    {
 | |
| 	      nrow--;
 | |
| 	      src = in_img + isub;
 | |
| 	      for (j = 0; j < num_cols; j++)
 | |
| 		sum[j] -= *src++;
 | |
| 	    }
 | |
| 	  isub += num_cols;
 | |
| 
 | |
| 	  if (iadd < itop)	/* add new row */
 | |
| 	    {
 | |
| 	      nrow++;
 | |
| 	      src = in_img + iadd;
 | |
| 	      for (j = 0; j < num_cols; j++)
 | |
| 		sum[j] += *src++;
 | |
| 	    }
 | |
| 	  iadd += num_cols;
 | |
| 
 | |
| 	  /* now we do the image columns using only the precalculated sums */
 | |
| 
 | |
| 	  the_sum = 0;		/* precalculation */
 | |
| 	  for (j = 0; j < hwc; j++)
 | |
| 	    the_sum += sum[j];
 | |
| 	  ncol = hwc;
 | |
| 
 | |
| 	  /* at the left margin, real index hwc lower */
 | |
| 	  for (j = hwc; j < win_cols; j++)
 | |
| 	    {
 | |
| 	      ncol++;
 | |
| 	      the_sum += sum[j];
 | |
| 	      *dest++ = the_sum / (ncol * nrow);
 | |
| 	    }
 | |
| 
 | |
| 	  ndiv = ncol * nrow;
 | |
| 	  /* in the middle, real index hwc + 1 higher */
 | |
| 	  for (j = 0; j < num_cols - win_cols; j++)
 | |
| 	    {
 | |
| 	      the_sum -= sum[j];
 | |
| 	      the_sum += sum[j + win_cols];
 | |
| 	      *dest++ = the_sum / ndiv;
 | |
| 	    }
 | |
| 
 | |
| 	  /* at the right margin, real index hwc + 1 higher */
 | |
| 	  for (j = num_cols - win_cols; j < num_cols - hwc - 1; j++)
 | |
| 	    {
 | |
| 	      ncol--;
 | |
| 	      the_sum -= sum[j];	/* j - hwc - 1 */
 | |
| 	      *dest++ = the_sum / (ncol * nrow);
 | |
| 	    }
 | |
| 	}
 | |
|   free (sum);
 | |
|   return SANE_STATUS_GOOD;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Find noise by adaptive thresholding
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_filter_madmean (const SANE_Parameters * params,
 | |
| 			 const SANE_Uint *in_img,
 | |
| 			 SANE_Uint ** out_img, int win_size,
 | |
| 			 int a_val, int b_val)
 | |
| {
 | |
|   SANE_Uint *delta_ij, *delta_ptr;
 | |
|   SANE_Uint *mad_ij;
 | |
|   const SANE_Uint *mad_ptr;
 | |
|   SANE_Uint *out_ij, *dest8;
 | |
|   double ab_term;
 | |
|   int num_rows, num_cols;
 | |
|   int threshold, itop;
 | |
|   size_t size;
 | |
|   int ival, i;
 | |
|   int depth;
 | |
|   SANE_Status ret = SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   DBG (10, "sanei_ir_filter_madmean\n");
 | |
| 
 | |
|   depth = params->depth;
 | |
|   if (depth != 8)
 | |
|     {
 | |
|       a_val = a_val << (depth - 8);
 | |
|       b_val = b_val << (depth - 8);
 | |
|     }
 | |
|   num_cols = params->pixels_per_line;
 | |
|   num_rows = params->lines;
 | |
|   itop = num_rows * num_cols;
 | |
|   size = itop * sizeof (SANE_Uint);
 | |
|   out_ij = malloc (size);
 | |
|   delta_ij = malloc (size);
 | |
|   mad_ij = malloc (size);
 | |
| 
 | |
|   if (out_ij && delta_ij && mad_ij)
 | |
|     {
 | |
|       /* get the differences to the local mean */
 | |
|       mad_ptr = in_img;
 | |
|       if (sanei_ir_filter_mean (params, mad_ptr, delta_ij, win_size, win_size)
 | |
| 	  == SANE_STATUS_GOOD)
 | |
| 	{
 | |
| 	  delta_ptr = delta_ij;
 | |
| 	    for (i = 0; i < itop; i++)
 | |
| 	      {
 | |
| 		ival = *mad_ptr++ - *delta_ptr;
 | |
| 		*delta_ptr++ = abs (ival);
 | |
| 	      }
 | |
| 	  /* make the second filtering window a bit larger */
 | |
| 	  win_size = MAD_WIN2_SIZE(win_size);
 | |
| 	  /* and get the local mean differences */
 | |
| 	  if (sanei_ir_filter_mean
 | |
| 	      (params, delta_ij, mad_ij, win_size,
 | |
| 	       win_size) == SANE_STATUS_GOOD)
 | |
| 	    {
 | |
| 	      mad_ptr = mad_ij;
 | |
| 	      delta_ptr = delta_ij;
 | |
| 	      dest8 = out_ij;
 | |
| 	      /* construct the noise map */
 | |
| 	      ab_term = (b_val - a_val) / (double) b_val;
 | |
| 		for (i = 0; i < itop; i++)
 | |
| 		  {
 | |
| 		    /* by calculating the threshold */
 | |
| 		    ival = *mad_ptr++;
 | |
| 		    if (ival >= b_val)	/* outlier */
 | |
| 		      threshold = a_val;
 | |
| 		    else
 | |
| 		      threshold = a_val + (double) ival *ab_term;
 | |
| 		    /* above threshold is noise, indicated by 0 */
 | |
| 		    if (*delta_ptr++ >= threshold)
 | |
| 		      *dest8++ = 0;
 | |
| 		    else
 | |
| 		      *dest8++ = 255;
 | |
| 		  }
 | |
| 	      *out_img = out_ij;
 | |
| 	      ret = SANE_STATUS_GOOD;
 | |
| 	    }
 | |
| 	}
 | |
|     }
 | |
|   else
 | |
|     DBG (5, "sanei_ir_filter_madmean: Cannot allocate buffers\n");
 | |
| 
 | |
|   free (mad_ij);
 | |
|   free (delta_ij);
 | |
|   return ret;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Add dark pixels to mask from static threshold
 | |
|  */
 | |
| void
 | |
| sanei_ir_add_threshold (const SANE_Parameters * params,
 | |
| 			const SANE_Uint *in_img,
 | |
| 			SANE_Uint * mask_img, int threshold)
 | |
| {
 | |
|   const SANE_Uint *in_ptr;
 | |
|   SANE_Uint *mask_ptr;
 | |
|   int itop, i;
 | |
| 
 | |
|   DBG (10, "sanei_ir_add_threshold\n");
 | |
| 
 | |
|   itop = params->pixels_per_line * params->lines;
 | |
|   in_ptr = in_img;
 | |
|   mask_ptr = mask_img;
 | |
| 
 | |
|     for (i = itop; i > 0; i--)
 | |
|       {
 | |
| 	if (*in_ptr++ <= threshold)
 | |
| 	  *mask_ptr = 0;
 | |
| 	mask_ptr++;
 | |
|       }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Calculate minimal Manhattan distances for an image mask
 | |
|  */
 | |
| void
 | |
| sanei_ir_manhattan_dist (const SANE_Parameters * params,
 | |
| 			const SANE_Uint * mask_img, unsigned int *dist_map,
 | |
| 			unsigned int *idx_map, unsigned int erode)
 | |
| {
 | |
|   const SANE_Uint *mask;
 | |
|   unsigned int *index, *manhattan;
 | |
|   int rows, cols, itop;
 | |
|   int i, j;
 | |
| 
 | |
|   DBG (10, "sanei_ir_manhattan_dist\n");
 | |
| 
 | |
|   if (erode != 0)
 | |
|     erode = 255;
 | |
| 
 | |
|   /* initialize maps */
 | |
|   cols = params->pixels_per_line;
 | |
|   rows = params->lines;
 | |
|   itop = rows * cols;
 | |
|   mask = mask_img;
 | |
|   manhattan = dist_map;
 | |
|   index = idx_map;
 | |
|   for (i = 0; i < itop; i++)
 | |
|     {
 | |
|       *manhattan++ = *mask++;
 | |
|       *index++ = i;
 | |
|     }
 | |
| 
 | |
|   /* traverse from top left to bottom right */
 | |
|   manhattan = dist_map;
 | |
|   index = idx_map;
 | |
|   for (i = 0; i < rows; i++)
 | |
|     for (j = 0; j < cols; j++)
 | |
|       {
 | |
| 	if (*manhattan == erode)
 | |
| 	  {
 | |
| 	    /* take original, distance = 0, index stays the same */
 | |
| 	    *manhattan = 0;
 | |
| 	  }
 | |
| 	else
 | |
| 	  {
 | |
| 	    /* assume maximal distance to clean pixel */
 | |
| 	    *manhattan = cols + rows;
 | |
| 	    /* or one further away than pixel to the top */
 | |
| 	    if (i > 0)
 | |
| 	      if (manhattan[-cols] + 1 < *manhattan)
 | |
| 		{
 | |
| 		  *manhattan = manhattan[-cols] + 1;
 | |
| 		  *index = index[-cols];	/* index follows */
 | |
| 		}
 | |
| 	    /* or one further away than pixel to the left */
 | |
| 	    if (j > 0)
 | |
| 	      {
 | |
| 		if (manhattan[-1] + 1 < *manhattan)
 | |
| 		  {
 | |
| 		    *manhattan = manhattan[-1] + 1;
 | |
| 		    *index = index[-1];	/* index follows */
 | |
| 		  }
 | |
| 		if (manhattan[-1] + 1 == *manhattan)
 | |
| 		  if (rand () % 2 == 0)	/* chose index */
 | |
| 		    *index = index[-1];
 | |
| 	      }
 | |
| 	  }
 | |
| 	manhattan++;
 | |
| 	index++;
 | |
|       }
 | |
| 
 | |
|   /* traverse from bottom right to top left */
 | |
|   manhattan = dist_map + itop - 1;
 | |
|   index = idx_map + itop - 1;
 | |
|   for (i = rows - 1; i >= 0; i--)
 | |
|     for (j = cols - 1; j >= 0; j--)
 | |
|       {
 | |
| 	if (i < rows - 1)
 | |
| 	  {
 | |
| 	    /* either what we had on the first pass
 | |
| 	       or one more than the pixel to the bottm */
 | |
| 	    if (manhattan[+cols] + 1 < *manhattan)
 | |
| 	      {
 | |
| 		*manhattan = manhattan[+cols] + 1;
 | |
| 		*index = index[+cols];	/* index follows */
 | |
| 	      }
 | |
| 	    if (manhattan[+cols] + 1 == *manhattan)
 | |
| 	      if (rand () % 2 == 0)	/* chose index */
 | |
| 		*index = index[+cols];
 | |
| 	  }
 | |
| 	if (j < cols - 1)
 | |
| 	  {
 | |
| 	    /* or one more than pixel to the right */
 | |
| 	    if (manhattan[1] + 1 < *manhattan)
 | |
| 	      {
 | |
| 		*manhattan = manhattan[1] + 1;
 | |
| 		*index = index[1];	/* index follows */
 | |
| 	      }
 | |
| 	    if (manhattan[1] + 1 == *manhattan)
 | |
| 	      if (rand () % 2 == 0)	/* chose index */
 | |
| 		*index = index[1];
 | |
| 	  }
 | |
| 	manhattan--;
 | |
| 	index--;
 | |
|       }
 | |
| }
 | |
| 
 | |
| 
 | |
| /* dilate or erode a mask image */
 | |
| 
 | |
| void
 | |
| sanei_ir_dilate (const SANE_Parameters *params, SANE_Uint *mask_img,
 | |
| 		unsigned int *dist_map, unsigned int *idx_map, int by)
 | |
| {
 | |
|   SANE_Uint *mask;
 | |
|   unsigned int *manhattan;
 | |
|   unsigned int erode;
 | |
|   unsigned int thresh;
 | |
|   int i, itop;
 | |
| 
 | |
|   DBG (10, "sanei_ir_dilate\n");
 | |
| 
 | |
|   if (by == 0)
 | |
|     return;
 | |
|   if (by > 0)
 | |
|     {
 | |
|       erode = 0;
 | |
|       thresh = by;
 | |
|     }
 | |
|   else
 | |
|     {
 | |
|       erode = 1;
 | |
|       thresh = -by;
 | |
|     }
 | |
| 
 | |
|   itop = params->pixels_per_line * params->lines;
 | |
|   mask = mask_img;
 | |
|   sanei_ir_manhattan_dist (params, mask_img, dist_map, idx_map, erode);
 | |
| 
 | |
|   manhattan = dist_map;
 | |
|   for (i = 0; i < itop; i++)
 | |
|     {
 | |
|       if (*manhattan++ <= thresh)
 | |
| 	*mask++ = 0;
 | |
|       else
 | |
| 	*mask++ = 255;
 | |
|     }
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Suggest cropping for dark margins of positive film
 | |
|  */
 | |
| void
 | |
| sanei_ir_find_crop (const SANE_Parameters * params,
 | |
|                     unsigned int * dist_map, int inner, int * edges)
 | |
| {
 | |
|   int width = params->pixels_per_line;
 | |
|   int height = params->lines;
 | |
|   uint64_t sum_x, sum_y, n;
 | |
|   int64_t sum_xx, sum_xy;
 | |
|   double a, b, mami;
 | |
|   unsigned int *src;
 | |
|   int off1, off2, inc, wh, i, j;
 | |
| 
 | |
|   DBG (10, "sanei_ir_find_crop\n");
 | |
| 
 | |
|   /* loop through top, bottom, left, right */
 | |
|   for (j = 0; j < 4; j++)
 | |
|     {
 | |
|       if (j < 2)        /* top, bottom */
 | |
|         {
 | |
|           off1 = width / 8;     /* only middle 3/4 */
 | |
|           off2 = width - off1;
 | |
|           n = width - 2 * off1;
 | |
|           src = dist_map + off1;        /* first row */
 | |
|           inc = 1;
 | |
|           wh = width;
 | |
|           if (j == 1)                   /* last row */
 | |
|             src += (height - 1) * width;
 | |
|         }
 | |
|       else              /* left, right */
 | |
|         {
 | |
|           off1 = height / 8;     /* only middle 3/4 */
 | |
|           off2 = height - off1;
 | |
|           n = height - 2 * off1;
 | |
|           src = dist_map + (off1 * width);      /* first column */
 | |
|           inc = width;
 | |
|           wh = height;
 | |
|           if (j == 3)
 | |
|             src += width - 1;                   /* last column */
 | |
|         }
 | |
| 
 | |
|       /* calculate linear regression */
 | |
|       sum_x = 0; sum_y = 0;
 | |
|       sum_xx = 0; sum_xy = 0;
 | |
|       for (i = off1; i < off2; i++)
 | |
|         {
 | |
|           sum_x += i;
 | |
|           sum_y += *src;
 | |
|           sum_xx += i * i;
 | |
|           sum_xy += i * (*src);
 | |
|           src += inc;
 | |
|         }
 | |
|       b = ((double) n * (double) sum_xy - (double) sum_x * (double) sum_y)
 | |
|           / ((double) n * (double) sum_xx - (double) sum_x * (double) sum_x);
 | |
|       a = ((double) sum_y - b * (double) sum_x) / (double) n;
 | |
| 
 | |
|       DBG (10, "sanei_ir_find_crop: y = %f + %f * x\n", a, b);
 | |
| 
 | |
|       /* take maximal/minimal value from either side */
 | |
|       mami = a + b * (wh - 1);
 | |
|       if (inner)
 | |
|         {
 | |
|           if (a > mami)
 | |
|             mami = a;
 | |
|         }
 | |
|       else
 | |
|         {
 | |
|           if (a < mami)
 | |
|             mami = a;
 | |
|         }
 | |
|       edges[j] = mami + 0.5;
 | |
|     }
 | |
|   edges[1] = height - edges[1];
 | |
|   edges[3] = width - edges[3];
 | |
| 
 | |
|   DBG (10, "sanei_ir_find_crop: would crop at top: %d, bot: %d, left %d, right %d\n",
 | |
|       edges[0], edges[1], edges[2], edges[3]);
 | |
| 
 | |
|   return;
 | |
| }
 | |
| 
 | |
| 
 | |
| /* Dilate clean image parts into dirty ones and smooth
 | |
|  */
 | |
| SANE_Status
 | |
| sanei_ir_dilate_mean (const SANE_Parameters * params,
 | |
|                       SANE_Uint **in_img,
 | |
|                       SANE_Uint * mask_img,
 | |
|                       int dist_max, int expand, int win_size,
 | |
|                       SANE_Bool smooth, int inner,
 | |
|                       int *crop)
 | |
| {
 | |
|   SANE_Uint *color;
 | |
|   SANE_Uint *plane;
 | |
|   unsigned int *dist_map, *manhattan;
 | |
|   unsigned int *idx_map, *index;
 | |
|   int dist;
 | |
|   int rows, cols;
 | |
|   int k, i, itop;
 | |
|   SANE_Status ret = SANE_STATUS_NO_MEM;
 | |
| 
 | |
|   DBG (10, "sanei_ir_dilate_mean(): dist max = %d, expand = %d, win size = %d, smooth = %d, inner = %d\n",
 | |
|     dist_max, expand, win_size, smooth, inner);
 | |
| 
 | |
|   cols = params->pixels_per_line;
 | |
|   rows = params->lines;
 | |
|   itop = rows * cols;
 | |
|   idx_map = malloc (itop * sizeof (unsigned int));
 | |
|   dist_map = malloc (itop * sizeof (unsigned int));
 | |
|   plane = malloc (itop * sizeof (SANE_Uint));
 | |
| 
 | |
|   if (!idx_map || !dist_map || !plane)
 | |
|     DBG (5, "sanei_ir_dilate_mean: Cannot allocate buffers\n");
 | |
|   else
 | |
|     {
 | |
|       /* expand dirty regions into their half dirty surround*/
 | |
|       if (expand > 0)
 | |
| 	sanei_ir_dilate (params, mask_img, dist_map, idx_map, expand);
 | |
|       /* for dirty pixels determine the closest clean ones */
 | |
|       sanei_ir_manhattan_dist (params, mask_img, dist_map, idx_map, 1);
 | |
| 
 | |
|       /* use the distance map to find how to crop dark edges */
 | |
|       if (crop)
 | |
|         sanei_ir_find_crop (params, dist_map, inner, crop);
 | |
| 
 | |
|       /* replace dirty pixels */
 | |
|       for (k = 0; k < 3; k++)
 | |
| 	{
 | |
| 	  manhattan = dist_map;
 | |
| 	  index = idx_map;
 | |
| 	  color = in_img[k];
 | |
| 	  /* first replacement */
 | |
| 	    for (i = 0; i < itop; i++)
 | |
| 	      {
 | |
| 		dist = *manhattan++;
 | |
| 		if ((dist != 0) && (dist <= dist_max))
 | |
| 		  color[i] = color[index[i]];
 | |
| 	      }
 | |
|           /* adapt pixels to their new surround and
 | |
|            * smooth the whole image or the replaced pixels only */
 | |
| 	  ret =
 | |
| 	    sanei_ir_filter_mean (params, color, plane, win_size, win_size);
 | |
| 	  if (ret != SANE_STATUS_GOOD)
 | |
| 	    break;
 | |
| 	  else
 | |
| 	    if (smooth)
 | |
|               {
 | |
|                 /* a second mean results in triangular blur */
 | |
|                 DBG (10, "sanei_ir_dilate_mean(): smoothing whole image\n");
 | |
|                 ret =
 | |
|                   sanei_ir_filter_mean (params, plane, color, win_size,
 | |
|                                         win_size);
 | |
|                 if (ret != SANE_STATUS_GOOD)
 | |
|                   break;
 | |
|               }
 | |
|             else
 | |
|               {
 | |
|                 /* replace with smoothened pixels only */
 | |
|                 DBG (10, "sanei_ir_dilate_mean(): smoothing replaced pixels only\n");
 | |
|                 manhattan = dist_map;
 | |
|                   for (i = 0; i < itop; i++)
 | |
|                     {
 | |
|                       dist = *manhattan++;
 | |
|                       if ((dist != 0) && (dist <= dist_max))
 | |
|                         color[i] = plane[i];
 | |
|                     }
 | |
|               }
 | |
|       }
 | |
|     }
 | |
|   free (plane);
 | |
|   free (dist_map);
 | |
|   free (idx_map);
 | |
| 
 | |
|   return ret;
 | |
| }
 |