Pages - Menu

Saturday, July 26, 2014

OpenCV: 'FAST' like Corner detection with scanning window

    The key to building many important OpenCV application, is to extract robust features from the image.
Depending on the application we tend to extract features such as corners, haar cascades, contours etc in both spatial and/or temporal dimensions.
The code here was initially implemented using OpenCV 1.0, the code works with latest version (2.4.9) as well.

 The program basically checks the neighboring pixels lying on a 'Window' around the pixel under test. Consider a sample image thresholded and is under process. The Pixel A as we see from the image below contains optimum number of 'Black Pixels' in the Scanning Window - 5 in total. Whereas Pixels B and C have 3 and 10 respectively. Based on the Window size and a series of tests and inspection we can safely assume that A is a corner while B and C are not. So depending on the Window size and Optimal number if Black or White pixels around the pixel under test, we can mark them as corners.

The Image below is a representative of the code that has been implemented.


Notice that in the same code below, the number of 'Black Pixels' are being counted.
Also try and experiment by changing
 if((sum > 2 ) && (sum < 5))
to
 if((sum >= 8 ) && (sum < 9))
or
 if((sum >= 10 ) && (sum <= 11))

Note: FAST has been ported to OpenCV. It is available natively in OpenCV.
http://docs.opencv.org/modules/features2d/doc/feature_detection_and_description.html#fast

Compatibility  > OpenCV 1.0

Files:
Download from DsynFLO box folder - https://app.box.com/s/h49lu1ht6mjbapym726z

Source Code:
//______________________________________________________________________________________
// Program : Corner Detection using OpenCV
// Author  : Bharath Prabhuswamy
//______________________________________________________________________________________
#include <cv.h>
#include <highgui.h>
#include <stdio.h>

bool find_corner(char* img_data,int img_width,int x,int y); // Routine to check whether a particular pixel is an Edgel or not

// Start of Main Loop
//------------------------------------------------------------------------------------------------------------------------
int main()
{
 CvCapture* capture = 0;
 IplImage* img = 0;
 //IplImage* img = cvLoadImage("test3.jpg"); // for still image

 capture = cvCaptureFromCAM( 0 );
  if ( !capture )             // Check for Camera capture
  return -1;
 
 cvNamedWindow("Camera",CV_WINDOW_AUTOSIZE);

 IplImage* gray = 0;
 IplImage* thres = 0;

 bool init = false;   // Flag to identify initialization of Image objects

     if(init == false)
 {
  img = cvQueryFrame( capture ); // Query for the frame

         if( !img )   // Exit if camera frame is not obtained
   return -1;

  // Creation of Intermediate 'Image' Objects required later
  gray = cvCreateImage( cvGetSize(img), 8, 1 );  // To hold Grayscale Image
  thres = cvCreateImage( cvGetSize(img), 8, 1 );  // To hold OTSU thresholded Image
 
  init = true;
 }

   
 int ihist[256];              // Array to store Histogram values
 float hist_val[256];  // Array to store Normalised Histogram values
 int pos ;   // Position or pixel value of the image
 float prbn;                    // First order cumulative
 float meanitr;                 // Second order cumulative
 float meanglb;   // Global mean level
 int OPT_THRESH_VAL;             // Optimum threshold value
 float param1,param2;            // Parameters required to work out OTSU threshold algorithm
 double param3;
 int h,w;   // Variables to store Image Height and Width
 
 h = img->height;  // Height and width of the Image
 w = img->width;

 CvPoint corner;
 bool corner_flag;

 int key = 0;
 while(key != 'q')
 {

  //Step : Capture Image from Camera
  //Info : Inbuit function from OpenCV
  //Note : 

  img = cvQueryFrame( capture );  // Query for the frame

  //Step : Convert Image captured from Camera to GrayScale
  //Info : Inbuit function from OpenCV
  //Note : Image from Camera and Grayscale are held using seperate "IplImage" objects

  cvCvtColor(img,gray,CV_RGB2GRAY); // Convert RGB image to Gray


  //Step : Threshold the image using optimum Threshold value obtained from OTSU method
  //Info : 
  //Note : 

  memset(ihist, 0, 256);

  for(int j = 0; j < gray->height; ++j) // Use Histogram values from Gray image
  {
   uchar* hist = (uchar*) (gray->imageData + j * gray->widthStep);
   for(int i = 0; i < gray->width; i++ )
   {
    pos = hist[i];  // Check the pixel value
    ihist[pos] += 1; // Use the pixel value as the position/"Weight"
   }
  }

  //Parameters required to calculate threshold using OTSU Method
  prbn = 0.0;                   // First order cumulative
  meanitr = 0.0;                // Second order cumulative
  meanglb = 0.0;                // Global mean level
  OPT_THRESH_VAL = 0;           // Optimum threshold value
  param1,param2;                // Parameters required to work out OTSU threshold algorithm
  param3 = 0.0;

  //Normalise histogram values and calculate global mean level
  for(int i = 0; i < 256; ++i)
  {
   hist_val[i] = ihist[i] / (float)(w * h);
   meanglb += ((float)i * hist_val[i]);
  }

  // Implementation of OTSU algorithm
  for (int i = 0; i < 255; i++)
  {
   prbn += (float)hist_val[i];
   meanitr += ((float)i * hist_val[i]);

   param1 = (float)((meanglb * prbn) - meanitr);
   param2 = (float)(param1 * param1) /(float) ( prbn * (1.0f - prbn) );

   if (param2 > param3)
   {
       param3 = param2;
       OPT_THRESH_VAL = i;     // Update the "Weight/Value" as Optimum Threshold value
   }
  }

  cvThreshold(gray,thres,OPT_THRESH_VAL,255,CV_THRESH_BINARY); //Threshold the Image using the value obtained from OTSU method

  int c = 0; 

  for( int y = 0; y < thres->height; ++y)  //Start full scan of the image by incrementing y
  {

   for(int x = 0; x < thres->width; ++x ) //Start full scan of the image by incrementing x
       {
        // Number of edgels in a particular blob
    corner_flag = find_corner(thres->imageData,thres->widthStep,x,y);
    if(corner_flag == true)  // Check for the Edgel and update Edgel storage
    {
     c++;
     corner.x = x;
     corner.y = y;
     cvCircle(img,corner,1,CV_RGB(255,0,0),1,8);

     corner_flag = false;
    }
   }
          
  }

  cvShowImage( "Camera",img);

  key = cvWaitKey(1); // OPENCV: wait for 1ms before accessing next frame
  //key = cvWaitKey(50000); // for still image

 } // End of while loop

 cvDestroyWindow( "Camera" ); // Release various parameters
 cvReleaseImage(&img);
 cvReleaseImage(&gray);
 cvReleaseImage(&thres);

     return 0;
}
// End of Main Loop
//------------------------------------------------------------------------------------------------------------------------


// Routine to check whether a particular pixel is an Edgel or not
bool find_corner(char* img_data,int img_width,int x,int y)
{ 
 const int wind_sz = 5 ;
 int wind_bnd = (wind_sz - 1) / 2;
 int sum = 0;
 bool result = false;
 uchar* ptr[wind_sz];
 int index =0;

 for(int k = (0-wind_bnd); k <= wind_bnd; ++k)
 {
   ptr[index] = (uchar*)(img_data + (y + k) *  img_width);
   index = index + 1 ;
 }

 for(int i = 0; i <= (wind_sz-1); ++i)
 {
  if((i == 0) || (i==(wind_sz-1)))
  {
      for (int j = (0-wind_bnd); j <= wind_bnd; ++j)
      {
   if(ptr[i][x+j] == 0)
      sum += 1;
   else
      continue;
      }
  }
  else
  {
      if(ptr[i][x-wind_bnd] == 0)
   sum += 1;
      else
   continue;

      if(ptr[i][x+wind_bnd] == 0)
   sum += 1;
      else
   continue;
  }
 }

    if((sum > 2 ) && (sum < 5))
    {
        result = true;
    }
    return result;
}
// EOF

No comments:

Post a Comment