学了几天OpenCV,写个小程序把这几天的所得综合实践一下。
绘图板小程序主要实现以下几个方面功能:
1. 滑动条控制画笔参数(颜色、粗细);
2. 鼠标控制绘图(停顿为实心圆,移动为平滑轨迹);
3. 设置按键功能(重来,保存,退出);
4. 调色盘显示取色。
5. 新增功能:右键漫水填充填色 2015/12/29
设立4个滑动条控制画笔的颜色(B、G、R)和粗细(Thick)
//全局变量
double Brush_r = 255.0;
double Brush_g = 255.0;
double Brush_b = 255.0;
int Brush_thick = 5;
void ON_TRACKBAR_B(int t, void*){//更改蓝色
Brush_b = t;
Brush_color=Scalar(Brush_b,Brush_g,Brush_r);
}
void ON_TRACKBAR_G(int t, void*){//更改绿色
Brush_g = t;
Brush_color = Scalar(Brush_b, Brush_g, Brush_r);
}
void ON_TRACKBAR_R(int t, void*){//更改红色
Brush_r = t;
Brush_color = Scalar(Brush_b, Brush_g, Brush_r);//注意颜色为BGR
}
void ON_TRACKBAR_THICK(int t, void*){//更改画笔粗细
Brush_thick = t;
}
响应鼠标的3种事件。
鼠标事件:EVENT_LBUTTONDOWN
动作:改变bool值drawflag表示开始绘图,记录当前坐标Current,并画点(实心圆)circle()
鼠标事件:EVENT_MOUSEMOVE
动作:识别是否同时左键按下,更新上一个坐标(Pre)和当前坐标(Current),绘制轨迹线line()
鼠标事件:EVENT_LBUTTONUP
动作:改变bool值drawflag表示停止绘图
void ON_MOUSE_HANDLE(int event,int x,int y,int flags,void* param){
Mat& picture = *(Mat *)param;
switch (event)
{
case EVENT_LBUTTONDOWN://左键按下
{
drawflag = 1;
Current=Point(x,y);
circle(picture, Current, Brush_thick/2, Brush_color, -1);//参数依据滑动条改变
break;
}
case EVENT_MOUSEMOVE://鼠标移动
{
if (drawflag){//左键是否为按下状态
Point Pre = Current;
Current = Point(x, y);
line(picture, Pre, Current, Brush_color, Brush_thick);//参数依据滑动条改变
}
break;
}
case EVENT_LBUTTONUP://左键抬起
{
drawflag = 0;
break;
}
}
}
新增功能:漫水填充
case EVENT_RBUTTONDOWN:
{
floodFill(picture, Point(x, y), Brush_color);
break;
}
要特别注意每条CASE后的break语句,不然Switch的各种Case会依次执行。
区别“=”和copyTo:
Mat platte = tempImage(Rect(0, 0, 50, 50));//调色盘指向的是图像上的左上角矩形区域
Mat temp = Mat(50, 50, CV_8UC3, Scalar(Brush_b, Brush_g, Brush_r));//参数随滑动条变化
temp.copyTo(platte);//只改变调色盘的内容,即改变当前图像左上角矩形区域的颜色
第3行 不能用 “platte = temp”代替, 它会令调色盘指向了temp
在程序的主循环中用waitKey()重复获取按键信息,以switch语句判别应该完成的功能,添加按键功能重来,保存,退出功能。
int c = waitKey(1);//不为0即可
switch (c)
{
case 114://按'r'键 清空绘图板
srcImage.copyTo(tempImage);
imshow(WINDOW_NAME1, tempImage);
break;
case 115://按's'键 save图像
platte.copyTo(temp2);//暂存调色盘
temp = Mat(50, 50, CV_8UC3, Scalar(0, 0,0));
temp.copyTo(platte);
imwrite("……/temp.jpg", tempImage);//保存不显示调色盘的图像
temp2.copyTo(platte);
break;
case 27: //按ESC键,程序退出
loopflag = 0; //退出主循环的控制值
break;
default://未获取以上按键、无按键输入时的操作
break;
}
#include
using namespace cv;
#define WINDOW_NAME1 "【绘图板】 Press 'r' restart ; Press 's' save ; Press 'Esc' quit . "
#define WINDOW_WIDTH 600
//全局函数声明
void ON_MOUSE_HANDLE(int event, int x, int y, int flags, void* param);
void ON_TRACKBAR_B(int t, void*);
void ON_TRACKBAR_G(int t, void*);
void ON_TRACKBAR_R(int t, void*);
//全局变量声明
bool drawflag = 0;
Point Current;
double Brush_r = 255.0;
double Brush_g = 255.0;
double Brush_b = 255.0;
int Brush_thick = 5;
String trackbarname_r = "红色值";
String trackbarname_g = "绿色值";
String trackbarname_b = "蓝色值";
String trackbarname_thick = "画笔粗细";
Scalar Brush_color = Scalar(255,255,255);
//入口函数
int main(){
system("color 9F");
Current = Point(0, 0);
Mat srcImage(600, 800, CV_8UC3), tempImage;
srcImage = Scalar(0, 0, 0);
srcImage.copyTo(tempImage);//保护源图
namedWindow(WINDOW_NAME1);
//创建滑动条控件
int threshold_b=255;
createTrackbar(trackbarname_b, WINDOW_NAME1, &threshold_b, 255, ON_TRACKBAR_B);
ON_TRACKBAR_B(threshold_b,0);//响应滑动条的回调函数
int threshold_g=255;
createTrackbar(trackbarname_g, WINDOW_NAME1, &threshold_g, 255, ON_TRACKBAR_G);
ON_TRACKBAR_G(threshold_g, 0);
int threshold_r=255;
createTrackbar(trackbarname_r, WINDOW_NAME1, &threshold_r, 255, ON_TRACKBAR_R);
ON_TRACKBAR_R(threshold_r, 0);
int threshold_thick = 5;
createTrackbar(trackbarname_thick, WINDOW_NAME1, &threshold_thick, 100, ON_TRACKBAR_THICK);
ON_TRACKBAR_THICK(threshold_thick, 0);
//设置鼠标操作回调函数
setMouseCallback(WINDOW_NAME1, ON_MOUSE_HANDLE, (void*)&tempImage);//tempImage是传递到回调函数中的参数param
//程序主循环,当进行绘制的标识符为真时,进行绘制
bool loopflag = 1;
while (loopflag)//
{
Mat platte = tempImage(Rect(0, 0, 50, 50));//将绘图板左上角小块矩形区域创建为调色盘
Mat temp = Mat(50, 50, CV_8UC3, Scalar(Brush_b, Brush_g, Brush_r));
temp.copyTo(platte);
imshow(WINDOW_NAME1, tempImage);
int c = waitKey(1);//获取按键
Mat temp2 = Mat(50, 50, CV_8UC3);//用于暂存调色盘
switch (c)
{
case 114://'r'
srcImage.copyTo(tempImage);
imshow(WINDOW_NAME1, tempImage);
break;
case 115://'s'
platte.copyTo(temp2);//避免保存图像显示调色盘
temp = Mat(50, 50, CV_8UC3, Scalar(0, 0,0));
temp.copyTo(platte);
imwrite("……/temp.jpg", tempImage);
temp2.copyTo(platte);
break;
case 27: //esc
loopflag = 0;
break;//按下ESC键,程序退出
default:
break;
}
}
return 0;
}
从运行效果一起来看一下绘图板发展:
该版本错误的将绘图操作放在了主循环里,每循环一次才画一个点,导致了不连续。并且每个点都是用circle()画空心的圆。
改在EVENT_MOUSEMOVE中用line()画极短的线。