next up previous contents
Next: zernike.h Up: CHO Data Previous: CHO Data

zernike.cpp

 
//////////////////////////////////////////////////////////////////////////
//
//
//                              zernike.cpp
//
//
//                           Michael Boland
//                          21 February 1996 
//
//
// Revisions:
//    13Jan96:  Added test code for use in determining z=1 for Zernike
//               calculations. -M. Boland
//    19Jan96:  Moved cell_zernike() to cell_z.cpp -M. Boland
//    21Feb96:  Moved cell_z.cpp to zernike.cpp. -M. Boland
//    28Feb96:  Added functions to support the external calculation of 
//                features for the Cell or for Objects. -M. Boland
//    08Mar96:  Fixed behavior for case where total intensity = 0
//                - M. Boland
//    15Jul96:  Added ability to specify the order of Zernike moments
//                to be calculated and to specify the radius of 
//                integration. -M. Boland
//
//
//////////////////////////////////////////////////////////////////////////

#include 
#include 
#include 
#include 
#include 

#include "cell.h"
#include "object.h"
#include "zernike.h"


//
// Calculates n! (uses double arithmetic to avoid overflow)
//
double factorial(long n)
{
	if(n < 0)
		return(0.0) ;
	if(n == 0)
		return(1.0) ;
	else
		return(n * factorial(n-1)) ;
}	


//
// Equations for Zernike moments from Prokop and Reeves, CVGIP: Graphical
//   Models and Image Processing, v 54 n 5, Sep 92, pp 438-460.
//

//
// Function to calculate the Zernike polynomial Rnl(r).
//  Note: 0 <= r <= 1
//

double zernikeR(int n, int l, double r)
{
  int m ;
  double sum = 0.0 ;
  
  if( ((n-l) % 2) != 0 )
    {
      cerr << "zernikeR(): improper values of n,l\n" ;
      return(0) ;
    }
  
  for(m = 0; m <= (n-l)/2; m++)
    {
      sum += (pow((double)-1.0,(double)m)) * ( factorial(n-m) ) / 
	( factorial(m) * (factorial((n - 2*m + l) / 2)) *
	  (factorial((n - 2*m - l) / 2)) ) *
	( pow((double)r, (double)(n - 2*m)) );
    }
  
  return(sum) ;
}

//
// Function for the Zernike moments.
//  Notes: requires xbar and ybar to perform translation and r_max to
//    normalize rho to fall between 0 and 1.
//

complex zernikeZ(Pixel pixel_array[], long num_pixels, 
			 double total_color_intensity, int n, int l, 
			 int color, double xbar, double ybar, double r_max)
{
  double rho ;		// radius of pixel from COM
  double theta ;		// angle of pixel
  complex integral(0,0) ;
  // summation of zernike calculations over all pixels 
  double x, y ;		// pixel coordinates
  double intensity ;	// pixel intensity
  
  if (!pixel_array)
    {
      cerr << "zernikeZ(): no pixels.\n" ;
      return(-1.0) ;
    }
  
  for(int i = 0; i < num_pixels; i++)
    {
      x = (double)(pixel_array[i].x) - (double)xbar ;
      y = (double)(pixel_array[i].y) - (double)ybar ;
      
      //
      // Normalize the intensity of each pixel to the total intensity
      //
      
      if(total_color_intensity > 0.0)
	{
	  intensity = (double)(pixel_array[i].intensity[color]) / 
	    (double)(total_color_intensity) ;
	}
      else
	{
	  intensity = 0.0 ;
	}

      //
      // Normalize the radial distance to each pixel to be <= 1
      // 
      
      if(r_max > 0.0)
	rho = (double)sqrt(x*x + y*y) / (double)r_max ;
      else
	rho = 0.0 ;
      
      if(rho <= 1.0)
	{
	  if(y == 0.0 && x == 0.0)
	    theta = 0.0 ;
	  else
	    theta = atan2(y, x) ;
	  integral += conj(zernikeR(n,l,rho) * polar(1.0, l*theta)) *
	    // 
	    // Confirm this formula
	    //	    intensity ;
	    intensity * rho ;
	}
    } // end for
  return(integral * (n+1)/PI) ;
}


int zernike(Moments* moments_struct, Pixel pixel_array[], long z_order,
	    long num_pixels, long radius, int color, 
	    double total_color_intensity, ofstream& outfile)
{
  // 
  // Number of features generated by Zernike moments up to order z_order.
  //
  long   z_num_features = (long)( ((z_order + 4) * 
				   (z_order + 1) - 2 * 
				   (long)(((long)z_order + 1) / (long)2) ) / 4 ) ;
  //
  // Storage for new features
  //
  double* zfeatures = new double[z_num_features + 1] ;
  int     zindex = 0 ;
  double  r_max ;
  
  if (z_order > 20 || z_order < 0)
    {
      cerr << "zernike(): You choice of z_order is invalid "
	   << "choose a value between 0 and 20.\n" ;
      exit(1) ;
    }
  if (!pixel_array)
    {
      cerr << "zernike(): No objects.\n" ;
      return(0) ;
    }
  
  if(radius == 0)
    {
      //
      // rog = radius of gyration about the com
      //
      double rog ;
      
      //
      // Radius of Gyration
      //
      // Formula from Prokop RJ, Reeves AP, CVGIP:Graphical Models and Image
      //  Processing, v54 n5, Sep 92, pp. 438-460.
      //
      
      if(moments_struct->mu00 > 0.0)
	rog = sqrt((moments_struct->mu20 + moments_struct->mu02) 
		   / moments_struct->mu00) ;
      else
	rog = 0.0 ;
      
      r_max = 2.0 * rog ;
    }
  else
    {
      r_max = (double)radius ;
    }
  
  
  //
  // Loop to calculate Zernike features
  //
  
  for (int n = 0; n <= z_order; n++)
    {
      for (int l = 0; l <= n; l++)
	if ((n-l) % 2 == 0)
	  {
	    zindex++ ;
	    zfeatures[zindex] = abs(zernikeZ(pixel_array, num_pixels,
					     total_color_intensity, n, l, color, 
					     moments_struct->xbar, 
					     moments_struct->ybar, r_max)) ;
	  }
    }
  
  //
  // Output of zernike features
  //
  
  outfile << "zernike_color_" << color << "\t" ;
  
  for (int i = 1; i <= z_num_features; i++) 
    {
      outfile << zfeatures[i] << "\t" ;
    }
  
  delete[] zfeatures ;
  return(1) ;
}

 



Copyright ©1999 Michael V. Boland
1999-09-18