
// filter.c	V1.01	28.2.2000

#include <stdio.h>
#include <stdlib.h>

#include "vision.h"
#include "filter.h"

#define DEBUG 0


/*****************************************************************
**********		UTILITY FUNCTIONS		**********
*****************************************************************/



/*******************************************
Function that is used to compare two long values. This function is
used only with quicksort. 

Parameters:
-----------
a, b:		Values, that are compared
*******************************************/

int CompareValues(int *a, int *b)
{
   if( (*a - *b) < 0)
      return -1;
   else if( (*a - *b) > 0)
      return 1;
   else
      return 0;
   
}



/*****************************************************************
**********		FILTERING FUNCTIONS		**********
*****************************************************************/



/************************************************************
Function for searching red, green or blue pixels. Pixel will be 
accepted if 100 * (color / red+green+blue) >= threshold and
red+green+blue >= min_intensity.

Parameters:
-----------

*picture:	Pointer to the struct, that contains the picture data.
color:		The color to be searched (0=blue, 1=green, 2=red).
threshold:	Persentage of the color in an acceptable pixel (0-100).
min_intensity:	Minimun intensity of an acceptable pixel.

************************************************************/

bitPicture *simpleFilter1(rgbPicture *picture, int color, int threshold,
                           int min_intensity)
{

   bitPicture *result = NULL;

   int i, j;
   int rows, cols;


   // ----- Check picture -----
   if(picture == NULL)
   {
      printf("Null picture parameter (simpleFilter1)!\n");
      return NULL;
   }
   rows = picture->y_size;
   cols = picture->x_size;


   // ----- Check color -----
   if( (color < 0) || (color > 2) )
      color = 0;


   // ----- Check threshold -----
   if(threshold < 0)
      threshold = 0;
   else if(threshold > 100)
      threshold = 100;


   // ----- Check min_intensity -----
   if(min_intensity < 1)
      min_intensity = 1;
   else if(min_intensity > 3*255)
      min_intensity = 3*255;
      
   
   // ----- Allocate memory for result picture -----
   result = (bitPicture *)malloc(sizeof(bitPicture));
   if(result != NULL)
   {
      result->buf = (unsigned char *)malloc(rows*cols*sizeof(unsigned char));
      result->x_size = cols;
      result->y_size = rows;
   }

   if( (result == NULL) || (result->buf == NULL))
   {
      printf("Memory allocation failed (simpleFilter1)!\n");
      return NULL;
   }
   
   
   // ----- Process pixels -----
   for(i=0; i<rows; i++)
   {
      for(j=0; j<cols; j++)
      {
            
         int color_sum = picture->buf[(i*cols+j)*3] + picture->buf[(i*cols+j)*3+1] +
		         picture->buf[(i*cols+j)*3+2];

         // ----- If the intensity and color of the pixel are valid -----
         if( (color_sum >= min_intensity) &&
             ((int)(100 * (double)picture->buf[(i*cols+j)*3+color] / 
                          (double)color_sum) >= threshold) )
	 {	    
	    result->buf[i*cols+j] = 1;	 
	 }
	 
	 else
	 {
	    result->buf[i*cols+j] = 0;
	 }
      
      }
   }

   return result;

}



/************************************************************
Function for searching red, green or blue pixels. The function will
first calculate the threshold color level. Pixels will then be
selected by calling the simpleFilter1 function.

Parameters:
-----------

*picture:	Pointer to the struct, that contains the picture data.
color:		The color to be searched (0=blue, 1=green, 2=red).
min_intensity:	Minimun intensity of an acceptable pixel.

************************************************************/

bitPicture *autoFilter(rgbPicture *picture, int color, int min_intensity)
{

   bitPicture *result = NULL;

   int i, j;
   int rows, cols;
   int threshold = 0;

   long color_level_freq[101];
   long number_of_pixels = 0;
   

   // ----- Check picture -----
   if(picture == NULL)
   {
      printf("Null picture parameter (autoFilter)!\n");
      return NULL;
   }
   rows = picture->y_size;
   cols = picture->x_size;


   // ----- Check color -----
   if( (color < 0) || (color > 2) )
      color = 0;


   // ----- Check min_intensity -----
   if(min_intensity < 1)
      min_intensity = 1;
   else if(min_intensity > 3*255)
      min_intensity = 3*255;
      
   
   // ----- Initialize color_levels -----
   for(i=0; i<101; i++)
      color_level_freq[i] = 0;
   
   
   // ----- Allocate memory for result picture -----
   result = (bitPicture *)malloc(sizeof(bitPicture));
   if(result != NULL)
   {
      result->buf = (unsigned char *)malloc(rows*cols*sizeof(unsigned char));
      result->x_size = cols;
      result->y_size = rows;
   }

   if( (result == NULL) || (result->buf == NULL))
   {
      printf("Memory allocation failed (autoFilter)!\n");
      return NULL;
   }
   


   // ----- Calculate color level frequencies -----
   for(i=0; i<rows; i++)
   {
      for(j=0; j<cols; j++)
      {
      
         int color_sum = picture->buf[(i*cols+j)*3] + picture->buf[(i*cols+j)*3+1] +
		         picture->buf[(i*cols+j)*3+2];

         if(color_sum >= min_intensity)
         {
            color_level_freq[(int)(100 * picture->buf[(i*cols+j)*3+color] / color_sum)]++;
            number_of_pixels++;
         }         
         
      }
   }


   if(DEBUG)
   {
      printf("Pixels: %ld\n", number_of_pixels);
      printf("Limit: %ld\n", (long)(PIXELS_THRESHOLD * (double)number_of_pixels));

      printf("Freqs:\n");
      for(i=0; i<101; i++)
      {
         printf("%d:\t%d\n", i, color_level_freq[i]);
      }
   }


   // ----- Calculate threshold color level -----
   if(number_of_pixels > 0)
   {
   
      long sum = 0;
   
      for(i=100; i>=0; i--)
      {
      
         sum = sum + color_level_freq[i];
         if(sum >= PIXELS_THRESHOLD * (double)number_of_pixels)
         {
            threshold = i;
            break;
         }
   
      }
   }

   if(DEBUG) printf("threshold=%d\n", threshold);

   return simpleFilter1(picture, color, threshold, min_intensity);

}



/***************************************************
Function, that applies median filtering to a picture.

Parameters:
-----------
*orig_picture:	Pointer to the struct, that contains the picture data.
mask_size:	Size of the filtering mask. Only odd positive values
                are accepted.
***************************************************/

int medianFilter(rgbPicture *picture, int mask_size)
{

   rgbPicture *result = NULL;

   int pic_color, pic_col, pic_row;
   int i, j;
   int rows, cols;
   int dxdy = (int)mask_size/2;

   long *mask_values;


   // ----- Check picture -----
   if(picture == NULL)
   {
      printf("Null picture parameter (medianFilter)!\n");
      return 0;
   }
   rows = picture->y_size;
   cols = picture->x_size;

   
   // ----- Accept only odd positive values for mask_size -----

   if( (mask_size < 0) || (mask_size%2) == 0 )
   {
      printf("Bad mask_size argument (medianFilter)!\n");
      return 0;
   }



   // ----- Allocate memory for result picture -----

   result = (rgbPicture *)malloc(sizeof(rgbPicture));
   if(result != NULL)
   {
      result->buf = (unsigned char *)malloc(3*rows*cols*sizeof(unsigned char));
      result->x_size = cols;
      result->y_size = rows;
   }
   if( (result == NULL) || (result->buf == NULL))
   {
      printf("Memory allocation failed (medianFilter)!\n");
      return 0;
   }



   // ----- Allocate memory for list of values under the mask -----

   mask_values = (long *)malloc(mask_size*mask_size*sizeof(long));
   if(mask_values == NULL)
   {
      printf("Memory allocation failed (medianFilter)!\n");
      return 0;
   }



   // ----- Process picture -----

   for(pic_color=0; pic_color < 3; pic_color++)
      for(pic_row=0; pic_row < rows; pic_row++)
         for(pic_col=0; pic_col < cols; pic_col++)
         {
         
            int temp_neighbors = 0;
                        
            // ----- Check values within the mask -----
            
            for(i=(pic_row - dxdy); i <= pic_row + dxdy; i++)
            {
               for(j=(pic_col - dxdy); j <= pic_col + dxdy; j++)
               {
         
                  // ----- If neighbor coordinates are valid -----
         
                  if( (i>=0) && (i<rows) && (j>=0) && (j<cols) )
                  {
                     mask_values[temp_neighbors] = picture->buf[(i*cols+j)*3+pic_color];
                     temp_neighbors++;
                  }
               }         
            }
            
         
            // ----- Sort mask_values -----
         
            qsort(mask_values, temp_neighbors, sizeof(long),
                 (int (*)(const void *, const void *)) CompareValues);


            // ----- Select median value -----
                  
            result->buf[(pic_row*cols+pic_col)*3+pic_color] =
               mask_values[(int)(temp_neighbors/2)];
         }



   // ----- Copy filtered values to original picture -----

   for(pic_color=0; pic_color < 3; pic_color++)
      for(pic_row=0; pic_row < rows; pic_row++)
         for(pic_col=0; pic_col < cols; pic_col++)
         {
            picture->buf[(pic_row*cols+pic_col)*3+pic_color] = 
                   result->buf[(pic_row*cols+pic_col)*3+pic_color];
          } 
 
 
   free(mask_values);
   free(result->buf);
   free(result);

   return 1;

}


