Opencv视频处理中的目标跟踪经常用到要在视频上画一个矩形框ROI,标注出要跟踪的物体,这里介绍两种在视频中绘制矩形框的方法,一种是“静态的”,一种是“动态的”。
静态的是指在绘制过程中,视频暂停播放,画面定格在鼠标左键单击上去时候播放的画面,这时候按着鼠标左键并拖动出一个感兴趣的区域,鼠标左键抬起完成矩形框的绘制。在整个绘制过程中,矩形是连续显示出来的,跟随鼠标位置变化而变化。
过程和实现都很简单,以下是具体代码实现,主要用到了鼠标响应事件:
#include "core/core.hpp"
#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp"
#include
using namespace cv;
using namespace std;
Mat image;
Mat imageCopy; //绘制矩形框时用来拷贝原图的图像
bool leftButtonDownFlag=false; //左键单击后视频暂停播放的标志位
Point originalPoint; //矩形框起点
Point processPoint; //矩形框终点
void onMouse(int event,int x,int y,int flags ,void* ustc); //鼠标回调函数
int main(int argc,char*argv[])
{
VideoCapture video(argv[1]);
double fps=video.get(CV_CAP_PROP_FPS); //获取视频帧率
double pauseTime=1000/fps; //两幅画面中间间隔
namedWindow("Friends");
setMouseCallback("Friends",onMouse);
while(true)
{
if(!leftButtonDownFlag) //判定鼠标左键没有按下,采取播放视频,否则暂停
{
video>>image;
}
if(!image.data||waitKey(pauseTime)==27) //图像为空或Esc键按下退出播放
{
break;
}
//两种情况下不在原始视频图像上刷新矩形
//1. 起点等于终点
//2. 左键按下且未抬起
if(originalPoint!=processPoint&&!leftButtonDownFlag)
{
rectangle(image,originalPoint,processPoint,Scalar(255,0,0),2);
}
imshow("Friends",image);
}
return 0;
}
//*******************************************************************//
//鼠标回调函数
void onMouse(int event,int x,int y,int flags,void *ustc)
{
if(event==CV_EVENT_LBUTTONDOWN)
{
leftButtonDownFlag=true; //标志位
originalPoint=Point(x,y); //设置左键按下点的矩形起点
processPoint=originalPoint;
}
if(event==CV_EVENT_MOUSEMOVE&&leftButtonDownFlag)
{
imageCopy=image.clone();
processPoint=Point(x,y);
if(originalPoint!=processPoint)
{
//在复制的图像上绘制矩形
rectangle(imageCopy,originalPoint,processPoint,Scalar(255,0,0),2);
}
imshow("Friends",imageCopy);
}
if(event==CV_EVENT_LBUTTONUP)
{
leftButtonDownFlag=false;
Mat rectImage=image(Rect(originalPoint,processPoint)); //子图像显示
imshow("Sub Image",rectImage);
}
}
鼠标左键按下,此时视频画面暂停,拖动左键,绘制出需要的矩形:
截取出来单独显示的子画面:
对应的,动态的是说在绘制矩形框的过程中,视频还是在正常播放的,截取的子画面是鼠标抬起时当前视频帧的画面。动态绘制不需要在鼠标事件中处理矩形框的绘制,相比静态的还更为简单一点:
#include "core/core.hpp"
#include "highgui/highgui.hpp"
#include "imgproc/imgproc.hpp"
#include
using namespace cv;
using namespace std;
Mat image;
Mat imageCopy; //绘制矩形框时用来拷贝原图的图像
bool leftButtonDownFlag=false; //左键单击后视频暂停播放的标志位
Point originalPoint; //矩形框起点
Point processPoint; //矩形框终点
void onMouse(int event,int x,int y,int flags ,void* ustc); //鼠标回调函数
int main(int argc,char*argv[])
{
VideoCapture video(argv[1]);
double fps=video.get(CV_CAP_PROP_FPS); //获取视频帧率
double pauseTime=1000/fps; //两幅画面中间间隔
namedWindow("Friends");
setMouseCallback("Friends",onMouse);
while(true)
{
video>>image;
if(!image.data||waitKey(pauseTime)==27) //图像为空或Esc键按下退出播放
{
break;
}
//两种情况下不在原始视频图像上刷新矩形
//1. 起点等于终点
//2. 左键按下且未抬起
if(originalPoint!=processPoint)
{
rectangle(image,originalPoint,processPoint,Scalar(255,0,0),2);
}
imshow("Friends",image);
}
return 0;
}
//*******************************************************************//
//鼠标回调函数
void onMouse(int event,int x,int y,int flags,void *ustc)
{
if(event==CV_EVENT_LBUTTONDOWN)
{
leftButtonDownFlag=true; //标志位
originalPoint=Point(x,y); //设置左键按下点的矩形起点
processPoint=originalPoint;
}
if(event==CV_EVENT_MOUSEMOVE&&leftButtonDownFlag)
{
processPoint=Point(x,y);
}
if(event==CV_EVENT_LBUTTONUP)
{
leftButtonDownFlag=false;
Mat rectImage=image(Rect(originalPoint,processPoint)); //子图像显示
imshow("Sub Image",rectImage);
}
}
鼠标左键按下,视频仍会继续播放,拖动鼠标,画出矩形框:
截取出来单独显示的子画面: