Home Blog Feed

Project Athena: Vision Implementation Part-I

This is first post in this series. Vision Implementation.

Our image processing approach uses simple filters, morphological operation and few transforms for object detection from OpenCV library. OpenCV comes with very high level object detection and segmentation algorithms. But there is a reason why we use simple operations.

To code then: Here is the snapshot of Work in Progress(WIP) working code. We will go block by block.

#include<stdio.h>
#include<cv.h>
#include<highgui.h>

void cvCreateShowWindow ( char* _name, IplImage *_src ) ;
IplImage* hsv_from_rgb  ( IplImage* _src )    ;
IplImage* hsv_threshold ( IplImage* _hsv_src,
                          CvScalar _min,
                          CvScalar _max )    ;
IplImage* hough_circles ( IplImage* _src )    ;
IplImage* find_contours ( IplImage* _src )    ;


int main(int argc, char* argv[])
{
  if ( argc < 2 ) {
    printf ("Error: no input image found\n" ) ;
    exit (1) ;
  }

  char* image = malloc ( sizeof ( argv[1][0] )
                         * strlen ( argv[1] ) + 1) ;
  strcpy ( image, argv[1] ) ;
  cvStartWindowThread();

  IplImage* tmp = cvLoadImage ( image, CV_LOAD_IMAGE_UNCHANGED ) ;
  cvCreateShowWindow("tmp", tmp);

  /* defines the stages of processing pipeline...*/
#define CV_ATHENA_HSV_CONVERSION     1
#define CV_ATHENA_HSV_THRESHOLD      1
  /* #define CV_ATHENA_ADAPTIVE_THRESHOLD    1 */
#define CV_ATHENA_SMOOTH_BEFORE_MORPH  1
#define CV_ATHENA_MORPH       1
#define CV_ATHENA_SMOOTH_BEFORE_THRESHOLD 1
#define CV_ATHENA_THRESHOLD     1
#define CV_ATHENA_SMOOTH_BEFORE_CANNY     1
#define CV_ATHENA_CANNY       1
#define CV_ATHENA_HOUGH_CIRCLES     1
  /* #define CV_ATHENA_CONTOURS             1 */

#ifdef CV_ATHENA_HSV_CONVERSION
  IplImage* hsv_src = tmp;
  IplImage* hsved = hsv_from_rgb(hsv_src);
  cvCreateShowWindow("hsved", hsved);
  cvSaveImage("hsved.jpg",hsved,0);
#endif /* CV_ATHENA_HSV_CONVERSION */


#ifdef CV_ATHENA_HSV_THRESHOLD
  IplImage* thresh_src = hsved ;
  IplImage* thresholded = hsv_threshold ( thresh_src,
                                          cvScalar(36, 0, 0,0),
                                          cvScalar(50, 255, 255,255)
                                          );
  cvCreateShowWindow("thresholded", thresholded);
  cvSaveImage("thresh.jpg",thresholded,0);
#endif /* CV_ATHENA_HSV_THRESHOLD*/


#ifdef CV_ATHENA_ADAPTIVE_THRESH
  IplImage* adaptive_src = thresholded ;
  IplImage* adaptive_thresh = cvCloneImage(adaptive_src) ;
  cvAdaptiveThreshold ( adaptive_src, adaptive_thresh, 255,
                        CV_ADAPTIVE_THRESH_GAUSSIAN_C,
                        CV_THRESH_BINARY, 35, 25
                        );
  cvCreateShowWindow("adaptive_thresh", adaptive_thresh);
  cvSaveImage("adaptive_thresh.jpg",adaptive_thresh,0);
#endif /* CV_ATHENA_ADAPTIVE_THRESH*/

#ifdef CV_ATHENA_SMOOTH_BEFORE_MORPH
  IplImage* smooth_src_morph = thresholded;
  IplImage* smoothed_morph = cvCloneImage (smooth_src_morph);
  cvSmooth ( smooth_src_morph, smoothed_morph, CV_GAUSSIAN,
             21, 21, 0, 0
             );
  cvCreateShowWindow("smoothed_morph", smoothed_morph);
  cvSaveImage("smoothed_morph.jpg",smoothed_morph,0);
#endif /* CV_ATHENA_SMOOTH_1*/

#ifdef CV_ATHENA_MORPH
  IplImage* morph_src = smoothed_morph;
  IplImage* morphed = cvCloneImage(morph_src);
  IplImage* morph_tmp = cvCloneImage(morph_src);
  int mask_strength = 14 ;
  cvMorphologyEx ( morph_src,morphed,morph_tmp,
                   NULL,CV_MOP_OPEN,mask_strength
                   );
  /* cvMorphologyEx ( morphed, morphed, morph_tmp,
     NULL,CV_MOP_CLOSE,2
     ); */
  cvCreateShowWindow("morphed", morphed);
  cvSaveImage("morphed.jpg",morphed,0);
#endif /* CV_ATHENA_MORPH*/


#ifdef CV_ATHENA_SMOOTH_BEFORE_THRESHOLD
  IplImage* smooth_src_thresh = morphed;
  IplImage* smoothed_thresh = cvCloneImage (smooth_src_thresh);
  cvSmooth ( smooth_src_thresh, smoothed_thresh, CV_GAUSSIAN,
             21, 21,0,0
             );
  cvCreateShowWindow("smoothed_thresh", smoothed_thresh);
  cvSaveImage("smoothed_thresh.jpg",smoothed_thresh,0);
#endif /* CV_ATHENA_SMOOTH_BEFORE_THRESHOLD*/

#ifdef CV_ATHENA_THRESHOLD
  IplImage* bin_thresh_src = smoothed_thresh ;
  IplImage* bin_threshed = cvCloneImage (bin_thresh_src );
  cvThreshold ( bin_thresh_src, bin_threshed,
                120, 255, CV_THRESH_BINARY
                );
  cvCreateShowWindow("bin_threshed", bin_threshed);
  cvSaveImage("bin_threshed.jpg",bin_threshed,0);
#endif /* CV_ATHENA_THRESHOLD */



#ifdef CV_ATHENA_SMOOTH_BEFORE_CANNY
  IplImage* smooth_src_hough = bin_threshed;
  IplImage* smoothed_hough = cvCloneImage (smooth_src_hough);
  cvSmooth ( smooth_src_hough, smoothed_hough,
             CV_GAUSSIAN, 71, 71, 0, 0
             );
  cvCreateShowWindow("smoothed_hough", smoothed_hough);
  cvSaveImage("smoothed_hough.jpg",smoothed_hough,0);
#endif /* CV_ATHENA_SMOOTH_BEFORE_CANNY*/

#ifdef CV_ATHENA_CANNY
  IplImage* canny_src = smoothed_hough ;
  IplImage* canny = cvClone ( canny_src );
  cvCanny(canny_src, canny, 10, 10, 3);
  cvCreateShowWindow("canny", canny);
  cvSaveImage("canny.jpg",canny,0);
#endif /* CV_ATHENA_CANNY*/

#ifdef CV_ATHENA_HOUGH_CIRCLES
  IplImage* hough_src = canny ;
  IplImage* cir = hough_circles ( hough_src );
  cvCreateShowWindow("cir", cir);
  cvSaveImage("cir.jpg",cir,0);
#endif /* CV_ATHENA_HOUGH_CIRCLES*/

#ifdef CV_ATHENA_CONTOURS
  IplImage* contour_src = cir ;
  IplImage* contoured = find_contours ( contour_src );
  cvCreateShowWindow("contoured", contoured);
  cvSaveImage("contoured.jpg",contoured,0);
#endif /* CV_ATHENA_CONTOURS*/


  cvWaitKey(0);
  cvDestroyWindow("tmp");
  cvDestroyWindow("hsved");

  return 0;

}/* end of main */

void cvCreateShowWindow(char* _name, IplImage *_src)
{
  cvNamedWindow ( _name, CV_WINDOW_NORMAL ) ;
  cvShowImage ( _name, _src ) ;

}

IplImage* hsv_from_rgb(IplImage* _src)
{

  IplImage* img = cvCloneImage ( _src ) ;
  cvCvtColor ( _src, img, CV_BGR2HSV );
  return img;

}

IplImage* hsv_threshold ( IplImage* _hsv_src,
                          CvScalar _min,
                          CvScalar _max )
{
  IplImage* thresholded = cvCreateImage(cvGetSize( _hsv_src ), 8, 1 );
  cvInRangeS( _hsv_src, _min, _max, thresholded );

  return thresholded;
}

IplImage* hough_circles ( IplImage* _src )
{
  CvMemStorage* storage = cvCreateMemStorage(0);
  /* CvSeq* circles = cvHoughCircles (_src, storage, CV_HOUGH_GRADIENT,
     1, _src->height/6, 100, 50,0,0
     ); */
  CvSeq* circles = cvHoughCircles ( _src, storage, CV_HOUGH_GRADIENT,
                                    2, 10, 200, 100 , 50, 130
                                    );
  IplImage* cir = cvClone(_src );
  cvSetZero (cir);
  size_t i;
  for ( i = 0; i < circles->total; i++)  {

    float* p = (float*)cvGetSeqElem(circles, i);
    CvPoint center = cvPoint( cvRound(p[0]), cvRound(p[1]) );
    int radius = cvRound(p[2]);
    cvCircle( cir, center, radius+10, CV_RGB(0,0,255), 2, 8, 0 );

  }

  return cir;
}

IplImage* find_contours ( IplImage* _src )
{
  CvMemStorage *mem = cvCreateMemStorage(0);
  CvSeq *contours = 0;
  int n = cvFindContours (_src, mem, &contours, sizeof(CvContour),
                          CV_RETR_CCOMP, CV_CHAIN_APPROX_NONE,
                          cvPoint(0,0)
                          );
  /* int n = cvFindContours ( gray, mem, &contours, sizeof(CvContour),
     CV_RETR_LIST, CV_CHAIN_APPROX_SIMPLE,
     cvPoint (0,0)
     ); */
  IplImage* res = cvCloneImage(_src) ;
  cvSetZero(res) ;
  for (; contours != 0; contours = contours->h_next)
    {
      cvDrawContours (res, contours, CV_RGB(255,0,0),
                      CV_RGB(0,0,0), -1, CV_FILLED, 8, cvPoint(0,0)
                      );
    }
  return res;
}


At present the code uses 9 of 11 stages in the pipeline. they are,

RGB to HSV conversion

the image is converted from RGB(RED-GREEN-BLUE) into HSV(HUE-SATURATIONN-VALUE) representation. easier to color based separation. The HUE value signifies the color of the objects. VALUE represents the brightness of the pixel. SATURATION represents the intensity.

project-athena-hsved.jpg

RGB to HSV

HSV Thresholding

the images is converted into binary image. The pixels whose HSV values lie between the specified range will appear white and the other pixels will turn black.

project-athena-thresh.jpg HSV-Threshold

Smoothing

Smoothing in most cases, is done to reduce noise.

project-athena-smoothed-morph.jpg

Smoothing before morphing

Morphing

The OPEN morphological operation is used to erode the tiny black regions. OPEN is a combination of erode and dilate operations which cleans the image a bit. Morphological Operation - OPEN

project-athena-morphed.jpg

Smoothing

project-athena-smoothed-thresh.jpg

Thresholding

This stages separates the object of interset after the morphological operation. This is similar to HSV thresholding.

project-athena-bin-threshed.jpg

Smoothing

project-athena-smoothed-hough.jpg

Canny edge detection

Canny edge detection, determines the boundaries of the object, and help in Circles detection using Hough Transform

project-athena-canny.jpg

Hough Transform for Circles

find the circles present in the image. and draws them.

project-athena-cir.jpg