zl程序教程

您现在的位置是:首页 >  后端

当前栏目

C++ opencv入门学习

2023-09-14 09:07:10 时间

图像和视频数据的读取
图像数据通过imread(path)来读取矩阵数据,视频是通过VideoCapture返回一个视频的输入流,从而可以一帧一帧的读取图像。 正如python中opencv通过numpy存储数据,c++中的图像数据通过一个简陋的矩阵类Mat类来存储图片数据。

int show_img(string &img_path) {
    Mat src = imread(path, 1);
    if (src.empty()) {
        printf("open pic failed");
        return -1;
    }
    cout << src.channels() << src.size() << endl;
    namedWindow("12", WINDOW_FREERATIO);   //定义一个可以自由缩放大小的窗
    imshow("12", src);                     //名字需要一样才能放在一起
    waitKey();
    destroyAllWindows();
    return 0;
}
void show_video(string &video_path) {
    VideoCapture cap;
    cap.open(path);
    if (!cap.isOpened()) {
        cout << "fail" << endl;
    }
    Mat img;
    while (true) {
        bool a = cap.read(img);
        if (!a) {
            break;
        }
        imshow("1", img);
        waitKey(1);
    }
    destroyAllWindows();
}

通过查看Mat类的构造函数

Mat(int rows, int cols, int type);
Mat(Size size, int type);
Mat(int rows, int cols, int type, const Scalar& s);
//例如
Mat a(Size(512,512), CV_8UC3, Scalar(0,0,255));

type:CV_8UC1, …, CV_64FC4,指明构造的位深和通道数,Size()矩阵大小,Scalar是图像的颜色(0,0,255)红色,还有许多种构造方法,用的时候查一下就可以了

裁剪图像
resize图像 裁切图像需要定义裁切的大小,用到了模板类Rect,可以在定义中找到构造函数,一般情况下Point指的的是左上角的点,Size是(width, height),分别为宽和高

void crop_img(string& path) {
    Mat img = imread(path);
    Mat imgcrop, resize_img;
    Rect roi(Point(100, 100), Size(200, 300));
    resize(img, resize_img, Size(), 0.5, 0.5, INTER_NEAREST);
    imgcrop = img(roi);
    imshow("1", img);
    imshow("1.5", resize_img);
    imshow("2", imgcrop);
    waitKey();
    destroyAllWindows();
}

轮廓检测
进行轮廓检测前,通常需要一些预处理操作,灰度处理或者阈值处理,进行高斯模糊(GaussianBlur),使用canny算子计算轮廓,之后利用用膨胀或者腐蚀操作使得轮廓尽可能是闭合的。

void preprocessing_(string &path) {

    Mat img = imread(path);
    Mat img_gray, img_Blur, img_Canny, img_Dil;

    cvtColor(img, img_gray, COLOR_BGR2GRAY);
    GaussianBlur(img_gray, img_Blur, Size(3, 3), 3, 0);
    Canny(img_Blur, img_Canny, 25, 75);

    Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3)); //膨胀的核大小
    dilate(img_Canny, img_Dil, kernel);
    //imshow("1", img_Dil);
    getContours(img_Dil, img);
    //画出轮廓
}

需要注意轮廓数据得保存格式vector>,可能会用到的一些函数arcLength可以计算轮廓得周长,contourArea计算轮廓的面积,通过这两个函数可以过滤掉一些不必要得噪声轮廓。approxPolyDP函数用于返回一个近似得多边形 画矩形框用rectangle(tl:top left, br:bottom right) 画轮廓用drawContours

void rectangle(InputOutputArray img, Point pt1, Point pt2,
                          const Scalar& color)
void drawContours( InputOutputArray image, InputArrayOfArrays contours,
                              int contourIdx, const Scalar& color)
void getContours(Mat imgDil, Mat img) {

    vector<vector<Point>> contours;      //数据存储样例 {{(1,1),(1,2),(1,3)},{(),(),()},{(),(),(),()}}
    vector<Vec4i> hierarchy;

    findContours(imgDil, contours, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
    //drawContours(img, contours, -1, Scalar(255, 0, 255), 2);

    vector<vector<Point>> conPoly(contours.size());
    vector<Rect> boundRect(contours.size());

    for (int i = 0; i < contours.size(); i++)
    {
        int area = contourArea(contours[i]);
        cout << "-------------------" << endl;
        cout << area << endl;
        string objectType;

        if (area > 1000)
        {
            float peri = arcLength(contours[i], true);   //perimeter(周长/边缘)
            approxPolyDP(contours[i], conPoly[i], 0.02 * peri, true);   //用更少的曲线来精确轮廓,所以输入和输出的数据类型应该是一致的,估计最小的角点
            cout << conPoly[i].size() << endl;

            boundRect[i] = boundingRect(conPoly[i]);  //返回包含轮廓点的最小矩形框, 

            int objCor = (int)conPoly[i].size();

            if (objCor == 3) { objectType = "Tri"; }
            else if (objCor == 4)
            {
                float aspRatio = (float)boundRect[i].width / (float)boundRect[i].height;
                cout << aspRatio << endl;
                if (aspRatio > 0.95 && aspRatio < 1.05) { objectType = "Square"; }
                else { objectType = "Rect"; }
            }
            else if (objCor > 4) { objectType = "Circle"; }

            drawContours(img, conPoly, i, Scalar(255, 0, 255), 2);
            rectangle(img, boundRect[i].tl(), boundRect[i].br(), Scalar(0, 255, 0), 5);     //画矩形框用rectangle(tl:top left, br:bottom right),画轮廓用drawContours
            //putText(img, objectType, { boundRect[i].x,boundRect[i].y }, FONT_HERSHEY_PLAIN, 1, Scalar(0, 69, 255), 2);
            putText(img, objectType, Point(boundRect[i].x, boundRect[i].y), FONT_HERSHEY_PLAIN, 1, Scalar(0, 0, 255), 1);
        }
    }
    imshow("img", img);
    waitKey();
    destroyAllWindows();
}

人脸检测
用opencv实现很简单得人脸检测

void face_detection(string &path) {
    Mat img = imread(path);
    CascadeClassifier face_detec;  //定义一个层叠的分类器(多尺度)
    face_detec.load("F:/WTY/桌面/opencv/resources/haarcascade_frontalface_default.xml");
    if (face_detec.empty()) {
        cout << "open failed" << endl;
    }
    vector<Rect> faces; 
    int minNeighbors = 3; //目标至少被检测出几次才算真的检测出来
    face_detec.detectMultiScale(img, faces, 1.1, minNeighbors);
    for (int i = 0; i < faces.size(); i++) {
        rectangle(img, faces.at(i), Scalar(0, 0, 255));
    }
    imshow("face", img);
    waitKey();
    destroyAllWindows();

}

透视变换
先计算一个仿射矩阵,把仿射矩阵用在图像得透视变换上。这里有一个需要注意的点,仿射变换在图像上的点需要固定,src和dst需要相互对应上,0->1->2->3->0始终按照这样一个顺序。通过findcontours和approxPolyDP得到的四个角点的顺序可能会不一致。

void warp_perspective(string &path) {
    Mat img = imread(path);
    float w = 200, h = 300;
    Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
    Point2f dst[4] = { {0.0f,0.0f},{w,0.0f},{0.0f,h},{w,h} };
    Mat matrix, warp_img;
    matrix = getPerspectiveTransform(src, dst);
    warpPerspective(img, warp_img, matrix, Size(200, 300));
    imshow("1", warp_img);
    waitKey();
    destroyAllWindows();
}

颜色检测
颜色检测一般在HSV颜色空间上进行,H色度(0,179),S饱和度(0,255),V明度(0,255)

void color_detection(string& path) {
    Mat img = imread(path);
    Mat hsv_img, mask;
    Size size = img.size();
    int hmin = 0, smin = 110, vmin = 153;
    int hmax = 19, smax = 240, vmax = 255;
    cvtColor(img, hsv_img, COLOR_BGR2HSV);
    namedWindow("trackbars", WINDOW_FREERATIO);
    createTrackbar("hue min", "trackbars", &hmin, 179);
    while (true) {
        Scalar lower(hmin, smin, vmin);
        Scalar upper(hmax, smax, vmax);
        inRange(hsv_img, lower, upper, mask);   //输出一个蒙版,检测在lower和upper之间的颜色
        imshow("trackbars", mask);
        imshow("img", img);
        waitKey(1);

    }
    destroyAllWindows();
}

原文:https://zhuanlan.zhihu.com/p/356982646