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.
i. For using high level features
extractors and classifiers, the object must have considerable amount of
texture. i.e corners, edges, gradients etc. in our case the ball does
not have much texture on it.
ii. To use classifiers, the code must
be trained before used to detect objects, which is a time consuming,
and tedious process.
iii. third and important reason is, "isn't it
enough to use simple transforms instead of complex algorithms, if the
former one does the intended job"
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.