美文网首页 移动 前端 Python Android Java
OpenCV(三)车牌识别SVM训练与hsv定位

OpenCV(三)车牌识别SVM训练与hsv定位

作者: zcwfeng | 来源:发表于2020-11-19 16:34 被阅读0次

    SVM训练

    简单来说,SVM就是用于区分不同的类型(车牌、非车牌)。SVM的训练数据既有特征又有标签,通过训练,让机器 可以自己找到特征和标签之间的联系,在面对只有特征没有标签的数据时,可以判断出标签。属于机器学习中的监 督学习。

    HOG特征

    在训练时,我们将使用HOG来提取一张图片的特征。HOG全称为:Histogram of Oriented Gridients,方向梯度
    直方图。是计算机视觉、模式识别领域很常用的一种描述图像局部纹理的特征。
    先计算图片某一区域中不同方向上梯度的值,然后进行累积,得到的直方图就可以代表这块区域,也就是作为特 征,可以输入到分类器里面。 简单来说,车牌的边缘与内部文字组成的一组信息(在边缘和角点的梯度值是很大 的,边缘和角点包含了很多物体的形状信息),HOG就是抽取这些信息组成一个直方图。

    样本+标签,数据格式
    xx.png--->HOG---> Mat(相当于图片数据矩阵)
    正样本对应标签----> 1
    负样本对应标签----> -1
    每张图片放在一行,每行就是一个样本数据
    XXXXXXXXXX
    XXXXXXXXXX
    XXXXXXXXXX
    XXXXXXXXXX

    在OpenCV中对图像使用HOG提取特征:

     //1、灰度化,保存至gray
    Mat gray;
    cvtColor(plate, gray, COLOR_BGR2GRAY); //2、二值化,保存至shold
    Mat shold;
    threshold(gray, shold, 0, 255, THRESH_OTSU); //3、提取特征,保存至features
    Mat features;
    getHogFeatures(svmHog, shold, features);
    
    -----------
    
    //提取特征
    void CarPlateRecgnize::getHogFeatures(HOGDescriptor *hog, Mat src, Mat &out) {
        //重新定义大小 缩放 提取特征的时候数据需要为  :CV_32S 有符号的32位数据
        Mat trainImg = Mat(hog->winSize, CV_32S);
        resize(src, trainImg, hog->winSize);
        //计算特征 获得float集合
        vector<float> d;
        hog->compute(trainImg, d, Size(8, 8));
    
        Mat features(d);
        //特征矩阵
        features.copyTo(out);
        features.release();
        trainImg.release();
    }
    
    
    

    训练数据

    能够提取特征后,接下来就要进行SVM的训练了。opencv中的svm是使用的LIBSVM,LIBSVM是台湾大学林智仁
    (Lin Chih-Jen)教授2001年开发的一套支持向量机的库。

    深度学习就是调整参数的过程。我们使用的参数都是默认的,肯定不是最好的,但是基本稳定。不同参数效
    果不同、训练检测速度不同,同时也影响着模型文件的大小。

    我们将使用136x36规格的车牌图片作为正样本,任意136x36的图片作为负样本来进行训练。

    struct TrainStruct{
        string file;
        int label;
    };
    ---------
        //样本集合
        vector<TrainStruct> svm_data;
    
    ----------
    
        //正样本 标签是:1
        for(auto file : pos_files){
            svm_data.push_back({file,1});
        }
        //负样本 标签为:-1
        vector<string> neg_files;
        getFiles(SVM_NEG, neg_files);
        for(auto file : neg_files){
            svm_data.push_back({file,-1});
        }
    

    现在我们的所有样本都被打上标签记录在 svm_data 中。接下来我们对样本进行特征提取并记录。

    Mat samples;
        vector<int> responses;
        for(auto data:svm_data){
            auto image = imread(data.file,IMREAD_GRAYSCALE);
            if (image.empty()) {
                printf("加载样本失败 image: %s.\n", data.file.c_str());
                continue;
            }
            //二值
            threshold(image, image, 0, 255, THRESH_BINARY+THRESH_OTSU);
            Mat feature;
            //获得hog特征
            getSvmHOGFeatures(image, feature);
            //调整为一行
            feature = feature.reshape(1, 1);
            // 图片数据:x x x
            // samples:x x x
            //         x x x
            //保存特征数据
            samples.push_back(feature);
            //记录对应的标签
            responses.push_back(data.label);
        }
    

    现在我们获得了samples与 labelsÏ两个数据。其中 samples 为样本特征数据,存储在矩阵中,每一行就是一个样 本的特征数据。加上我们正负样本各有三个,那么 samples 中记录的数据就为:宽:x,高为:3+3=6。看起来是 这样的:

    样本.png

    同时与 samples 对应的有一个 labels 标签集合,记录这些特征数据所属于的标签。 我们准备样本时,将正样本标 签置为1,负样本为-1。因此现在 labels 中的数据为 {1,1,1,-1,-1,-1}与 samples 相匹配。
    下面我们将 samples 与 lables 包装为OpenCV中的训练数据集: TrainData

    训练

    我们的训练数据 TrainData 已经准备完成,接下来需要使用OpenCV提供的SVM进行训练。

    //训练数据  行
        Ptr <TrainData> trainData = TrainData::create(samples, SampleTypes::ROW_SAMPLE, responses);
        printf("训练数据准备完成,开始训练!\n");
        //其他参数都是默认值 为了把
        //最后一个参数设置true 则会创建更平衡的验证子集
        //也就是如果是2类分类的话能得到更准确的结果
        classifier->trainAuto(trainData, 10, SVM::getDefaultGrid(SVM::C),
                              SVM::getDefaultGrid(SVM::GAMMA), SVM::getDefaultGrid(SVM::P),
                              SVM::getDefaultGrid(SVM::NU), SVM::getDefaultGrid(SVM::COEF),
                              SVM::getDefaultGrid(SVM::DEGREE), true);
    
        classifier->save( SVM_XML );
    
        printf("训练完成 ,模型保存: %s  \n", SVM_XML);
    

    HSV 定位

    我们已经使用了边缘定位,边缘定位是通过图像的各种处理,将车牌区域连接成一块整体从而进行轮廓查 找得到的候选车牌集合,但是边缘定位在某些情况下可能把背景也混合成一块整体。这样得到的车牌并不是最合适 的。所以我们再加入颜色定位同时进行筛选。以蓝色车牌举例,如果以BGR值来确定颜色,就算B为255,但是一 旦混合了G与R得到的结果可能并不是蓝色。以BGR来进行颜色的分辨并不容易。

    而HSV(也可称为HSB)是根据颜色的直观特性创建的一种颜色空间,也称六角锥体模型。这个模型中颜色的参数分 别是:

    • 色度(Hue):用角度度量,取值范围为0°~360°,从红色开始按逆时针方向计算,红色为0°,绿色为120°, 蓝色为240°;
    • 饱和度(Saturation): 取值范围为0-1。降低S,饱和度越低,颜色越白;
    • 透明度 (Value/brightness ): 取值范围为0-1。降低V,亮度月底,颜色越黑 。
    hsv.png

    经过查询发现,H为:200°-280°范围,S为0.37-1之间,V也是0.37-1之间。这个范围可能并不是非常精准,但是足 够了。
    而OpenCV中H被定义为:0-180的范围(缩小一半)。而S与V都为0-255的范围(乘以255)。
    因此最终认为 使用OpenCV获得HSV图像,某个像素点,H在100-140S与V都在95-255之间则该像素点为蓝色。那么使用颜色 定位的流程为:

    转换HSV流程.png

    转换HSV

     Mat hsv;
    cvtColor(src, hsv, COLOR_BGR2HSV);
    

    颜色匹配

    循环处理每个像素点,根据蓝色HSV取值范围,如果像素点为蓝色,则将其HSV数据置为:0,0,255(只保留亮 度);若像素点非蓝色,则置为0,0,0(纯黑色)。这样只有蓝色的区域被保留下来。

        //3通道
        int chanles = hsv.channels();
        //高
        int h = hsv.rows;
        //宽数据长度
        int w = hsv.cols * chanles;
        //判断数据是否为一行存储的
        //头一行的末尾在内存里和下一行的开头是相连的
        if (hsv.isContinuous()) {
            w *= h;
            h = 1;
        }
        for (size_t i = 0; i < h; i++)
        {
            //第i行的数据 一行 hsv的数据 uchar = java byte
            uchar* p = hsv.ptr<uchar>(i);
            //处理3个数据 所以+=3
            for (size_t j = 0; j < w; j+=3)
            {
                int h = int(p[j]);
                int s = int(p[j+1]);
                int v = int(p[j + 2]);
                //是否为蓝色像素点的标记
                bool blue = false;
                // h:蓝色就可以了
                //当V和S都达到最高值,也就是1时(opencv*255,0-255),颜色是最纯正的。
                // 降低S,颜色越发趋向于变白。降低V,颜色趋向于变黑,当V为0时,颜色变为黑色。
                if (h >= 100 && h <= 140 && s >= 95 && s <= 255 && v >= 95 && v <= 255) {
                    blue = true;
                }
                //把蓝色像素 凸显出来 ,其他的区域全变成黑色
                if (blue) {
                    p[j] = 0;
                    p[j+1] = 0;
                    p[j + 2] = 255;
                }
                else {
                    //变成黑色
                    p[j] = 0;
                    p[j + 1] = 0;
                    p[j + 2] = 0;
                }
            }
        }
    

    此时,显示出来的图像为:

    HSV结果.png

    之所以会呈现红色,是因为 imshow 会将输出的Mat视为BGR数据进行显示。那么在我们的处理中蓝色被处理为: 0,0,255。如果看为BGR,则为R=255,所以为红色。

    分离HSV

     vector<Mat> hsv_split;
    split(hsv, hsv_split);
    

    对上一步的结果进行分离,得到了三个元素(H、S、V)的向量集合 vector 。在这个集合中,我们直接取出第2个元 素,即为S饱和度数据矩阵。此矩阵Mat即为二值化的图像。

    二值化.png

    接下来的操作和边缘定位一致,对二值图像进行闭操作、查找轮廓、初步筛选最后旋转角度。即可获得候选车牌集 合。

    后续处理

    OpenCV(三)应用车牌识别

        //闭操作
        Mat close;
        Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));
        morphologyEx(shold, close, MORPH_CLOSE, element);
        imshow("close",close);
        waitKey();
    
        //6、查找轮廓
        //获得初步筛选车牌轮廓================================================================
        //轮廓检测
        vector< vector<Point> > contours;
        //查找轮廓 提取最外层的轮廓  将结果变成点序列放入 集合
        findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
        //遍历
        vector<RotatedRect> vec_color_roi;
        for (vector<Point> point : contours) {
            RotatedRect rotatedRect = minAreaRect(point);
            //rectangle(src, rotatedRect.boundingRect(), Scalar(255, 0, 255));
            //进行初步的筛选 把完全不符合的轮廓给排除掉 ( 比如:1x1,5x1000 )
            if (verifySizes(rotatedRect)) {
                vec_color_roi.push_back(rotatedRect);
            }
        }
    
        tortuosity(src, vec_color_roi, dst);
    

    补充知识

    机器学习算法-------> 核函数

    交错数据

    例子:
    花生,石头,瓜子散落在桌子上,用里拍桌子,物体起落有高有低,就容易在空间维度划分区域
    

    二维线性不可分------> 三维空间

    SVM 向量机

    上面的参数可以模拟运行

    在线PS工具

    跳变-----》黑色,白色变化(干扰物比较小)用来区分干扰,因为我们的数据是按照行来的 如GB888888

    相关文章

      网友评论

        本文标题:OpenCV(三)车牌识别SVM训练与hsv定位

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