目录
Github代码地址:https://github.com/Qinong/OpenCV.git
第2章 图片点击与绘图
2.1 图像点击
2.1.1 在图上点击
highgui 模块中有使程序对鼠标或键盘事件做出响应、在图像上绘制形状或写人文本的函数。
可以让鼠标在置于图像窗口上时运行特定的指令。要实现这个功能,需定义一个合适的回调函数。回调函数不会被显式地调用,但是会在响应特定事件(这里是指有关鼠标与图像窗口交互的事件)的时候被程序调用。为了能被程序识别,回调函数需要具有特定的签名,并且必须注册。对于鼠标事件处理两数,回调函数必须具有这种签名:
void onMouse( int event, int x, int y, int flags, void* param)
event:表示触发回调的数的鼠标事件的类型。
x,y:两个参数是事件发生时鼠标的位置,用像素坐标表示。
flags:表示事件发生时按下了鼠标的哪个键。
param:指向任意对象的指针,作为附加的参数发送给函数。
你可用下面的方法 在程序中注册回调函数:
void cv::setMouseCallback(const String &winname,cv::MouseCallback onMouse,void *userdata=(void*)0)
- winname:窗口名称
- onMouse:鼠标事件的回调函数
- userdata:传递给回调函数的可选参数
- event:所发生的事件
EVENT_MOUSEMOVE 0 鼠标移动
EVENT_LBUTTONDOWN 1 按下鼠标左键
EVENT_RBUTTONDOWN 2 按下鼠标右键
EVENT_MBUTTONDOWN 3 按下鼠标中键
EVENT_LBUTTONUP 4 放开鼠标左键
EVENT_RBUTTONUP 5 放开鼠标右键
EVENT_MBUTTONUP 6 放开鼠标中键
EVENT_LBUTTONDBLCLK 7 鼠标左键双击
EVENT_RBUTTONDBLCLK 8 鼠标右键双击
EVENT_MBUTTONDBLCLK 9 鼠标中键双击
- x,y:鼠标所在图像的坐标
- flags:代表拖拽事件
EVENT_FLAG_LBUTTON 1 按住左键拖拽
EVENT_FLAG_RBUTTON 2 按住右键拖拽
EVENT_FLAG_MBUTTON 4 按住中键拖拽
EVENT_FLAG_CTRLKEY 8 按住ctrl键拖拽
EVENT_FLAG_SHIFTKEY 16 按住shift键拖拽
EVENT_FLAG_ALTKEY 32 按住alt键拖拽
- param: 自己定义的onMouse事件的ID
(1)显示鼠标点击点的像素值
void onMouse( int event, int x, int y, int flags, void* param) {
cv::Mat *im= reinterpret_cast<cv::Mat*>(param);
switch (event) { // 调度事件
case cv::EVENT_LBUTTONDOWN: // 鼠标左键按下的事件
// 显示鼠标点按下的像素值(x,y)
std::cout << "at (" << x << "," << y << ") value is: "
<< static_cast<int>(im->at<uchar>(cv::Point(x,y))) << std::endl;
break;
}
}
2.2 在图像上绘图
2.2.1 在图像上绘制基本图像
OpenCV 提供了几个用于在图像上绘制形状的函数,基本的形状绘制函数有circle、ellipse、line 和 rectangle。
void cv::circle (InputOutputArray img, // 图像
Point center, // 圆心点
int radius, // 半径
const Scalar &color, // 颜色
int thickness=1, // 线条粗细
int lineType=LINE_8, // 线条类型
int shift=0) // 中心坐标和半径值中的小数位数
(1)在图上画圆
cv::Mat image2 = image1.clone();
cv::circle(image2, // 目标图像
cv::Point(1500,2000), // 中心点坐标
500, // 半径
0, // 颜色
50); // 厚度
cv::namedWindow("Image2",0);
cv::imshow("Image2", image2);
cv::waitKey(0);
2.2.2 在图像上写入文本
在OpenCV 的方法和函数中,你也可以在图像上写人文本,方法如下所示:
void cv::putText(cv::Mat& img, // 待绘制的图像
const string& text, // 待绘制的文字
cv::Point origin, // 文本框的左下角
int fontFace, // 字体 (如cv::FONT_HERSHEY_PLAIN)
double fontScale, // 尺寸因子,值越大文字越大
cv::Scalar color, // 线条的颜色(RGB)
int thickness = 1, // 线条宽度
int lineType = 8, // 线型(4邻域或8邻域,默认8邻域)
bool bottomLeftOrigin = false // 指定的点为插入文字的位置(true=文字的左上角位置,false=文字的左下角位置)
);
(1)在图像上写入文本
cv::Mat image3 = image1.clone();
cv::putText(image3, // destination image
"This is a Audi.", // text
cv::Point(1000,2500), // text position
cv::FONT_HERSHEY_PLAIN, // font type
20.0, // font scale
255, // text color (here white)
20); // text thickness
cv::namedWindow("Image3",0);
cv::imshow("Image3", image3);
cv::waitKey(0);
2.2.3 在图像上插入logo
假设我们要把一 个小图像复制到一个大图像上。例如要把下面的logo插人到测试图像中。
为了实现这个功能,可以定义一个兴趣区域 (Region Of Interest, ROI),在此处进行复制操作,这个ROI的位置将决定标志的插人位置。
第一步是定义 ROI。定义后,就可以把 ROI当作一个普通的cv::Mat 实例进行操作。接着,可以用下面的方法插人标志:
// cv::Rect矩形类参数设置
cv::Rect(int x, int y, // 左上角坐标
int width, int height); // 矩形的宽和高
cv:Range类型用于指元连续的整数序列,包合两个元素start和end,通过cv:Range (int start, int end)进行初始化。生成结果包括起始值,但不含终值。
(1)方法1:图片插入logo
// 方法1:图片插入logo
cv::Mat image4 = image1.clone();
cv::Mat logo= cv::imread("Audi_logo.jpeg");
// 在图像的右下角定义一个ROI
cv::Mat imageROI(image4,
cv::Rect(image4.cols-logo.cols, // ROI坐标
image4.rows-logo.rows,
logo.cols,logo.rows));// ROI大小
// 插入logo
logo.copyTo(imageROI);
cv::namedWindow("Image4",0);
cv::imshow("Image4", image4);
cv::waitKey(0);
(2)方法2:图片插入logo
// 方法2:图片插入logo
cv::Mat image5 = image1.clone();
cv::Mat logo1= cv::imread("Audi_logo.jpeg",cv::IMREAD_GRAYSCALE);
// ROI用cv::Range描述
imageROI= image5(cv::Range(image5.rows-logo.rows,image5.rows),
cv::Range(image5.cols-logo.cols,image5.cols));
// 用logo作为掩码(必须是灰度图像)
cv::Mat mask(logo1);
// 插入logo,只负责掩码不为0的位置
logo.copyTo(imageROI,mask);
cv::namedWindow("Image5",0);
cv::imshow("Image5", image5);
cv::waitKey(0);
运行上述代码后,你将得到下面的图像。
2.3 完整代码
#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
void onMouse( int event, int x, int y, int flags, void* param) {
cv::Mat *im= reinterpret_cast<cv::Mat*>(param);
switch (event) { // 调度事件
case cv::EVENT_LBUTTONDOWN: // 鼠标左键按下的事件
// 显示鼠标点按下的像素值(x,y)
std::cout << "at (" << x << "," << y << ") value is: "
<< static_cast<int>(im->at<uchar>(cv::Point(x,y))) << std::endl;
break;
}
}
int main() {
// code1
// 显示鼠标点击点的像素值
cv::Mat image1 = cv::imread("Audi_RS7.jpg");
if (image1.empty()) {
std::cout << "Error reading image..." << std::endl;
return 0;
}
cv::namedWindow("Image1",0);
cv::imshow("Image1", image1); // show the image
cv::waitKey(0); // 0 to indefinitely wait for a key pressed
cv::setMouseCallback("Image1", onMouse, reinterpret_cast<void*>(&image1));
// code2
// 在图上画圆
cv::Mat image2 = image1.clone();
cv::circle(image2, // 目标图像
cv::Point(1500,2000), // 中心点坐标
500, // 半径
0, // 颜色
50); // 厚度
cv::namedWindow("Image2",0);
cv::imshow("Image2", image2);
cv::waitKey(0);
// code3
// 在图上写入文本
cv::Mat image3 = image1.clone();
cv::putText(image3, // destination image
"This is a Audi.", // text
cv::Point(1000,2500), // text position
cv::FONT_HERSHEY_PLAIN, // font type
20.0, // font scale
255, // text color (here white)
20); // text thickness
cv::namedWindow("Image3",0);
cv::imshow("Image3", image3);
cv::waitKey(0);
// code4
// 方法1:图片插入logo
cv::Mat image4 = image1.clone();
cv::Mat logo= cv::imread("Audi_logo.jpeg");
// 在图像的右下角定义一个ROI
cv::Mat imageROI(image4,
cv::Rect(image4.cols-logo.cols, // ROI坐标
image4.rows-logo.rows,
logo.cols,logo.rows));// ROI大小
// 插入logo
logo.copyTo(imageROI);
cv::namedWindow("Image4",0);
cv::imshow("Image4", image4);
cv::waitKey(0);
// code5
// 方法2:图片插入logo
cv::Mat image5 = image1.clone();
cv::Mat logo1= cv::imread("Audi_logo.jpeg",cv::IMREAD_GRAYSCALE);
// ROI用cv::Range描述
imageROI= image5(cv::Range(image5.rows-logo.rows,image5.rows),
cv::Range(image5.cols-logo.cols,image5.cols));
// 用logo作为掩码(必须是灰度图像)
cv::Mat mask(logo1);
// 插入logo,只负责掩码不为0的位置
logo.copyTo(imageROI,mask);
cv::namedWindow("Image5",0);
cv::imshow("Image5", image5);
cv::waitKey(0);
return 0;
}