项目功能实现:通过移动鼠标,在图片上实现类似画板绘制矩形的功能,并把绘制的矩形区域单独显示出来
按照之前的博文结构来,这里就不在赘述了
一、头文件
mouse.h
#pragma once#include<opencv2/opencv.hpp>using namespace cv;class MOUSE {
public:void mouse(Mat& image);
};#pragma once
二、函数实现
mouse.cpp
Point start_point(-1, -1);
Point end_point(-1, -1);
首先对起始和结束的位置进行初始化
Mat picture;
因为要在原图上进行操作,且每次绘制完矩形之后要重新初始化,故通过picture进行克隆原图像操作
setMouseCallback("Mouse_control", on_draw, (void*)(&image));
想要实现鼠标响应事件,需要调用setMouseCallback这个函数
参数一:显示窗口名称
参数二:响应函数
参数三:传入的个人参数数据信息,这里是传入了一张图片
响应函数on_draw的实现
Mat image = *((Mat*)userdata);
因为传入的是一张图片,故这里就接收一下
鼠标事件包含很多种,这里使用到的是按下、移动、抬起
OpenCV官网API传送门
鼠标按下事件:event == EVENT_LBUTTONDOWN)
按下的位置是矩形的起始位置,通过start_point.x = x; start_point.y = y;
得到矩形的起始位置信息
鼠标抬起事件:event == EVENT_LBUTTONUP
抬起的位置是矩形的结束位置,通过end_point.x = x; end_point.y = y;
得到矩形的结束位置信息
结束位置与起始位置的差就是矩形的宽和高,通过dx = abs(end_point.x - start_point.x); dy = abs(end_point.y - start_point.y);
得到dx和dy就是矩形的宽和高信息,这里加绝对值是防止用户从下往上绘制矩形情况无法绘制
Rect box(start_point.x, start_point.y, dx, dy);
通过起始位置和宽高信息可以将矩形绘制出来
rectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);
因为要在原图上进行操作,故通过rectangle函数在原图上进行绘制矩形
详细参数使用可参考博文:十四、图像几何形状绘制
参数一:原图
参数二:在原图上要绘制的区域
参数三:绘制的颜色
参数四:线段粗细,-1表示填充,其他正整数表示线条粗细程度,越大越粗
参数五:消除锯齿,因为像素点是浮点数,在连线的时候会出现锯齿,LINE_AA也可以用16代替,表示通过周围16个像素点信息进行消除锯齿
参数六:偏置项类似微调的作用,这里默认0即可
Rect box(start_point.x, start_point.y, dx, dy);
imshow("ROI", image(box));
通过起始点坐标信息以及宽高信息进行绘制ROI区域,得到对象box
然后显示ROI区域即可
鼠标移动事件:event == EVENT_MOUSEMOVE
因为每次绘制矩形的时候,前面的矩形都得消失,说白了就是初始化了,通过picture.copyTo(image);
每次调用原始图像进行操作即可,克隆一下原始图像,进行存储,移动的过程中实时的进行刷新,每次都对原图进行操作
ectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);
绘制矩形区域
#include"mouse.h"
#include<iostream>
#include<opencv2/opencv.hpp>Point start_point(-1, -1);
Point end_point(-1, -1);
Mat picture;static void on_draw(int event, int x, int y, int flags, void* userdata) {Mat image = *((Mat*)userdata);if (event == EVENT_LBUTTONDOWN) {//鼠标按下start_point.x = x;start_point.y = y;std::cout << "start point is: " << start_point << std::endl;}else if (event == EVENT_LBUTTONUP) {end_point.x = x;end_point.y = y;int dx, dy;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);if (dx > 0 && dy > 0) {//只有进行了移动,有距离差才算开始绘制矩形std::cout << "end point is: " << end_point << std::endl;Rect box(start_point.x, start_point.y, dx, dy);picture.copyTo(image);imshow("ROI", image(box));rectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);imshow("Mouse_control", image);//重新初始化起点坐标,为下一次绘制做准备start_point.x = -1;start_point.y = -1;}}else if (event == EVENT_MOUSEMOVE) {if (start_point.x > 0 && start_point.y > 0) {//只有起始位置不为0才进行操作,否则操作就没啥意义end_point.x = x;end_point.y = y;int dx, dy;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);if (dx > 0 && dy > 0) {//起始位置和结束位置必须有距离Rect box(start_point.x, start_point.y, dx, dy);picture.copyTo(image);rectangle(image, box, Scalar(255, 0, 0), 2, LINE_AA, 0);imshow("Mouse_control", image);}}}
}void MOUSE::mouse(Mat& image) {namedWindow("Mouse_control", WINDOW_FREERATIO);setMouseCallback("Mouse_control", on_draw, (void*)(&image));imshow("Mouse_control", image);picture = image.clone();
}
三、主函数
yy_main.cpp
#include <opencv2/opencv.hpp>
#include <iostream>
#include "mouse.h"using namespace cv;
using namespace std;int main(int argc, char** argv) {Mat src = cv::imread("E:/C++_workspace/beyond.jpg", IMREAD_COLOR);if (src.empty()) {printf("load image is false...\n");return -1;}namedWindow("yanyu", WINDOW_FREERATIO);imshow("yanyu", src);MOUSE yy;yy.mouse(src);waitKey(0);destroyAllWindows();return 0;
}
项目结构如下:
效果图如下:
对图片进行移动鼠标绘制矩形,并单独显示该区域
四、课后作业
项目功能实现:通过移动鼠标,在图片上实现类似画板绘制圆的功能,并把绘制的圆形区域单独显示出来
mouse.cpp
核心小技巧为:
circle(mask, start_point, r, Scalar(255, 255, 255), -1);
通过圆心和半径绘制填充的白色圆形区域
picture.copyTo(result, mask);
利用copyTo函数的特性, 将picture所对应的mask中的非零区域拷贝到result中
imshow("ROI", result);
显示即可
#include"mouse.h"
#include<iostream>
#include<opencv2/opencv.hpp>Point start_point(-1, -1);
Point end_point(-1, -1);
Mat picture;static void on_draw(int event, int x, int y, int flags, void* userdata) {Mat image = *((Mat*)userdata);Mat mask, result;mask = Mat::zeros(image.size(), CV_8UC3);if (event == EVENT_LBUTTONDOWN) {//鼠标按下start_point.x = x;start_point.y = y;std::cout << "start point is: " << start_point << std::endl;}else if (event == EVENT_LBUTTONUP) {//鼠标抬起end_point.x = x;end_point.y = y;int dx, dy;int r;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);r = sqrt(abs(pow(dx, 2)) + abs(pow(dy, 2)));if (dx > 0 && dy > 0) {std::cout << "end point is: " << end_point << std::endl;std::cout << "r is: " << r << std::endl;circle(image, start_point, r, Scalar(0, 255, 0), 1);imshow("Mouse_control", image);circle(mask, start_point, r, Scalar(255, 255, 255), -1);picture.copyTo(result, mask);imshow("ROI", result);//重新初始化起点坐标,为下一次绘制做准备start_point.x = -1;start_point.y = -1;}}else if (event == EVENT_MOUSEMOVE) {if (start_point.x > 0 && start_point.y > 0) {end_point.x = x;end_point.y = y;int dx, dy;double r;dx = abs(end_point.x - start_point.x);dy = abs(end_point.y - start_point.y);r = sqrt(pow(dx, 2) + pow(dy, 2));if (dx > 0 && dy > 0) {picture.copyTo(image);circle(image, start_point, r, Scalar(0, 255, 0), 2, 8, 0);imshow("Mouse_control", image);}}}
}void MOUSE::mouse(Mat& image) {namedWindow("Mouse_control", WINDOW_FREERATIO);setMouseCallback("Mouse_control", on_draw, (void*)(&image));imshow("Mouse_control", image);picture = image.clone();
}
效果图如下: