预先声明:javascript
本人并不是计算机或软件专业科班出身,只是我的研究中用到了OpenCV,所以该blog只是我的笔记性质,不能保证正确。
同时可能有一些C++、计算机理论乃至图像处理科学上的低级错误,欢迎指出。java
本笔记主要包括以下内容ios
setMouseCallback()为OpenCV中的鼠标回调函数,具体在highgui.hpp
的提供的简单说明为:git
/** @brief Sets mouse handler for the specified window
@param winname Name of the window.
@param onMouse Mouse callback. See OpenCV samples, such as https://github.com/opencv/opencv/tree/master/samples/cpp/ffilldemo.cpp, on how to specify and use the callback.
@param userdata The optional parameter passed to the callback.github
该函数的基本格式为:web
void setMouseCallback(const String& winname, MouseCallback onMouse, void* userdata = 0);
下面是各个参数的具体含义:小程序
@param winname Name of the window.svg
简单说,就是制定了鼠标事件的具体窗口。winname应该为具体的窗口的标示;函数
@param onMouse Mouse callback. ui
用于设置鼠标的回调函数,简单说,被调用的函数原型为:
void onMouse(int event, int x, int y, int flags, void*)
具体的各参数解释详见第2部分。
@param userdata The optional parameter passed to the callback.
我的理解,这是定义传输到回调函数的用户数据。若是无特殊需求,通常不用定义便可。
使用下面的定义进行说明。
void onMouse(int event, int x, int y, int flags, void*)
event是鼠标的回调事件,当有鼠标动做时,将回传信息到onMouse中,同时返回鼠标的坐标(也就是x和y的值)。
下面是highgui.hpp
中的内容:
//! Mouse Events see cv::MouseCallback
enum MouseEventTypes {
EVENT_MOUSEMOVE = 0, //!< indicates that the mouse pointer has moved over the window.
EVENT_LBUTTONDOWN = 1, //!< indicates that the left mouse button is pressed.
EVENT_RBUTTONDOWN = 2, //!< indicates that the right mouse button is pressed.
EVENT_MBUTTONDOWN = 3, //!< indicates that the middle mouse button is pressed.
EVENT_LBUTTONUP = 4, //!< indicates that left mouse button is released.
EVENT_RBUTTONUP = 5, //!< indicates that right mouse button is released.
EVENT_MBUTTONUP = 6, //!< indicates that middle mouse button is released.
EVENT_LBUTTONDBLCLK = 7, //!< indicates that left mouse button is double clicked.
EVENT_RBUTTONDBLCLK = 8, //!< indicates that right mouse button is double clicked.
EVENT_MBUTTONDBLCLK = 9, //!< indicates that middle mouse button is double clicked.
EVENT_MOUSEWHEEL = 10,//!< positive and negative values mean forward and backward scrolling, respectively.
EVENT_MOUSEHWHEEL = 11 //!< positive and negative values mean right and left scrolling, respectively.
};
这是highgui.hpp
中指定的类型,具体的意义上面的代码解释的已经基本比较清楚了,event的事件主要有11种。
同时,实际使用中可使用highgui_c.h
中指定的CV_EVENT_XXXX的事件。
简单说,flags就是鼠标的拖动事件。具体的定义详见下面highgui.hpp
中指定的类型。
//! Mouse Event Flags see cv::MouseCallback
enum MouseEventFlags {
EVENT_FLAG_LBUTTON = 1, //!< indicates that the left mouse button is down.
EVENT_FLAG_RBUTTON = 2, //!< indicates that the right mouse button is down.
EVENT_FLAG_MBUTTON = 4, //!< indicates that the middle mouse button is down.
EVENT_FLAG_CTRLKEY = 8, //!< indicates that CTRL Key is pressed.
EVENT_FLAG_SHIFTKEY = 16,//!< indicates that SHIFT Key is pressed.
EVENT_FLAG_ALTKEY = 32 //!< indicates that ALT Key is pressed.
};
此时鼠标所在点的位置。
参考其余博客及例程,编写了小程序,实如今图像上画直线的功能:
//该程序实如今图像上画直线的功能;
#include "stdafx.h"
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <iostream>
#include <stdio.h>
using namespace cv;
using namespace std;
#define WINDOW "图像"
//定义全局变量
Mat g_srcImage,g_dstImage; //初始化两个图像矩阵
Point previousPoint;//在图像中定义2维点,分别具备x和y两个坐标
bool dbFlag = false; //定义dbFlag为划线的标志
//声明鼠标回调函数
void MouseEvent(int event, int xMouse, int yMouse, int flags, void*);
//main函数
int main(int argc, char** argv)
{
// 读取图像为g_srcImage
g_srcImage = imread("c:\\images\\flower4.jpg");
if (g_srcImage.empty())
return -1;
//将g_srcImage克隆(clone)给g_dstImage
g_dstImage = g_srcImage.clone(); //注(1):clone与直接赋值的区别
//显示初始图像
imshow(WINDOW, g_srcImage);
//回调函数响应
setMouseCallback(WINDOW, MouseEvent, 0);
waitKey(0);
return 0;
}
void MouseEvent(int event, int xMouse, int yMouse, int flags, void*)
{
char temp[30]; //定义char型变量存储显示的文字
//当没有任何键按下(或起始点还未肯定时),显示鼠标所在点的坐标
if (event == EVENT_MOUSEMOVE && (dbFlag == 0)) //dbFlag == 0保证了第一次按下左键时才为真
{
g_srcImage = g_dstImage.clone(); //将g_dstImage克隆给g_srcImage,以后再g_srcImage上作处理
Point ptTempnoline(xMouse, yMouse); //定义二维点ptTempnoline,也就是无划线的时候的点,赋值给鼠标所在点
//在鼠标所在点输出坐标(左上角为(0,0))(操做对象为g_srcImage)
sprintf_s(temp, "(%d,%d)", xMouse, yMouse);
putText(g_srcImage, temp, ptTempnoline, FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255)); //将坐标点显示到g_srcImage上
//显示g_srcImage
imshow(WINDOW, g_srcImage);
}
//当按下第一次左键时,肯定直线起点坐标,同时画一个圆点,并显示此时坐标
else if (event == EVENT_LBUTTONDOWN &&(dbFlag == 0)) //dbFlag == 0保证了第一次按下左键时为真
{
g_srcImage = g_dstImage.clone(); //将dstImage克隆给srcImage
previousPoint = Point(xMouse, yMouse);//定义二维点previousPoint,也就是划线的起始点
//在鼠标所在点(起始点)输出坐标(操做对象为g_srcImage)
sprintf_s(temp, "(%d,%d)", xMouse, yMouse);
putText(g_srcImage, temp, previousPoint, FONT_HERSHEY_PLAIN,1, Scalar(0, 0, 255));
//在鼠标所在点(起始点)画实心圆点(操做对象为g_srcImage)
circle(g_srcImage, previousPoint, 3, cvScalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
//输出g_srcImage
imshow(WINDOW, g_srcImage);
//将操做完成后的g_srcImage克隆给g_dstImage
g_dstImage = g_srcImage.clone();
//将标识符dbFlag赋值为1,标识此时已经有了第一次鼠标按下了
dbFlag = 1;
}
//当按下第一次左键后,此时移动鼠标,应在起始点和当前点之间具备备选直线(有且仅有这一条)
//为了保证只有一条曲线,须要在g_srcImage上作操做,同事不停的把原有的图像g_dstImage克隆给g_srcImage
//换句话说,只要不是画的咱们想要的直线,不该该将其给g_dstImage
else if (event == EVENT_MOUSEMOVE && (dbFlag == 1)) //dbFlag == 1保证了此时为左键按下后的状态
{
g_srcImage = g_dstImage.clone(); //将g_dstImage克隆给g_srcImage,保证了每次移动鼠标后,都是在以前的图像上画线,避免有其余备选直线的存在
Point ptTemp(xMouse, yMouse); //定义二维点ptTemp,是此时鼠标所在点
//在鼠标所在点(起始点)输出坐标(操做对象为g_srcImage)
sprintf_s(temp, "(%d,%d)", xMouse, yMouse);
putText(g_srcImage, temp, ptTemp, FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255));
//在鼠标所在点和起始点之间画备选直线(操做对象为g_srcImage)。
line(g_srcImage, previousPoint, ptTemp, Scalar(0, 0, 255), 2, 5, 0);
imshow(WINDOW, g_srcImage);
}
//按下第一次左键后,再次按下左键,应在起始点和当前点之间画直线,并在终点画圆点并标明坐标
else if (event == EVENT_LBUTTONDOWN && (dbFlag == 1)) //dbFlag == 1保证了此时为第二次左键按下的状态
{
g_srcImage = g_dstImage.clone(); //将g_dstImage克隆给g_srcImage,保证了每次移动鼠标后,都是在以前的图像上画线
Point ptDst(xMouse, yMouse);//定义二维点ptDst,是终点
//在鼠标所在点(终止点)输出坐标(操做对象为g_srcImage)
sprintf_s(temp, "(%d,%d)", xMouse, yMouse);
putText(g_srcImage, temp, ptDst, FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255));
//在鼠标所在点(终止点)画实心圆点(操做对象为g_srcImage)
circle(g_srcImage, ptDst, 3, cvScalar(255, 0, 0, 0), CV_FILLED, CV_AA, 0);
//在鼠标所在点(终止点)和起始点之间画直线(操做对象为g_srcImage)。
line(g_srcImage, previousPoint, ptDst, Scalar(0, 0, 255), 2, 5, 0);
imshow(WINDOW, g_srcImage);
//将操做完成后的g_srcImage克隆给g_dstImage
g_dstImage = g_srcImage.clone();
//将dbFlag复位为0,从新开始画下一条直线。
dbFlag = 0;
}
}
g_srcImage
和g_dstImage
,用于在不一样的状况下(如画线和备选直线时)进行操做。可是刚开始时,直接使用了g_dstImage = g_srcImage
进行赋值,这致使错误。缘由为:调用拷贝构造函数或赋值操做符时,只会拷贝Mat的头部,并增长reference count,并不会真正拷贝数据。若是要进行数据拷贝,能够调用clone或copyTo函数。dbFlag
的布尔型变量对是否按下起始点进行表示。其实该程序还能够进行简化…putText()
, line()
和circle()
函数的应用。