7/1/12

Mouse event in OpenCV C++ is tough~

Was being frustrated last two days as I was having problems with cvSetMouseCallback in OpenCV. Thanks so much to Jean-Marc Pelletier, finally, the work-in-progress code is grabbing user click CvPoint(x,y), able to pass *param and check if the point resides in a contour.
#include <stdio.h>

#include <cv.h>

#include <highgui.h>

//Function declarations

void onMouse(int event, int x, int y, int flags, void *param); //This will be called when the user performs a mouse action.

CvContour* contourFromPosition(CvContour *contours, int x, int y); //Returns a pointer to the inner-most contour surrounding the given point

char pointIsInsideContour(CvContour *contour, int x, int y); //Returns whether the given position is inside a contour

int main (int argc, const char * argv[]) {

// exit main program loop?

char quit = 0;

char grab_frame = 1;

// allocated memory

CvMemStorage *storage = cvCreateMemStorage(0);

CvSeq *contours = 0;

// initializes camera

CvCapture *camera = cvCreateCameraCapture(0);

if(!camera){

printf("Could not find a camera to capture from...\n");

return -1;

}

// get the video image size

IplImage *imgsize;

imgsize = cvQueryFrame( camera );

if( !imgsize ) return -1;

// get a buffer image that is the same size as img1 and 2

IplImage *imggray1 = cvCreateImage( cvGetSize( imgsize ), IPL_DEPTH_8U, 1);

IplImage *imggray2 = cvCreateImage( cvGetSize( imgsize ), IPL_DEPTH_8U, 1);

IplImage *imggray3 = cvCreateImage( cvGetSize( imgsize ), IPL_DEPTH_8U, 1);

IplImage *imgcross = cvCreateImage( cvGetSize( imgsize ), IPL_DEPTH_8U, 1);

// create a window

cvNamedWindow("Video", 0);

// define kernal for image smoothing

IplConvKernel *kernel = cvCreateStructuringElementEx(3, 3, 1, 1, CV_SHAPE_CROSS, NULL);

// set the mouse callback function.

cvSetMouseCallback("Video", onMouse, (void*) &contours);

// exit the loop when the user sets quit to true by pressing the "esc" key

while(!quit){

int c = cvWaitKey(30); //Wait 30 ms for the user to press a key.

//Respond to key pressed.

switch(c){

case 32: //Space

grab_frame = !grab_frame; //Reverse the value of grab_frame. That way, the user can toggle by pressing the space bar.

break;

case 27: //Esc: quit application when user presses the 'esc' key.

quit = 1; //Get out of loop

break;

};

//If we don't have to grab a frame, we're done for this pass.

if(!grab_frame)continue;

//Grab a frame from the camera.

IplImage *img1 = cvQueryFrame(camera);

if(!img1)continue;

// convert from RGB to greyscale

cvCvtColor(img1, imggray1, CV_RGB2GRAY);

//Grab 2nd frame from the camera.

IplImage *img2 = cvQueryFrame(camera);

if(!img2)continue;

// convert from RGB to greyscale before processing further.

cvCvtColor(img2, imggray2, CV_RGB2GRAY);

// compute difference

cvAbsDiff( imggray1, imggray2, imggray3 );

// convert to bw

IplImage* imgbw = cvCreateImage(cvGetSize(imggray3),IPL_DEPTH_8U,1);

cvThreshold(imggray3, imgbw, 16, 255, CV_THRESH_BINARY);

// apply convolution kernalwhiq

cvMorphologyEx( imgbw, imgcross , NULL , kernel, CV_MOP_OPEN, 1);

// find connected components

cvFindContours(imgcross, storage, &contours, sizeof(CvContour), CV_RETR_TREE, CV_CHAIN_APPROX_NONE, cvPoint(0,0));

// Create a clone for contour coloring, or else, contours will be 0 at the end

CvSeq *contours_replica = contours;

// loop through all contours found

for( ; contours_replica != 0; contours_replica = contours_replica->h_next )

{

// draw contours area

CvScalar color = CV_RGB( rand()&255, rand()&255, rand()&255 );

cvDrawContours( img1, contours_replica, color, color, -1, CV_FILLED, 8 );

CvRect r1 = cvBoundingRect(contours_replica, 1);

// get the centroid of the contour

CvPoint point = cvPoint(

r1.x + (r1.width / 2),

r1.y + (r1.height / 2)

);

//fprintf(stderr, "x: %d y:%d\n", r1.x, r1.y);

// draw circle on centroid

// cvDrawCircle ( img1, point, 10, CV_RGB(255,0,0), 1, 8, 0 )

cvRectangle(img1, cvPoint(r1.x, r1.y), cvPoint((r1.x + r1.width),(r1.y + r1.height)), CV_RGB(255,0,0),  1, 8, 0);

}

// display the image in the window.

cvShowImage("Video", img1);

}

//Clean up before quitting.

cvDestroyAllWindows();

//Release the camera

cvReleaseCapture(&camera);

//release memory

cvReleaseMemStorage(&storage);

//Release images

cvReleaseImage(&imggray1);

cvReleaseImage(&imggray2);

cvReleaseImage(&imggray3);

cvReleaseImage(&imgcross);

return 0;

}

// when clicks happen in window

void onMouse(int event, int x, int y, int flags, void *param){

CvContour *contours = *(CvContour **)param;

CvContour *ctr; // point to the specific contour the user clicked

double area; // hold the area of the contour the user clicked

// do not proceed if there is no contour

if(!contours)return;

//printf("contours: %d", contours);

//"event" tells us what occured

switch(event){

case CV_EVENT_LBUTTONDOWN: //single click

case CV_EVENT_LBUTTONDBLCLK: //double click

printf("Click: %d %d\n",x,y);  //Write out where the user clicked.

//Check if user click in contour

ctr = contourFromPosition(contours, x, y);

// if user did click inside contour

if(ctr){

//Calculate the area of the contour.

area = fabs(cvContourArea(ctr, CV_WHOLE_SEQ, 0));

printf("Area: %f\n",area);    }

break;

}

}

CvContour* contourFromPosition(CvContour *contours, int x, int y){

CvContour *contour;

CvContour *tmpContour=0, *output=0;

// do not proceed if there is no contour

if(!contours)return 0;

// points to the first contour in the list

contour = contours;

while(contour){

// check if the point the user clicked on is inside the current contour, if yes...

if(pointIsInsideContour(contour, x, y)){

output = contour;

// check all the contours

if(contour->v_next){

tmpContour = contourFromPosition((CvContour *)contour->v_next,x,y);

if(tmpContour)output = tmpContour;

}

}

// move to the following contour

contour = (CvContour *)(contour->h_next);

}

return output;

}

// returns whether the (x,y) point is inside the given contour.

char pointIsInsideContour(CvContour *contour, int x, int y){

//We know that a point is inside a contour if we can find one point in the contour that is immediately to the left,

//one that is immediately on top, one that is immediately to the right and one that is immediately below the (x,y) point.

//We will update the boolean variables below when these are found:

char found_left=0, found_top=0, found_right=0, found_bottom=0;

int count, i;

CvPoint *contourPoint;

// do not proceed if there is no contour

if(!contour)return 0;

count = contour->total; //The total field holds the number of points in the contour.

for(i=0;i<count;i++){ //So, for every point in the contour...

//We retrieve a pointer to the point at index i using the useful macro CV_GET_SEQ_ELEM

contourPoint = (CvPoint *)CV_GET_SEQ_ELEM(CvPoint,contour,i);

if(contourPoint->x == x){ //If the point is on the same vertical plane as (x,y)...

if(contourPoint->y < y)found_top = 1; //and is above (x,y), we found the top.

else found_bottom = 1; //Otherwise, it's the bottom.

}

if(contourPoint->y == y){ //Do the same thing for the horizontal axis...

if(contourPoint->x < x)found_left = 1;

else found_right = 1;

}

}

return found_left && found_top && found_right && found_bottom; //Did we find all four points?

}

Bài đăng phổ biến