移动 前端 Python Android Java音视频-RTMP 知识总结

OpenCV(二)应用车牌识别

2020-11-14  本文已影响0人  zcwfeng

车牌识别分成两个过程 :即车牌检测和字符识别

车牌定位对一个包含车牌的图像进行分析,最终截取出只包含车牌的一个图块。这个步骤的主要目的是降低了在车牌识别过程中的计算量。如果直接对原始的图像进行车牌识别,会非常的慢,因此需要检测的过程。在本系统中,我们使用SVM(支持向量机)这个机器学习算法去判别截取的图块是否是真的“车牌”。

字符识别这个步骤的主要目的就是从上一个车牌检测步骤中获取到的车牌图像,进行光学字符识别(OCR)这个过程。其中用到的机器学习算法是著名的人工神经网络(ANN)中的多层感知机(MLP)模型。非常火的“深度学习”其实就是多隐层的人工神经网络,与其有非常紧密的联系。通过了解光学字符识别(OCR)这个过程,也可以知晓深度学习所基于的人工神经网路技术的一些内容。

了解OpenCV对图像的各种处理,包括了:模糊、灰度化、二值化、边缘检测、轮廓查找等。

车牌定位

车牌定位就是在一副图片中发现仅包含车牌的图块,以此提高整体识别的准确率与速度。这个过程非常重要,如果这步失败了,后面的字符识别过程就别想了。车牌的定位可以多种方法结合,所有定位方法最终将得到的疑似车牌的待识别图片中的位置矩形集合。最终利用SVM支持向量机来进行最终的评选。我们将使用两种方法,我将他们称之为边缘定位颜色定位

边缘定位

如果待识别的图片中车牌没有大的旋转或变形,那么其中必然包括很多垂直边缘,如果能够找到一个包含很多垂直如果待识别的图片中车牌没有大的旋转或变形,那么其中必然包括很多垂直边缘,如果能够找到一个包含很多垂直边缘的矩形块,那么有很大的可能性它就是车牌。 整个流程为:

车牌识别流程.png

接下来,我们来一步步完成这些处理。待识别车牌的图片:
使用OpenCV读取存入Mat: Mat src = imread("图片地址");

高斯模糊

 //预处理 :去噪 让车牌区域更加突出
    Mat blur;
    //1、高斯模糊(平滑) (1、为了后续操作 2、降噪 )
    GaussianBlur(src,blur,Size(5,5),0);
    imshow("原图",src);
    imshow("高斯模糊",blur);
高斯模糊.png

这一步的效果经过高斯模糊后的图片,可以看出图像变的模糊了。这步的作用是为接下来的边缘检测去除干扰的噪声。弱化外部环境的某些可能存在的细小边缘。如果不进行高斯模糊,在进行边缘检测时与进行高斯模糊模糊后Sobel更加的清晰

对比.png

灰度化

  Mat gray;
    //2、灰度化 去掉颜色 因为它对于我们这里没用  降噪
    cvtColor(blur,gray,COLOR_BGR2GRAY);
    imshow("灰度", gray);
灰度.jpg

将图像进行灰度化。这意味着后面的所有操作都不能基于色彩信息了。对于边缘定位方法,颜色没有作用。而且边缘检测的Sobel算子仅能对灰度图像有效果,不能将色彩图像作为输入。

边缘检测

Mat sobel_16;
    //3、 边缘检测 让车牌更加突出  在调用时需要以16位来保存数据 在后续操作 以及显示的时候需要转回8位
    Sobel(gray, sobel_16,CV_16S,1,0);
    //转为8位 ,CV_8U
    Mat sobel;
    convertScaleAbs(sobel_16,sobel);

    imshow("Sobel", sobel);
边缘话.jpg

边缘检测的目的是标识图像中亮度变化明显的点 。可以看出,经过这步后车牌字符更加的明显。

二值化

 //4、二值化 黑白
    Mat shold;
    threshold(sobel, shold,0,255, THRESH_OTSU );
    imshow("二值", shold);
    waitKey();
二值化.jpeg

将灰度图像(每个像素点有256个取值可能)转化为二值图像(不是黑就是白) 为后面的形态学操作准备。

闭操作

//5、闭操作
    // 将相邻的白色区域扩大 连接成一个整体
    Mat close;
    Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));
#if 1
//    Mat d;
//    dilate(shold,d,element);
//    imshow("膨胀", d);
//    Mat e;
//    erode(d,e,element);
//    imshow("腐蚀", e);
#endif
    morphologyEx(shold, close, MORPH_CLOSE, element);
    imshow("闭操作", close);
//    waitKey();
闭操作.jpg

车牌是不是被连成了一块整体的白色?闭操作的作用就如上面代码中的注释。闭操作通常消弥狭窄的间断和长细的鸿沟,消除小的空洞,并填补轮廓线中的断裂。其实闭操作是由两步合起来的结果:膨胀与腐蚀。开操作、闭操作、腐蚀、膨胀都成为形态学操作。在图像处理技术中,有一些的操作会对图像的形态发生改变,这些操作一般称之为形态学操作。形态学操作的对象是二值化图像。

将上面代码注释打开测试膨胀---->腐蚀====》相当闭操作

反过来,腐蚀-----> 膨胀=====》相当开操作,我们这里不适用,因为会导致一篇黑色腐蚀后没东西。

膨胀-腐蚀-闭操作.png

腐蚀和膨胀是对白色部分(高亮部分)而言的,不是黑色部分。膨胀就是图像中的高亮部分进行膨胀,“领域扩张”,效果图拥有比原图更大的高亮区域。腐蚀就是原图中的高亮部分被腐蚀,“领域被蚕食”,效果图拥有比原图更小的高亮区域。

查找轮廓

经过了上一步的操作后,可以发现,车牌被连接成为一块明显的矩形区域。现在对图片内的轮廓进行查找:

//6、查找轮廓
    //获得初步筛选车牌轮廓================================================================
    //轮廓检测
    vector< vector<Point> > contours;
    //查找轮廓 提取最外层的轮廓  将结果变成点序列放入 集合
    findContours(close, contours, RETR_EXTERNAL, CHAIN_APPROX_NONE);
    //遍历
    vector<RotatedRect> vec_sobel_roi;
    for (vector<Point> point:contours) {
        //把找到的轮廓的顶点连在一起构成一个形状
        RotatedRect rotatedRect = minAreaRect(point);

        //画矩形
//        rectangle(src, rotatedRect.boundingRect(), Scalar(255, 0, 255));
        //进行初步的筛选 把完全不符合的轮廓给排除掉 ( 比如:1x1,5x1000 )
        if (verifySizes(rotatedRect)) {
            vec_sobel_roi.push_back(rotatedRect);
        }
    }

图中所有的轮廓。这个算法会把全图的轮廓都计算出来,因此要进行筛选: verifySizes

初步筛选

int CarPlateLocation::verifySizes(RotatedRect rotated_rect) {
    if (rotated_rect.size.empty()) {
        return 0;
    }
    //容错率
    float error = 0.75f;

    //训练时候模型的宽高 136 * 36
    //获得宽高比
    float aspect = float(136) / float(36);

    //最小 最大面积 不符合的丢弃
    //给个大概就行 随时调整
    //尽量给大一些没关系, 这还是初步筛选。
    int min = 20 * aspect * 20;
    int max = 180 * aspect * 180;

    //比例浮动 error认为也满足
    //最小宽、高比
    float rmin = aspect - aspect * error;
    //最大的宽高比
    float rmax = aspect + aspect * error;
    //矩形的面积
    float area = rotated_rect.size.height * rotated_rect.size.width;
    //矩形的比例
    float r = (float) rotated_rect.size.width / (float) rotated_rect.size.height;
    if ((area < min || area > max) || (r < rmin || r > rmax))
        return 0;
    return 1;// true
}

符合条件的矩形现在进入了:vec_sobel_roi向量集合。

矫正

比如我们使用的测试图片。因为可能斜的,我们尽可能的对其进行矫正。

/**
* 矫正
*/
void CarPlateLocation::tortuosity(Mat src, vector<RotatedRect> &rects, vector<Mat> &dst_plates) {
    //循环要处理的矩形
    for (RotatedRect roi_rect : rects) {
        //float r = (float)roi_rect.size.width / (float)roi_rect.size.height;
        //矩形角度
        float roi_angle = roi_rect.angle;
        //矩形大小
        Size roi_rect_size = roi_rect.size;



        //让rect在一个安全的范围(不能超过src)
        Rect2f rect;
        safeRect(src, roi_rect, rect);


        //候选车牌
        //抠图  这里不是产生一张新图片 而是在src身上定位到一个Mat 让我们处理
        //数据和src是同一份
        Mat src_rect = src(rect);
        //真正的候选车牌
        Mat dst;
        //不需要旋转的 旋转角度小没必要旋转了
        if (roi_angle - 5 < 0 && roi_angle + 5 > 0) {
            dst = src_rect.clone();
        } else {
            //相对于roi的中心点 不减去左上角坐标是相对于整个图的
            //减去左上角则是相对于候选车牌的中心点 坐标
            Point2f roi_ref_center = roi_rect.center - rect.tl();
            Mat rotated_mat;
            //矫正 rotated_mat: 矫正后的图片
            rotation(src_rect, rotated_mat, roi_rect_size, roi_ref_center, roi_angle);
            dst = rotated_mat;
        }
//        imshow("矫正前",src_rect);
//        imshow("矫正后",dst);
//        waitKey();
        //定义大小
        Mat plate_mat;
        //高+宽
        plate_mat.create(32, 136, CV_8UC3);
        resize(dst, plate_mat, plate_mat.size());

        dst_plates.push_back(plate_mat);
        dst.release();
    }
}

这步操作就是,将斜的矩形,rotation到横平竖直的样子

SVM 评测

在经过上诉一系列的处理后,我们得到可能为车牌的Mat类型向量集合。但是车牌到底是哪一个呢?接下来我们将使用SVM进行最终的确定。

搜索下概念

支持向量机(Support Vector Machine, SVM)是一类按监督学习(supervised learning)方式对数据进行二元分类的广义线性分类器(generalized linear classifier),其决策边界是对学习样本求解的最大边距超平面(maximum-margin hyperplane)[1-3] 。

SVM实际上是一个分类器。可以利用其来对筛选出来的车牌分类为车牌与非车牌。

  svm = SVM::load(svm_model);
    //参数1的宽-参数2的宽 结果与参数3的余数为0  高也一样
    svmHog = new HOGDescriptor(Size(128, 64), Size(16, 16), Size(8, 8), Size(8, 8), 3);

--------

int index = -1;
    float minScore = FLT_MAX; //float的最大值
    //使用 svm 进行 评测
    for (int i = 0; i < plates.size(); ++i) {
        //候选车牌
        Mat plate = plates[i];
        //抽取车牌特征 HOG
        Mat gray;
        cvtColor(plate, gray, COLOR_BGR2GRAY);
        //二值化 必须是以单通道进行
        Mat shold;
        threshold(gray, shold, 0, 255, THRESH_OTSU);

        //提取特征
        Mat features; //Mat:矩阵(集合)
        getHogFeatures(svmHog, shold, features);
        //把特征置为一行
        Mat samples = features.reshape(1, 1);
        //原始模式
        // svm: 直接告诉你这个数据是属于什么类型.
        // RAW_OUTPUT:让svm 给出一个评分
        /*char name[100];
        sprintf(name,"候选车牌%d",i);
        imshow(name,plate);*/

        float score = svm->predict(samples, noArray(), StatModel::Flags::RAW_OUTPUT);
//        imshow("候选",plate);
//        printf("评分:%f\n",score);
//        waitKey();
        if (score < minScore) {
            minScore = score;
            index = i;
        }
        gray.release();
        shold.release();
        features.release();
        samples.release();
    }

在使用SVM前,首先需要进行模型的训练,而且在训练时我们这里使用的是HOG特征的提取。

拓展知识概念

AI 似乎很神秘,实际上还是大数据的应用
案例: 我们在支付宝扫五福功能
SVM: 支持向量机
分类工具 算法
非黑即白
正样本:福字
非正样本:非福字
ANN:人工神经网络
其实,最基本了解,都是先训练模型,提供正负样本进行训练,通过大量数据训练结果再通过算法进行识别功能。
305911 公式 灰度转换(灰度化)
0.30R+0.59B+0.11G = gray
将BRG -----> gray
图像去噪,边缘检测
去除干扰信息,除了对我们有用的图像数据,就是噪声。都要去掉,所以要灰度化
二值化 非黑即白,黑白图片(0~255)
图片大小 和 宽高,格式(RGB,ARGB等等)有关
形态学操作 变色,转换格式(开操作,闭操作,腐蚀,膨胀等)
类似和面过程,面粉洒水
初步筛选 人工给定条件,让SVM提升效率

上一篇下一篇

猜你喜欢

热点阅读