cpp编写opencv程序#

配置cpp环境#

Clion中添加依赖#

  1. 在当前工程下新建一个cmake文件夹,将FindOpenCV.cmake文件拷贝进去
  2. 修改cmake文件指向自己的opencv所在的库路径

cmake配置文件

1
2
3
4
5
6
7
8
9
cmake_minimum_required(VERSION 3.14)
project(study_opencv_cpp)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake)
find_package(OpenCV REQUIRED)

add_executable(study_opencv_cpp02 02-图片剪切.cpp)
target_link_libraries(study_opencv_cpp02 ${OpenCV_LIBS})

Mat类#

数据类型定义#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
CV_位数(S|U|F)C通道数量
位数:
    8 , 16, 32 ,64  单通道颜色取值范围是0-255,所以通常是8

S|U|F:
    S--代表---signed int---有符号整形
    U--代表--unsigned int--无符号整形
    F--代表--float---------单精度浮点型

C通道数量:
    通道数量 = 1:灰度图片,单通道图像
    通道数量 = 3:灰度图片,单通道图像
    通道数量 = 4:带Alph通道的RGB图像,png图片是这种

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){

    Mat img = imread("../assets/lena.jpg",IMREAD_COLOR);
    cout<<"read img success!";
    imshow("img",img);

    // 获取图片的大小
    Size imgSize = img.size();
    cout<<"img size:"<<imgSize;
    // 获取图片的通道数量
    int channels = img.channels();
    // 获取数据类型
    cout<<"channels:"<<channels<<"type:"<<img.type()<<endl;


    // 创建一个图片对象
    Mat dst(400,600,CV_8UC3);
    // 在图片中间增加一根红色的线
    for(int i=0;i<600;i++){
        dst.at<Vec3b>(200,i) = Vec3b(0,0,255);
    }

    namedWindow("dst",WINDOW_NORMAL);
    imshow("dst",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

图像剪切#

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){
    Mat img = imread("../assets/itheima.jpg",IMREAD_COLOR);

    // 定义要截取的矩形框
    Rect rect(100,50,200,200);

    // 这样即可完成截取
    Mat roi = img(rect);

    imshow("src",img);
    imshow("roi",roi);


    waitKey(0);
    destroyAllWindows();
    return 0;
}

直方图#

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
//
// Created by itheima on 6/12/19.
//
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){
    // 读取图片
    Mat img = imread("../assets/lena.jpg",IMREAD_COLOR);
    imshow("src",img);
    // 单通道处理数据
    vector<Mat> bgr_channels;
    // 颜色分割
    split(img,bgr_channels);

    int histSize=256;
    float range[] = {0,256};
    const float * histRange = {range};
    // 用于说明bin宽度是否相等
//    bool uniform=true;
    // 当计算多通道直方图的时候,是否进行累加
//    bool accumulate=false;

    Mat b_hist,g_hist,r_hist;
    calcHist(&bgr_channels[0],1,0,Mat(),b_hist,1,&histSize,&histRange);
    calcHist(&bgr_channels[1],1,0,Mat(),g_hist,1,&histSize,&histRange);
    calcHist(&bgr_channels[2],1,0,Mat(),r_hist,1,&histSize,&histRange);

    int histsize = 256;

    // 创建一个灰色图像
    int hist_w = 512;
    int hist_h = 400;
    int bin_w = cvRound(512.0/histSize);

    //进行归一化处理       最小值   最大值  模式
    normalize(b_hist,b_hist,0,hist_h,NORM_MINMAX);
    cout<<"归一化之后:"<<b_hist<<endl;

    Mat histImage(hist_h,hist_w,CV_8UC3,Scalar(0,0,0));
    // 绘制图像
    for(int i=0;i<histsize;i++){
        line(histImage,Point(bin_w*i,hist_h),Point(bin_w*(i),hist_h-b_hist.at<float>(i-1)),Scalar(255,0,0),2,LINE_AA);
    }

    imshow("histgrom",histImage);
    waitKey(0);
    destroyAllWindows();
}

直方图绘制工具#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
#ifndef OPENCV_DEMO_COMMON_H
#define OPENCV_DEMO_COMMON_H

#include <iostream>
#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;

/**
 * 直接传入直方图数据
 */
void drawHistgrom(string title,Mat &hist) {
    int hist_h = 500;
    int bin_w = 4;
    int hist_w = 4 * 256;

    // 将数据归一化到0-hist_h之间
    Mat outRatios;
    normalize(hist,outRatios,0,hist_h,NORM_MINMAX);

    Mat histImage(hist_h, hist_w, CV_8UC3);
    for (int i = 0; i < 256; i++) {
        Point pt1(i * bin_w, hist_h);
        Point pt2(i * bin_w, hist_h - (int) outRatios.at<float>(i));
        line(histImage, pt1, pt2, Scalar(0, 255, 255), 1, LINE_AA);
    }


    imshow(title, histImage);
}

/**
 * 传入进来的上图像
 */
void showHistgrom(string title,Mat &img) {
    int histSize=256;
    float range[] = {0,256};
    const float * histRange = {range};

    Mat hist;
    calcHist(&img,1,0,Mat(),hist,1,&histSize,&histRange);

    drawHistgrom(title,hist);
}

#endif //OPENCV_DEMO_COMMON_H

直方图均衡化#

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
#include <iostream>
#include <opencv2/opencv.hpp>
#include "common.h"


using namespace std;
using namespace cv;


int main() {
    Mat img = imread("../assets/itheima.jpg", IMREAD_COLOR);

    //将图像转成灰度图像
    Mat gray;
    cvtColor(img, gray, COLOR_BGR2GRAY);
    imshow("gray",gray);
    // 计算直方图
    Mat hist;
    int histSize = 256;
    float range[] = {0, 255.0};
    const float *histRange[] = {range};
    calcHist(&gray, 1, 0, Mat(), hist, 1, &histSize, histRange);

    // 计算直方图的概率
    Mat ratios = hist / (img.rows * img.cols);


    drawHistgrom("src hist", ratios);


    float sum1 = 0;
    // 计算直方图的累加概率
    for (int i = 0; i < ratios.rows; i++) {
        sum1 += ratios.at<float>(i);
        ratios.at<float>(i) = sum1;
    }
    cout<<ratios<<endl;

    // 绘制直方图
    drawHistgrom("leiji hist", ratios);

    Mat dst(gray.rows, gray.cols, CV_8UC1);
    // 对原图像进行处理
    for (int row = 0; row < img.rows; ++row) {
        for (int col = 0; col < img.cols; ++col) {
            // 获取每个位置的灰度值
            int color = gray.at<uint8_t>(row, col);
            // 根据颜色值从累计概率中获取累计概率
            float ratio = ratios.at<float>(color);
            // 计算新的颜色值
            color = cvRound(255 * ratio);
            // 填充到原图中
            dst.at<uchar>(row, col) = color;
        }
    }
    imshow("dst", dst);
    showHistgrom("dst hist",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

直方图匹配#

示例代码

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
#include <iostream>
#include <opencv2/opencv.hpp>
#include "common.h"


using namespace std;
using namespace cv;

Mat getAccumulate(Mat &src);

Mat histogramMatch(Mat &src, Mat &refer);

int main(){
    // 原图像
    Mat src = imread("../assets/1.jpg",IMREAD_COLOR);
    // 参考图
    Mat refer = imread("../assets/2.jpg",IMREAD_COLOR);

    vector<Mat> srcChannels;
    vector<Mat> referChannels;

    split(src,srcChannels);
    split(refer,referChannels);

    Mat b_channel = histogramMatch(srcChannels[0], referChannels[0]);
    Mat g_channel = histogramMatch(srcChannels[1], referChannels[1]);
    Mat r_channel = histogramMatch(srcChannels[2], referChannels[2]);

    cout<<"开始图像的合并:"<<endl;
    Mat dst;
    vector<Mat> channels = {b_channel,g_channel,r_channel};
    merge(channels,dst);



    // 显示图像
    imshow("src",src);
    imshow("refer",refer);
    imshow("dst",dst);

    // 绘制直方图
//    showHistgrom("src hist",src);
//    showHistgrom("refer hist",refer);
//    showHistgrom("dst hist",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

Mat histogramMatch(Mat &src, Mat &refer) {// 计算src的累计概率
    Mat srcRatios = getAccumulate(src);

    // 计算src的累计概率
    Mat referRatios = getAccumulate(refer);

    // 执行关系映射
    Mat map(256, 1, CV_8UC1);

    // 遍历原图像
    for (int i = 0; i < 256; ++i) {
        float srcRatio = srcRatios.at<float>(i);

        float min = 10;
        int k = 0;
        // 去参考图的直方图中查找最小的差值
        for (int j = 0; j < 256; ++j) {
            float referRatio = referRatios.at<float>(j);
            // 计算差值
            float diff = abs(srcRatio - referRatio);
            if (diff < min) {
                min = diff;
                k = j;
            }
        }
        // 当上面循环执行完毕,就找到了最小的差值  120 ---》 200
        map.at<uchar>(i) = k;
    }

    Mat dst(src.rows, src.cols, src.type());

    // 处理原图,将原图的颜色映射到新的颜色空间
    for (int row = 0; row < dst.rows; ++row) {
        for (int col = 0; col < dst.cols; ++col) {
            uchar color = src.at<uchar>(row, col);
            uchar newColor = map.at<uchar>(color);
            dst.at<uchar>(row, col) = newColor;
        }
    }

    return dst;
}

Mat getAccumulate(Mat &src) {
    Mat hist_src;
    int histSize = 256;
    float range[] = {0.0, 255.0};
    const float *histRange[] = {range};

    calcHist(&src, 1, 0, Mat(), hist_src, 1, &histSize, histRange);

    // 1.计算每个像素的概率
    Mat ratios = hist_src/(src.rows*src.cols);

    float sum = 0;
    // 2. 计算累计概率
    for (int i = 0; i < hist_src.rows; ++i) {
        sum += ratios.at<float>(i);
        ratios.at<float>(i) = sum;
    }
    // 计算累计概率
    return ratios;
}

模板匹配#

模板匹配就是在整个图像区域发现与给定子图像匹配的小块区域。

在OpenCV中提供了6种匹配度量方法。

  1. 平方差匹配法CV_TM_SQDIFF
  2. 归一化平方差匹配法CV_TM_SQDIFF_NORMED
  3. 相关匹配法CV_TM_CCORR
  4. 归一化相关匹配法CV_TM_CCORR_NORMED
  5. 系数匹配法CV_TM_CCOEFF
  6. 化相关系数匹配法CV_TMCCOEFF_NORMED

通常来讲,随着从简单测量方法(平方差)到更复杂的测量方法(相关系数法),我们可以获得越来越准确的匹配。然而这同时也会以越来越大的计算量为代价。对于选取何种方法,针对不同的匹配情况进行对此分析比较,选取更适合自己应用场景同时兼顾速度和精度的最佳方案。 注意 :对于方法SQDIFF和SQDIFF_NORMED两种方法来讲,越小的值就有着更高的匹配结果,而其余的方法则是数值越大匹配效果越好。

示例代码如下:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <iostream>
#include <opencv2/opencv.hpp>
#include <stdio.h>

using namespace std;
using namespace cv;

int main(){
    // 读取原图
    Mat src = imread("../assets/zhaonimei.jpg",IMREAD_COLOR);
    imshow("src",src);

    // 模板
    Mat temp = imread("../assets/mei.jpg",IMREAD_COLOR);
    imshow("teamplate",temp);
    // 调用api查找内容
    int width = src.cols - temp.cols;
    int height = src.rows - temp.rows;
    Mat result(height,width,CV_8UC1);
    matchTemplate(src,temp,result,TM_SQDIFF);

    // 将结果进行归一化处理  , 可做可不做
    normalize(result,result,0,1,NORM_MINMAX);

    Point minLoc,maxLoc;
    double min,max;
    // 求最大值最小值
    minMaxLoc(result,&min,&max,&minLoc,&maxLoc);

    printf("min:%f ,max:%f..\n",min,max);

    rectangle(src,Rect(minLoc.x,minLoc.y,temp.cols,temp.rows),Scalar(0,0,255),2,LINE_AA);


    imshow("dst",src);
    waitKey(0);
    destroyAllWindows();
    return 0;
}

图像的卷积#

卷积核像一个老奶奶,眼疾手快又很有耐心的把每个像素按照周围的像素要么拔高,要么抹平。拔高就是找边缘、锐化,抹平就是模糊、中值滤波

filter2d#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){
    Mat img = imread("../assets/lena.jpg",IMREAD_COLOR);
    imshow("src",img);

    // 定义卷积核
    Mat kernel = (Mat_<char>(3,3)<<-1,-1,-1,
                                        -1,9,-1,
                                        -1,-1,-1);

    // 进行图像的卷积操作
    Mat dst;
    filter2D(img,dst,-1,kernel);

    imshow("dst",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

Sobel算子#

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace std;
using namespace cv;



int main(){
    Mat src_img = imread("../assets/zqbb.jpg",IMREAD_COLOR);
    namedWindow("src",CV_WINDOW_AUTOSIZE);
    imshow("src",src_img);


    Mat gray;
    cvtColor(src_img,gray,COLOR_BGR2GRAY);

    GaussianBlur(gray,gray,Size(3,3),0);

    // sobel
    Mat xgray,ygray;

//    Sobel(gray,xgray,CV_16S,1,0);
//    Sobel(gray,ygray,CV_16S,0,1);

    Scharr(gray,xgray,CV_16S,1,0);
    Scharr(gray,ygray,CV_16S,0,1);

    convertScaleAbs(xgray,xgray);
    convertScaleAbs(ygray,ygray);

    imshow("xgray",xgray);
    imshow("ygray",ygray);

    //将两张图片融合在一起
    const Size size = gray.size();
    int height = size.height;
    int width = size.width;

    Mat result = Mat::zeros(gray.size(),gray.type());
//
//    for(int row=0;row<height;row++){
//        for(int col=0;col<width;col++){
//            int xv = xgray.at<uchar>(row,col);
//            int yv = ygray.at<uchar>(row,col);
//            result.at<uchar>(row,col) = xv + yv;
//        }
//    }
    add(xgray,ygray,result);

    imshow("result",result);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

拉普拉斯算子#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){
    Mat img = imread("../assets/zqbb.jpg",IMREAD_GRAYSCALE);

    Mat dst;
    Laplacian(img,dst,-1,5);

    imshow("src",img);
    imshow("dst",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

均值滤波#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){
    Mat img = imread("../assets/itheima.jpg",IMREAD_COLOR);
    imshow("src",img);

    Mat dst;
    blur(img,dst,Size(5,5));
    imshow("dst",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

中值滤波#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
using namespace std;
using namespace cv;

int main(){
    Mat img = imread("../assets/itheima_salt.jpg",IMREAD_COLOR);
    imshow("src salt",img);

    Mat dst;
    medianBlur(img,dst,5);

    imshow("dst",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

高斯滤波#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){
    Mat img = imread("../assets/itheima.jpg",IMREAD_COLOR);

    Mat dst;
    GaussianBlur(img,dst,Size(5,5),0);

    imshow("src",img);
    imshow("dst",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

Canny算法#

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(){
    Mat img = imread("../assets/itheima.jpg",IMREAD_COLOR);

    Mat dst;
    Canny(img,dst,50,120);

    imshow("src",img);
    imshow("dst",dst);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

双边滤波#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int sigmaColor = 0;
int sigmaSpace = 0;
// 回调函数
void changeSigma(int val,void* userdata,int index){

    switch (index){
        case 0:
            sigmaColor = val;
            break;
        case 1:
            sigmaSpace = val;
            break;
    }

    Mat src = *(Mat *)userdata;
    Mat dst;
    bilateralFilter(src,dst,10,sigmaColor,sigmaSpace);
    imshow("dst",dst);
}


int main(){
    Mat img = imread("../assets/shuaige.jpg",IMREAD_COLOR);

    imshow("src",img);
    // 创建窗口
    namedWindow("dst");
    // 创建滑动条
    createTrackbar("sigmaColor","dst",0,255,[](int val,void* userdata){ changeSigma(val,userdata,0); },&img);
    createTrackbar("sigmaSpace","dst",0,255,[](int val,void* userdata){ changeSigma(val,userdata,1); },&img);
    // 初始化图像
    changeSigma(0,&img,0);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

膨胀与腐蚀#

形态学变化是基于图像形状的一些简单操作。操作对象一般是二值图像,需要两个输入,一个是我们的原图,另一个是3x3的结构元素(内核),决定了膨胀操作的本质。常见的操作是图像的膨胀和腐蚀。以及他们的进阶操作注入Opening、Closing、Gradient等等。(参考

结构元素的形状

MORPH_RECT 矩形
MORPH_ELLIPSE 椭圆形
MORPH_CROSS 十字型

膨胀Dilation#

跟卷积操作非常类似.有图像A和3x3的结构元素,结构元素在A上进行滑动.计算结构元素在A上覆盖的最大像素值来替换当前结构元素对应的正中间的元素

膨胀的作用:

  1. 对象边缘增加一个像素
  2. 使对象边缘平滑
  3. 减少了对象与对象之间的距离

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/**
 * 膨胀: 用内核中最大的值填充当前像素点的值
 */
void dilation(){

    Mat img = imread("../assets/morph-j.jpg",IMREAD_GRAYSCALE);
    imshow("src",img);

    Mat dst;

    Mat kernel = getStructuringElement(MORPH_RECT,Size(5,5));
    dilate(img,dst,kernel);

    // 将膨胀之后的图像显示出来
    imshow("dilation",dst);
}

腐蚀Erosion#

腐蚀和膨胀过程类似,唯一不同的是以覆盖的最小值替换当前的像素值

侵蚀的作用:

  1. 对象边缘减少一个像素
  2. 对象边缘平滑
  3. 弱化了对象与对象之间连接

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
/**
 * 腐蚀: 用内核覆盖的最小像素值替换当前像素值
 */
void erosion(){
    Mat img = imread("../assets/morph-j.jpg",IMREAD_COLOR);
    imshow("src",img);


    Mat dst;
    Mat kernel = getStructuringElement(MORPH_RECT,Size(5,5));

    // 腐蚀
    erode(img,dst,kernel);
    // 显示图像
    imshow("dst",dst);
}

开操作Opening#

先腐蚀,后膨胀,主要应用在二值图像或灰度图像

先腐蚀: 让当前窗口中最小的颜色值替换当前颜色值

后膨胀: 让当前窗口中最大的颜色值替换当前颜色值

作用:

  1. 一般用于消除小的干扰或噪点(验证码图的干扰线)
  2. 可以通过此操作配合结构元素,提取图像的横线和竖线

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
 * 开操作: 先腐蚀,再膨胀
 */
void openning(){
    Mat img = imread("../assets/morph-opening.jpg",IMREAD_COLOR);
    imshow("src",img);


    Mat dst;
    Mat kernel = getStructuringElement(MORPH_RECT,Size(5,5));

    morphologyEx(img,dst,MORPH_OPEN,kernel);

    imshow("dst",dst);
}

去除验证码上面的干扰线#

闭操作Closing#

先膨胀,后侵蚀

一般用于填充内部缝隙

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
/**
 *  先膨胀再腐蚀
 */
void closing(){
    Mat img = imread("../assets/morph-closing.jpg",IMREAD_COLOR);
    imshow("src",img);

    // 定义结构元素
    Mat dst;
    Mat kernel = getStructuringElement(MORPH_RECT,Size(3,3));

    // 进行形态学变换
    morphologyEx(img,dst,MORPH_CLOSE,kernel);
    imshow("dst",dst);
}

小结#

形态学变换中还有一些其它的变换,大家可以自行尝试查看执行效果

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
开运算
    先对图像腐蚀,再膨胀
    dst=open(src,element)=dilate(erode(src,element))

闭运算
    先膨胀再腐蚀
    dst=close(src,element)=erode(dilate(src,element))

形态梯度
    膨胀图和腐蚀图之差
    dst=morph_grad(src,element)=dilate(src,element)erode(src,element)

顶冒
    原图和开运算图之差
    dst=tophat(src,element)=srcopen(src,element)

黑冒
    闭运算结果和原图之差
    dst=blackhat(src,element)=close(src,element)src

透视变换#

透视变换(Perspective Transformation)是将图片投影到一个新的视平面(Viewing Plane),也称作投影映射(Projective Mapping)。如下图所示

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
/**
 图片的透视变换

 # 左上角
pt1 = (120,90)
# 右上角
pt2 = (350,90)

# 左下角
pt3 = (60,470)
# 右下角
pt4 = (430,470)
 */
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

int main(int argc,char** argv){
    string path = "/home/kaijun/Pictures/zhengjianzhao.png";

    Mat src = imread(path,IMREAD_COLOR);

    imshow("src",src);

    vector<Point2f> sourcePoint={Point2f(120,90),Point2f(350,90),Point2f(60,470),Point2f(430,470)};

    // 处理完成之后,将图像映射到新的空间中去
    vector<Point2f> dstPoint = {Point2f(0,0),Point2f(480,0),Point2f(0,600),Point2f(480,600)};

    // 计算透视变换的转换矩阵
    Mat matrix = getPerspectiveTransform(sourcePoint,dstPoint);

    // 调用转换函数
    Mat dst;
    warpPerspective(src,dst,matrix,Size(480,600));

    imshow("dst",dst);

    waitKey(0);
    return 0;
}

霍夫圆#

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;


int main() {


    Mat frame = imread("../assets/water_coins.jpg", IMREAD_COLOR);
    imshow("frame", frame);

    Mat grayImg;
    cvtColor(frame, grayImg, COLOR_BGR2GRAY);
    imshow("grayimg", grayImg);

    vector<Vec3f> circles;
//      HoughCircles(grayImg,circles,HOUGH_GRADIENT,1,150,100,30,50,90);
    HoughCircles(grayImg, circles, HOUGH_GRADIENT, 1, 10, 100, 30, 5, 30);


    for (int i = 0; i < circles.size(); ++i) {
        Vec3f c = circles.at(i);
        Point center(c[0], c[1]);
        float radius = c[2];
        cout << radius << endl;
        circle(frame, center, radius, Scalar(0, 255, 0), 2, LINE_AA);
    }


    imshow("rest", frame);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

轮廓提取#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
#include <iostream>
#include <opencv2/opencv.hpp>
#include <stdio.h>

using namespace std;
using namespace cv;


Mat img;

void changeThresh(int val,void* userdata){
    // 1. 定义结果矩阵
    Mat cannyImg;

    // 使用Canny算法获取图像的边缘信息
    Canny(img,cannyImg,50,val);

    // 调用api从canny图像上查找轮廓
    vector<vector<Point>> contours;
    vector<Vec4i> hierachy;

    findContours(cannyImg,contours,hierachy,RETR_EXTERNAL,CHAIN_APPROX_NONE);

    Mat dst = Mat::zeros(img.size(), img.type());
    drawContours(dst,contours,-1,Scalar(255,255,0),2,LINE_AA);


    // 绘制轮廓
    for(int i=0;i<contours.size();i++){
       vector<Point> c = contours[i];
       // 计算面积
       double area = contourArea(c);
       cout<<"面积为:"<<area<<endl;
       // 计算弧长
       double arc = arcLength(c,true);
       cout<<"弧长为:"<<endl;

       // 计算外切圆
       Point2f center;
       float radius;
       minEnclosingCircle(c,center,radius);
       cout<<"圆心坐标:"<<center<<"  半径:"<<radius;

       circle(dst,center,radius,Scalar(0,0,255),2,LINE_AA);
    }

    imshow("dst",dst);
    int i = *static_cast<int *>(userdata);
    cout<<val<<"======"<<(i)<<endl;

}

int main(){
    img = imread("../assets/shape.jpg", IMREAD_COLOR);

    imshow("src",img);

    int thresh=104;
    int userdata=100;

    cout<<userdata<<"=="<<&userdata<<endl;
    namedWindow("dst");
    createTrackbar("thresh","dst",&thresh,255,changeThresh,&userdata);
    changeThresh(104,&userdata);
//    imshow("dst",img);


    waitKey(0);
    destroyAllWindows();
    return 0;
}

人脸识别小案例#

简单人脸识别#

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

CascadeClassifier face_detect = CascadeClassifier("../assets/haarcascade_frontalface_default.xml");
CascadeClassifier eye_detect = CascadeClassifier("../assets/haarcascade_eye.xml");


int main(){
    Mat img = imread("../assets/lena.jpg",IMREAD_COLOR);

    vector<Rect> faces;
    face_detect.detectMultiScale(img,faces,1.3);

    for (int i = 0; i < faces.size(); ++i) {
        // 绘制人脸
        Rect face = faces.at(i);
        rectangle(img,face,Scalar(255,255,0),2);

        // 从图像上截取出人脸
        Mat faceImg = img(face);

        // 查找眼睛,并且绘制眼睛
        vector<Rect> eyes;
        eye_detect.detectMultiScale(faceImg,eyes,1.3);
        for (int j = 0; j < eyes.size(); ++j) {
            Rect eye = eyes.at(j);
            rectangle(faceImg,eye,Scalar(0,255,255),2);
        }
    }
    imshow("img",img);

    waitKey(0);
    destroyAllWindows();
    return 0;
}

人脸替换#

示例代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;
using namespace cv;

CascadeClassifier face_detect = CascadeClassifier("../assets/haarcascade_frontalface_default.xml");
CascadeClassifier eye_detect = CascadeClassifier("../assets/haarcascade_eye.xml");


int main(){
    Mat img = imread("../assets/lena.jpg",IMREAD_COLOR);
    imshow("img",img);

    vector<Rect> faces;
    face_detect.detectMultiScale(img,faces,1.3);


    for (int i = 0; i < faces.size(); ++i) {
        // 找到人脸位置
        Rect face = faces.at(i);

        // 创建一个和原图一样大小的蒙板
        Mat mask = Mat::zeros(img.size(),CV_8UC1);
        // 设置感兴趣的区域为全白色
        mask(face).setTo(255);
        imshow("mask",mask);

        // 使用蒙板,从原图中抠出感兴趣的图像
        Mat copyFace;
        img.copyTo(copyFace,mask);
        imshow("copyFace",copyFace);

        // 对拷贝出来的人脸进行Canny边缘提取
        Mat cannyImg;
        Canny(copyFace,cannyImg,100,200);
        imshow("canny",cannyImg);

        // 将原图,人脸位置留空
        img(face) = Vec3b(0,0,0);
        imshow("img kong",img);

        // 将单通道的canny图转成BGR三通道的图像
        Mat cannyImg3;
        cvtColor(cannyImg,cannyImg3,COLOR_GRAY2BGR);

        // 将留空的原图和3通道的Canny图加在一起
        Mat dst;
        add(cannyImg3,img,dst);
        imshow("dst",dst);

    }

    waitKey(0);
    destroyAllWindows();
    return 0;
}