opencv学习笔记(十一)利用鼠标画出一个矩形

#include 
#include 
void my_mouse_callback(int event, int x, int y, int flags, void* param ); 
CvRect box;
bool drawing_box = false;
void draw_box( IplImage* img, CvRect rect ) 
{
cvRectangle (
img, 
cvPoint(box.x,box.y),//确定左上角坐标
cvPoint(box.x+box.width,box.y+box.height),//确定右下角坐标
cvScalar(0xff,0xff,0xff)    //线条白色
);
}
int main( int argc, char* argv[] ) 
{
  box = cvRect(-1,-1,0,0);
  IplImage* image = cvCreateImage(cvSize(200,200),IPL_DEPTH_8U,3);
  //创建一个200×200图像。如果太小了,我们可以弄大一点。
  cvZero( image );//全变黑
  IplImage* temp = cvCloneImage( image ); 
  cvNamedWindow( "Box Example" );
  cvSetMouseCallback( 
    "Box Example", 
    my_mouse_callback, 
    (void*) image 
    //传入的函数的void* param 参数是(void*) image,
    //即我们前面定义的IplImage* image
  );
  while(1) 
  {
    cvCopyImage( image, temp );
    if( drawing_box ) draw_box( temp, box ); 
    cvShowImage( "Box Example", temp ); 
    if( cvWaitKey( 15 )==27 ) break;
  }
  cvReleaseImage( &image );
  cvReleaseImage( &temp );
  cvDestroyWindow( "Box Example" );
}
void my_mouse_callback(int event, int x, int y, int flags, void* param)
{
  IplImage* image = (IplImage*) param;
  switch( event ) {
    case CV_EVENT_MOUSEMOVE: {
      if( drawing_box ) {
        box.width  = x-box.x;
        box.height = y-box.y;
      }
    }
    break;
    case CV_EVENT_LBUTTONDOWN: {
      drawing_box = true;
      box = cvRect( x, y, 0, 0 );
    }
    break;   
    case CV_EVENT_LBUTTONUP: {
      drawing_box = false; 
      if( box.width<0  ) { 
        box.x+=box.width;  
        box.width *=-1; 
      }
      if( box.height<0 ) { 
        box.y+=box.height; 
        box.height*=-1; 
      }
      draw_box( image, box );
    }
    break;   
  }
}

程序详细理解:

cvSetMouseCallback()函数的第三个参数是我们要传回回调函数中的参数,然后回调函数对它做了更改,那它就变了。在这个程序中,我们在回调函数中将这个参数定义为
IplImage* image = (IplImage*) param;
所以它就是一个图片结构(名叫image),然后我们在回调函数中对图片做了什么更改,图片就被改变了。
然后我一直对为什么要copy一个temp出来不理解。后来明白了,我们在temp上画图——依然会触发鼠标事件,从而触发回调函数,从而实现了对图片image进行了操作。
“在结束之前,为了实时显示所画的图形,将所画的图放在temp变量中,待画图结束时在image中存放这一次画图的最终结果。”
就是说我们之所以有一个temp,是因为如果没有temp,直接在image画,那么会显示过程,那么画一个矩形,你看到的结果是这样:

opencv学习笔记(十一)利用鼠标画出一个矩形_第1张图片

一个大矩形由无数个小矩形组成,但我们只要最终结果。

下面的程序做了一点小改动,将temp和image用两个窗口同时显示出来,然后在temp上画矩形(显示过程),在image上显示最终结果。
// cvCopyImage( image, temp );这句话也要注释掉,否则两个窗口的显示结果一样。
然后可以明显看到差别。

#include 
#include 

void my_mouse_callback(int event, int x, int y, int flags, void* param );

CvRect box;
bool drawing_box = false;

void draw_box( IplImage* img, CvRect rect ) 
{
cvRectangle (img, 
             cvPoint(box.x,box.y),//确定左上角坐标
             cvPoint(box.x+box.width,box.y+box.height),
                                        //确定右下角坐标
             cvScalar(0xff,0xff,0xff)); //线条白色
}

int main( int argc, char* argv[] ) {
  box = cvRect(-1,-1,0,0);
  IplImage* image = cvCreateImage( 
    cvSize(600,600),
    IPL_DEPTH_8U,
    3
  );//创建一个200×200图像。太小了,我们可以弄大一点。
  cvZero( image );//全变黑
  IplImage* temp = cvCloneImage( image ); 
  cvNamedWindow( "Box Example" ,CV_WINDOW_AUTOSIZE);
  cvNamedWindow( "原始窗口" ,CV_WINDOW_AUTOSIZE);
  cvSetMouseCallback( "Box Example", 
    my_mouse_callback, 
    (void*) image 
  );
  while(1) {
    if( drawing_box ) draw_box( temp, box ); 
    cvShowImage( "Box Example", temp );
    cvShowImage( "原始窗口", image ); 
    if( cvWaitKey( 15 )==27 ) break;
  }
  cvReleaseImage( &image );
  cvReleaseImage( &temp );
  cvDestroyWindow( "Box Example" );
}
void my_mouse_callback(
   int event, int x, int y, int flags, void* param )
{
  IplImage* image = (IplImage*) param;
  switch( event ) {
    case CV_EVENT_MOUSEMOVE: {
      if( drawing_box ) {
        box.width  = x-box.x;
        box.height = y-box.y;
      }
    }
    break;
    case CV_EVENT_LBUTTONDOWN: {
      drawing_box = true;
      box = cvRect( x, y, 0, 0 );
    }
    break;   
    case CV_EVENT_LBUTTONUP: {
      drawing_box = false; 
      if( box.width<0  ) { 
        box.x+=box.width;  
        box.width *=-1; 
      }
      if( box.height<0 ) { 
        box.y+=box.height; 
        box.height*=-1; 
      }
      draw_box( image, box );
    }
    break;   
  }
}

11.1 bool

bool取值false和true,0为false,非0为true。(例如-1和2都是true)。
程序中,bool drawing_box = false;
就是定义了一个bool量。

11.2 CvRect数据类型

它是OpenCV里面的基本数据类型
功能: 包含4个数据成员,x,y,width,height,通过定义矩形左上角坐标和矩形的宽和高来确定一个矩形。
OpenCV里面的基本数据类型
结构体功能: 通过矩形左上角坐标和矩形的宽和高来确定一个矩形区域
typedef struct CvRect
  {
  int x; /* 方形的左上角的x-坐标 */
  int y; /* 方形的左上角的y-坐标*/
  int width; /* 宽 */
  int height; /* 高 */
  }
  x,y用来确定区域左上角的坐标
width和height为矩形区域的宽和高。
然后cvRect就是它的内联函数。

11.3 cvCopyImage

如果设定了ROI等参数的时候,cvCopy只是复制被设定的区域,复制到一个和所设定参数相吻合的新的IplImage中。
cvCopy的原型是:
void cvCopy( const CvArr* src, CvArr* dst, const CvArr* mask=NULL );
src
输入数组。
dst
输出数组。
在使用这个函数之前,你必须用cvCreateImage()一类的函数先开一段内存,然后传递给dst。cvCopy会把src中的数据复制到dst的内存中。

11.4 cvCloneImage

cvCloneImage的原型是:
IplImage* cvCloneImage( const IplImage* image );
在使用函数之前,不用开辟内存。该函数会自己开一段内存,然后复制好image里面的数据,然后把这段内存中的数据返回给你。

11.5 两者区别

clone是把所有的都复制过来,也就是说不论你是否设置ROI,COI等影响copy的参数,clone都会原封不动的克隆过来。

copy就不一样,只会复制ROI区域等。用clone复制之后,源图像在内存中消失后,复制的图像也变了,而用copy复制,源图像消失后,复制的图像不变。

cvCloneImage直接克隆录像,包括图像的ROI信息。
而cvCopyImage复制图像,复制图像的ROI部分。
copy的图像大小必须和所设定的ROI大小一致,而clone的图像的大小必须与原图像一致。

11.6鼠标事件

与键盘事件响应不同,鼠标事件响应采用回调函数的方式来处理。即,为了可以响应鼠标点击事件,首先必须创建一个回调函数,使鼠标点击事件发生时OpenCV可以调用这个函数。创建这个函数以后,需要在OpenCV中注册这个函数,以便特定窗口被触发鼠标事件以后,OpenCV可以正确调用这个函数。
让我们从这个回调函数开始,回调函数callback可以是满足指定输入参数以及返同参数类型的任何函数。这里,我们必须清楚告诉回调函数触发的事件以及触发位置。函数还需要被告知,用户是否在触发鼠标事件时同时触发了Shift或者Alt等键。

11.6.1 CvMouseCallback
void CvMouseCallback(
int event,
int x,
int y,
int flags,
void* param
);
现在,当回调函数被调用,OpenCV会给函数传入合适的值。第一个参数event必须为表11—1中的一个值。
表11—1:鼠标事件类型
事件名称 数值
CV_EVENT_MOUSEMOVE (鼠标移动) 0
CV_EVENT_LBUTTONDOWN (左键按下) 1
CV_EVENT_RBUTTONDOWN (右键按下) 2
CV_EVENT_MBUTTONDOWN (中间按下) 3
CV_EVENT_LBUTTONUP (左键抬起) 4
CV_EVENT_RBUTTONUP (右键抬起) 5
CV_EVENT_MBUTTONUP (中间抬起) 6
CV_EVENT_LBUTTONDBLCLK (左边双击) 7
CV_EVENT_RBUTTONDBLCLK (右边双击) 8
CV_EVENT_MBUTTONDBLCLK (中间双击) 9
第二个以及第三个参数会被设置成事件发生时鼠标位置的x,y坐标值。值得指出的是,这些坐标代表窗口中图像的像素坐标,与窗口的大小没有关系。
第四个参数flags,每一位指定了在事件发生时的不同状态。
例如,CV_EVENT_FLAG_SHIFTKEY的值为16(flags的第五位为1),如果想知道Shift键是否被触发,我们可以用flags与位掩码(1<<4求与)。表11-2列出了所有的标志。
表11-2:鼠标事件标志
标志名称 数值
CV_EVENT_FLAG_LBUTTON 1
CV_EVENT_FLAG_RBUTTON 2
CV_EVENT_FLAG_MBUTTON 4
CV_EVENT_FLAG_CTRLKEY 8
CV_EVENT_FLAG_SHIFTKEY 16
CV_EVENT_FLAG_ALTKEY 32
最后一个参数是一个void指针,可以用来以任何结构方式传递额外的参数信息。通常情况下,当回调函数为一个类的静态成员时。在这种情况下,可能需要传递这个类的指针以确定对哪一个类实例产生影响。

11.6.2 cvSetMouseCallback
该函数需要三个参数:
void cvSetMouseCallback(
const char* window_ name,
CvMouseCallback on_mouse,
void*param=NULL
);
第一个参数指定了回调函数需要注册到的窗口,也就是产生事件的窗口。只有在这个指定的窗口中触发的事件才会调用回调函数。
第二个参数为回调函数。
最后,第三个参数用来传递额外的信息给前面提到的void* param参数。
——第三个参数表示用户定义的传递到回调函数的参数。
这个参数是一个void指针,可以用来以任何结构方式传递额外的参数信息。

你可能感兴趣的:(opencv)