【OpenCV学习笔记】2.4制作绘图板

学了几天OpenCV,写个小程序把这几天的所得综合实践一下。

一、功能简介

绘图板小程序主要实现以下几个方面功能:
1. 滑动条控制画笔参数(颜色、粗细);
2. 鼠标控制绘图(停顿为实心圆,移动为平滑轨迹);
3. 设置按键功能(重来,保存,退出);
4. 调色盘显示取色。
5. 新增功能:右键漫水填充填色 2015/12/29

1.滑动条控制

设立4个滑动条控制画笔的颜色(B、G、R)和粗细(Thick)

(1)、为画笔颜色、粗细定义全局变量并赋初值

//全局变量
double Brush_r = 255.0;
double Brush_g = 255.0;
double Brush_b = 255.0;
int Brush_thick = 5;

(2)、响应滑动条的回调函数

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;
}


2、鼠标控制

响应鼠标的3种事件。

(1)、鼠标左键按下开始绘图

鼠标事件:EVENT_LBUTTONDOWN
动作:改变bool值drawflag表示开始绘图,记录当前坐标Current,并画点(实心圆)circle()

(2)、在左键按下的同时移动鼠标,绘制鼠标移动的轨迹

鼠标事件:EVENT_MOUSEMOVE
动作:识别是否同时左键按下,更新上一个坐标(Pre)和当前坐标(Current),绘制轨迹线line()

(3)、左键抬起停止绘图

鼠标事件: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会依次执行。


3、调色盘功能

区别“=”和copyTo:

  • “Mat A=B”意味着 A将指向B的同一块内存;
  • “A.copyTo(B)”意味着A的内容复制给了B,B的地址不变。
        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


4、按键功能

在程序的主循环中用waitKey()重复获取按键信息,以switch语句判别应该完成的功能,添加按键功能重来,保存,退出功能。

  • waitKey()参数为n表示每等待n毫秒获取按键值一次;但当n为0时,表示无限等待。
  • default项很重要,没有default,当没有输入时,主循环无法继续。
        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;
        }       


二、main函数的写法

#include<opencv2/opencv.hpp>
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;   
}


三、运行效果

从运行效果一起来看一下绘图板发展:

1、跟随鼠标轨迹

该版本错误的将绘图操作放在了主循环里,每循环一次才画一个点,导致了不连续。并且每个点都是用circle()画空心的圆。
这里写图片描述

2、平滑绘图

改在EVENT_MOUSEMOVE中用line()画极短的线。
这里写图片描述

3、加入颜色调整

该版本缺点是不知道将会出来什么样的颜色。
这里写图片描述

4、设立调色盘

同时优化保存画面时去掉调色盘。
这里写图片描述

5、加入调整粗细

同时调整笔触,落点为实心圆。
这里写图片描述

6、新增:漫水填充

增加右键功能——floodFill填充颜色
这里写图片描述
大功告成!欢迎朋友们指点……

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值