OpenCV 4.5.1的学习

环境:OpenCV4.5.1 + VS2019

目录

001.安装并搭建opencv4.5.1环境

002.图像色彩空间转换

003.图像对象的创建与赋值

004.图像像素的读写操作

005.图像像素的算术操作(加减乘除4种不同的API实现

006.滚动条-调整图像亮度

007.滚动条-传递参数

008.键盘响应操作

009.OpenCV自带颜色表操作

010.图像像素的逻辑操作(与,或,非,异或

011.通道合并与分离

012.图像色彩空间转换(提取轮廓然后换绿幕

013.图像像素值统计(min,max,mean均值,standard deviation标准方差

014.图像几何形状绘制(圆,矩形,直线,椭圆

015.随机数与随机颜色

016.多边形填充与绘制

017.鼠标操作与响应(提取选中的ROI区域

018.图像像素类型转换和归一化

019.图像放缩与插值

020.图像翻转

021.图像旋转

022.视频文件摄像头使用

023.视频处理与保存

024.图像直方图

025.二维直方图

026.直方图均衡化

027.图像卷积操作(会变模糊,且卷积核尺寸越大则越模糊

028.高斯模糊

029.高斯双边模糊(可磨皮操作

030.案例:实时人脸检测

代码总结

001.安装并搭建opencv4.5.1环境

sample里面是事例代码

//配置vs和OPenCV

(一)在 vs中打开“视图”-“属性管理器”-

“Microsoft.Cpp.x64.user”右击“属性”-“VC++目录”-

(1)配置包含目录:“包含目录”编辑-新建-路径-找到

opencv4.5.1-build-includ-选择文件夹。再次新建包含路径 新建-路径-找到opencv4.5.1-opencv-build-includ-opencv2 点选着路径 点确定

(2)配置库目录 “库目录”-“编辑”-新建-找到-“OpenCV4.5.1-opencv-build-x64-cv15-lib”选择路径

(二)配置连接器:

-“输入”-“附加依赖项”-“编辑”找到“OpenCV4.5.1-opencv-build-x64-cv15-lib-opencv_world451.lib”点击确定。

#include<opencv2\opencv.cpp>
#include<iostrem>

(三) 配置环境变量:

计算机=>属性 =>高级系统设置=>环境变量 (OpenCV4.5.1-opencv-build-x64-vc15-bin路径)填好,重启vs.

(找不到某个头文件的时候)项目名称那右击环境目录。

(四)输入代码,读取图像操作

1.读取一个图像,一个参数的时候就只是地址。两个参数的时候可以是图像的模式

Mat :所有的图像类型,是个二维矩阵。

2.图像的读取,有1个参数就只填图像地址,2个参数时可以填写加载图像格式(例如灰度图像IMREAD_GRAYSCALE,加载透明通道 IMREAD_UNCHANGED hsv色彩图使用IMREAD_ANYCOLOR 32为 ANYPATH)

Mat src= imread("地址/地址")

src.depth()获取图像深度。

3.显示图像 两个参数 显示的图像为AUTOSIZE

imshow("窗口名称",图像地址名称scr)

waitKey()停顿,参数代表停顿的时间,如果参数为1,则表示停顿1毫秒然后继续执行下面的程序。如果是0,就会一直阻塞停顿在那。

destoryAllWindw() 把前面所有的窗口全部销毁掉。

4.创建一个窗口:第一个参数是窗口名称,第二个参数窗口的显示模式(AUTOSIZE ,WINDOW_GUI_NORMAL是绘制没有状态栏和工具栏的窗口的旧方法,而WINDOW_GUI_EXPANDED是新的增强型GUI WINDOW_FREERATIO)

namedWindow("输入窗口",windows_freeratio)

if(src.empty()){

printf("图像不显示")

}

5.cvtCOLOR图像转换

COLOR_BGR2GRAY =6 把彩色转灰度

COLOR_GRAY2BGR =8 把彩色转恢复

COLOR_BGR2HSV=40 把RGB转HSV

COLOR_HSV2BGR=54 把HSV转RGB

6.用来保存 第一个参数文件保存路径,第二个参数是要保存的对象名称

只支持8位的图像显示 H 0-180 控制亮度通道 s控制色彩控到 ,v控制

imwrite(“”,src)

002.图像色彩空间转换

void colorSpace_Demo(Mat &image);

    void QuickDemo::colorSpace_Demo(Mat &image) {
    Mat gray, hsv; //定义2个矩阵类型的图像
    cvtColor(image, hsv, COLOR_BGR2HSV);//转换成hdv (图像转换函数,第三个参数是转成的类型
    cvtColor(image, gray, COLOR_BGR2GRAY);//转成灰度
    imshow("HSV", hsv);//显示图片
    imshow("灰度", gray);//显示图片
    //imwrite("F:\\文件夹\\C++\\OPENCV4入门学习\\图\\hsv.png", hsv);//保存图片 (保存地址,保存图的名称)
    //imwrite("F:\\文件夹\\C++\\OPENCV4入门学习\\图\\gray.png", gray);//保存
    }

003.图像对象的创建与赋值

void mat_creation_demo(/*Mat& image*/);

    void QuickDemo::mat_creation_demo(/*Mat& image*/) {
    //Mat m1, m2;
    //m1 = image.clone();
    //image.copyTo(m2);
     
    //创建空白图形
    Mat m3 = Mat::ones(Size(400, 400), CV_8UC3);//8位的无符号的3通道(改1则为单通道
    //ones 改 zeros则初始化为0
    //长度 = 通道数 * 宽度
    m3 = Scalar(255, 0, 0);//给三个通道都赋值,单通道则 m3 = 127;
    //m3初始化为蓝色
    cout << "width:" << m3.cols << endl << "hight:" << m3.rows << endl << "channels:" << m3.channels() << endl;
    //显示宽度,长度,通道数
    //cout << m3 << endl;
     
    Mat m4;
    //m4 = m3;//直接赋值 则m4变,m3也变(同体
    //m4 = m3.clone();//m4为m3的克隆,m4变,m3不会变(不同体
    m3.copyTo(m4);//把m3赋值给m4,m4为蓝色
    m4 = Scalar(0, 255, 255);//改变m4的颜色为黄色
    imshow("图像3", m3);//标题和图像名称  显示图像3 纯蓝色
    imshow("图像4", m4);
    }

004.图像像素的读写操作

void pixel_visit_demo(Mat &image);

    void QuickDemo::pixel_visit_demo(Mat &image) {
    int dims = image.channels();
    int h = image.rows;
    int w = image.cols;
     
    //数组下标访问像素值
    /*
    for (int row = 0; row < h; row++) {
    for (int col = 0; col < w; col++) {
    if (dims == 1) {//单通道的灰度图像
    int pv = image.at<uchar>(row, col);//得到像素值
    image.at<uchar>(row, col) = 255 - pv;//给像素值重新赋值(取反
    }
    if (dims == 3) {//三通道的彩色图像
    Vec3b bgr = image.at<Vec3b>(row, col);//opencv特定的类型,获取三维颜色,3个值
    image.at<Vec3b>(row, col)[0] = 255 - bgr[0];
    image.at<Vec3b>(row, col)[1] = 255 - bgr[1];
    image.at<Vec3b>(row, col)[2] = 255 - bgr[2]; //对彩色图像读取其像素值,并将其改写
    }
    }
    }
    */
     
    //指针访问模式
    for (int row = 0; row < h; row++) {
    uchar* current_row = image.ptr<uchar>(row);
    for (int col = 0; col < w; col++) {
    if (dims == 1) {//单通道的灰度图像
    int pv = image.at<uchar>(row, col);//得到像素值
    *current_row++ = 255 - pv;//给像素值重新赋值(取反
    }
    if (dims == 3) {//三通道的彩色图像
    *current_row++ = 255 - *current_row;//指针每做一次运算,就向后移动一位
    *current_row++ = 255 - *current_row;
    *current_row++ = 255 - *current_row;
    }
     
    }
    }
    namedWindow("像素读写演示", WINDOW_FREERATIO);
    imshow("像素读写演示", image);
    //imwrite("E:/2021.9.26备份/图片/Camera Roll/003颜色取反.png", image);//保存
    }

005.图像像素的算术操作(加减乘除4种不同的API实现

void operators_demo(Mat &image);

    void QuickDemo::operators_demo(Mat &image) {
    Mat dst = Mat::zeros(image.size(), image.type());
    Mat m = Mat::zeros(image.size(), image.type());
    dst = image - Scalar(50, 50, 50);
    m = Scalar(50, 50, 50);
     
    multiply(image, m, dst);//乘法操作 api
    imshow("乘法操作", dst);
     
    add(image, m, dst);//加法操作 api
    imshow("加法操作", dst);
     
    subtract(image, m, dst);//减法操作 api
    imshow("减法操作", dst);
     
    divide(image, m, dst);//除法操作 api
    imshow("除法操作", dst);
     
    //加法操作底层
    /*
    int dims = image.channels();
    int h = image.rows;
    int w = image.cols;
    for (int row = 0; row < h; row++) {
    for (int col = 0; col < w; col++) {
    Vec3b p1 = image.at<Vec3b>(row, col);//opencv特定的类型,获取三维颜色,3个值
    Vec3b p2 = m.at<Vec3b>(row, col);
    dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p1[0] + p2[0]);
    dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] + p2[1]);
    dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);
    }
    }
    namedWindow("加法操作底层", WINDOW_FREERATIO);
    imshow("加法操作底层", dst);
    */
    }

006.滚动条-调整图像亮度

void tracking_bar_demo1(Mat &image);

    Mat src, dst, m;
    int lightness = 50;//定义初始化的亮度为50
    static void on_track(int, void*) {
    m = Scalar(lightness, lightness, lightness);//创建调整亮度的数值
    add(src, m, dst);
    //subtract(src, m, dst);//定义亮度变换为减
    imshow("亮度调整", dst);//显示调整亮度之后的图片
    }
     
    void QuickDemo::tracking_bar_demo1(Mat &image) {
    namedWindow("亮度调整", WINDOW_AUTOSIZE);
    dst = Mat::zeros(image.size(), image.type());//图片的初始化创建一个和image大小相等,种类相同的图像
    m = Mat::zeros(image.size(), image.type());//图片的初始化创建一个和image大小相等,种类相同的图像
    src = image;//给src赋值
    int max_value = 100;//定义最大值为100
    createTrackbar("Value Bar", "亮度调整", &lightness, max_value, on_track);//调用函数实现功能
    on_track(50, 0);
    }

007.滚动条-传递参数

void tracking_bar_demo2(Mat &image);

    static void on_lightness(int b, void* userdata) {
    Mat image = *((Mat*)userdata);
    Mat dst = Mat::zeros(image.size(), image.type());
    Mat m = Mat::zeros(image.size(), image.type());
    m = Scalar(b, b, b);//创建调整亮度的数值
    addWeighted(image, 1.0, m, 0, b, dst);//融合两张图 dst = image * 1.0 + m * 0 + b
    imshow("亮度&对比度调整", dst);//显示调整亮度之后的图片
    }
    static void on_contrast(int b, void* userdata) {
    Mat image = *((Mat*)userdata);
    Mat dst = Mat::zeros(image.size(), image.type());
    Mat m = Mat::zeros(image.size(), image.type());
    double contrast = b / 100.0;
    addWeighted(image, contrast, m, 0.0, 0, dst);
    imshow("亮度&对比度调整", dst);//显示调整亮度之后的图片
    }
     
    void QuickDemo::tracking_bar_demo2(Mat& image) {
    namedWindow("亮度&对比度调整", WINDOW_AUTOSIZE);
    int lightness = 50;//定义初始化的亮度为50
    int max_value = 100;//定义最大值为100
    int contrast_value = 100;
    createTrackbar("Value Bar", "亮度&对比度调整", &lightness, max_value, on_lightness, (void*)(&image));//调用函数实现功能
    createTrackbar("Contrast Bar", "亮度&对比度调整", &contrast_value, 200, on_contrast, (void*)(&image));//调用函数实现功能
    on_lightness(50, &image);
    }

008.键盘响应操作

void key_demo(Mat &image);

    void QuickDemo::key_demo(Mat& image) {
    Mat dst = Mat::zeros(image.size(), image.type());
    while (true) {
    char c = waitKey(100);//等待100ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    if (c == 49) {//key#1
    cout << "you enter key #1" << endl;
    cvtColor(image, dst, COLOR_BGR2GRAY); //按键盘1,则转换后为灰度图像
    }
    if (c == 50) {//key#2
    cout << "you enter key #2" << endl;
    cvtColor(image, dst, COLOR_BGR2HSV); //按键盘1,则转换后为HSV图像
    }
    if (c == 51) {//key#3
    cout << "you enter key #3" << endl;
    dst = Scalar(50, 50, 50);
    cvtColor(image, dst, COLOR_BGR2HSV); //直接1到3会报错,则先转换为HSV图像
    add(image, dst, dst); //按键盘1,则转换后为增加亮度后的图像
    }
    imshow("键盘响应",dst);//输出图像
    }
    }

009.OpenCV自带颜色表操作

void color_style_demo(Mat& image);

    void QuickDemo::color_style_demo(Mat& image) {
    int colormap[] = {//共19种
    COLORMAP_AUTUMN,
    COLORMAP_BONE,
    COLORMAP_CIVIDIS,
    COLORMAP_DEEPGREEN,
    COLORMAP_HOT,
    COLORMAP_HSV,
    COLORMAP_INFERNO,
    COLORMAP_JET,
    COLORMAP_MAGMA,
    COLORMAP_OCEAN,
    COLORMAP_PINK,
    COLORMAP_PARULA,
    COLORMAP_RAINBOW,
    COLORMAP_SPRING,
    COLORMAP_TWILIGHT,
    COLORMAP_TURBO,
    COLORMAP_TWILIGHT,
    COLORMAP_VIRIDIS,
    COLORMAP_TWILIGHT_SHIFTED,
    COLORMAP_WINTER
    };
    Mat dst;
    int index = 0; //初始化为指向0的位置
    while (true) {
    char c = waitKey(500);//等待半秒(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    if (c == 49) {//key#1 按下按键1时。保存图片到指定位置
    cout << "you enter key #1" << endl;
    imwrite("F:/文件夹/C++/OPENCV4入门学习/图/颜色表的成果.jpg", dst);
    }
    applyColorMap(image, dst, colormap[index % 19]);//循环展示19种图片(产生伪色彩图像)
    index++;
    imshow("循环播放", dst);
    }
    }

010.图像像素的逻辑操作(与,或,非,异或)

void bitwise_demo(Mat& image);

    void QuickDemo::bitwise_demo(Mat& image) {
    //绘制两张图
    Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
    Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
    rectangle(m1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);//-1 =》小于0为填充,大于0为绘制
       // Rect(左上角x,左上角y,矩形长,矩形宽)    |=》搞锯齿的(表示四领域或者八领域的绘制
       //最后的参数0表示中心坐标 或 半径坐标的小数位
    rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);//小于0为填充,大于0为绘制
    imshow("m1", m1);
    imshow("m2", m2);
    //进行逻辑操作
    Mat dst;
    bitwise_and(m1, m2, dst);//位操作 与
    imshow("像素位操作 与", dst);
    bitwise_or(m1, m2, dst);//位操作 或
    imshow("像素位操作 或", dst);
    // dst = ~image;//位操作 非(取反
    bitwise_not(image, dst);//位操作 非(取反
    imshow("像素位操作 非", dst);
    bitwise_xor(m1, m2, dst);//位操作 异或
    imshow("像素位操作 异或", dst);
    }

与,或,异或 效果如图:

非操作:

011.通道合并与分离

void channels_demo(Mat& image);

    void QuickDemo::channels_demo(Mat& image) {
    vector<Mat>mv;//可存放Mat类型的容器
    split(image, mv);//将多通道 拆分成 单通道(通道分离
    //imshow("蓝色", mv[0]);
    //imshow("绿色", mv[1]);
    //imshow("红色", mv[2]);
    
    // 三个通道分别为 B G R
    // 0,1,2 三个通道分别代表 B G R
    //关闭其中两个通道,则意味着 只开启剩余那个通道
    Mat dst;
    mv[0] = 0;
    mv[2] = 0;// 关0,1则红色  关1,2则蓝色
    merge(mv, dst);//合并mv和dst
    imshow("绿色", dst);
    int from_to[] = { 1,2,1,1,2,0 };
    //把通道相互交换,第0->第2,第1->第1,第2->第0
    mixChannels(&image, 1, &dst, 1, from_to, 3);//3表示有3对要交换(即3个通道
    //参数为要进行混合的图像的地址,参数2为混合后图像的存放地址
    imshow("通道混合", dst);
    imshow("原图image不会变", image);
    }

只开放绿色通道 / B与R交换后的通道混合

012.图像色彩空间转换(提取轮廓然后换绿幕

void inrange_demo(Mat& image);

    void QuickDemo::inrange_demo(Mat& image) {
    //提取任务的轮廓
    Mat hsv;
    cvtColor(image, hsv, COLOR_BGR2HSV);//先把RGB色彩空间转换到hsv的空间中
    Mat mask;//其次提取图片的mask
    inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);//通过inRange提取hsv色彩空间的颜色
    //35,43,46根据图片表中的绿色最低来确定最小值(hmin,smin,vmim
    //77,255,255    最大值
    //参数一为低范围,参数二高范围
    //将hsv中的由低到高的像素点提取出来并存储到mask中
    imshow("mask", mask);//此时mask为白底
    Mat redback = Mat::zeros(image.size(), image.type());
    redback = Scalar(40, 40, 200);//红色背景图
    bitwise_not(mask, mask);//取反变成黑底
    imshow("mask", mask);
    image.copyTo(redback, mask);//将mask中不为0部分(白色像素点)对应的原图 拷贝到 redback上,mask通过inRange得到
    imshow("roi区域提取", redback);
    }

HSV色彩空间的颜色:

013.图像像素值统计(min,max,mean均值,standard deviation标准方差

void pixel_statistic_demo(Mat& image);

    void QuickDemo::pixel_statistic_demo(Mat& image) {
    double minv, maxv;
    Point minLoc, maxLoc;//定义地址
    vector<Mat> mv;//可存放Mat类型的容器
    split(image, mv);//将多通道 拆分成 单通道(通道分离
    for (int i = 0; i < mv.size(); i++) {
    //分别打印各个通道的数值
    minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat());//求出图像的最大值和最小值及其位置
    //参数一:输入单通道的数组
    //参数二:返回最小值的指针参数三:返回最大值的指针
    //参数四:返回最小值位置的指针参数五:返回最大值位置的指针
    cout << "No.channels:" << i << "  minvalue:" << minv << "  maxvalue:" << maxv << endl;
    }
    Mat mean, stddev;
    meanStdDev(image, mean, stddev);//求出图像的均值的方差
    cout << "mean:" << mean << endl;
    cout << "stddev:" << stddev << endl;
    }

014.图像几何形状绘制(圆,矩形,直线,椭圆

void drawing_demo(Mat& image);

    void QuickDemo::drawing_demo(Mat& image) {
    Rect rect;//矩形尺寸
    rect.x = 200;//起始点x坐标
    rect.y = 200;//起始点y坐标
    rect.width = 150;//矩形宽度
    rect.height = 200;//矩形高度
    Mat bg = Mat::zeros(image.size(), image.type());
    rectangle(bg, rect, Scalar(0, 0, 255), -1, 8, 0);//画矩形
    //参数一:绘图的底图或画布名称   参数二:图片的起始,宽高
    //参数三:填充颜色 参数四:>0为线宽,<0为填充
    //参数五:领域填充(控制边缘锯齿 参数六:默认值为0
    circle(bg, Point(350, 400), 25, Scalar(0, 255, 0), 2, LINE_AA, 0);//画圆
    //参数二:图片中心的位置 参数三:表示圆的半径为25
    line(bg, Point(100, 100), Point(350, 400), Scalar(255, 0, 0), 8, LINE_AA, 0);//画直线
    //参数二:线段起点坐标 参数三:线段终点坐标 LINE_AA表示去掉锯齿
    RotatedRect rrt;//角度构造
    rrt.center = Point(200, 200);//中心点位置
    rrt.size = Size(100, 200);//x正沿x正方向,y正沿y正方向(可以是负的
    rrt.angle = 0.0;//顺时针的角度(0-360度
    ellipse(bg, rrt, Scalar(255, 0, 255), 2, 8);//画椭圆
    imshow("矩形,圆,直线,椭圆的绘制", bg);
    }

015.随机数与随机颜色

void random_demo();

    void QuickDemo::random_demo() {
    Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);//创建画布
    int w = canvas.cols;
    int h = canvas.rows;
    RNG rng(12345);//产生随机数(12345为随机数的种子,默认的
    while (true) {
    char c = waitKey(10);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 推出应用程序
    break;
    }
    int x1 = rng.uniform(0, canvas.cols);//将随机坐标控制在画布范围内
    int y1 = rng.uniform(0, canvas.rows);
    int x2 = rng.uniform(0, w);
    int y2 = rng.uniform(0, h);
    int r = rng.uniform(0, 255);//将随机颜色控制在255范围内
    int g = rng.uniform(0, 255);//将随机颜色控制在255范围内
    int b = rng.uniform(0, 255);//将随机颜色控制在255范围内
    //canvas = Scalar(0, 0, 0);//想要每次都只出现一条线而不是叠加,则加上此句
    line(canvas, Point(x1, y1), Point(x2, y2), Scalar(r, g, b), 2, LINE_AA);//画直线
    //参数二:线段起点坐标参数三:线段终点坐标2为线宽 LINE_AA表示去掉锯齿
    imshow("随机绘制演示", canvas);
    }
    }

016.多边形填充与绘制

void polyline_drawing_demo(Mat& image);

    void QuickDemo::polyline_drawing_demo(Mat& image) {
    Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
    Point p1(150, 100);//第一个点的坐标
    Point p2(350, 200);//  二
    Point p3(240, 300);//  三
    Point p4(150, 300);//  四
    Point p5(50, 200);//  五
    vector<Point> pts;//搞一个容器,用来装 点
    pts.push_back(p1);//将点放进容器内
    pts.push_back(p2);//因 未初始化数组容量,所以要用 push_back 操作
    pts.push_back(p3);//若 已初始化,可以用 数组下标 来操作
    pts.push_back(p4);
    pts.push_back(p5);
    //fillPoly(canvas, pts, Scalar(122, 155, 255), 8, 0);//填充多边形
    //polylines(canvas, pts, true, Scalar(90, 0, 255), 2, 8, 0);//绘制多边形
    //参数一:画布参数二:点集参数三:一定要写true(封闭图形
    //参数倒3:线宽(最少为1参数倒2:线的渲染方式参数倒1:相对左上角(0,0)的位置
     
    //单个API搞定多边形的绘制和填充
    vector<vector<Point>> contours;//搞一个容器,用来装 多边形的点集
    contours.push_back(pts);//将一个多边形的点集放进容器内,作为一个元素
    drawContours(canvas, contours, -1, Scalar(0, 0, 255), -1);//参数倒1:<0表示填充,>0表示线宽
    //参数二:多边形的点集参数三:-1为绘制全部的多边形;0为绘制第一个,1为绘制第二个,以此类推
    imshow("多边形绘制", canvas);
    }

017.鼠标操作与响应(提取选中的ROI区域

void mouse_drawing_demo(Mat& image);

    //选中的矩形区域提取
    Point sp(-1, -1);//鼠标的起始位置
    Point ep(-1, -1);//鼠标的结束位置
    Mat temp;
    static void on_draw(int event, int x, int y, int flags, void* userdata) {
    //参数一(event)为鼠标事件
    Mat image = *((Mat*)userdata);
    if (event == EVENT_LBUTTONDOWN) {//若鼠标的左键按下
    sp.x = x;
    sp.y = y;//此时鼠标的起始位置坐标
    cout << "start point" << sp << endl;
    }
    else if (event == EVENT_LBUTTONUP) {//若鼠标的左键抬起
    ep.x = x;
    ep.y = y;//此时鼠标的结束位置坐标
    int dx = ep.x - sp.x;
    int dy = ep.y - sp.y;
    if (dx > 0 && dy > 0) {//若鼠标有移动过
    Rect box(sp.x, sp.y, dx, dy);
    imshow("ROI区域", image(box));
    rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
    imshow("鼠标绘制", image);//这里是为了显示结果
    sp.x = -1;//复位,为下一次做准备
    sp.y = -1;//复位,为下一次做准备
    }
    }
    else if (event == EVENT_MOUSEMOVE) {//若鼠标正在移动
    if (sp.x > 0 && sp.y > 0) {
    ep.x = x;
    ep.y = y;//此时鼠标的结束位置坐标
    int dx = ep.x - sp.x;
    int dy = ep.y - sp.y;
    if (dx > 0 && dy > 0) {//若鼠标有移动过
    Rect box(sp.x, sp.y, dx, dy);
    temp.copyTo(image);//为了不将鼠标移动过程中的框也显示出来
    rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
    imshow("鼠标绘制", image);//这里是为了每次重新提取都将前面的覆盖
    }
    }
    }
    }
    void QuickDemo::mouse_drawing_demo(Mat& image) {
    namedWindow("鼠标绘制", WINDOW_AUTOSIZE);
    setMouseCallback("鼠标绘制",on_draw, (void*)(&image));
    //设置窗口是回调函数,参数二表示调用on_draw
    imshow("鼠标绘制", image);
    temp = image.clone();
    }

018.图像像素类型转换和归一化

void norm_demo(Mat& image);

    void QuickDemo::norm_demo(Mat& image) {
    Mat dst;
    cout << image.type() << endl;//打印图片的类型
    image.convertTo(image, CV_32F);//将image的数据转换成浮点型float32位数据
    cout << image.type() << endl;//打印转换后的图片数据类型
    normalize(image, dst, 1.0, 0, NORM_MINMAX);//进行归一化操作
    //参数一:要进行归一化的图片参数二:归一化后要输出的图片
    //参数三:alpha参数四:beta参数五:归一化方法
    cout << dst.type() << endl;//打印归一化后的图像的类型
    imshow("图像的归一化", dst);//显示归一化后的图像
    //CV_8UC3   原本为 3通道,每个通道8位的UC(无符号)类型
    //CV_32FC3  转换后 3通道,每个通道32位的浮点数类型
    /*
    归一化方法:
    NORM_L1(依据sum)b不用,a为归一化后矩阵的范数值
    NORM_L2(依据单位向量为1)b不用,a为 同上
    NORM_MINMAX(依据最大值)b不用,a为 同上
    NORM_INF(依据min与max的差值)a为归一化后的最小值,b归一化后的最大值
    */
    }

019.图像放缩与插值

void resize_demo(Mat& image);

    void QuickDemo::resize_demo(Mat& image) {
    Mat zoomin, zoomout;
    int h = image.rows;
    int w = image.cols;
    resize(image, zoomout, Size(w / 2, h / 2), 0, 0, INTER_LINEAR);// INTER_LINEAR 为线性插值
    //若Size里的值没变,则按照参数四fx(水平轴)和参数五fy(垂直轴)来进行放缩操作
    //参数六:插值的方法
    imshow("zoomout", zoomout);
    resize(image, zoomin, Size(w * 1.5, h * 1.5), 0, 0, INTER_LINEAR);
    imshow("zoomin", zoomin);
    }

020.图像翻转

void flip_demo(Mat& image);

    void QuickDemo::flip_demo(Mat& image) {
    Mat dst;
    flip(image, dst, 0);// 0 上下翻转 x对称
    imshow("图像上下翻转", dst);
    flip(image, dst, 1);// 1 左右翻转 y对称
    imshow("图像左右翻转", dst);
    flip(image, dst, -1);//-1 上下左右都翻转(相当于旋转180°)
    imshow("图像上下左右翻转", dst);
    }

021.图像旋转

void rotate_demo(Mat& image);

    void QuickDemo::rotate_demo(Mat& image) {
    Mat dst, M;//M为2*3的变换矩阵(旋转矩阵)
    int w = image.cols;//图片宽度
    int h = image.rows;//图片高度
    M = getRotationMatrix2D(Point(w / 2, h / 2), 45, 1.0);//获得旋转矩阵 M
    //参数一:原来图像的中心点位置参数二:旋转角度(逆时针)参数三:图像本身大小的放大缩小
    double cos = abs(M.at<double>(0, 0));//取绝对值
    double sin = abs(M.at<double>(0, 1));
    /*
    [x'] = [ cos  sin] * [x]
    [y']   [-sin  cos]   [y],
    M =[ cos  sin  0]
    [-sin  cos  0], (第三列用来控制平移)
    */
    double nw = cos * w + sin * h;//旋转后图像所占矩形的宽
    double nh = sin * w + cos * h;//旋转后图像所占矩形的高
    //更新 新的中心  (将新中心平移到正确位置上)
    M.at<double>(0, 2) += (nw / 2 - w / 2);//将矩形的宽高 加上偏差量  (新M的第一列最后的值)
    M.at<double>(1, 2) += (nh / 2 - h / 2);//将矩形的宽高 加上偏差量  (新M的第二列最后的值)
    warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 255, 0));//进行旋转
    //参数四:原来图像的中心点位置参数五:插值方式
    //参数六:边缘的处理方式参数七:边缘底图的颜色
    //namedWindow("旋转演示", WINDOW_FREERATIO); //可调整显示图片的窗口大小
    imshow("旋转演示", dst);
    }

022.视频文件摄像头使用

void video_demo1(Mat& image);

    void QuickDemo::video_demo1(Mat& image) {
    //读已有视频
    VideoCapture capture("E:/2021.9.26备份/图片/Camera Roll/人脸素材.mp4");//读取视频地址
    Mat frame;//定义一个二值化的 frame
    while (true) {
    capture.read(frame);
    //flip(frame, frame, 1);// 1 左右翻转 y对称 (镜像)
    if (frame.empty())//如果读入失败
    {
    break;//若视频为空,则跳出操作
    }
    imshow("frame", frame);//显示视频
    colorSpace_Demo(frame);//对视频调用之前的demo
    int c = waitKey(1);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    }
    capture.release();//释放相机的资源
    /*
    //调用电脑摄像头
    VideoCapture capture(0);
    Mat frame;//定义一个二值化的 frame
    while (true) {
    capture.read(frame);
    if (frame.empty())//如果读入失败
    {
    break;//若视频为空,则跳出操作
    }
    flip(frame, frame, 1);// 1 左右翻转 y对称 (镜像)
    imshow("frame", frame);//显示视频
    int c = waitKey(10);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    }
    */
    }

023.视频处理与保存

void video_demo2(Mat& image);

    void QuickDemo::video_demo2(Mat& image) {
    //视频的属性:SD(标清),HD(高清),UHD(超清),蓝光。
    VideoCapture capture("E:/2021.9.26备份/图片/Camera Roll/人脸素材.mp4");//读取视频地址
    int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);//获取视频的宽度
    int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT);//获取视频的高度
    int count = capture.get(CAP_PROP_FRAME_COUNT);//获取视频总的帧数
    //fps是衡量处理视频的能力 (一秒钟处理多少张图片的能力,处理速度越快则越好)
    double fps = capture.get(CAP_PROP_FPS);
    cout << "frame width:" << frame_width << endl;
    cout << "frame height:" << frame_height << endl;
    cout << "FPS:" << fps << endl;
    cout << "Number of frame:" << count << endl;
    VideoWriter writer("F:/文件夹/C++/OPENCV4入门学习/图/test.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_height), true);
    //参数一:保存地址参数二:获取图片的格式(编码方式)参数三:图片是帧数参数四:视频宽高参数五:与原来颜色保持一致
    //等全部运行完再去查看视频是否保存成功
    Mat frame;
    while (true) {
    capture.read(frame);
    //flip(frame, frame, 1);// 1 左右翻转 y对称 (镜像)
    if (frame.empty())//如果读入失败
    {
    break;//若视频为空,则跳出操作
    }
    imshow("frame", frame);//显示视频
    colorSpace_Demo(frame);//对视频调用之前的demo
    writer.write(frame);
    int c = waitKey(1);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    }
    //release
    writer.release();
    capture.release();//释放相机的资源
    }

024.图像直方图

void histogram_demo(Mat& image);

    void QuickDemo::histogram_demo(Mat& image) {
    //三通道分离
    vector<Mat> bgr_plane;
    split(image, bgr_plane);
    //定义参数变量
    const int channels[1] = { 0 };
    const int bins[1] = { 256 };//总共 256 个灰度级别
    float hranges[2] = { 0,255 };//每个通道的取值范围是 0 到 255
    const float* ranges[1] = { hranges };
    Mat b_hist;
    Mat g_hist;
    Mat r_hist;
    //计算 Blue,Green,Red 通道的直方图
    calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);//第一个通道
    calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
    calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);
    //参数一:要计算直方图的数据参数二:1表示只有一张图(输入图像的格式)
    //参数三:需要统计直方图的第几个通道参数四:掩模,mask必须是8位的数组且和参数一的大小一致
    //参数五:b_hist表示直方图的输出参数六:1表示维度是一维的(输出直方图的维度dims)
    //参数七:直方图中每个维度需分成的区间个数参数八:ranges表示直方图的取值范围(区间)
    
    //显示直方图
    int hist_w = 512;//设置 画布宽度 为512
    int hist_h = 400;//设置 画布高度 为400
    int bin_w = cvRound((double)hist_w / bins[0]);//每个 bin 占的宽度
      //cvRound()四舍五入返回数值
    Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);//创建画布
     
    //归一化直方图数据(归一化到大小一致的范围内)
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());//histImage.rows是为了不超出画布许可的高度范围
    normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    //参数一:要进行归一化的图片参数二:归一化后要输出的图片
    //参数三:alpha参数四:beta参数五:归一化方法
    
    //绘制直方图曲线
    for (int i = 1; i < bins[0]; i++) {//每个bin占2个像素的位置
    line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
    Point(bin_w * (i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 3, 0);
    line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
    Point(bin_w * (i), hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, 3, 0);
    line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
    Point(bin_w * (i), hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, 3, 0);
    //从前一个位置到当前位置连上一条线
    }
    //显示直方图
    namedWindow("Histogram Demo", WINDOW_AUTOSIZE);
    imshow("Histogram Demo", histImage);
    }

025.二维直方图

void histogram_2d_demo(Mat& image);

    void QuickDemo::histogram_2d_demo(Mat& image) {
    //2D直方图
    Mat hsv, hs_hist;
    cvtColor(image, hsv, COLOR_BGR2HSV);//先把RGB色彩空间转换到hsv的空间中
    int hbins = 30, sbins = 32;
    int hist_bins[] = { hbins, sbins };//h和s这两个维度需分成的 区间个数
    float h_range[] = { 0,180 };//h的取值范围
    float s_range[] = { 0,256 };//s的取值范围
    const float* hs_ranges[] = { h_range, s_range };
    int hs_channels[] = { 0,1 };
    //计算通道的直方图
    calcHist(&hsv, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_ranges, true, false);
    //参数一:要计算直方图的数据参数二:1表示只有一张图(输入图像的格式)
    //参数三:需要统计直方图的第几个通道(前两个)参数四:掩模,mask必须是8位的数组且和参数一的大小一致
    //参数五:b_hist表示直方图的输出参数六:2表示维度是二维的(输出直方图的维度dims)
    //参数七:直方图中每个维度需分成的区间个数参数八:hs_ranges表示直方图的取值范围(区间)
    //参数九:是否对得到的直方图进行归一化处理参数十:在多个图像时,是否累计计算像素值的个数
    double maxVal = 0;
    minMaxLoc(hs_hist, 0, &maxVal, 0, 0);//寻找最大值和最小值及其位置(这里先找到最大值)
    //参数一:输入单通道的数组
    //参数二:返回最小值的指针参数三:返回最大值的指针
    //参数四:返回最小值位置的指针参数五:返回最大值位置的指针
    int scale = 10;
    Mat hist2d_image = Mat::zeros(sbins * scale, hbins * scale, CV_8UC3);//创建空白图像
    for (int h = 0; h < hbins; h++) {
    for (int s = 0; s < sbins; s++) {
    float binVal = hs_hist.at<float>(h, s);
    int intensity = cvRound(binVal * 255 / maxVal);
    rectangle(hist2d_image, Point(h * scale, s * scale),
    Point((h + 1) * scale - 1, (s + 1) * scale - 1), Scalar::all(intensity), -1);
    }
    }
    //显示直方图
    //applyColorMap(hist2d_image, hist2d_image, COLORMAP_JET);//产生伪色彩图像
    namedWindow("H-S Histogram", WINDOW_AUTOSIZE);
    imshow("H-S Histogram", hist2d_image);
    //imwrite("F:/文件夹/C++/OPENCV4入门学习/图/hist_2d.png", hist2d_image);
    }

026.直方图均衡化

void histogram_eq_demo(Mat& image);

直方图均衡化是通过调整图像的灰阶分布,使得在0~255灰阶上的分布更加均衡,提高了图像的对比度,达到改善图像主观视觉效果的目的。对比度较低的图像适合使用直方图均衡化方法来增强图像细节。
    void QuickDemo::histogram_eq_demo(Mat& image) {
    //直方图均衡化 (目的是对比度拉伸,即 对比度会更强)
    //用途:用于图像增强,人脸检测,卫星遥感(提升图像质量)。
    //opencv中,均衡化的图像只支持单通道
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);
    imshow("灰度图像", gray);
    Mat dst;
    equalizeHist(gray, dst);
    imshow("直方图均衡化演示", dst);
    }

对三通道的图像进行直方图均衡化:

    Mat image;
    Mat imageRGB[3];
    split(image, imageRGB);
    for (int i = 0; i < 3; i++)
    {
    equalizeHist(imageRGB[i], imageRGB[i]);
    }
    merge(imageRGB, 3, image);
    imshow("直方图均衡化图像增强效果", image);

027.图像卷积操作(会变模糊,且卷积核尺寸越大则越模糊

void blur_demo(Mat& image);

    void QuickDemo::blur_demo(Mat& image) {//会变模糊,且卷积核尺寸越大则越模糊
    Mat dst;
    blur(image, dst, Size(15, 15), Point(-1, -1));//均值滤波 均值模糊
    //参数三:卷积核的大小参数四:卷积的起始点(Point(-1, -1)则默认取核的中心)
    //参数三中:
    //Size(15, 1) 左右晃动的模糊(只有行的话
    //Size(1, 15) 上下(  列
    imshow("图像卷积操作", dst);
    }

028.高斯模糊

void gaussian_blur_demo(Mat& image);

    void QuickDemo::gaussian_blur_demo(Mat& image) {
    //中心值最大,离中心越远值越小
    Mat dst;
    GaussianBlur(image, dst, Size(5, 5), 15);
    //参数三:高斯矩阵的大小(正数且奇数)
    //参数四:sigmaX 和 sigmaY 为15 
    //(参数三和四都 值越大则越模糊,且参数四的影响更明显)
    imshow("高斯模糊", dst);
    }

高斯卷积数学表达式说明:

高斯卷积的图像说明:

029.高斯双边模糊(可磨皮操作)

(同时考虑空间临近信息与颜色相似信息,在滤除噪声、平滑图像的同时,又做到边缘保存)

高斯双边模糊:可以在去除噪声的同时,保持边缘信息相对清晰。但是,相比于大多数滤波器,双边滤波的速度是非常慢的。)

void bifilter_demo(Mat& image);

    void QuickDemo::bifilter_demo(Mat& image) { //可做磨皮操作
    Mat dst;
    bilateralFilter(image, dst, 0, 100, 10);
    //参数三:色彩空间参数四:坐标空间(双边是指 色彩空间 和 坐标空间
    namedWindow("高斯双边模糊", WINDOW_AUTOSIZE);
    imshow("高斯双边模糊", dst);
    }

30.案例:实时人脸检测

需先下载好相关文件(人脸检测用到的库文件,包括Tensorflow模型以及配置文件)如图:

若找不到该文件可点击此处进行下载

quickdemo.cpp

    #include <iostream>
    #include <opencv2/dnn.hpp>
    //#include <opencv2/opencv.hpp>
    #include "quickopencv.h"
     
    using namespace std;
    using namespace cv;
     
    void QuickDemo::face_detection_demo() {
    //加载权重文件
    string root_dir = "F:/BaiduNetdiskDownload/opencv/sources/samples/dnn/face_detector/";
    //读出一个网络文件(读取深度学习Tensorflow模型以及配置文件)
    //pb文件就是他的模型;pbtxt是配置文件
    dnn::Net net = dnn::readNetFromTensorflow(root_dir + "opencv_face_detector_uint8.pb", root_dir + "opencv_face_detector.pbtxt");
     
    VideoCapture capture(0);//加载视频
    //VideoCapture capture("E:/2021.9.26备份/图片/Camera Roll/人脸素材.mp4");//加载视频
    Mat frame;//定义一个二值化的 frame
    while (true) {
    capture.read(frame);
    flip(frame, frame, 1);// 1 左右翻转 y对称 (镜像)
    if (frame.empty())//如果读入失败
    {
    break;//若视频为空,则跳出操作
    }
    //读模型
    Mat blob = dnn::blobFromImage(frame, 1.0, Size(300, 300), Scalar(104, 177, 123), false, false);//对图像进行预处理
    net.setInput(blob);//准备数据(blob就是 NCHW n多少个 c通道数 h高度 w宽度
    //获取数据
    Mat probs = net.forward();//获取推理后的数据(完成推理)
    Mat detectionMat(probs.size[2], probs.size[3], CV_32F, probs.ptr<float>());//构建Mat图像
    /*
    输出:(得出的blob)
    第一个维度:有多少张图像,每张图有个编号;
    维度二:image对应第几批次第几张图;
    维度三:有多少个框
    维度四:每个框有7个值,有7列
    */
    //解析结果
    for (int i = 0; i < detectionMat.rows; i++) {
    float con = detectionMat.at<float>(i, 2);
    if (con > 0.5) {//大于0.5就是人脸
    //获取矩形坐标(第3,4,5,6参数)
    int x1 = static_cast<int>(detectionMat.at<float>(i, 3) * frame.cols);
    int y1 = static_cast<int>(detectionMat.at<float>(i, 4) * frame.rows);
    int x2 = static_cast<int>(detectionMat.at<float>(i, 5) * frame.cols);
    int y2 = static_cast<int>(detectionMat.at<float>(i, 6) * frame.rows);
    Rect box(x1, y1, x2 - x1, y2 - y1);
    rectangle(frame, box, Scalar(0, 0, 255), 2, 8, 0);
    }
    }
    imshow("人脸检测演示", frame);
    int c = waitKey(1);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    }
    }

quickopencv.h

    #pragma once
     
    #include <opencv2/opencv.hpp>
     
    using namespace cv;
     
    class QuickDemo {
    public:
    void face_detection_demo();//030.案例:实时人脸检测
    };

源.cpp

    #include <iostream>
    #include <opencv2/opencv.hpp>
    #include "quickopencv.h"
     
    using namespace std;
    using namespace cv;
     
    int main(int argc, char* argv[])
    {
    //const char* imagename = "E:\\2021.9.26备份\\图片\\Camera Roll\\001.jpg"; //此处为你自己的图片路径
    const char* imagename = "E:/2021.9.26备份/图片/Camera Roll/007.jpg"; //此处为你自己的图片路径
     
    //从文件中读入图像
    Mat img = imread(imagename, 1);//彩色
    //Mat img = imread(imagename, IMREAD_GRAYSCALE);//黑白
     
    //如果读入图像失败
    if (img.empty())
    {
    fprintf(stderr, "Can not load image %s\n", imagename);
    return -1;
    }
     
    //创建一个新窗口,参数1为名称,参数2代表一个自由的比例
    namedWindow("image", WINDOW_FREERATIO); //可调整显示图片的窗口大小
     
    //显示图像
    imshow("image", img);//(名称,对象)
     
    QuickDemo pd;//创建类对象
    pd.mouse_drawing_demo(img);
     
    waitKey(); //此函数等待按键,按键盘任意键就返回
    //括号中参数为延时时间,单位ms
    destroyAllWindows();//销毁前面创建的显示窗口
    return 0;
    }

————————————————

版权声明:本文为CSDN博主「chxin14160」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。

原文链接:https://blog.csdn.net/weixin_47059239/article/details/122852104

代码总结

一、代码+注释

quickopencv.h

    #pragma once
     
    #include <opencv2/opencv.hpp>
     
    using namespace cv;
     
    class QuickDemo {
    public:
    void colorSpace_Demo(Mat &image);//002.图像色彩空间转换
    void mat_creation_demo(/*Mat& image*/);//003.图像对象的创建与赋值
    void pixel_visit_demo(Mat &image);//004.图像像素的读写操作
    void operators_demo(Mat &image);//005.图像像素的算术操作(加减乘除4种不同的API实现
    void tracking_bar_demo1(Mat &image);//006.滚动条-调整图像亮度
    void tracking_bar_demo2(Mat &image);//007.滚动条-传递参数
    void key_demo(Mat &image);//008.键盘响应操作
    void color_style_demo(Mat& image);//009.OpenCV自带颜色表操作
    void bitwise_demo(Mat& image);//010.图像像素的逻辑操作(与,或,非,异或
    void channels_demo(Mat& image);//011.通道合并与分离
    void inrange_demo(Mat& image);//012.图像色彩空间转换(提取轮廓然后换绿幕
    void pixel_statistic_demo(Mat& image);//013.图像像素值统计(min,max,mean均值,standard deviation标准方差
    void drawing_demo(Mat& image);//014.图像几何形状绘制(圆,矩形,直线,椭圆
    void random_demo();//015.随机数与随机颜色
    void polyline_drawing_demo(Mat& image);//016.多边形填充与绘制
    void mouse_drawing_demo(Mat& image);//017.鼠标操作与响应(提取选中的ROI区域
    void norm_demo(Mat& image);//018.图像像素类型转换和归一化
    void resize_demo(Mat& image);//019.图像放缩与插值
    void flip_demo(Mat& image);//020.图像翻转
    void rotate_demo(Mat& image);//021.图像旋转
    void video_demo1(Mat& image);//022.视频文件摄像头使用
    void video_demo2(Mat& image);//023.视频处理与保存
    void histogram_demo(Mat& image);//024.图像直方图
    void histogram_2d_demo(Mat& image);//025.二维直方图
    void histogram_eq_demo(Mat& image);//026.直方图均衡化
    void blur_demo(Mat& image);//027.图像卷积操作(会变模糊,且卷积核尺寸越大则越模糊
    void gaussian_blur_demo(Mat& image);//028.高斯模糊
    void bifilter_demo(Mat& image);//029.高斯双边模糊(可磨皮操作
    };

quickdemo.cpp

    #include <iostream>
    //#include <opencv2/opencv.hpp>
    #include "quickopencv.h"
     
    using namespace std;
    //using namespace cv;
     
    void QuickDemo::colorSpace_Demo(Mat &image) {
    Mat gray, hsv; //定义2个矩阵类型的图像
    cvtColor(image, hsv, COLOR_BGR2HSV);//转换成hdv (图像转换函数,第三个参数是转成的类型
    cvtColor(image, gray, COLOR_BGR2GRAY);//转成灰度
    imshow("HSV", hsv);//显示图片
    imshow("灰度", gray);//显示图片
    //imwrite("F:\\文件夹\\C++\\OPENCV4入门学习\\图\\hsv.png", hsv);//保存图片 (保存地址,保存图的名称)
    //imwrite("F:\\文件夹\\C++\\OPENCV4入门学习\\图\\gray.png", gray);//保存
    }
     
    void QuickDemo::mat_creation_demo(/*Mat& image*/) {
    //Mat m1, m2;
    //m1 = image.clone();
    //image.copyTo(m2);
     
    //创建空白图形
    Mat m3 = Mat::ones(Size(400, 400), CV_8UC3);//8位的无符号的3通道(改1则为单通道
    //ones 改 zeros则初始化为0
    //长度 = 通道数 * 宽度
    m3 = Scalar(255, 0, 0);//给三个通道都赋值,单通道则 m3 = 127;
    //m3初始化为蓝色
    cout << "width:" << m3.cols << endl << "hight:" << m3.rows << endl << "channels:" << m3.channels() << endl;
    //显示宽度,长度,通道数
    //cout << m3 << endl;
     
    Mat m4;
    //m4 = m3;//直接赋值 则m4变,m3也变(同体
    //m4 = m3.clone();//m4为m3的克隆,m4变,m3不会变(不同体
    m3.copyTo(m4);//把m3赋值给m4,m4为蓝色
    m4 = Scalar(0, 255, 255);//改变m4的颜色为黄色
    imshow("图像3", m3);//标题和图像名称  显示图像3 纯蓝色
    imshow("图像4", m4);
    }
     
    void QuickDemo::pixel_visit_demo(Mat &image) {
    int dims = image.channels();
    int h = image.rows;
    int w = image.cols;
     
    //数组下标访问像素值
    /*
    for (int row = 0; row < h; row++) {
    for (int col = 0; col < w; col++) {
    if (dims == 1) {//单通道的灰度图像
    int pv = image.at<uchar>(row, col);//得到像素值
    image.at<uchar>(row, col) = 255 - pv;//给像素值重新赋值(取反
    }
    if (dims == 3) {//三通道的彩色图像
    Vec3b bgr = image.at<Vec3b>(row, col);//opencv特定的类型,获取三维颜色,3个值
    image.at<Vec3b>(row, col)[0] = 255 - bgr[0];
    image.at<Vec3b>(row, col)[1] = 255 - bgr[1];
    image.at<Vec3b>(row, col)[2] = 255 - bgr[2]; //对彩色图像读取其像素值,并将其改写
    }
    }
    }
    */
     
    //指针访问模式
    for (int row = 0; row < h; row++) {
    uchar* current_row = image.ptr<uchar>(row);
    for (int col = 0; col < w; col++) {
    if (dims == 1) {//单通道的灰度图像
    int pv = image.at<uchar>(row, col);//得到像素值
    *current_row++ = 255 - pv;//给像素值重新赋值(取反
    }
    if (dims == 3) {//三通道的彩色图像
    *current_row++ = 255 - *current_row;//指针每做一次运算,就向后移动一位
    *current_row++ = 255 - *current_row;
    *current_row++ = 255 - *current_row;
    }
     
    }
    }
    namedWindow("像素读写演示", WINDOW_FREERATIO);
    imshow("像素读写演示", image);
    //imwrite("E:/2021.9.26备份/图片/Camera Roll/003颜色取反.png", image);//保存
    }
     
    void QuickDemo::operators_demo(Mat &image) {
    Mat dst = Mat::zeros(image.size(), image.type());
    Mat m = Mat::zeros(image.size(), image.type());
    dst = image - Scalar(50, 50, 50);
    m = Scalar(50, 50, 50);
     
    multiply(image, m, dst);//乘法操作 api
    imshow("乘法操作", dst);
     
    add(image, m, dst);//加法操作 api
    imshow("加法操作", dst);
     
    subtract(image, m, dst);//减法操作 api
    imshow("减法操作", dst);
     
    divide(image, m, dst);//除法操作 api
    imshow("除法操作", dst);
     
    //加法操作底层
    /*
    int dims = image.channels();
    int h = image.rows;
    int w = image.cols;
    for (int row = 0; row < h; row++) {
    for (int col = 0; col < w; col++) {
    Vec3b p1 = image.at<Vec3b>(row, col);//opencv特定的类型,获取三维颜色,3个值
    Vec3b p2 = m.at<Vec3b>(row, col);
    dst.at<Vec3b>(row, col)[0] = saturate_cast<uchar>(p1[0] + p2[0]);
    dst.at<Vec3b>(row, col)[1] = saturate_cast<uchar>(p1[1] + p2[1]);
    dst.at<Vec3b>(row, col)[2] = saturate_cast<uchar>(p1[2] + p2[2]);
    }
    }
    namedWindow("加法操作底层", WINDOW_FREERATIO);
    imshow("加法操作底层", dst);
    */
    }
     
    /*
    Mat src, dst, m;
    int lightness = 50;//定义初始化的亮度为50
    static void on_track(int, void*) {
    m = Scalar(lightness, lightness, lightness);//创建调整亮度的数值
    add(src, m, dst);
    //subtract(src, m, dst);//定义亮度变换为减
    imshow("亮度调整", dst);//显示调整亮度之后的图片
    }
    void QuickDemo::tracking_bar_demo1(Mat &image) {
    namedWindow("亮度调整", WINDOW_AUTOSIZE);
    dst = Mat::zeros(image.size(), image.type());//图片的初始化创建一个和image大小相等,种类相同的图像
    m = Mat::zeros(image.size(), image.type());//图片的初始化创建一个和image大小相等,种类相同的图像
    src = image;//给src赋值
    int max_value = 100;//定义最大值为100
    createTrackbar("Value Bar", "亮度调整", &lightness, max_value, on_track);//调用函数实现功能
    on_track(50, 0);
    }
    */
     
    static void on_lightness(int b, void* userdata) {
    Mat image = *((Mat*)userdata);
    Mat dst = Mat::zeros(image.size(), image.type());
    Mat m = Mat::zeros(image.size(), image.type());
    m = Scalar(b, b, b);//创建调整亮度的数值
    addWeighted(image, 1.0, m, 0, b, dst);//融合两张图 dst = image * 1.0 + m * 0 + b
    imshow("亮度&对比度调整", dst);//显示调整亮度之后的图片
    }
    static void on_contrast(int b, void* userdata) {
    Mat image = *((Mat*)userdata);
    Mat dst = Mat::zeros(image.size(), image.type());
    Mat m = Mat::zeros(image.size(), image.type());
    double contrast = b / 100.0;
    addWeighted(image, contrast, m, 0.0, 0, dst);
    imshow("亮度&对比度调整", dst);//显示调整亮度之后的图片
    }
     
    void QuickDemo::tracking_bar_demo2(Mat& image) {
    namedWindow("亮度&对比度调整", WINDOW_AUTOSIZE);
    int lightness = 50;//定义初始化的亮度为50
    int max_value = 100;//定义最大值为100
    int contrast_value = 100;
    createTrackbar("Value Bar", "亮度&对比度调整", &lightness, max_value, on_lightness, (void*)(&image));//调用函数实现功能
    createTrackbar("Contrast Bar", "亮度&对比度调整", &contrast_value, 200, on_contrast, (void*)(&image));//调用函数实现功能
    on_lightness(50, &image);
    }
     
    void QuickDemo::key_demo(Mat& image) {
    Mat dst = Mat::zeros(image.size(), image.type());
    while (true) {
    char c = waitKey(100);//等待100ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    if (c == 49) {//key#1
    cout << "you enter key #1" << endl;
    cvtColor(image, dst, COLOR_BGR2GRAY); //按键盘1,则转换后为灰度图像
    }
    if (c == 50) {//key#2
    cout << "you enter key #2" << endl;
    cvtColor(image, dst, COLOR_BGR2HSV); //按键盘1,则转换后为HSV图像
    }
    if (c == 51) {//key#3
    cout << "you enter key #3" << endl;
    dst = Scalar(50, 50, 50);
    cvtColor(image, dst, COLOR_BGR2HSV); //直接1到3会报错,则先转换为HSV图像
    add(image, dst, dst); //按键盘1,则转换后为增加亮度后的图像
    }
    imshow("键盘响应",dst);//输出图像
    }
    }
     
    void QuickDemo::color_style_demo(Mat& image) {
    int colormap[] = {//共19种
    COLORMAP_AUTUMN,
    COLORMAP_BONE,
    COLORMAP_CIVIDIS,
    COLORMAP_DEEPGREEN,
    COLORMAP_HOT,
    COLORMAP_HSV,
    COLORMAP_INFERNO,
    COLORMAP_JET,
    COLORMAP_MAGMA,
    COLORMAP_OCEAN,
    COLORMAP_PINK,
    COLORMAP_PARULA,
    COLORMAP_RAINBOW,
    COLORMAP_SPRING,
    COLORMAP_TWILIGHT,
    COLORMAP_TURBO,
    COLORMAP_TWILIGHT,
    COLORMAP_VIRIDIS,
    COLORMAP_TWILIGHT_SHIFTED,
    COLORMAP_WINTER
    };
    Mat dst;
    int index = 0; //初始化为指向0的位置
    while (true) {
    char c = waitKey(500);//等待半秒(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    if (c == 49) {//key#1 按下按键1时。保存图片到指定位置
    cout << "you enter key #1" << endl;
    imwrite("F:/文件夹/C++/OPENCV4入门学习/图/颜色表的成果.jpg", dst);
    }
    applyColorMap(image, dst, colormap[index % 19]);//循环展示19种图片(产生伪色彩图像)
    index++;
    imshow("循环播放", dst);
    }
    }
     
    void QuickDemo::bitwise_demo(Mat& image) {
    //绘制两张图
    Mat m1 = Mat::zeros(Size(256, 256), CV_8UC3);
    Mat m2 = Mat::zeros(Size(256, 256), CV_8UC3);
    rectangle(m1, Rect(100, 100, 80, 80), Scalar(255, 255, 0), -1, LINE_8, 0);//-1 =》小于0为填充,大于0为绘制
       // Rect(左上角x,左上角y,矩形长,矩形宽)    |=》搞锯齿的(表示四领域或者八领域的绘制
       //最后的参数0表示中心坐标 或 半径坐标的小数位
    rectangle(m2, Rect(150, 150, 80, 80), Scalar(0, 255, 255), -1, LINE_8, 0);//小于0为填充,大于0为绘制
    imshow("m1", m1);
    imshow("m2", m2);
    //进行逻辑操作
    Mat dst;
    bitwise_and(m1, m2, dst);//位操作 与
    imshow("像素位操作 与", dst);
    bitwise_or(m1, m2, dst);//位操作 或
    imshow("像素位操作 或", dst);
    // dst = ~image;//位操作 非(取反
    bitwise_not(image, dst);//位操作 非(取反
    imshow("像素位操作 非", dst);
    bitwise_xor(m1, m2, dst);//位操作 异或
    imshow("像素位操作 异或", dst);
    }
     
    void QuickDemo::channels_demo(Mat& image) {
    vector<Mat>mv;//可存放Mat类型的容器
    split(image, mv);//将多通道 拆分成 单通道(通道分离
    //imshow("蓝色", mv[0]);
    //imshow("绿色", mv[1]);
    //imshow("红色", mv[2]);
    
    // 三个通道分别为 B G R
    // 0,1,2 三个通道分别代表 B G R
    //关闭其中两个通道,则意味着 只开启剩余那个通道
    Mat dst;
    mv[0] = 0;
    mv[2] = 0;// 关0,1则红色  关1,2则蓝色
    merge(mv, dst);//合并mv和dst
    imshow("绿色", dst);
    int from_to[] = { 1,2,1,1,2,0 };
    //把通道相互交换,第0->第2,第1->第1,第2->第0
    mixChannels(&image, 1, &dst, 1, from_to, 3);//3表示有3对要交换(即3个通道
    //参数为要进行混合的图像的地址,参数2为混合后图像的存放地址
    imshow("通道混合", dst);
    imshow("原图image不会变", image);
    }
    void QuickDemo::inrange_demo(Mat& image) {
    //提取任务的轮廓
    Mat hsv;
    cvtColor(image, hsv, COLOR_BGR2HSV);//先把RGB色彩空间转换到hsv的空间中
    Mat mask;//其次提取图片的mask
    inRange(hsv, Scalar(35, 43, 46), Scalar(77, 255, 255), mask);//通过inRange提取hsv色彩空间的颜色
    //35,43,46根据图片表中的绿色最低来确定最小值(hmin,smin,vmim
    //77,255,255    最大值
    //参数一地范围为,参数二高范围
    //将hsv中的由低到高的像素点提取出来并存储到mask中
    imshow("mask", mask);//此时mask为白底
    Mat redback = Mat::zeros(image.size(), image.type());
    redback = Scalar(40, 40, 200);//红色背景图
    bitwise_not(mask, mask);//取反变成黑底
    imshow("mask", mask);
    image.copyTo(redback, mask);//将mask中不为0部分(白色像素点)对应的原图 拷贝到 redback上,mask通过inRange得到
    imshow("roi区域提取", redback);
    }
     
     
    void QuickDemo::pixel_statistic_demo(Mat& image) {
    double minv, maxv;
    Point minLoc, maxLoc;//定义地址
    vector<Mat> mv;//可存放Mat类型的容器
    split(image, mv);//将多通道 拆分成 单通道(通道分离
    for (int i = 0; i < mv.size(); i++) {
    //分别打印各个通道的数值
    minMaxLoc(mv[i], &minv, &maxv, &minLoc, &maxLoc, Mat());//求出图像的最大值和最小值及其位置
    //参数一:输入单通道的数组
    //参数二:返回最小值的指针参数三:返回最大值的指针
    //参数四:返回最小值位置的指针参数五:返回最大值位置的指针
    cout << "No.channels:" << i << "  minvalue:" << minv << "  maxvalue:" << maxv << endl;
    }
    Mat mean, stddev;
    meanStdDev(image, mean, stddev);//求出图像的均值的方差
    cout << "mean:" << mean << endl;
    cout << "stddev:" << stddev << endl;
    }
     
    void QuickDemo::drawing_demo(Mat& image) {
    Rect rect;//矩形尺寸
    rect.x = 200;//起始点x坐标
    rect.y = 200;//起始点y坐标
    rect.width = 150;//矩形宽度
    rect.height = 200;//矩形高度
    Mat bg = Mat::zeros(image.size(), image.type());
    rectangle(bg, rect, Scalar(0, 0, 255), -1, 8, 0);//画矩形
    //参数一:绘图的底图或画布名称   参数二:图片的起始,宽高
    //参数三:填充颜色 参数四:>0为线宽,<0为填充
    //参数五:领域填充(控制边缘锯齿 参数六:默认值为0
    circle(bg, Point(350, 400), 25, Scalar(0, 255, 0), 2, LINE_AA, 0);//画圆
    //参数二:图片中心的位置 参数三:表示圆的半径为25
    line(bg, Point(100, 100), Point(350, 400), Scalar(255, 0, 0), 8, LINE_AA, 0);//画直线
    //参数二:线段起点坐标 参数三:线段终点坐标 LINE_AA表示去掉锯齿
    RotatedRect rrt;//角度构造
    rrt.center = Point(200, 200);//中心点位置
    rrt.size = Size(100, 200);//x正沿x正方向,y正沿y正方向(可以是负的
    rrt.angle = 0.0;//顺时针的角度(0-360度
    ellipse(bg, rrt, Scalar(255, 0, 255), 2, 8);//画椭圆
    imshow("矩形,圆,直线,椭圆的绘制", bg);
    }
     
    void QuickDemo::random_demo() {
    Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);//创建画布
    int w = canvas.cols;
    int h = canvas.rows;
    RNG rng(12345);//产生随机数(12345为随机数的种子,默认的
    while (true) {
    char c = waitKey(10);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 推出应用程序
    break;
    }
    int x1 = rng.uniform(0, canvas.cols);//将随机坐标控制在画布范围内
    int y1 = rng.uniform(0, canvas.rows);
    int x2 = rng.uniform(0, w);
    int y2 = rng.uniform(0, h);
    int r = rng.uniform(0, 255);//将随机颜色控制在255范围内
    int g = rng.uniform(0, 255);//将随机颜色控制在255范围内
    int b = rng.uniform(0, 255);//将随机颜色控制在255范围内
    //canvas = Scalar(0, 0, 0);//想要每次都只出现一条线而不是叠加,则加上此句
    line(canvas, Point(x1, y1), Point(x2, y2), Scalar(r, g, b), 2, LINE_AA);//画直线
    //参数二:线段起点坐标参数三:线段终点坐标2为线宽 LINE_AA表示去掉锯齿
    imshow("随机绘制演示", canvas);
    }
    }
     
    void QuickDemo::polyline_drawing_demo(Mat& image) {
    Mat canvas = Mat::zeros(Size(512, 512), CV_8UC3);
    Point p1(150, 100);//第一个点的坐标
    Point p2(350, 200);//  二
    Point p3(240, 300);//  三
    Point p4(150, 300);//  四
    Point p5(50, 200);//  五
    vector<Point> pts;//搞一个容器,用来装 点
    pts.push_back(p1);//将点放进容器内
    pts.push_back(p2);//因 未初始化数组容量,所以要用 push_back 操作
    pts.push_back(p3);//若 已初始化,可以用 数组下标 来操作
    pts.push_back(p4);
    pts.push_back(p5);
    //fillPoly(canvas, pts, Scalar(122, 155, 255), 8, 0);//填充多边形
    //polylines(canvas, pts, true, Scalar(90, 0, 255), 2, 8, 0);//绘制多边形
    //参数一:画布参数二:点集参数三:一定要写true(封闭图形
    //参数倒3:线宽(最少为1参数倒2:线的渲染方式参数倒1:相对左上角(0,0)的位置
     
    //单个API搞定多边形的绘制和填充
    vector<vector<Point>> contours;//搞一个容器,用来装 多边形的点集
    contours.push_back(pts);//将一个多边形的点集放进容器内,作为一个元素
    drawContours(canvas, contours, -1, Scalar(0, 0, 255), -1);//参数倒1:<0表示填充,>0表示线宽
    //参数二:多边形的点集参数三:-1为绘制全部的多边形;0为绘制第一个,1为绘制第二个,以此类推
    imshow("多边形绘制", canvas);
    }
     
    //选中的矩形区域提取
    Point sp(-1, -1);//鼠标的起始位置
    Point ep(-1, -1);//鼠标的结束位置
    Mat temp;
    static void on_draw(int event, int x, int y, int flags, void* userdata) {
    //参数一(event)为鼠标事件
    Mat image = *((Mat*)userdata);
    if (event == EVENT_LBUTTONDOWN) {//若鼠标的左键按下
    sp.x = x;
    sp.y = y;//此时鼠标的起始位置坐标
    cout << "start point" << sp << endl;
    }
    else if (event == EVENT_LBUTTONUP) {//若鼠标的左键抬起
    ep.x = x;
    ep.y = y;//此时鼠标的结束位置坐标
    int dx = ep.x - sp.x;
    int dy = ep.y - sp.y;
    if (dx > 0 && dy > 0) {//若鼠标有移动过
    Rect box(sp.x, sp.y, dx, dy);
    imshow("ROI区域", image(box));
    rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
    imshow("鼠标绘制", image);//这里是为了显示结果
    sp.x = -1;//复位,为下一次做准备
    sp.y = -1;//复位,为下一次做准备
    }
    }
    else if (event == EVENT_MOUSEMOVE) {//若鼠标正在移动
    if (sp.x > 0 && sp.y > 0) {
    ep.x = x;
    ep.y = y;//此时鼠标的结束位置坐标
    int dx = ep.x - sp.x;
    int dy = ep.y - sp.y;
    if (dx > 0 && dy > 0) {//若鼠标有移动过
    Rect box(sp.x, sp.y, dx, dy);
    temp.copyTo(image);//为了不将鼠标移动过程中的框也显示出来
    rectangle(image, box, Scalar(0, 0, 255), 2, 8, 0);
    imshow("鼠标绘制", image);//这里是为了每次重新提取都将前面的覆盖
    }
    }
    }
    }
    void QuickDemo::mouse_drawing_demo(Mat& image) {
    namedWindow("鼠标绘制", WINDOW_AUTOSIZE);
    setMouseCallback("鼠标绘制",on_draw, (void*)(&image));
    //设置窗口是回调函数,参数二表示调用on_draw
    imshow("鼠标绘制", image);
    temp = image.clone();
    }
     
    void QuickDemo::norm_demo(Mat& image) {
    Mat dst;
    cout << image.type() << endl;//打印图片的类型
    image.convertTo(image, CV_32F);//将image的数据转换成浮点型float32位数据
    cout << image.type() << endl;//打印转换后的图片数据类型
    normalize(image, dst, 1.0, 0, NORM_MINMAX);//进行归一化操作
    //参数一:要进行归一化的图片参数二:归一化后要输出的图片
    //参数三:alpha参数四:beta参数五:归一化方法
    cout << dst.type() << endl;//打印归一化后的图像的类型
    imshow("图像的归一化", dst);//显示归一化后的图像
    //CV_8UC3   原本为 3通道,每个通道8位的UC(无符号)类型
    //CV_32FC3  转换后 3通道,每个通道32位的浮点数类型
    /*
    归一化方法:
    NORM_L1(依据sum)b不用,a为归一化后矩阵的范数值
    NORM_L2(依据单位向量为1)b不用,a为 同上
    NORM_MINMAX(依据最大值)b不用,a为 同上
    NORM_INF(依据min与max的差值)a为归一化后的最小值,b归一化后的最大值
    */
    }
     
    void QuickDemo::resize_demo(Mat& image) {
    Mat zoomin, zoomout;
    int h = image.rows;
    int w = image.cols;
    resize(image, zoomout, Size(w / 2, h / 2), 0, 0, INTER_LINEAR);// INTER_LINEAR 为线性插值
    //若Size里的值没变,则按照参数四fx(水平轴)和参数五fy(垂直轴)来进行放缩操作
    //参数六:插值的方法
    imshow("zoomout", zoomout);
    resize(image, zoomin, Size(w * 1.5, h * 1.5), 0, 0, INTER_LINEAR);
    imshow("zoomin", zoomin);
    }
     
    void QuickDemo::flip_demo(Mat& image) {
    Mat dst;
    flip(image, dst, 0);// 0 上下翻转 x对称
    imshow("图像上下翻转", dst);
    flip(image, dst, 1);// 1 左右翻转 y对称
    imshow("图像左右翻转", dst);
    flip(image, dst, -1);//-1 上下左右都翻转(相当于旋转180°)
    imshow("图像上下左右翻转", dst);
    }
     
    void QuickDemo::rotate_demo(Mat& image) {
    Mat dst, M;//M为2*3的变换矩阵(旋转矩阵)
    int w = image.cols;//图片宽度
    int h = image.rows;//图片高度
    M = getRotationMatrix2D(Point(w / 2, h / 2), 45, 1.0);//获得旋转矩阵 M
    //参数一:原来图像的中心点位置参数二:旋转角度(逆时针)参数三:图像本身大小的放大缩小
    double cos = abs(M.at<double>(0, 0));//取绝对值
    double sin = abs(M.at<double>(0, 1));
    /*
    [x'] = [ cos  sin] * [x]
    [y']   [-sin  cos]   [y],
    M =[ cos  sin  0]
    [-sin  cos  0], (第三列用来控制平移)
    */
    double nw = cos * w + sin * h;//旋转后图像所占矩形的宽
    double nh = sin * w + cos * h;//旋转后图像所占矩形的高
    //更新 新的中心  (将新中心平移到正确位置上)
    M.at<double>(0, 2) += (nw / 2 - w / 2);//将矩形的宽高 加上偏差量  (新M的第一列最后的值)
    M.at<double>(1, 2) += (nh / 2 - h / 2);//将矩形的宽高 加上偏差量  (新M的第二列最后的值)
    warpAffine(image, dst, M, Size(nw, nh), INTER_LINEAR, 0, Scalar(255, 255, 0));//进行旋转
    //参数四:原来图像的中心点位置参数五:插值方式
    //参数六:边缘的处理方式参数七:边缘底图的颜色
    //namedWindow("旋转演示", WINDOW_FREERATIO); //可调整显示图片的窗口大小
    imshow("旋转演示", dst);
    }
     
    void QuickDemo::video_demo1(Mat& image) {
    //读已有视频
    VideoCapture capture("E:/2021.9.26备份/图片/Camera Roll/人脸素材.mp4");//读取视频地址
    Mat frame;//定义一个二值化的 frame
    while (true) {
    capture.read(frame);
    //flip(frame, frame, 1);// 1 左右翻转 y对称 (镜像)
    if (frame.empty())//如果读入失败
    {
    break;//若视频为空,则跳出操作
    }
    imshow("frame", frame);//显示视频
    colorSpace_Demo(frame);//对视频调用之前的demo
    int c = waitKey(1);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    }
    capture.release();//释放相机的资源
    /*
    //调用电脑摄像头
    VideoCapture capture(0);
    Mat frame;//定义一个二值化的 frame
    while (true) {
    capture.read(frame);
    if (frame.empty())//如果读入失败
    {
    break;//若视频为空,则跳出操作
    }
    flip(frame, frame, 1);// 1 左右翻转 y对称 (镜像)
    imshow("frame", frame);//显示视频
    int c = waitKey(10);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    }
    */
    }
     
    void QuickDemo::video_demo2(Mat& image) {
    //视频的属性:SD(标清),HD(高清),UHD(超清),蓝光。
    VideoCapture capture("E:/2021.9.26备份/图片/Camera Roll/人脸素材.mp4");//读取视频地址
    int frame_width = capture.get(CAP_PROP_FRAME_WIDTH);//获取视频的宽度
    int frame_height = capture.get(CAP_PROP_FRAME_HEIGHT);//获取视频的高度
    int count = capture.get(CAP_PROP_FRAME_COUNT);//获取视频总的帧数
    //fps是衡量处理视频的能力 (一秒钟处理多少张图片的能力,处理速度越快则越好)
    double fps = capture.get(CAP_PROP_FPS);
    cout << "frame width:" << frame_width << endl;
    cout << "frame height:" << frame_height << endl;
    cout << "FPS:" << fps << endl;
    cout << "Number of frame:" << count << endl;
    VideoWriter writer("F:/文件夹/C++/OPENCV4入门学习/图/test.mp4", capture.get(CAP_PROP_FOURCC), fps, Size(frame_width, frame_height), true);
    //参数一:保存地址参数二:获取图片的格式(编码方式)参数三:图片是帧数参数四:视频宽高参数五:与原来颜色保持一致
    //等全部运行完再去查看视频是否保存成功
    Mat frame;
    while (true) {
    capture.read(frame);
    //flip(frame, frame, 1);// 1 左右翻转 y对称 (镜像)
    if (frame.empty())//如果读入失败
    {
    break;//若视频为空,则跳出操作
    }
    imshow("frame", frame);//显示视频
    colorSpace_Demo(frame);//对视频调用之前的demo
    writer.write(frame);
    int c = waitKey(1);//等待10ms(1s = 1000ms),做视频处理都是1
    if (c == 27) {//按 esc 退出应用程序
    break;
    }
    }
    //release
    writer.release();
    capture.release();//释放相机的资源
    }
     
    void QuickDemo::histogram_demo(Mat& image) {
    //三通道分离
    vector<Mat> bgr_plane;
    split(image, bgr_plane);
    //定义参数变量
    const int channels[1] = { 0 };
    const int bins[1] = { 256 };//总共 256 个灰度级别
    float hranges[2] = { 0,255 };//每个通道的取值范围是 0 到 255
    const float* ranges[1] = { hranges };
    Mat b_hist;
    Mat g_hist;
    Mat r_hist;
    //计算 Blue,Green,Red 通道的直方图
    calcHist(&bgr_plane[0], 1, 0, Mat(), b_hist, 1, bins, ranges);//第一个通道
    calcHist(&bgr_plane[1], 1, 0, Mat(), g_hist, 1, bins, ranges);
    calcHist(&bgr_plane[2], 1, 0, Mat(), r_hist, 1, bins, ranges);
    //参数一:要计算直方图的数据参数二:1表示只有一张图(输入图像的格式)
    //参数三:需要统计直方图的第几个通道参数四:掩模,mask必须是8位的数组且和参数一的大小一致
    //参数五:b_hist表示直方图的输出参数六:1表示维度是一维的(输出直方图的维度dims)
    //参数七:直方图中每个维度需分成的区间个数参数八:ranges表示直方图的取值范围(区间)
    
    //显示直方图
    int hist_w = 512;//设置 画布宽度 为512
    int hist_h = 400;//设置 画布高度 为400
    int bin_w = cvRound((double)hist_w / bins[0]);//每个 bin 占的宽度
      //cvRound()四舍五入返回数值
    Mat histImage = Mat::zeros(hist_h, hist_w, CV_8UC3);//创建画布
     
    //归一化直方图数据(归一化到大小一致的范围内)
    normalize(b_hist, b_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());//histImage.rows是为了不超出画布许可的高度范围
    normalize(g_hist, g_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    normalize(r_hist, r_hist, 0, histImage.rows, NORM_MINMAX, -1, Mat());
    //参数一:要进行归一化的图片参数二:归一化后要输出的图片
    //参数三:alpha参数四:beta参数五:归一化方法
    
    //绘制直方图曲线
    for (int i = 1; i < bins[0]; i++) {//每个bin占2个像素的位置
    line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(b_hist.at<float>(i - 1))),
    Point(bin_w * (i), hist_h - cvRound(b_hist.at<float>(i))), Scalar(255, 0, 0), 2, 3, 0);
    line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(g_hist.at<float>(i - 1))),
    Point(bin_w * (i), hist_h - cvRound(g_hist.at<float>(i))), Scalar(0, 255, 0), 2, 3, 0);
    line(histImage, Point(bin_w * (i - 1), hist_h - cvRound(r_hist.at<float>(i - 1))),
    Point(bin_w * (i), hist_h - cvRound(r_hist.at<float>(i))), Scalar(0, 0, 255), 2, 3, 0);
    //从前一个位置到当前位置连上一条线
    }
    //显示直方图
    namedWindow("Histogram Demo", WINDOW_AUTOSIZE);
    imshow("Histogram Demo", histImage);
    }
     
    void QuickDemo::histogram_2d_demo(Mat& image) {
    //2D直方图
    Mat hsv, hs_hist;
    cvtColor(image, hsv, COLOR_BGR2HSV);//先把RGB色彩空间转换到hsv的空间中
    int hbins = 30, sbins = 32;
    int hist_bins[] = { hbins, sbins };//h和s这两个维度需分成的 区间个数
    float h_range[] = { 0,180 };//h的取值范围
    float s_range[] = { 0,256 };//s的取值范围
    const float* hs_ranges[] = { h_range, s_range };
    int hs_channels[] = { 0,1 };
    //计算通道的直方图
    calcHist(&hsv, 1, hs_channels, Mat(), hs_hist, 2, hist_bins, hs_ranges, true, false);
    //参数一:要计算直方图的数据参数二:1表示只有一张图(输入图像的格式)
    //参数三:需要统计直方图的第几个通道(前两个)参数四:掩模,mask必须是8位的数组且和参数一的大小一致
    //参数五:b_hist表示直方图的输出参数六:2表示维度是二维的(输出直方图的维度dims)
    //参数七:直方图中每个维度需分成的区间个数参数八:hs_ranges表示直方图的取值范围(区间)
    //参数九:是否对得到的直方图进行归一化处理参数十:在多个图像时,是否累计计算像素值的个数
    double maxVal = 0;
    minMaxLoc(hs_hist, 0, &maxVal, 0, 0);//寻找最大值和最小值及其位置(这里先找到最大值)
    //参数一:输入单通道的数组
    //参数二:返回最小值的指针参数三:返回最大值的指针
    //参数四:返回最小值位置的指针参数五:返回最大值位置的指针
    int scale = 10;
    Mat hist2d_image = Mat::zeros(sbins * scale, hbins * scale, CV_8UC3);//创建空白图像
    for (int h = 0; h < hbins; h++) {
    for (int s = 0; s < sbins; s++) {
    float binVal = hs_hist.at<float>(h, s);
    int intensity = cvRound(binVal * 255 / maxVal);
    rectangle(hist2d_image, Point(h * scale, s * scale),
    Point((h + 1) * scale - 1, (s + 1) * scale - 1), Scalar::all(intensity), -1);
    }
    }
    //显示直方图
    //applyColorMap(hist2d_image, hist2d_image, COLORMAP_JET);//产生伪色彩图像
    namedWindow("H-S Histogram", WINDOW_AUTOSIZE);
    imshow("H-S Histogram", hist2d_image);
    //imwrite("F:/文件夹/C++/OPENCV4入门学习/图/hist_2d.png", hist2d_image);
    }
     
    void QuickDemo::histogram_eq_demo(Mat& image) {
    //直方图均衡化 (目的是对比度拉伸,即 对比度会更强)
    //用途:用于图像增强,人脸检测,卫星遥感(提升图像质量)。
    //opencv中,均衡化的图像只支持单通道
    Mat gray;
    cvtColor(image, gray, COLOR_BGR2GRAY);
    imshow("灰度图像", gray);
    Mat dst;
    equalizeHist(gray, dst);
    imshow("直方图均衡化演示", dst);
    }
     
    void QuickDemo::blur_demo(Mat& image) {//会变模糊,且卷积核尺寸越大则越模糊
    Mat dst;
    blur(image, dst, Size(15, 15), Point(-1, -1));//均值滤波 均值模糊
    //参数三:卷积核的大小参数四:卷积的起始点(Point(-1, -1)则默认取核的中心)
    //参数三中:
    //Size(15, 1) 左右晃动的模糊(只有行的话
    //Size(1, 15) 上下(  列
    imshow("图像卷积操作", dst);
    }
     
    void QuickDemo::gaussian_blur_demo(Mat& image) {
    //中心值最大,离中心越远值越小
    Mat dst;
    GaussianBlur(image, dst, Size(5, 5), 15);
    //参数三:高斯矩阵的大小(正数且奇数)
    //参数四:sigmaX 和 sigmaY 为15 
    //(参数三和四都 值越大则越模糊,且参数四的影响更明显)
    imshow("高斯模糊", dst);
    }
     
    void QuickDemo::bifilter_demo(Mat& image) { //可做磨皮操作
    Mat dst;
    bilateralFilter(image, dst, 0, 100, 10);
    //参数三:色彩空间参数四:坐标空间(双边是指 色彩空间 和 坐标空间
    namedWindow("高斯双边模糊", WINDOW_AUTOSIZE);
    imshow("高斯双边模糊", dst);
    }

源.cpp

    #include <iostream>
    #include <opencv2/opencv.hpp>
    #include "quickopencv.h"
     
    using namespace std;
    using namespace cv;
     
    int main(int argc, char* argv[])
    {
    //const char* imagename = "E:\\2021.9.26备份\\图片\\Camera Roll\\001.jpg"; //此处为你自己的图片路径
    const char* imagename = "E:/2021.9.26备份/图片/Camera Roll/007.jpg"; //此处为你自己的图片路径
     
    //从文件中读入图像
    Mat img = imread(imagename, 1);//彩色
    //Mat img = imread(imagename, IMREAD_GRAYSCALE);//黑白
     
    //如果读入图像失败
    if (img.empty())
    {
    fprintf(stderr, "Can not load image %s\n", imagename);
    return -1;
    }
     
    //创建一个新窗口,参数1为名称,参数2代表一个自由的比例
    namedWindow("image", WINDOW_FREERATIO); //可调整显示图片的窗口大小
     
    //显示图像
    imshow("image", img);//(名称,对象)
     
    QuickDemo pd;//创建类对象
    pd.bifilter_demo(img);
     
    waitKey(); //此函数等待按键,按键盘任意键就返回
    //括号中参数为延时时间,单位ms
    destroyAllWindows();//销毁前面创建的显示窗口
    return 0;
    }
     
     
    /* 显示图片的测试代码
    int main(int argc, char* argv[])
    {
    //const char* imagename = "E:\\2021.9.26备份\\图片\\Camera Roll\\001.jpg"; //此处为你自己的图片路径
    const char* imagename = "E:/2021.9.26备份/图片/Camera Roll/0041.jpg"; //此处为你自己的图片路径
    //从文件中读入图像
    Mat img = imread(imagename, 1);//彩色
    //Mat img = imread(imagename, IMREAD_GRAYSCALE);//黑白
    //如果读入图像失败
    if (img.empty())
    {
    fprintf(stderr, "Can not load image %s\n", imagename);
    return -1;
    }
    //创建一个新窗口,参数1为名称,参数2代表一个自由的比例
    namedWindow("image", WINDOW_FREERATIO); //可调整显示图片的窗口大小
    //显示图像
    imshow("image", img);//(名称,对象)
    waitKey(); //此函数等待按键,按键盘任意键就返回
    //括号中参数为延时时间,单位ms
    destroyAllWindows();//销毁前面创建的显示窗口
    return 0;
    }*/

二、相关图片

012.图像色彩空间转换(提取轮廓然后换绿幕)

HSV色彩空间的颜色:

021.图像旋转

027.图像卷积操作(会变模糊,且卷积核尺寸越大则越模糊

028.高斯模糊

高斯卷积数学表达式说明:

高斯卷积的图像说明:

029.高斯双边模糊(可磨皮操作)

### 回答1: opencv-4.5.1.exe是一个计算机图像处理库的安装文件。OpenCV是一个开源的计算机视觉和机器学习软件库,提供了丰富的图像处理和计算机视觉功能。 opencv-4.5.1.exe是OpenCV库的一个特定版本的安装文件。通过运行这个安装文件,可以将OpenCV库安装到计算机上,以便在自己的项目中使用。 OpenCV库提供了很多功能,包括图像处理、特征提取、物体检测、目标跟踪、立体视觉等。它提供了一系列的图像处理算法和工具,方便开发人员进行图像处理和计算机视觉应用的开发。 OpenCV库支持多种编程语言,包括C++、Python和Java等,因此可以在不同的开发环境中使用。 通过安装opencv-4.5.1.exe,您可以轻松地将OpenCV库集成到您的开发环境中,并利用其强大的图像处理功能来实现您的项目需求。通过调用OpenCV提供的API,您可以快速开发出高效的图像处理和计算机视觉应用。 总之,opencv-4.5.1.exe是OpenCV库的一个特定版本的安装文件,通过安装它可以将OpenCV集成到开发环境中,方便进行图像处理和计算机视觉应用的开发。 ### 回答2: OpenCV是一个开源的计算机视觉库,可用于开发各种图像处理和计算机视觉应用程序。OpenCV-4.5.1.exe是OpenCV的安装文件,它是用于在Windows操作系统上安装OpenCV库的可执行文件。 通过运行OpenCV-4.5.1.exe,您可以将OpenCV库和相关的开发工具和示例程序安装到您的计算机上。安装过程中,您将被引导选择安装路径、编译器和其他配置选项。一旦安装完成,您可以在编写和运行OpenCV应用程序时使用安装的库和工具。 在安装OpenCV之后,您将能够使用包括图像加载、图像处理、特征提取、目标检测等在内的各种功能。OpenCV提供了丰富的函数和工具,以帮助开发人员简化图像处理和计算机视觉任务的实现。 通过运行OpenCV-4.5.1.exe,您可以快速轻松地开始使用OpenCV,并从这个功能强大的计算机视觉库中受益。不仅可以用于学习和研究,OpenCV还广泛应用于工业、医疗、安防、自动驾驶等多个领域。 值得注意的是,OpenCV-4.5.1.exe是针对Windows操作系统的安装文件,如果您使用其他操作系统,您需要下载适用于该操作系统的安装包。同时,为了保证OpenCV库的正常运行,您还需要根据您的开发环境和需求做一些配置和设置,例如配置编译器和环境变量等。 总而言之,OpenCV-4.5.1.exe是安装OpenCV库的可执行文件,通过运行该文件,您可以在Windows操作系统上安装OpenCV并开始开发图像处理和计算机视觉应用程序。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值