OpenCV的鼠标操作——用鼠标画矩形(代码解读)
起因
知乎上被邀请回答一个问题,关于OpenCV的鼠标操作的问题。我发现回答下来写了不少东西,可以整理为一篇文章发出来,顺便说下不少人关心的如何用操作鼠标,比如如何用鼠标在图像上画一个矩形或者说选择一个矩形的ROI。
知乎上的问题问的是下面这段代码是什么意思。
这里写图片描述正好,这段代码我是看过的,而且就在最近两周。所以正好可以说道一下。
介绍
这一段代码我最初是在contrib模块里面tracking模块的samples里面看到的,出自roiSelector.hpp。这个文件的作用就是用鼠标在图片中选择一个矩形区域。感兴趣的读者可以到这里知道源代码。为了照顾一部分人,更加直白的说法是这段代码在下面这样的路径下:
opencv3.2\opencv_contrib-master\modules\tracking\samples
**注意,这里是opencv contrib模块,不是官网下载的那个OpenCV. **
显然,我们知道要用一个鼠标选择一个矩形区域,鼠标的运动可以细分为一下三个动作:
- 鼠标左键按下
- 鼠标非水平非垂直地滑动
- 鼠标左键抬起。
在roiSelector.hpp
的代码中,在处理EVENT_LBUTTONUP
(鼠标左键抬起事件)之前,还分别先对EVENT_LBUTTONDOWN
(鼠标左键按下事件)和EVENT_MOUSEMOVE
(鼠标移动事件)进行了处理。
为了说明题主给出的代码的具体含义,必须先明白前两个事件,也即鼠标左键按下和鼠标滑动,都是怎么处理的。为了方便说明,这里不讨论代码中从矩形中心开始画矩形的情况,我把这三个事件的代码简化如下,为了放方便说明,调整了顺序:
PS:加一句也许是废话的话,在OpenCV中,矩形的表示方式是(x,y,width,height),也即是矩形框左上角坐标,外加宽高。而OpenCV的图像坐标系也是以图像左上角为原点,越往右x越大,越往下y越大。
代码解读
代码经过了简化,但是应该已经足够说明问题。
简化后的代码如下:
switch (event)
{
// start to select the bounding box
case cv::EVENT_LBUTTONDOWN:
data->isDrawing = true;
data->box = cv::Rect2d(x, y, 0, 0);
break;
// update the selected bounding box
case cv::EVENT_MOUSEMOVE:
if (data->isDrawing)
{
data->box.width = x - data->box.x;
data->box.height = y - data->box.y;
}
break;
// cleaning up the selected bounding box
case cv::EVENT_LBUTTONUP:
data->isDrawing = false;
if (data->box.width < 0)
{
data->box.x += data->box.width;
data->box.width *= -1;
}
if (data->box.height < 0)
{
data->box.y += data->box.height;
data->box.height *= -1;
}
break;
}
case 1
这里先说第一个case,也即第一个动作,鼠标左键按下事件:EVENT_LBUTTONDOWN。
// 若鼠标左键按下,则矩形初始化为以鼠标当时坐标为左上角坐标,宽高都为0的矩形。
// 且开始画flag为真,左键不按下滑动鼠标则不会开始画。
case cv::EVENT_LBUTTONDOWN:
data->isDrawing = true;
data->box = cv::Rect2d(x, y, 0, 0);
break;
case 2
第二个case,也即第二个动作,鼠标滑动事件:EVENT_MOUSEMOVE。
// 如果鼠标开始滑动,更新矩形的宽高。
// 用滑动时鼠标所在的坐标x减去初始的x为矩形的宽度。
// 坐标y减去矩形初始的y为矩形的宽。
// 比如鼠标往左水平移动了5个像素,那么宽为5px。
// 鼠标垂直向下移动了10个像素,那么矩形高为10px。
case cv::EVENT_MOUSEMOVE:
if (data->isDrawing)
{
data->box.width = x - data->box.x;
data->box.height = y - data->box.y;
}
break;
case 3
// cleaning up the selected bounding box
// 无视上面这句英文。
// 这里最后的一个动作,鼠标左键抬起(释放)。
// 如果矩形宽小于0,结合前面说的OpenCV的坐标系方向,
// 说明鼠标滑动的时候是从右往左滑动的,所以这个时候原本鼠标左键按下的起始点
// 就不再是矩形的左上角的点,所以需要用原本的x减去矩形宽度
// 才是现在的矩形的左上角的x坐标。由于此时宽度为负数,所以下面用加号表示相减。
// 然后乘以-1使得宽度从负数变成正数。
// 下面的高度为负同理。// 这里用两个if而不是if...else...的原因就是隐含如果宽高不为负,
// 那么最后的宽高就是鼠标释放的时候坐标减去初始左上角坐标的得到的宽高。
case cv::EVENT_LBUTTONUP:
data->isDrawing = false;
if (data->box.width < 0)
{
data->box.x += data->box.width;
data->box.width *= -1;
}
if (data->box.height < 0)
{
data->box.y += data->box.height;
data->box.height *= -1;
}
break;
}
公众号CVPy,分享OpenCV和Python的实战内容。。欢迎关注。
公众号CVPy