美文网首页收藏
4h 上手 C++ 版 Opencv

4h 上手 C++ 版 Opencv

作者: my_passion | 来源:发表于2022-02-18 22:33 被阅读0次
先上源码运行图, 分析和详细见后面
myTest.png read 网络摄像头.png Gray image.png Canny.png imageResize(0.5 0.5).png imageResize(640, 480).png [ 1706 * 1279 ] RectCrop(200, 100, 300, 300);.png [ 1706 * 1279 ] RectCrop(500, 200, 1200, 1000).png 画 Shapes 和 Text .png 图片中 4个点 => 确定1个 矩形 area.png 被标注的 srcImag.png wrapImag.png 想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework.png 想 据 colorDetectRange 检测物体 .png image.png Image Dilate.png Image + Contours + boundRect + contoursTypeText.png FaceDetection: ImageWithDetectPositionRect.png Project1-虚拟绘画: 从摄像头 read 的 每张 Image -> ColorDetection: 滤出 ColorDetectRange (green/purple) 的 imageMask -> Shapes Detection:每个 Shape -> contours(轮廓) -> 边界矩形 -> 特定位置 绘制 指定 Color(green/purple).png Project3: 牌照 检测器.png 保存的 Crop(剪裁) 牌照1.png 保存的 Crop(剪裁) 牌照2.png 保存的 Crop(剪裁) 牌照3.png
// source code 
https://github.com/murtazahassan/Learn-OpenCV-cpp-in-4-Hours.git

0 Windows下 环境配置

    step1   opencv 官网下载 opencv  
        
            .exe

    step2   增加 bin folder 到 环境变量 path
        
            D:\OpenCv\ExtractDir\opencv\build\x64\vc15\bin

                vs2019 -> 用 opencv 解压包 opencv\build\x64\vc15
        
    step3   建 vs 工程 C++ console
                
                平台目标 设为 x64 // debug 右侧 
                
                    空项目

    step4   在 工程-属性-配置 下 add 目录

            VC++ 目录
                
                1   增加 Build 目录
                
                        ...\opencv\build\include
                        
                2   增加 库目录
                
                        ...\opencv\build\x64\vc15\lib
            
            链接器 -> input -> 附加依赖项
                
                3   增加 linker input: opencv_world450d.lib // ...\opencv\build\x64\vc15\lib 目录下 
                                                      |
                                                d for debug 
                                                without d for release
        
                
                
                项目
                    属性 
                        VC++ 目录
                        链接器
                        
    step5   运行 test code 
            
            先 add lena image 到 源文件 
                
                本例中 the image 用 "Resourses/test.png"
                
                    源码 中的 Resources 文件夹 copy 1份放 test.cpp 目录下 
                
            // test.cpp 
            #include <opencv2/imgcodecs.hpp>
            #include <opencv2/highgui.hpp>
            #include <opencv2/imgproc.hpp>
            #include <iostream>

            using namespace cv;
            using namespace std;

            /////////////////  Images  //////////////////////

            void main() {

                string path = "Resources/myTest.png"; 
                Mat img = imread(path);
                imshow("Image", img);
                waitKey(0);
            }
            
            运行后, 图片可以用鼠标拖动

chapter1 Read Images / Videos / Webcams (网络摄像头)

    1   Images 

        1]  构造 "Image" 对象
        
            imread(imagePathString)
                    
                用 Mat 类型 ( Handle ) 管理
        
        2]  imshow "Image" 对象
        
        3]  waitKey(delay = 0); // delay = 0 => image 一直不 close

    2   Video  
    
        1]  构造 VideoCapture 类型 对象
                    
                    Ctor: para 为 vedioPathString

        2]  循环 从其中 
                        
                2-1]    read "Image" // one by one
                    
                            VideoCaptureObj.read(Mat型对象)
                                                    |
                                                    |
                                        Mat(Handle) 型对象 作 arg 
                            
                                            接收 "Image" 管理权
                
                2-2]    imshow "Image" 
                
                2-3]    waitKey(delay = 20); // dealy 越大, 视频显示越慢 

    3   Webcam

        构造 VideoCapture 类型 对象
                    
                    Ctor: para 为 cameraId
                                    |
                                    |/
                        1个摄像头, 只用 0
                        n  摄像头, 0 ~ n-1

chapter2 基本函数

    imread

    cvtColor

    GaussianBlur

    Canny

    ...

chapter3 Resize(伸/缩) 和 Crop(剪裁) Images


    resize(imgSrc, imgDst, Size(640, 480) );
                                |
                                |/
                                精确
                                
    resize(imgSrc, imgDst, Size(),0.5,0.5);
                            |       |   |
                            |_ _ _ _|_ _| 比例
    
    
    
        Rect crop(200, 100, 300, 300);
            Rect crop(x, y, width, height);
                        |       |   |
                        |       |   |
                     剪裁起点  宽    高 
                        |
                        |/
                    左上角 x = 0, y = 0
                    
                    
            [ 1706 * 1279 ]
                Rect crop(500, 200, 1200, 1000);
            
        imgCrop = img(crop);

chapter4 画 Shapes 和 Text


    [0] Position + Scalar(BGR)
    
    [2] shapes
    
        circle
        rectangle
        line
        
    [2] Text
        putText

chapter5 Wrap(包装) Perspective(视角)

    思想
    
        [1] 图片中 4个点 => 确定1个 矩形 area
    
            Point2f src[4] = {Point1(左上), Point2(右上), Point3(左下), Point4(右下)}
        
        [2] get `源图 到 dst 图` 的 `矩形 area` 间 `转换 矩阵`
                                                                
                getPerspectiveTransform

                    Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
                    Point2f dst[4] = { {0.0f,0.0f},{weight,0.0f},{0.0f,height},{weight, height} };
                    transformMatrix = getPerspectiveTransform(src, dst);
        
        [3] warpPerspective
        
            warpPerspective(srcImag, warpImag, transformMatrix, Point(weight, height));

chapter6 Color Detection (颜色识别)

    前提
    
        目标 检测物体 的 `color 范围` 在 源图中 是 `独特的` (与其他物体 不同 )
        
    解决的问题 
        
        [1] 源图 +    合理 colorDetectRange => `过滤出 BGR 图 / 图中 某些部分的 framework` 
                                                |
                                                |/
                                            想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework
        
        [2] 源图 +    `目标 检测物体` => 如何设 colorDetectRange
                            |
                            |/
                        想 据 colorDetectRange 检测物体 
                            
                            
            1   通常, 可以准确定义 你想要的 color, 如 红 / 黄 等 
                
                    但 实际中, 由于 lighting、shadow 等因素,
                        
                        Color 不是一种精确的 Color 
                            
                            => 用 `range(范围)` 去 界定
            
            2   逆向思维 
            
                知道 图 => 想检测出其中 某个物体(如 汽车框架 )
                    
                    如何设 detect 门限: lower(下限) & upper(上限) ?
                
                        思路 
                        
                            上/下 限 * 每个维度用1个 Trackbar
                            
                                排列组合 + `手动拖动 Trackbar` 
                                
                                    => 可 `快速` 尝试出 合理 门限 range
                        
            3   实现 

                [1] 源图 -> HSV 图: 带像素(BGR) 的
                    cvtColor(img, imgHSV, COLOR_BGR2HSV)

                [2] createTrackbar("Hue Min", "Trackbars", &hmin, 179); // 等 
                
                [3] while 
                    
                    inRange(imgHSV,     lower, upper,           mask);
                                |         |     |                   |
                                |/        |/    |/                  |
                            HSVImage    detect 的 Color 范围   colorDetectOutputImage

                    imshow("Image Mask", mask);

chapter7 Shapes Detection (Shape 识别)

    1   目标 
        
        检测 different Shapes 或 contours (轮廓) of Images
        
    2   思想 
        
        [1] 预处理 
                
                以得到 更好的 edge 特性 
        
        [2] 据 edge (边缘) 知 Shape
                |
                |   即 
                |/
              contours (轮廓)
            
                每种 Shape 有其 独特 contours
                
                    若 发现 edges (边缘) of Images, 则可发现 `哪种 Shape 是 那种 edges/contours`
            
    3   4种 图
    
            img
            |
            |/
        1]  cvtColor 
            |
            |/
            imgGray
            |
            |/
        2]  GaussianBlur
            |
            |/
            imgBlur
            |
            |/
        3]  Canny
            |
            |/
            imgCanny
            |   
            |   
            |/
        4]  dilate + getStructuringElement
            |
            |/
            imgDilate
            
        三角形 
            
            imgCanny : 轮廓线 small gap
    
            imgDilate: 轮廓线 是 solid(实) 线
                
                => 对 检测来说, imgDilate 的 边缘特性 更好 
                
    4   实现 
        
        [1] 预处理
                
                得 imgDilate
        
        [2] geontours(Mat imgDilate, Mat img) 
        
            1]  发现 contoursVec
            
                findContours(imgDilate, contoursVec, ...)
                
            循环 
            
                2]  DP 算法 计算出 每个 `contours(轮廓) 的 近似多边形` 

                approxPolyDP(contoursVec[i], approxPolygonVec[i], ...)
                                                    |
                                                    |/
                                                size() 即 `边数`
                                                
                3]  boundRectVec[i] = boundingRect(approxPolygonVec[i]);
                    
                    据 近似多边形 求 其相应 
                        
                        点集 最外面的 `边界矩形` boundRectVec[i]
                                |                   |
                                |/                  |/
                              up-right              .width / .height
                                                    |
                                                    |/
                                                    可用于其他 计算: 如 width / .height 在 [0.95, 1.05] => 正方形 
                
                4]  当前 轮廓 `近似多边形 / 边界矩形 / 轮廓类型` 
                            
                        1>  `匹配` 源图 中 `哪个 shape
                                |
                                |/
                                搜索 
                            
                            并 加到 源图上
            
                    drawContours(img, approxPolygonVec)
                                  |
                                  | 引用传递
                                  |/
                                approxPolygonVec 中 有效元素 
                                    
                                    approxPolygon 的 轮廓 加到 img 中(n 个 shapes) `相应` shape 上   
                    
                    rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), ... );
                                |
                                |/
                               加 边界矩形 
                               
                    putText(img, objectType, { boundRectVec[i].x, boundRectVec[i].y - 5 },...);
                            |
                            |/
                            加 contoursTypeText 

8 Face Detection (人脸识别)

    1   目标
        
        detect faces: 在 Images 中
        
    2   思想
    
            人脸: 人脸特征族
            |\
            |   如
            |
        [1] 族 分类 (类型) 对象 
                |
                |/
                CascadeClassifier 对象 

                    [2] .load("族检测器") 
                            |
                            |   如: 第三方库 
                            |/
                            额骨人脸检测器: frontal face detector
                                    |
                                    |   视为
                                    |
                                族特征数据库
                                |\          |
                                |           |   xml 文件 
                                |           |/
                                |           ...haarcascade_frontalface_default.xml
                                | 依赖于 
                                |
                            族检测算法
                            |\
                            |
                    [3] .detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);
                                            |               |
                                            |               |vector<Rect>
                                            |/              |/
                                            检测源图      目标 检测 PositionArea ( 源图中 )

                    
        [4] rectangle(img, detectedFacesAreaVec[i].tl(), detectedFacesAreaVec[i].br(), Scalar(255, 0, 255), 3);
                
                目标 检测 PositionArea 加到 源图中 
                            |
                            |   如 
                            |/
                        矩形 

应用

Project1 virtual paint (虚拟绘画)

    功能 
    
        [1] 开 摄像头 
            VideoCapture vc(0);

        while (true)

            [2] 从 摄像头中 每 read 1 张 Image
                vc.read(img);

            [3] ColorDetection
                
                    滤出 设定(固定) ColorDetectRange 的 imageMask
            
            [4] Shapes Detection: 针对 滤出的 imageMask 

                        每个 Shape 
                        
                            contours(轮廓) 
                            
                                边界矩形 
                                
                                    Point(x + width/2, y) 返回  
                                    
                                        放 pointVec
                                        |
                                        |   出 shape 循环 
                                        |/
                                    作 center 在 源图上 绘画 Circle

Project3 License Plate Detector (牌照 检测器)


    思路
        
        [0] 人脸 识别 
             |
             |   
             |/
            人脸族 特征
            
                人脸族数据库 / 额骨人脸检测器 haarcascade_frontalface_default.xml
              |
              |
              |/
            车牌族 特征 
            
                车牌族数据库 / 俄罗斯 车牌系统 检测器 haarcascade_russian_plate_number.xml
            
        [1] 从 网络摄像头 read 每张 Image 
                                    |
                                    |   含 
                                    |/
        [2] `若干` 
            
                目标检测 PositionArea (Rect: 矩形)
                            |
                            |   用于 
                            |/
                    1]  Crop(剪裁)    该 Image 
                            |
                            |   
                            |/
                        CropPartImage 写/imwrite 到 指定目录指定 .png 文件 
                        
                            imwrite("Resources/Plates/" + to_string(i) + ".png", imgCrop);
                
                    2]  加到 源 Image 

= = = = = = 详细

chapter1 Read Images / Videos / Webcams (网络摄像头)

    1   Images 

        1]  构造 "Image" 对象
        
            imread(imagePathString)
                    
                用 Mat 类型 ( Handle ) 管理
        
        2]  imshow "Image" 对象
        
        3]  waitKey(delay = 0); // delay = 0 => image 一直不 close

    2   Video  
    
        1]  构造 VideoCapture 类型 对象
                    
                    Ctor: para 为 vedioPathString

        2]  循环 从其中 
                        
                2-1]    read "Image" // one by one
                    
                            VideoCaptureObj.read(Mat型对象)
                                                    |
                                                    |
                                        Mat(Handle) 型对象 作 arg 
                            
                                            接收 "Image" 管理权
                
                2-2]    imshow "Image" 
                
                2-3]    waitKey(delay = 20); // dealy 越大, 视频显示越慢 

    3   Webcam

        构造 VideoCapture 类型 对象
                    
                    Ctor: para 为 cameraId
                                    |
                                    |/
                        1个摄像头, 只用 0
                        n  摄像头, 0 ~ n-1

    
    // chapter1.cpp
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv; // cv.imread() / imshow / ...
    using namespace std;


    /////////////////  1    Images  //////////////////////
    /*
    void main() {

        string path = "Resources/test.png";
        Mat img = imread(path);
        imshow("Image", img);
        waitKey(0); // delay = 0
    }
    */

    ///////////////  2  Video  //////////////////////
    /*
    void main() {

        string path = "Resources/test_video.mp4";
        VideoCapture vc(path);
        Mat img;
        while (true) {
            vc.read(img);
            imshow("Image", img);
            waitKey(20);
        }
    }
    */

    /////////////////  3    Webcam  //////////////////////
    void main() {

        VideoCapture vc(0);
        Mat img;

        while (true) {

            vc.read(img);
            imshow("Image", img);
            waitKey(1);
        }
    }

chapter2 基本函数

    imread

    cvtColor

    GaussianBlur

    Canny

    ...
    
    // Chapter2.cpp
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;

    ///////////////  Basic Functions  //////////////////////

    void main() {

        string path = "Resources/myTest.png";
        Mat img = imread(path);
        Mat imgGray, imgBlur, imgCanny, imgDil, imgErode;

        cvtColor(img, imgGray, COLOR_BGR2GRAY);
        GaussianBlur(imgGray, imgBlur, Size(7, 7), 5, 0);
        Canny(imgBlur, imgCanny, 25,75);

        Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
        dilate(imgCanny, imgDil, kernel);
        erode(imgDil, imgErode, kernel);

        imshow("Image", img);
        imshow("Image Gray", imgGray);
        imshow("Image Blur", imgBlur);
        imshow("Image Canny", imgCanny);
        imshow("Image Dilation", imgDil);
        imshow("Image Erode", imgErode);
        waitKey(0);
    }

chapter3 Resize(伸/缩) 和 Crop(剪裁) Images


    resize(imgSrc, imgDst, Size(640, 480) );
                                |
                                |/
                                精确
                                
    resize(imgSrc, imgDst, Size(),0.5,0.5);
                            |       |   |
                            |_ _ _ _|_ _| 比例
    
    
    
        Rect crop(200, 100, 300, 300);
            Rect crop(x, y, width, height);
                        |       |   |
                        |       |   |
                     剪裁起点  宽    高 
                        |
                        |/
                    左上角 x = 0, y = 0
                    
                    
            [ 1706 * 1279 ]
                Rect crop(500, 200, 1200, 1000);
            
        imgCrop = img(crop);
        
        // chapter3.cpp
        #include <opencv2/imgcodecs.hpp>
        #include <opencv2/highgui.hpp>
        #include <opencv2/imgproc.hpp>
        #include <iostream>

        using namespace cv;
        using namespace std;

        ///////////////  Resize and Crop //////////////////////

        void main() {

            string path = "Resources/myTest.png";
            Mat img = imread(path);
            Mat imgResize, imgCrop;

            // [ 1706 * 1279 ]
            cout << img.size() << endl;

            resize(img, imgResize, Size(640, 480));

            // resize(img, imgResize, Size(),0.5,0.5);

            // Rect crop(200, 100, 300, 300);
            
            Rect crop(500, 200, 1200, 1000);
            imgCrop = img(crop);

            imshow("Image", img);
            imshow("Image Resize", imgResize);
            imshow("Image Crop", imgCrop);
            waitKey(0);
        }

chapter4 画 Shapes 和 Text


    [0] Position + Scalar(BGR)
    
    [2] shapes
    
        circle
        rectangle
        line
        
    [2] Text
        putText
    
    // chapter4.cpp
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;

    //////////////  Draw Shapes and Text //////////////////////

    void main() {

        // Blank(空) Image 
        Mat img(512, 512, CV_8UC3, Scalar(255, 255, 255)); // 

        circle(img, Point(256, 256), 155, Scalar(0, 69, 255),FILLED);
        rectangle(img, Point(130, 226), Point(382, 286), Scalar(255, 255, 255), FILLED);
        line(img, Point(130, 296), Point(382, 296), Scalar(255, 255, 255), 2);
        
        putText(img, "Hello Opencv!", Point(180, 262), FONT_HERSHEY_DUPLEX, 0.75, Scalar(0, 69, 255),2);

        imshow("Image", img);
        waitKey(0);
    }

chapter5 Wrap(包装) Perspective(视角)

    思想
    
        [1] 图片中 4个点 => 确定1个 矩形 area
    
            Point2f src[4] = {Point1(左上), Point2(右上), Point3(左下), Point4(右下)}
        
        [2] get `源图 到 dst 图` 的 `矩形 area` 间 `转换 矩阵`
                                                                
                getPerspectiveTransform

                    Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
                    Point2f dst[4] = { {0.0f,0.0f},{weight,0.0f},{0.0f,height},{weight, height} };
                    transformMatrix = getPerspectiveTransform(src, dst);
        
        [3] warpPerspective
        
            warpPerspective(srcImag, warpImag, transformMatrix, Point(weight, height));
    
    // chapter5.cpp
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;

    ///////////////  Warp Images  //////////////////////

    void main() {

        string path = "Resources/cards.jpg";
        Mat img = imread(path);
        Mat matrix, imgWarp;
        float weight = 250, height = 350;
        
        Point2f src[4] = { {529,142},{771,190},{405,395},{674,457} };
        Point2f dst[4] = { {0.0f,0.0f},{weight,0.0f},{0.0f,height},{weight, height} };

        matrix = getPerspectiveTransform(src, dst);
        warpPerspective(img, imgWarp, matrix, Point(weight, height));

        // 标注 源图中 4个顶点: 以各顶点为圆心画 r = 10 的 圆
        for (int i = 0; i < 4; i++)
            circle(img, src[i], 10, Scalar(0, 0, 255), FILLED);

        imshow("Image", img);
        imshow("Image Warp", imgWarp);
        waitKey(0);
    }

chapter6 Color Detection (颜色识别)

    前提
    
        目标 检测物体 的 `color 范围` 在 源图中 是 `独特的` (与其他物体 不同 )
        
    解决的问题 
        
        [1] 源图 +    合理 colorDetectRange => `过滤出 BGR 图 / 图中 某些部分的 framework` 
                                                |
                                                |/
                                            想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework
        
        [2] 源图 +    `目标 检测物体` => 如何设 colorDetectRange
                            |
                            |/
                        想 据 colorDetectRange 检测物体 
                            
                            
            1   通常, 可以准确定义 你想要的 color, 如 红 / 黄 等 
                
                    但 实际中, 由于 lighting、shadow 等因素,
                        
                        Color 不是一种精确的 Color 
                            
                            => 用 `range(范围)` 去 界定
            
            2   逆向思维 
            
                知道 图 => 想检测出其中 某个物体(如 汽车框架 )
                    
                    如何设 detect 门限: lower(下限) & upper(上限) ?
                
                        思路 
                        
                            上/下 限 * 每个维度用1个 Trackbar
                            
                                排列组合 + `手动拖动 Trackbar` 
                                
                                    => 可 `快速` 尝试出 合理 门限 range
                        
            3   实现 

                [1] 源图 -> HSV 图: 带像素(BGR) 的
                    cvtColor(img, imgHSV, COLOR_BGR2HSV)

                [2] createTrackbar("Hue Min", "Trackbars", &hmin, 179); // 等 
                
                [3] while 
                    
                    inRange(imgHSV,     lower, upper,           mask);
                                |         |     |                   |
                                |/        |/    |/                  |
                            HSVImage    detect 的 Color 范围   colorDetectOutputImage

                    imshow("Image Mask", mask);
    
    // chapter6.cpp
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;

    ///////////////  Color Detection  //////////////////////

    void main() {

        string path = "Resources/lambo.png";     // [1] 想据 colorDetectRange 滤出 BGR 图 / 图中 某些部分的 framework
        // string path = "Resources/shapes.png"; // [2] 想 据 colorDetectRange 检测物体 

        Mat img = imread(path);
        Mat imgHSV, mask;
        int hmin = 0, smin = 110, vmin = 153;
        int hmax = 19, smax = 240, vmax = 255;

        cvtColor(img, imgHSV, COLOR_BGR2HSV);

        namedWindow("Trackbars", (640, 200));
        createTrackbar("Hue Min", "Trackbars", &hmin, 179);
        createTrackbar("Hue Max", "Trackbars", &hmax, 179);
        createTrackbar("Sat Min", "Trackbars", &smin, 255);
        createTrackbar("Sat Max", "Trackbars", &smax, 255);
        createTrackbar("Val Min", "Trackbars", &vmin, 255);
        createTrackbar("Val Max", "Trackbars", &vmax, 255);

        while (true) {

            Scalar lower(hmin, smin, vmin);
            Scalar upper(hmax, smax, vmax);
            inRange(imgHSV, lower, upper, mask);

            imshow("Image", img);
            imshow("Image HSV", imgHSV);
            imshow("Image Mask", mask);
            waitKey(1);
        }
    }

chapter7 Shapes Detection (Shape 识别)

    1   目标 
        
        检测 different Shapes 或 contours (轮廓) of Images
        
    2   思想 
        
        [1] 预处理 
                
                以得到 更好的 edge 特性 
        
        [2] 据 edge (边缘) 知 Shape
                |
                |   即 
                |/
              contours (轮廓)
            
                每种 Shape 有其 独特 contours
                
                    若 发现 edges (边缘) of Images, 则可发现 `哪种 Shape 是 那种 edges/contours`
            
    3   4种 图
    
            img
            |
            |/
        1]  cvtColor 
            |
            |/
            imgGray
            |
            |/
        2]  GaussianBlur
            |
            |/
            imgBlur
            |
            |/
        3]  Canny
            |
            |/
            imgCanny
            |   
            |   
            |/
        4]  dilate + getStructuringElement
            |
            |/
            imgDilate
            
        三角形 
            
            imgCanny : 轮廓线 small gap
    
            imgDilate: 轮廓线 是 solid(实) 线
                
                => 对 检测来说, imgDilate 的 边缘特性 更好 
                
    4   实现 
        
        [1] 预处理
                
                得 imgDilate
        
        [2] geontours(Mat imgDilate, Mat img) 
        
            1]  发现 contoursVec
            
                findContours(imgDilate, contoursVec, ...)
                
            循环 
            
                2]  DP 算法 计算出 每个 `contours(轮廓) 的 近似多边形` 

                approxPolyDP(contoursVec[i], approxPolygonVec[i], ...)
                                                    |
                                                    |/
                                                size() 即 `边数`
                                                
                3]  boundRectVec[i] = boundingRect(approxPolygonVec[i]);
                    
                    据 近似多边形 求 其相应 
                        
                        点集 最外面的 `边界矩形` boundRectVec[i]
                                |                   |
                                |/                  |/
                              up-right              .width / .height
                                                    |
                                                    |/
                                                    可用于其他 计算: 如 width / .height 在 [0.95, 1.05] => 正方形 
                
                4]  当前 轮廓 `近似多边形 / 边界矩形 / 轮廓类型` 
                            
                        1>  `匹配` 源图 中 `哪个 shape
                                |
                                |/
                                搜索 
                            
                            并 加到 源图上
            
                    drawContours(img, approxPolygonVec)
                                  |
                                  | 引用传递
                                  |/
                                approxPolygonVec 中 有效元素 
                                    
                                    approxPolygon 的 轮廓 加到 img 中(n 个 shapes) `相应` shape 上   
                    
                    rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), ... );
                                |
                                |/
                               加 边界矩形 
                               
                    putText(img, objectType, { boundRectVec[i].x, boundRectVec[i].y - 5 },...);
                            |
                            |/
                            加 contoursTypeText 
                        
    // chapter7.cpp                 
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;

    ///////////////  Color Detection  //////////////////////

    void getContours(Mat imgDilate, Mat img) {

        vector< vector<Point> > contoursVec;    // 图中有 n 个 轮廓 
        vector<Vec4i> hierarchy;            // Vec4i: 4 个 integral values

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

        vector<vector<Point> > approxPolygonVec(contoursVec.size());
        vector<Rect> boundRectVec(contoursVec.size());
         
        for (int i = 0; i < contoursVec.size(); i++)
        {
            int area = contourArea(contoursVec[i]);
            cout << area << endl;
            
            if (area > 1000) 
            {
                float peri = arcLength(contoursVec[i], true);
                approxPolyDP(contoursVec[i], approxPolygonVec[i], 0.02 * peri, true);

                cout << approxPolygonVec[i].size() << endl;
                boundRectVec[i] = boundingRect(approxPolygonVec[i]);
            
                int edgesNum = (int)approxPolygonVec[i].size();
                string contoursType;
                if (edgesNum == 3) 
                { contoursType = "Tri"; }
                else if (edgesNum == 4)
                { 
                    float aspRatio = (float)boundRectVec[i].width / (float)boundRectVec[i].height;
                    cout << aspRatio << endl;
                    if (aspRatio> 0.95 && aspRatio< 1.05)
                    { contoursType = "Square"; }
                    else { contoursType = "Rect";}
                }
                else if (edgesNum > 4) 
                { contoursType = "Circle"; }

                drawContours(img, approxPolygonVec, i, Scalar(255, 0, 255), 2);
                rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), Scalar(0, 255, 0), 5);
                putText(img, contoursType, { boundRectVec[i].x, boundRectVec[i].y - 5 }, FONT_HERSHEY_PLAIN,1, Scalar(0, 69, 255), 2);
            }
        }
    }

    void main() {

        string path = "Resources/shapes.png";
        Mat img = imread(path);
        Mat imgGray, imgBlur, imgCanny, imgDilate;

        // [1] Preprocessing
        cvtColor(img, imgGray, COLOR_BGR2GRAY);
        GaussianBlur(imgGray, imgBlur, Size(3, 3), 3, 0);
        Canny(imgBlur, imgCanny, 25, 75);
        Mat kernel = getStructuringElement(MORPH_RECT, Size(3, 3));
        dilate(imgCanny, imgDilate, kernel);

        imshow("Image", img);
        // [2] 据 imgDilate getContours
        getContours(imgDilate, img);

        imshow("Image + Contours + boundRect + contoursTypeText", img);
        imshow("Image Gray", imgGray);
        imshow("Image Blur", imgBlur);
        imshow("Image Canny", imgCanny);
        imshow("Image Dilate", imgDilate);

        waitKey(0);
    }

8 Face Detection (人脸识别)

    1   目标
        
        detect faces: 在 Images 中
        
    2   思想
    
            人脸: 人脸特征族
            |\
            |   如
            |
        [1] 族 分类 (类型) 对象 
                |
                |/
                CascadeClassifier 对象 

                    [2] .load("族检测器") 
                            |
                            |   如: 第三方库 
                            |/
                            额骨人脸检测器: frontal face detector
                                    |
                                    |   视为
                                    |
                                族特征数据库
                                |\          |
                                |           |   xml 文件 
                                |           |/
                                |           ...haarcascade_frontalface_default.xml
                                | 依赖于 
                                |
                            族检测算法
                            |\
                            |
                    [3] .detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);
                                            |               |
                                            |               |vector<Rect>
                                            |/              |/
                                            检测源图      目标 检测 PositionArea ( 源图中 )

                    
        [4] rectangle(img, detectedFacesAreaVec[i].tl(), detectedFacesAreaVec[i].br(), Scalar(255, 0, 255), 3);
                
                目标 检测 PositionArea 加到 源图中 
                            |
                            |   如 
                            |/
                        矩形 
    
    // chapter8.cpp
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/objdetect.hpp> // 
    #include <iostream>

    using namespace cv;
    using namespace std;


    ///////////////  Images  //////////////////////

    void main() {

        string path = "Resources/myTest.jpg";
        Mat img = imread(path);

        // [1]
        CascadeClassifier faceCascade;

        // [2]
        faceCascade.load("Resources/haarcascade_frontalface_default.xml");

        if (faceCascade.empty()) { cout << "XML file not loaded" << endl;}

        vector<Rect> targetDetectPositionAreaVec;

        // [3]
        faceCascade.detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);

        imshow("srcImage", img);

        for (int i = 0; i < targetDetectPositionAreaVec.size(); i++)
        {
            // [4]
            rectangle(img, targetDetectPositionAreaVec[i].tl(), targetDetectPositionAreaVec[i].br(), Scalar(255, 0, 255), 3);
        }

        imshow("ImageWithDetectPositionRect", img);
        waitKey(0);
    }

应用

Project1 virtual paint (虚拟绘画)

    功能 
    
        [1] 开 摄像头 
            VideoCapture vc(0);

        while (true)

            [2] 从 摄像头中 每 read 1 张 Image
                vc.read(img);

            [3] ColorDetection
                
                    滤出 设定(固定) ColorDetectRange 的 imageMask
            
            [4] Shapes Detection: 针对 滤出的 imageMask 

                        每个 Shape 
                        
                            contours(轮廓) 
                            
                                边界矩形 
                                
                                    Point(x + width/2, y) 返回  
                                    
                                        放 pointVec
                                        |
                                        |   出 shape 循环 
                                        |/
                                    作 center 在 源图上 绘画 Circle
                                    
    Note:   本程序 逻辑不严谨
    
    // project1:
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;


    /////////////////  Project 1 - Virtual Painter //////////////////////

    Mat img;
    vector<vector<int> > newPoints;  // to store all points

    /////////////////////  COLOR VALUES ////////////////////////////////
                               // hmin, smin, vmin hmax, smax, vmax

    // 指定了 ColorDetectRange
    vector<vector<int> > myColors{ {124,48,117,143,170,255},   // Purple
                                    {68,72,156,102,126,255} };// Green

    vector<Scalar> myColorValues{ {255,0,255},      // Purple
                                    {0,255,0} };    // Green    
    ////////////////////////////////////////////////////////////////////

    Point getContours(Mat image) {

        vector<vector<Point>> contoursVec;
        vector<Vec4i> hierarchy;

        findContours(image, contoursVec, hierarchy, RETR_EXTERNAL, CHAIN_APPROX_SIMPLE);
        //drawContours(img, contoursVec, -1, Scalar(255, 0, 255), 2);
        vector<vector<Point>> contoursPolygonVec(contoursVec.size());
        vector<Rect> boundRectVec(contoursVec.size());

        Point myPoint(0, 0); // vector<Point> 才对

        for (int i = 0; i < contoursVec.size(); i++)
        {
            int area = contourArea(contoursVec[i]);
            cout << area << endl;

            string objectType;

            if (area > 1000)
            {
                float peri = arcLength(contoursVec[i], true);
                approxPolyDP(contoursVec[i], contoursPolygonVec[i], 0.02 * peri, true);

                cout << contoursPolygonVec[i].size() << endl;
                boundRectVec[i] = boundingRect(contoursPolygonVec[i]);
                myPoint.x = boundRectVec[i].x + boundRectVec[i].width / 2;
                myPoint.y = boundRectVec[i].y;

                //drawContours(img, contoursPolygonVec, i, Scalar(255, 0, 255), 2);
                //rectangle(img, boundRectVec[i].tl(), boundRectVec[i].br(), Scalar(0, 255, 0), 5);
            }
        }
        return myPoint;
    }

    void findColor(Mat img)
    {
        Mat imgHSV;
        cvtColor(img, imgHSV, COLOR_BGR2HSV);

        for (int i = 0; i < myColors.size(); i++)
        {
            Scalar lower(myColors[i][0], myColors[i][1], myColors[i][2]);
            Scalar upper(myColors[i][3], myColors[i][4], myColors[i][5]);
            Mat mask;
            inRange(imgHSV, lower, upper, mask);
            imshow(to_string(i), mask);

            Point myPoint = getContours(mask);

            if (myPoint.x != 0 ) {
                newPoints.push_back({ myPoint.x,myPoint.y,i });
            }
        }
    }

    void drawOnCanvas(vector<vector<int>> newPoints, vector<Scalar> myColorValues)
    {

        for (int i = 0; i < newPoints.size(); i++)
        {
            circle(img, Point(newPoints[i][0],newPoints[i][1]), 10, myColorValues[newPoints[i][2]], FILLED);
        }
    }

    void main() {

        VideoCapture vc(0);

        while (true) {

            vc.read(img);

            findColor(img);

            drawOnCanvas(newPoints, myColorValues);

            imshow("Image", img);
            waitKey(1);
        }
    }

Project2 Document Scanner

待更新

Project3 License Plate Detector (牌照 检测器)


    思路
        
        [0] 人脸 识别 
             |
             |   
             |/
            人脸族 特征
            
                人脸族数据库 / 额骨人脸检测器 haarcascade_frontalface_default.xml
              |
              |
              |/
            车牌族 特征 
            
                车牌族数据库 / 俄罗斯 车牌系统 检测器 haarcascade_russian_plate_number.xml
            
        [1] 从 网络摄像头 read 每张 Image 
                                    |
                                    |   含 
                                    |/
        [2] `若干` 
            
                目标检测 PositionArea (Rect: 矩形)
                            |
                            |   用于 
                            |/
                    1]  Crop(剪裁)    该 Image 
                            |
                            |   
                            |/
                        CropPartImage 写/imwrite 到 指定目录指定 .png 文件 
                        
                            imwrite("Resources/Plates/" + to_string(i) + ".png", imgCrop);
                
                    2]  加到 源 Image 
    
    // Project3
    #include <opencv2/imgcodecs.hpp>
    #include <opencv2/highgui.hpp>
    #include <opencv2/imgproc.hpp>
    #include <opencv2/objdetect.hpp>
    #include <iostream>

    using namespace cv;
    using namespace std;

    ///////////////  Project 3 - License Plate Detector //////////////////////

    void main() {

        Mat img;
        VideoCapture vc(0);

        CascadeClassifier plateCascade;
        plateCascade.load("Resources/haarcascade_russian_plate_number.xml");

        if (plateCascade.empty()) { cout << "XML file not loaded" << endl; }

        vector<Rect> targetDetectPositionAreaVec;

        while (true) {

            // [1]
            vc.read(img);

            // [2]
            plateCascade.detectMultiScale(img, targetDetectPositionAreaVec, 1.1, 10);

            for (int i = 0; i < targetDetectPositionAreaVec.size(); i++)
            {
                // [3] image Crop
                Mat imgCrop = img(targetDetectPositionAreaVec[i]);

                //imshow(to_string(i), imgCrop);

                // [4]
                imwrite("Resources/Plates/" + to_string(i) + ".png", imgCrop);

                // [5]
                rectangle(img, targetDetectPositionAreaVec[i].tl(), targetDetectPositionAreaVec[i].br(), Scalar(255, 0, 255), 3);
            }

            imshow("Image", img);
            waitKey(1);
        }
    }

相关文章

网友评论

    本文标题:4h 上手 C++ 版 Opencv

    本文链接:https://www.haomeiwen.com/subject/xvtylrtx.html