基於OpenCV的車牌識別(Sobel、顏色定位)

yxc發表於2018-11-15

車牌識別大體上需要經歷過Sobel定位、顏色定位、SVM對定位來的候選車牌進行評測,給出評分,最後通過提取HOG特徵按照訓練模型進入ANN識別。

這一章節介紹 定位相關的邏輯程式碼,其中定位用到 Sobel定位(邊緣檢測定位), 顏色定位:對應程式碼裡的CarSobelPlateLocation,CarColorPlateLocation;兩者定位後得到一些候選的圖片,把這些圖片送去SVM進行評測,SVM基於HOG提取邊緣資訊特徵,HOG類同之前處理紋理特徵的LBP,專案程式碼在Clion上開發的,原始碼地址前往車牌定位

Sobel定位

CarSobelPlateLocation,通過以下的一些步驟進行降噪:

  • 高斯模糊
  • 灰度化
  • 邊緣化
  • 二值化
  • 閉操作
高斯模糊
//預處理 :去噪 讓車牌區域更加突出
    Mat blur;
    //1、高斯模糊(平滑) (1、為了後續操作 2、降噪 )
    GaussianBlur(src, blur, Size(5, 5), 0);
    //imshow("高斯模糊",blur);
複製程式碼
灰度化
 Mat gray;
    //2、灰度化 去掉顏色 因為它對於我們這裡沒用  降噪
    cvtColor(blur, gray, COLOR_BGR2GRAY);
    imshow("灰度", gray);
複製程式碼
邊緣化
 Mat sobel_16;
    //3、 邊緣檢測 讓車牌更加突出  在呼叫時需要以16位來儲存資料 在後續操作 以及顯示的時候需要轉回8位
    Sobel(gray, sobel_16, CV_16S, 1, 0);
    //轉為8位
    Mat sobel;
    convertScaleAbs(sobel_16, sobel);
    imshow("Sobel", sobel);
複製程式碼
二值化
//4. 二值化 黑白
    Mat shold;
    //大律法   最大類間演算法
    threshold(sobel, shold, 0, 255, THRESH_OTSU + THRESH_BINARY);
    imshow("二值", shold);
複製程式碼
閉操作
//5、閉操作
    // 將相鄰的白色區域擴大 連線成一個整體
    Mat close;
    Mat element = getStructuringElement(MORPH_RECT, Size(17, 3));
    morphologyEx(shold, close, MORPH_CLOSE, element);
    imshow("閉操作", close);
複製程式碼

以上的操作是在處理降噪,第六步初步賽選。

第六步:最大面積、最小面積.寬高逼。

//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);
        }
    }
複製程式碼

初步賽選:寬高比 float aspec,把不符合的刪除掉(1 * 1的, 5* 1000的等候選矩形)

int CarPlateLocation::verifySizes(RotatedRect rotated_rect) {
    //容錯率
    float error = 0.75f;

    //訓練時候模型的寬高 136 * 32
    //獲得寬高比
    float aspect = float(136) / float(32);

    //最小 最大面積 不符合的丟棄
    //給個大概就行 隨時調整
    //儘量給大一些沒關係, 這還是初步篩選。
    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;
}
複製程式碼

把斜的圖片轉正:仿射變換

//1、矯正前 2、矯正後 3、矩形的大小 4、矩形中心點座標  5、角度
void CarPlateLocation::rotation(Mat src, Mat &dst, Size rect_size,
                                Point2f center, double angle) {

    //獲得旋轉矩陣
    Mat rot_mat = getRotationMatrix2D(center, angle, 1);

    //運用仿射變換
    Mat mat_rotated;
    //矯正後 大小會不一樣,但是對角線肯定能容納
    int max = sqrt(pow(src.rows, 2) + pow(src.cols, 2));
    //仿射變換
    warpAffine(src, mat_rotated, rot_mat, Size(max, max),
               CV_INTER_CUBIC);
    imshow("旋轉前", src);
    imshow("旋轉", mat_rotated);
    //擷取 儘量把車牌多餘的區域擷取掉
    getRectSubPix(mat_rotated, Size(rect_size.width, rect_size.height), center, dst);
    imshow("擷取", dst);
    mat_rotated.release();
    rot_mat.release();
}
複製程式碼

顏色定位

HSV顏色模型

色調(H), 飽和度(S), 明度(V);

BGR 轉成 HSV

cvtColor(src,hsv,COLOR_BGR2HSV);
複製程式碼
色調H

用角度度量,取值範圍為0°~360°,從紅色開始按逆時針方向計算,紅色為0°,綠色為120°,藍色為240°。它們的補色是:黃色為60°,青色為180°,品紅為300°;

飽和度S

飽和度S表示顏色接近光譜色的程度。一種顏色,可以看成是某種光譜色與白色混合的結果。其中光譜色所佔的比例愈大,顏色接近光譜色的程度就愈高,顏色的飽和度也就愈高。飽和度高,顏色則深而豔。光譜色的白光成分為0,飽和度達到最高。通常取值範圍為0%~100%,值越大,顏色越飽和。

明度V

明度表示顏色明亮的程度,對於光源色,明度值與發光體的光亮度有關;對於物體色,此值和物體的透射比或反射比有關。通常取值範圍為0%(黑)到100%(白)

在OpenCV中hsv 資料為8UC則取值分別為 0-180 0-255 0-255 ,即藍色應該是120

基於OpenCV的車牌識別(Sobel、顏色定位)

按照上面的表格找到藍色區域 (100 ~ 124), 然後將HSV中的H、S轉為 0, V變為255。其它區域的HSV賦值為0.

//3通道
    int chanles = hsv.channels();
    //高
    int h = hsv.rows;
    //寬資料長度
    int w = hsv.cols * 3;

    //判斷資料是否為一行儲存的
    //記憶體足夠的話 mat的資料是一塊連續的記憶體進行儲存
    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);

        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;
            //藍色
            if (h >= 100 && h <= 124 && s >= 43 && s <= 255 && v >= 46 && v <= 255) {
                blue = true;
            }

            if (blue){
                p[j] = 0;
                p[j + 1]=0;
                p[j + 2]=255;
            }else {
                //hsv 模型 h:0 紅色 亮度和飽和度都是0 ,也就變成了黑色
                p[j] = 0;
                p[j + 1] = 0;
                p[j + 2] = 0;
            }
        }
    }
複製程式碼

得到下面的圖:

基於OpenCV的車牌識別(Sobel、顏色定位)

接下來抽取亮度:

//把亮度資料抽出來
    //把h、s、v分離出來
    vector<Mat> hsv_split;
    split(hsv, hsv_split);

複製程式碼

然後跟sobel一樣通過二值化、大律法等操作

    // 整個圖片+經過初步賽選的車牌 + 得到的候選車牌
    tortuosity(src, vec_sobel_roi, dst);

    for (Mat s: dst) {
        imshow("候選", s);
        waitKey();
    }
複製程式碼

篩選出來一個集合:

基於OpenCV的車牌識別(Sobel、顏色定位)

把兩個結合結合起來,然後通過SVM進行評測, 因為不像人臉檢測是沒有現成的模型。

vector< Mat > sobel_plates;
//sobel定位
plateLocation->location(src, sobel_plates);

//顏色定位
vector< Mat > color_plates;
plateColorLocation->location(src, color_plates);

vector<Mat> plates;
//把sobel_plates的內容 全部加入plates向量
plates.insert(plates.end(),sobel_plates.begin(), sobel_plates.end());
plates.insert(plates.end(), color_plates.begin(), color_plates.end());
複製程式碼

SVM

blog.csdn.net/liukun321/a…

簡單來說,SVM就是用於區分不同的型別(車牌、非車牌)。SVM的訓練資料既有特徵又有標籤,通過訓練,讓機器可以自己找到特徵和標籤之間的聯絡,在面對只有特徵沒有標籤的資料時,可以判斷出標籤。屬於機器學習中的監督學習。線性可分、線性不可分,不可分的時候用核函式來區分:

核函式: 用於將不同型別進行提維

人臉檢測用的LBP提取特徵,這裡採取HOG來提取特徵。

SVM load模型, 模型是同樣是xml檔案

svm = SVM::load(svm_model);
CarPlateRecgnize p("/Users/xiuchengyin/Documents/Tina-NDK/OpencvCarRecgnize/resource/HOG_SVM_DATA2.xml");
複製程式碼

HOG特徵

區域性歸一化的梯度方向直方圖,是一種對影象區域性重疊區域的密集型描述符, 它通過計算區域性區域的梯度方向直方圖來構成特徵。

引數1(檢測視窗)的寬- 引數2(塊大小)的寬 結果與引數3(塊滑動增量)的餘數要為0 高也一樣

引數4是胞元大小,引數5是梯度方向

HOGDescriptor hog(Size(128, 64), Size(16, 16), Size(8, 8), Size(8, 8), 3);

初始化HOG變數

 //引數1的寬-引數2的寬 結果與引數3的餘數為0  高也一樣
    svmHog = new HOGDescriptor(Size(128,64),Size(16,16),Size(8,8),Size(8,8),3);
複製程式碼

基於OpenCV的車牌識別(Sobel、顏色定位)

基於OpenCV的車牌識別(Sobel、顏色定位)

檢測視窗被分為:((128-16)/8+1)*((64-16)/8+1)=105個塊(Block);

一個Block有4個胞元(Cell);

一個Cell的Hog描述子向量的長度是9;

統計梯度直方圖特徵,就是將梯度方向(0-360)劃分為x個區間,將影象化為16x16的若干個視窗,每個視窗又劃分為x個block,每個block再化為4個cell(8x8)。對每一個cell,算出每一畫素點的梯度方向,按梯度方向增加對應bin的值,最終綜合N個cell的梯度直方圖組成特徵。

簡單來說,車牌的邊緣與內部文字組成的一組資訊(在邊緣和角點的梯度值是很大的,邊緣和角點包含了很多物體的形狀資訊),HOG就是抽取這些資訊組成一個直方圖。

HOG : 梯度方向弱化光照的影響,適合捕獲輪廓。

LBP : 中心畫素的LBP值反映了該畫素周圍區域的紋理資訊。

SVM 依據HOG提取的特徵將所給的候選圖片進行評分,選取最優的:

string CarPlateRecgnize::plateRecgnize(Mat src) {
    vector< Mat > sobel_plates;
    //sobel定位
    sobelPlateLocation->location(src, sobel_plates);
    //顏色定位
    vector< Mat > color_plates;
    colorPlateLocation->location(src, color_plates);
    vector< Mat > plates;
    //把sobel_plates的內容 全部加入plates向量
    plates.insert(plates.end(),sobel_plates.begin(), sobel_plates.end());
    plates.insert(plates.end(), color_plates.begin(), color_plates.end());

    int index = -1;
    float minScore = FLT_MAX; //float的最大值
    //使用 svm 進行 評測
    for (int i = 0;i< plates.size();++i)
    {
        Mat plate = plates[i];
        //先灰度化,再二值化,灰度化只剩下一個通道
        Mat gray;
        cvtColor(plate, gray,COLOR_BGR2GRAY);
        //二值化 必須是以單通道進行
        Mat shold;
        threshold(gray, shold, 0, 255, THRESH_OTSU + THRESH_BINARY);
        //提取特徵
        Mat features;
        getHogFeatures(svmHog, shold, features);
        //features 進行轉化,把資料儲存成一行
        Mat samples = features.reshape(1,1);
        //轉化資料儲存格式
        samples.convertTo(samples, CV_32FC1 );

        //原始模式
        // svm: 直接告訴你這個資料是屬於什麼型別.
        // RAW_OUTPUT:讓svm 給出一個評分
//        char name[100];
//        sprintf(name, "候選車牌%d", i);
//        imshow(name, plate);

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

    Mat dst;
    if (index >= 0) {
        dst = plates[index].clone();
    }
//    imshow("車牌", dst);
//    waitKey();
//    釋放
    for (Mat p : plates) {
        p.release();
    }
    return string("123");
}
複製程式碼

svm評分如下:

/Users/xiuchengyin/Documents/Tina-NDK/OpencvCarRecgnize/cmake-build-debug/OpencvCarRecgnize
評分:-1.224322
評分:1.255759
評分:1.831937
評分:-0.070820
評分:1.525869
評分:1.117042
複製程式碼

測試最終取出來的就是我們的車牌選圖了。

參考:github.com/liuruoze/Ea…

www.cnblogs.com/subconsciou…

相關文章