当前位置:   article > 正文

C++ opencv 学习_opencv c++

opencv c++

1、创建窗口

c++写法:

void test2()
{
    Mat img = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    cout<<img.depth()<<" "<<img.channels()<<endl;
    namedWindow("new",cv::WINDOW_AUTOSIZE);
    imshow("new",img);
    waitKey(0);
    destroyAllWindows();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

python写法:

def test():
    cv2.namedWindow('new', cv2.WINDOW_NORMAL)
    cv2.imshow('new', 0)
    key = cv2.waitKey(0)
    if key & 0xFF  == ord('q'):
        cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

2、读取图片

c++写法,操作图像,将图像转换为hsv然后调整亮度值,转换会BGR格式显示

void test2()
{
    Mat img = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    cout<<img.depth()<<" "<<img.channels()<<endl;
    namedWindow("new",cv::WINDOW_AUTOSIZE);
    imshow("new",img);
    waitKey(0);

    Mat gray,hsv;
    cvtColor(img,gray,cv::COLOR_BGR2GRAY);
    imshow("new",gray);
    waitKey(0);

    cvtColor(img,hsv,cv::COLOR_BGR2HSV);
    imshow("new",hsv);
    waitKey(0);

    Mat channels[3];
    // 将HSV图像分离为三个单独的通道,这将把HSV图像的三个通道分别存储在channels数组的不同元素中
    split(hsv,channels);
    int bright = 50;
    channels[2] += bright;
    // 将亮度通道的像素值限制在0到255之间,大于255截断为255
    threshold(channels[2],channels[2],255,255,cv::THRESH_TRUNC);
    // 将修改后的通道合并为单个图像
    cv::merge(channels, 3, hsv);
    Mat outputImage;
    cv::cvtColor(hsv, outputImage, cv::COLOR_HSV2BGR);

    imshow("new",outputImage);
    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
@param filename Name of file to be loaded.
@param flags Flag that can take values of cv::ImreadModes

CV_EXPORTS_W Mat imread( const String& filename, int flags = IMREAD_COLOR );

flags标识符介绍:
IMREAD_GRAYSCALE = 0, 灰度图
IMREAD_COLOR = 1,原本是什么颜色就显示什么颜色
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
def test2():
    cv2.namedWindow('new', cv2.WINDOW_AUTOSIZE)
    img = cv2.imread(img_name, 1)
    cv2.imshow('new', img)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
	cv2.imwrite('1.png',img)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7

3、视频采集

def test3():
    # cv2.namedWindow('new', cv2.WINDOW_AUTOSIZE)
    # 获取视频设置 
    cap = cv2.VideoCapture(0)
    while True:
        ret, frame = cap.read()
        cv2.imshow('video', frame)
        key = cv2.waitKey(10)
        if (key & 0xFF) == ord('q'):
            break
    cap.release()
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

录制视频

def test4():
    fourcc = cv2.VideoWriter_fourcc(*'MJPG')
    vw = cv2.VideoWriter('./out.mp4', fourcc, 25, (1920, 1080))
    cap = cv2.VideoCapture(0)

    # 判断摄像头是否打开状态
    while cap.isOpened():
        ret, frame = cap.read()
        if ret == True:
            cv2.imshow('video', frame)

            # 写数据到多媒体文件
            vw.write(frame)

            key = cv2.waitKey(10)
            if (key & 0xFF) == ord('q'):
                break
        else:
            break
    cap.release()
    cv2.release()
    cv2.destroyAllWindows()
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

在这里插入图片描述
在这里插入图片描述

import cv2


# 色彩空间
# hsv:色相、饱和度、明亮度
# yuv:4:2:0 4:2:2 4:4:4

def callback():
    pass

def test7():
    cv2.namedWindow('color',cv2.WINDOW_NORMAL)
    img = cv2.imread('./1.png')
    colorspaces = [cv2.COLOR_BGR2RGBA,cv2.COLOR_BGR2BRGA,
                   cv2.COLOR_BGR2GRAY,cv2.COLOR_BGR2HSV_FULLL,
                   cv2.COLOR_BGR2YUV]
    cv2.createTrackbar('curcolor','color',0,len(colorspaces),callback)
    while True:
        index = cv2.getTrackbarPos('curcolor','color')
        cvt_img = cv2.cvtColor(img,colorspaces[index])
        cv2.imshow('color',img)
        key = cv2.waitKey(10)
        if key & 0xFF == ord('q'):
            break
    cv2.destroyAllWindows()

if __name__ == '__main__':
    test7()
  • 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

4、Mat的使用

深拷贝和浅拷贝

void test3()
{
    Mat img,m1,m2;
    img = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    // 克隆,深拷贝
    m1 = img.clone();
    img.copyTo(m2);

    // 创建空白图像
    Mat m3 = Mat::zeros(Size(8,8),CV_8UC1);
    // 创建全部为1的通道,如果创建的是3通道,则只有每个元素的第一个通道为1
    Mat m4 = Mat::ones(Size(8,8),CV_8UC3);
    cout<<m3.cols<<m3.rows<<m3.channels()<<endl;

    // 3通道时候:每个元素的第一个通道为1
    m4 = 127;
    // 给3个通道都复值
    m4 = Scalar(127,127,127);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

像素值操作:
1、根据下标操作
2、根据指针操作

void test4()
{
    // 像素遍历操作
    namedWindow("new",WINDOW_NORMAL);
    Mat img,gray;
    img = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    for(int i=0;i<img.rows;i++){
        for(int j=0;j<img.cols;j++){
            Vec3b bgr = img.at<Vec3b>(i,j);
            img.at<Vec3b>(i,j)[0] = 255 - bgr[0];
            img.at<Vec3b>(i,j)[1] = 255 - bgr[1];
            img.at<Vec3b>(i,j)[2] = 255 - bgr[2];
        }
    }
    imshow("new",img);
    waitKey(0);

    cvtColor(img,gray,cv::COLOR_BGR2GRAY);
    for(int i=0;i<gray.rows;i++){
        for(int j=0;j<gray.cols;j++){
            int pt = img.at<uchar>(i,j);
            img.at<uchar>(i,j) = 255 - pt;
        }
    }
    imshow("new",gray);
    waitKey(0);

    // 基于指针访问 速度更快
    for(int i=0;i<img.rows;i++){
        uchar *current_row = img.ptr<uchar>(i);
        for(int j=0;j<img.cols;j++){
            if(img.channels() == 1){
                int pt = *current_row;
                *current_row++ = 255 - pt;
            }else if(img.channels() == 3){
                *current_row++ = 255 - *current_row;
                *current_row++ = 255 - *current_row;
                *current_row++ = 255 - *current_row;
            }
        }
    }
    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

像素值的加减乘除

void test5()
{
    // 像素操作
    namedWindow("new",WINDOW_NORMAL);
    Mat img,gray;
    img = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");

    Mat m = Mat::zeros(img.size(),img.type());
    m = Scalar(2,2,2);
    Mat dst;

    // 乘法运算,超过255的时候会截断
    multiply(img,m,dst);

    // 加法操作
    add(img,m,dst);

    // 减法
    subtract(img,m,dst);

    // 除法
    divide(img,m,dst);

    // saturate_cast会做一个范围判定,是否在uchar范围内
    // saturate_cast<uchar>(p1,p2);
    imshow("new",dst);
    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

GUI操作

void on_trac(int val,void *userdata)
{
    Mat *data = (Mat*)userdata;
    Mat m = Mat::zeros(data->size(),data->type());
    m = Scalar(val,val,val);

    Mat dst;
    add(*data,m,dst);
    imshow("new",dst);
}

void test6()
{
    namedWindow("new",WINDOW_AUTOSIZE);
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    int maxValue = 100;
    int light = 50;
    cout<<src.type()<<endl;
    createTrackbar("Value Bar","new",&light,maxValue,on_trac,(void *)(&src));
    on_trac(50,&src);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

addWeighted函数解释

dst = src1 * alpha + src2 * beta + gamma
  • 1

5、异或操作

// 异或操作
void test7()
{
    Mat m1 = Mat::zeros(Size(255,255), CV_8UC3);
    Mat m2 = Mat::zeros(Size(255,255), CV_8UC3);
    rectangle(m1,Rect(100,100,80,80),Scalar(255,255,0),-1,LINE_8,0);
    rectangle(m2,Rect(130,130,80,80),Scalar(255,0,255),-1,LINE_8,0);
    imshow("new",m1);
    waitKey(0);
    imshow("new",m2);
    waitKey(0);

    Mat dst;
    // 按位与(都为真就为真)
    bitwise_and(m1,m2,dst);
    imshow("new",dst);
    waitKey(0);

    // 按位或(一个为真就为真)
    bitwise_or(m1,m2,dst);
    imshow("new",dst);
    waitKey(0);

    // 按位取反
    bitwise_not(m1,dst);
    imshow("new",dst);
    waitKey(0);

    // 异或(不同为真,相同为假)
    bitwise_xor(m1,m2,dst);
    imshow("new",dst);
    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

6、通道分离,通道合并

// 通道分离,通道合并
void test8()
{
    namedWindow("new",WINDOW_AUTOSIZE);
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    imshow("new",src);
    waitKey(0);
    vector<Mat> mv;
    split(src,mv);
    imshow("new",mv[0]);
    waitKey(0);
    imshow("new",mv[1]);
    waitKey(0);
    imshow("new",mv[2]);
    waitKey(0);

    // 将蓝色和绿色通道全部置0,只保留红色通道,然后合并
    mv[0] = 0;
    mv[1] = 0;
    Mat dst;
    merge(mv,dst);
    imshow("new",dst);
    waitKey(0);

    // 通道融合
    // 第三个参数,0通道到2通道,1通道到1通道,2通道到0通道
    int from_to[] = {0,2,1,1,2,0};
    mixChannels(&src,1,&dst,1,from_to,3);
    imshow("new",dst);
    waitKey(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

7、色彩空间转换

// 色彩空间转换
void test9()
{
    namedWindow("new",WINDOW_AUTOSIZE);
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/green.jpg");
    Mat dst,mask;
    cvtColor(src,dst,COLOR_BGR2HSV);

    // 出来一张2值化的图像,绿色背景是白色,人物是黑色
    inRange(dst,Scalar(35,43,46),Scalar(77,255,255),mask);

    Mat redback = Mat::zeros(src.size(),src.type());
    redback = Scalar(40,40,200);

    // 取放之后人物酒变成白色了,背景是黑色的
    bitwise_not(mask,mask);

    // 只拷贝不为0的区域,为0的区域不拷贝
    src.copyTo(redback,mask);
    imshow("new",redback);
    waitKey(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

8、最大值、最小值

// 最大值、最小值
// 均值,方差
// 方差衡量数据集中值的离散程度,数据集中差异较小时,标准方差较小,差异较大时,方差较大(携带有效信息就越多)
// 1、计算出均值
// 2、计算每个元素与平均值的差的平方
// 3、计算平方差的平均值,得到的平方差相加,然后除以数据点的总数
// 4、取平方根:将平均平方差的值进行平方根运算,得到标准方差。

void test10()
{
    namedWindow("new",WINDOW_AUTOSIZE);
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/green.jpg",cv::IMREAD_GRAYSCALE);
    double minv,maxv;
    Point minLoc,maxLoc;
    minMaxLoc(src,&minv,&maxv,&minLoc,&maxLoc,Mat());
    cout<<"minv:"<<minv<<" maxv:"<<maxv<<" minLoc:"<<minLoc<<" maxLoc:"<<maxLoc<<endl;
    Mat mean,stddev;
    meanStdDev(src,mean,stddev);
    cout<<mean<<" "<<stddev<<endl;
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

9、绘制图像

void test11()
{
    namedWindow("new",WINDOW_AUTOSIZE);
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/green.jpg",cv::IMREAD_COLOR);
    // 绘制矩形
    Mat bg = Mat::zeros(src.size(),src.type());
    Mat dst;
    Rect rect(100,100,200,200);
    rectangle(bg,rect,Scalar(20,20,255),2,LINE_8,0);

    // 绘制圆,圆心坐标Point,15是半径
    circle(src,Point(350,400),150,Scalar(255,0,0),2,LINE_8,0);
    // 图像融合
    addWeighted(src,0.7,bg,0.3,0,dst);

    // 绘制线 两个点的位置
    line(bg,Point(100,100),Point(350,400),Scalar(0,255,0),2,LINE_8,0);

    // 绘制椭圆
    RotatedRect rrt;
    rrt.center = Point(200,200);
    rrt.size = Size(100,200);
    rrt.angle = 0;
    ellipse(src,rrt,Scalar(0,255,255),2,8);
    imshow("new",dst);
    waitKey(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

10、多边形绘制

// 多边形绘制
void test13()
{
    Mat bg = Mat::zeros(Size(512,512),CV_8UC3);
    Point p1(100,200);
    Point p2(350,100);
    Point p3(450,280);
    Point p4(320,450);
    Point p5(80,400);
    vector<Point> pts;
    pts.push_back(p1);
    pts.push_back(p2);
    pts.push_back(p3);
    pts.push_back(p4);
    pts.push_back(p5);
    polylines(bg,pts, true,Scalar(0,0,255),2,LINE_8,0);
    // 填充多边形
//    fillPoly()
    imshow("new",bg);
    waitKey(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

11、随机数

void test12()
{
    Mat bg = Mat::zeros(Size(512,512),CV_8UC3);
    int w = bg.cols;
    int h = bg.rows;
    // 随机数
    RNG rng(12345);
    while (true){
        int c = waitKey(10);
        if(c == 27) break;
        int x1 = rng.uniform(0,w);
        int y1 = rng.uniform(0,h);
        int x2 = rng.uniform(0,w);
        int y2 = rng.uniform(0,h);
        // 每次画之前清理一下
        bg = Scalar(0,0,0);
        line(bg,Point(x1,y1),Point(x2,y2),
             Scalar(rng.uniform(0,255)
             ,rng.uniform(0,255),rng.uniform(0,255)),1,LINE_AA,0);
        imshow("new",bg);
    }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22

12、鼠标实时绘制矩形

Point sp(-1, -1);
Point ed(-1, -1);
Mat temp;

static void on_draw(int event, int x, int y, int flags, void* userdata)
{
    Mat img = *((Mat*)userdata);
    if (event == EVENT_LBUTTONDOWN) {
        sp.x = x;
        sp.y = y;
        cout << "sp.x:" << sp.x << " sp.y:" << sp.y << endl;
    }
    else if (event == EVENT_LBUTTONUP) {
        ed.x = x;
        ed.y = y;
        cout << "ed.x:" << ed.x << " ed.y:" << ed.y << endl;
        int x1 = x - sp.x;
        int y1 = y - sp.y;
        if (x1 > 0 && y1 > 0) {
            Rect rect(sp.x, sp.y, x1, y1);
            temp.copyTo(img);
            imshow("ROI区域", img(rect));
            rectangle(img,rect,Scalar(0,0,255),2,8,0);
            imshow("new", img);
            sp.x = -1;
            sp.y = -1;
        }
    }
    else if (event == EVENT_MOUSEMOVE) {
        if (sp.x > 0 && sp.y > 0) {
            ed.x = x;
            ed.y = y;
            int dx = ed.x - sp.x;
            int dy = ed.y - sp.y;
            if (dx > 0 && dy > 0) {
                Rect box(sp.x, sp.y, dx, dy);
                temp.copyTo(img);
                rectangle(img, box, Scalar(0, 0, 255), 2, 8, 0);
                imshow("new", img);
            }
        }
    }
}

void test14()
{
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    namedWindow("new", WINDOW_AUTOSIZE);
    setMouseCallback("new", on_draw, (void*)&src);
    src.copyTo(temp);
    imshow("new", src);
    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
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54

13、归一化

13、normalize归一化原理解释
函数原型,归一化类型,常见的有NORM_L1, NORM_L2, NORM_INF, NORM_MINMAX,不同的norm计算方式不一样

注意:
alpha:如果norm_type为NORM_MINMAX ,则alpha为最小值或最大值;如果norm_type为其他类型,则为归一化要乘的系数
beta:如果norm_type为NORM_MINMAX ,则beta为最小值或最大值;如果norm_type为其他类型,beta被忽略,此处不会被用到,一般传入0【重点】

void cv::normalize    (    
	InputArray     src,
	InputOutputArray     dst,
	double     alpha = 1,
	double     beta = 0,
	int     norm_type = NORM_L2,
	int     dtype = -1,
	InputArray     mask = noArray() 
)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

13.1、NORM_L1

输入值:
[2, 3, 4;
 3, 4, 5;
 4, 5, 6]
 
输出结果:
[0.055555556, 0.083333336, 0.11111111;
 0.083333336, 0.11111111, 0.1388889;
 0.11111111, 0.1388889, 0.16666667]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

1、先求出norm
公式:L1_norm = ∥src1∥L1=∑I|src1(I)|,即各数的绝对值之和
2+3+4+5+3+4+5+4+5+6 = 36

2、再求dst
公式:dst = alphasrc/L1_norm
dst[0] = 1
2/ 36 = 0.055555556

应用场景:机器学习、信号处理、图像去噪。
用途:L2规范化倾向于使得所有元素的大小都差不多,这在避免过拟合(如机器学习中的权重衰减)、信号去噪(通过最小化能量)等场景中非常有用。
优势:防止数据中的个别点对总体结果产生过大影响,提高模型的泛化能力。

13.2、NORM_L2

输出结果:
 [0.16012815, 0.24019222, 0.32025629;
 0.24019222, 0.32025629, 0.40032035;
 0.32025629, 0.40032035, 0.48038444]
  • 1
  • 2
  • 3
  • 4

1、先求出norm
公式:L2_norm = 矩阵中各元素的平方和的开方(欧几里德)(L2-范数)
4+9+16+9+16+25+16+25+36 = 开平方之后 = 12.4899959967968

2、再求dst
公式:dst = alphasrc/L2_norm
dst[0] = 1
2/ 12.4899959967968= 0.16012815

应用场景:稀疏编码、特征选择、压缩感知。
用途:L1规范化倾向于产生稀疏的解,即大部分元素为零,少数元素非零。这在特征选择中特别有用,可以自动地忽略不重要的特征。
优势:促进稀疏性,有助于提高模型的可解释性,减少模型复杂度。

13.3、NORM_INF

输出结果:
 [0.33333334, 0.5, 0.66666669;
 0.5, 0.66666669, 0.83333337;
 0.66666669, 0.83333337, 1]
  • 1
  • 2
  • 3
  • 4

1、先求出norm
INF_norm = 矩阵中所有元素的最大值max

2.、再求dst
公式:dst = alphasrc/INF_norm
dst[0] = 1.0
2/ 6 = 0.33333334

应用场景:优化问题、数值计算。
用途:限制数组或向量的无穷范数(即最大绝对值)。在某些优化问题中,可能需要将解的范围限制在一定界限内,使用NORM_INF规范化可以达到这一目的。
优势:保证了数据中没有极端的大值,有利于数值稳定性

13.4、NORM_MINMAX
1、 alpha、beta分别为归一化后的最小值、最大值
函数会自动判断哪个为最大值,最小值
在这里插入图片描述

输出结果: [0, 0.25, 0.5;
 0.25, 0.5, 0.75;
 0.5, 0.75, 1]
 
 min=2,max=6
 (1-0) * (2-2)/(6-2) + 0 =0
 (1-0) * (3-2)/(6-2) + 0 = 0.25
 (1-0) * (4-2)/(6-2) + 0 = 0.25

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

应用场景:图像处理、数据预处理。
用途:将数据规范化到一个特定的范围内,常用于调整图像亮度和对比度,或在输入机器学习模型前将特征缩放到同一范围。这有助于提高算法的收敛速度和精度。
优势:简单直观,易于实现,可以使数据分布更均匀。

总结:

NORM_MINMAX适用于需要将数据规范化到相同尺度的场景。
NORM_INF适用于需要控制数据的最大值不超过特定阈值的场景。
NORM_L1适用于需要稀疏解的场景,如特征选择或构建稀疏模型。
NORM_L2适用于需要平滑解、防止过拟合的场景,如机器学习中的正则化处理。

// 1、MINMAX
void test15()
{
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    namedWindow("new", WINDOW_AUTOSIZE);

    Mat img,img2;
    cout<<src.type()<<endl;
    src.convertTo(src,CV_32FC3);
    cout<<src.type()<<endl;

    normalize(src,img,1.0,0,NORM_MINMAX);
    // imshow想显示浮点数据,必须归一化0-1之间
    imshow("new",img);
    normalize(img,img2,255.0,0,NORM_MINMAX);
    img2.convertTo(img2,CV_8UC3);
    imshow("new1",img2);
    waitKey(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

14、resize操作

// resize
void test16()
{
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    namedWindow("new", WINDOW_AUTOSIZE);

    Mat zoomin,zoomout;
    int w = src.cols;
    int h = src.rows;
    resize(src,zoomin,Size(w/2,h/2),0,0,INTER_LINEAR);
    resize(src,zoomout,Size(w*1.5,h*1.5),0,0,INTER_LINEAR);
    imshow("1",src);
    imshow("2",zoomin);
    imshow("3",zoomout);
    waitKey(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16

15、旋转翻转

void test17()
{
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
//    namedWindow("new", WINDOW_AUTOSIZE);
    Mat dst,left,m1,m3;
    flip(src,dst,0);
    flip(src,left,1);
    flip(src,m1,-1);
    imshow("上下翻转",dst);
    imshow("左右翻转",left);
    imshow("180度旋转",m1);
    waitKey(0);

    // 图像旋转
    Mat d,M;
    int w = src.cols;
    int h = src.rows;
    M = getRotationMatrix2D(Point2f(w/2,h/2),45,1.0);
    double cos = abs(M.at<double>(0,0));
    double sin = abs(M.at<double>(0,1));

    int nw = cos * w + sin * h;
    int nh = sin * w + cos * h;
    M.at<double>(0,2) += (nw/2-w/2);
    M.at<double>(1,2) += (nh/2-h/2);
    warpAffine(src,d,M,Size(nw,nh),INTER_LINEAR,0,Scalar(0,0,255));
    imshow("new",d);
    waitKey(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

16、视频操作


void test18()
{
    VideoCapture cap(0);
    Mat fram;
    while(true){
        cap.read(fram);
        if(fram.empty()){
            break;
        }
        flip(fram,fram,1);
        imshow("new",fram);
        int n = waitKey(10);
        if(n == 27){
            break;
        }
    }
    cap.release();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

17、模糊操作

// 图像卷积模糊操作
void test19()
{
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    namedWindow("new", WINDOW_AUTOSIZE);
    Mat dst;
    blur(src,dst,Size(3,3),Point(-1,-1));
    imshow("new",dst);
    waitKey(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

18、高斯模糊操作

// 高斯双边模糊
void test21()
{
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    namedWindow("new", WINDOW_AUTOSIZE);
    Mat dst;
    bilateralFilter(src,dst,0,100,10);
    imshow("new",dst);
    waitKey(0);
}

// 高斯模糊
void test20()
{
    Mat src = imread("/Users/zhulei/CLionProjects/opencv_test/1.jpg");
    namedWindow("new", WINDOW_AUTOSIZE);
    Rect rect(100,100,500,500);
    Mat roi = src(rect);
    GaussianBlur(roi,roi,Size(0,0),15);
    roi.copyTo(src(rect));
    imshow("new",src);
    waitKey(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23

在这里插入图片描述

19、边缘填充:cv::copyMakeBorder()

作用:在图像四周填充指定像素形成边框。

在这里插入图片描述

利用此函数,对图片进行padding操作

void test5()
{
	int width = 384,height = 226;
	Mat img = imread("H:\\face\\_00067cfb-e535423e.jpg");
	
	float rw = (float)width/img.cols;
	float rh = (float)height/img.rows;
	cout << "rw:" << rw << " rh:" << rh << endl;
	
	float min_ratoi = min(rw, rh);
	// 384 * 216
	 resize(img, img, Size(img.cols *min_ratoi, img.rows *min_ratoi));
	//resize(img, img, Size(),min_ratoi,min_ratoi);
	imshow("new", img);

	// 边缘填充
	int delta_width = width - img.cols;
	int delta_height = height - img.rows;
	int top = delta_height/2;
	int bottom = delta_height -top;
	int left = delta_width/2;
	int right = delta_width-left;
	cout << "del_width:" << delta_width << "del_height:" << delta_height << endl;
	cout <<"top:"<< top <<"bottom:" << bottom<<"left:" << left<<"right:" << right << endl;
	
	RNG rng;
	Scalar color = Scalar(rng.uniform(0, 255), rng.uniform(0, 255), rng.uniform(0, 255));

	Mat m1, m2, m3, m4, m5, m6,m7,m8;
	// 使用常数填充
	copyMakeBorder(img, m1, top, bottom, left, right, BORDER_CONSTANT, color);
	// 复制最边缘像素
	copyMakeBorder(img, m2, top, bottom, left, right, BORDER_REPLICATE, color);
	// 反射法。以两边为轴
	copyMakeBorder(img, m3, top, bottom, left, right, BORDER_REFLECT, color);
	// 环绕式填充
	copyMakeBorder(img, m4, top, bottom, left, right, BORDER_WRAP, color);
	// 反射(Reflect)边缘填充,也称为反射101。图像的边缘像素被复制,并且第二边缘内部的像素被镜像填充。
	copyMakeBorder(img, m5, top, bottom, left, right, cv::BORDER_REFLECT_101, color);
	// 反射(Reflect)边缘填充,图像的边缘像素被复制,并且第二边缘内部的像素被镜像填充
	copyMakeBorder(img, m6, top, bottom, left, right, cv::BORDER_REFLECT101, color);
	// 默认边缘填充方式
	copyMakeBorder(img, m7, top, bottom, left, right, cv::BORDER_DEFAULT, color);
	// 孤立边缘填充。每个通道被独立填充,而不考虑其他通道。
	copyMakeBorder(img, m8, top, bottom, left, right, cv::BORDER_ISOLATED, color);
	
	imshow("new1", m1);
	imshow("new2", m2);
	imshow("new3", m3);
	imshow("new4", m4);
	imshow("new5", m5);
	imshow("new6", m6);
	imshow("new7", m7);
	imshow("new8", m8);
	waitKey(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

20、adaptiveThreshold函数

在这里插入图片描述

#include <opencv2/imgproc.hpp>
函数说明:void cv::adaptiveThreshold( InputArray src, OutputArray dst, double maxValue, int adaptiveMethod, int thresholdType, int blockSize, double C );
输入参数:
				src					8位单通道图像。
				dst					与src大小和类型相同的目标图像。
				maxValue			指定给满足条件的像素的非零值
				adaptiveMethod		自适应阈值算法。BORDER_REPLICATE|BORDER_ISOLATED用于处理边界。
							cv::ADAPTIVE_THRESH_MEAN_C = 0 			窗口均值阈值法。计算出领域的平均值再减去参数double C的值
							cv::ADAPTIVE_THRESH_GAUSSIAN_C = 1		高斯分布阈值法。计算出领域的高斯均值再减去参数double C的值
				thresholdType		阈值化类型(只有两个取值)。
							cv::THRESH_BINARY = 0 				若大于thresh,则设置为maxval,否则设置为0。(常用)
							cv::THRESH_BINARY_INV = 1 			若大于thresh,则设置为0,否则设置为maxval(反操作)。
				blockSize			像素邻域大小(单位):357,依此类推。自适应阈值算法的阈值计算时使用。
				C					偏移值。自适应阈值算法的阈值计算时使用。

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
void test6()
{
	Mat img = imread("H:\\face\\_00067cfb-e535423e.jpg");
	resize(img, img, Size(384, 226));
	Mat srcGray;
	cvtColor(img, srcGray, COLOR_BGR2GRAY);
	cv::Mat ADAPTIVE_THRESH_MEAN_C0, ADAPTIVE_THRESH_MEAN_C1, ADAPTIVE_THRESH_GAUSSIAN_C0, ADAPTIVE_THRESH_GAUSSIAN_C1;
	int blockSize = 5;
	int constValue = 10;
	const int maxVal = 255;
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_MEAN_C0, maxVal, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, constValue);
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_MEAN_C1, maxVal, cv::ADAPTIVE_THRESH_MEAN_C, cv::THRESH_BINARY, blockSize, constValue);
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_GAUSSIAN_C0, maxVal, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, blockSize, constValue);
	cv::adaptiveThreshold(srcGray, ADAPTIVE_THRESH_GAUSSIAN_C1, maxVal, cv::ADAPTIVE_THRESH_GAUSSIAN_C, cv::THRESH_BINARY_INV, blockSize, constValue);

	imshow("new1", ADAPTIVE_THRESH_MEAN_C0);
	imshow("new2", ADAPTIVE_THRESH_MEAN_C1);
	imshow("new3", ADAPTIVE_THRESH_GAUSSIAN_C0);
	imshow("new4", ADAPTIVE_THRESH_GAUSSIAN_C1);
	waitKey(0);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21

21、仿射变化之平移

第一行的(1,0,tx)和(x,y,1)一一对应相乘

在这里插入图片描述

# 仿射变化之平移
def test3():
    img = cv2.imread("./1.png")
    h,w,ch = img.shape
    # 平移操作
    # 变换矩阵,最少是float32
    M = np.float32([[1,0,200],[0,1,0]])
    img = cv2.warpAffine(img,M,dsize=(w,h))
    cv2.imshow("new",img)
    cv2.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

22、仿射变化之获取变换矩阵,旋转矩阵

# 仿射变化之获取变换矩阵,旋转矩阵
# opencv提供获取变换矩阵的api
def test4():
    img = cv2.imread("./1.png")
    h,w,ch = img.shape
    # 获取变换矩阵
    # 按住(100,100)这个点,逆时钟旋转15度,1表示不缩放
    M = cv2.getRotationMatrix2D((100,100),15,1)
    print(M)
    img = cv2.warpAffine(img,M,dsize=(w,h))
    cv2.imshow("new",img)
    cv2.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

23、通过三个点来确定变换矩阵

# 通过三个点来确定变换矩阵
def test5():
    img = cv2.imread("./1.png")
    h,w,ch = img.shape
    src = np.float32([[200,100],[300,100],[200,300]])
    dst = np.float32([[100,150],[360,200],[280,120]])
    M = cv2.getAffineTransform(src,dst)
    print(M)
    img = cv2.warpAffine(img,M,dsize=(w,h))
    cv2.imshow("new",img)
    cv2.waitKey(0)

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12

24、透视变换

透视变换就是一种坐标系变换成另一种坐标系,简单来说可以把一张斜的图变正

# 透视变换
# 透视变换就是一种坐标系变换成另一种坐标系,简单来说可以把一张斜的图变正
def test6():
    img = cv2.imread("./1.png")
    h,w,ch = img.shape

    # 获取变换矩阵
    src = np.float32([[100,120],[300,120],[100,300],[300,300]])
    dst = np.float32([[0,0],[400,0],[0,300],[400,300]])
    M = cv2.getPerspectiveTransform(src,dst)
    img = cv2.warpPerspective(img,M,dsize=(400,300))
    cv2.imshow("new",img)
    cv2.waitKey(0)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

25、卷积核

(1 * 1+1 * 0+1 * 1) + (0 * 0+1 * 1+1*0) + (0 * 1+0 * 0+1 * 1) = 4
在这里插入图片描述

计算padding填充多少

h2是卷积之后的高度,h1是原图像高度,f卷积核高度,p是填充多少,s是卷积核步长
h2 = (h1 - F + 2p)/s + 1

计算一下
// 在不进行填充的情况下,5*5的图像,在经过3*3卷积之后的结果就变成3*3了5-3+0/1 + 1 = 3

// 如果想保证原图像不变就需要进行padding操作
5 = (5-3+2p)/1 + 1
p = 1
// 所以需要在原图像周围补充一圈,具体补充什么值可以通过borderType进行设置
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

26、方盒、均值滤波

使用常量进行填充,卷积核里面

borderType   描述要添加哪种边框。
(1)BORDER_REPLICATE   :复制法,即用复制最边缘像素。
(2)BORDER_REFLECT     :反射法,对感兴趣的图像中的像素在两边进行复制。例如:fedcba | abcdefgh | hgfedcb
(3)BORDER_REFLECT_101 :反射法,即用以最边缘像素为轴,对称。gfedcb | abcdefgh | gfedcba
(4)BORDER_WRAP        :外包装法。abcdefgh | abcdefgh | abcdefgh
(5)BORDER_CONSTANT    :常量法,即用常数值填充


Mat img = (Mat_<uint8_t>(5, 5) << 3, 5, 7, 9, 2
	, 13, 15, 71, 19, 12
	, 23, 25, 27, 29, 22
	, 1, 4, 2, 5, 6
	, 31, 51, 21, 12, 7);
Mat dst, dst2;
blur(img, dst, Size(3, 3),Point(-1,-1),BORDER_CONSTANT);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

输出结果:
dst:[ 4, 13, 14, 13, 5;
9, 21, 23, 22, 10;
9, 20, 22, 21, 10;
15, 21, 20, 15, 9;
10, 12, 11, 6, 3]
在这里插入图片描述

默认使用的是BORDER_REFLECT_101 ,即用以最边缘像素为轴,进行复制,所以结果就是一下结果

	Mat img = (Mat_<uint8_t>(5, 5)<<3,5,7,9,2, 13
								, 15, 71, 19, 12
								,23, 25, 27, 29, 22
								,1, 4, 2, 5, 6
								,31, 51,21, 12, 7);
	Mat dst,dst2;
	blur(img, dst, Size(3, 3));
	boxFilter(img, dst2, -1, Size(3, 3));
	cout << "dst:" << dst << endl;
	cout << "dst2:" << dst2 << endl;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

在这里插入图片描述

在这里插入图片描述

27、高斯滤波

在这里插入图片描述

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

28、中值滤波

原理:取卷积核的中位数,作为卷积核的结果,当卷积核在图像上进行移动,获取到的数之后进行排序,取中位数作为卷积核的结果。

注意:默认中值滤波默认填充方式是,使用边界像素的复制来进行填充
在这里插入图片描述

	Mat img = (Mat_<uint8_t>(5, 5) << 3, 5, 7, 9, 2
		, 13, 15, 71, 19, 12
		, 23, 25, 27, 29, 22
		, 1, 4, 2, 5, 6
		, 31, 51, 21, 12, 7);
	Mat dst, dst2;
	medianBlur(img, dst, 3);

	cout << "dst:" << dst << endl;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

输出结果:
dst:[ 5, 7, 9, 9, 9;
13, 15, 19, 19, 12;
13, 15, 19, 19, 12;
23, 23, 21, 12, 7;
31, 21, 12, 7, 7]

29、sobel算子

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

30、scharr算子

在这里插入图片描述

31、拉普拉多算子

边缘处的二阶导数=0,我们可以利用这一特征去寻找图像的边缘,注意:二阶导数为0的位子也可能是无意义的位置(噪声)

在这里插入图片描述

在这里插入图片描述

32、canny算子

在这里插入图片描述
在这里插入图片描述

33、腐蚀、膨胀操作

膨胀操作时腐蚀操作相反的操作,基本原理只要保证卷积核的锚点为0,其他部位不管是否为0,都会变成非0

在这里插入图片描述
在这里插入图片描述

    Mat img = imread("E:\\chrome_down\\yolov5-master\\data\\images\\out0.jpg",IMREAD_GRAYSCALE);
    Mat dst;
    // 获取形态学卷积核
    Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));
    // 腐蚀操作 去除图像边缘信息,会让图片变瘦
    Mat kernel = Mat::ones(Size(3, 3), CV_8UC1);
    erode(img, dst, kernel);
	
	// 膨胀操作,会让图片变胖
    Mat dst2;
    dilate(img, dst2, kernel1);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11

34、开运算、闭运算

开运算:腐蚀之后在膨胀(提供另一种,去除图像外噪声的思路)
闭运算:膨胀之后腐蚀 去除图像内部噪声
在这里插入图片描述

    Mat img = imread("E:\\chrome_down\\yolov5-master\\data\\images\\out0.jpg", IMREAD_GRAYSCALE);
    Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));

    Mat dst,dst1;
    // 开运算:腐蚀之后在膨胀(提供另一种,去除噪声的思路)
    morphologyEx(img, dst, MORPH_OPEN, kernel1);

    // 闭运算:膨胀之后腐蚀 去除图像内部噪声
    morphologyEx(img, dst1, MORPH_CLOSE, kernel1);

    imshow("new", dst);
    imshow("new", dst1);
    waitKey(0);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

35、形态学梯度

梯度 = 原图 - 腐蚀之后图
腐蚀之后的图变小, 原图减去腐蚀之后的图就会的到,腐蚀掉的边缘信息

    Mat img = imread("E:\\chrome_down\\yolov5-master\\data\\images\\out0.jpg", IMREAD_GRAYSCALE);
    Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));
    Mat dst;
    morphologyEx(img, dst, MORPH_GRADIENT, kernel1);
    imshow("new", dst);
    waitKey(0);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

36、顶帽操作

顶帽 = 原图 - 开运算(腐蚀+膨胀)
开运算是去除外图形外的噪音,原图减去开运算就得到,去除的外噪声

    Mat img = imread("E:\\chrome_down\\yolov5-master\\data\\images\\out0.jpg", IMREAD_GRAYSCALE);
    Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));
    Mat dst;
    morphologyEx(img, dst, MORPH_TOPHAT, kernel1);
    imshow("new", dst);
    waitKey(0);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

37、黑帽操作

黑帽操作 = 原图 - 闭运算(膨胀 + 腐蚀)
闭运算是去除图形内部的噪声,原图减去闭运算就得到,去除的内噪声

    Mat img = imread("E:\\chrome_down\\yolov5-master\\data\\images\\out0.jpg", IMREAD_GRAYSCALE);
    Mat kernel1 = getStructuringElement(MORPH_RECT, Size(5, 5));
    Mat dst;
    morphologyEx(img, dst, MORPH_BLACKHAT, kernel1);
    imshow("new", dst);
    waitKey(0);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

38、图像轮廓

图像轮廓是具有相同颜色灰度连续的轮廓点的曲线

在这里插入图片描述
从右到左,从里到外
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

    vector<vector<Point>> threshCnts;
    findContours(image_2_CLOSE.clone(), threshCnts, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
  • 1
  • 2

39、图像金字塔

在这里插入图片描述
在这里插入图片描述

在这里插入图片描述

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/天景科技苑/article/detail/916068
推荐阅读
相关标签
  

闽ICP备14008679号