前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >车牌识别从0到1之C++实现

车牌识别从0到1之C++实现

作者头像
用户9831583
发布2022-06-16 14:32:01
5140
发布2022-06-16 14:32:01
举报
文章被收录于专栏:码出名企路

车牌识别理论不做概述,网上比比皆是,请自行搜索哦。一个纯干货的公众号,断断续续写了一周,贴出代码,供交流学习

完整测试视频 http://mpvideo.qpic.cn/0b78omaakaaa4iab6q2a7jpfa46davzqabia.f10002.mp4?dis_k=9d499190dcbab2995e57bf15fbec140a&dis_t=1655361080&vid=wxv_1202375059562840064&format_id=10002&support_redirect=0&mmversion=false

图像处理部分

读入图像

代码语言:javascript
复制
void img_read(Mat& img)
{
    
    cvtColor(img,gray_img,CV_BGR2GRAY);
    imshow("gray",gray_img);
    waitKey(0);
}

图像缩放

代码语言:javascript
复制
//对图像进行缩放
void img_resize(Mat& gray)
{
    double w=gray.rows, h=gray.cols;
    if (h>MAX_WIDTH)
    {
        double change_rate=float(MAX_WIDTH/h);
        cout<<change_rate<<endl;

        resize(gray,resize_img,cv::Size(MAX_WIDTH, w*change_rate),
        cv::INTER_LINEAR);
    }
    cout<<h<<w;
    imshow("resize_img",resize_img);
    waitKey(0);   
}

可能车牌

代码语言:javascript
复制
/返回所有可能的矩形点集
vector<vector<Point>> img_contour(Mat& img)
{   
    Mat img_,copy_img,open_img,gauss_img,thresh_img,edge_img,
    edge_img1,edge_img2;
   /*
    IplImage* img_copy = cvCreateImage(cv::Size(img.cols, img.rows), 
    img.step, img.channels());
    Mat img_copy_=cvarrToMat(img_copy);
    cvCopy(img,img_copy);*/

    img.copyTo(copy_img);
    GaussianBlur(img,gauss_img, cv::Size(3,3), 0, 0, cv::BORDER_DEFAULT);
    imshow("GaussianBlur",gauss_img);
    waitKey(0);   

    Mat element = getStructuringElement(MORPH_RECT, Size(20,20));
    morphologyEx(gauss_img, img_, CV_MOP_OPEN, element);
    imshow("morphologyEx",img_);
    waitKey(0);  
    //图像叠加
    addWeighted(gauss_img,1,img_,-1,0,open_img);
    imshow("addWeighted",open_img);
    waitKey(0); 

    threshold(open_img, thresh_img, 0, 255, THRESH_BINARY && CV_THRESH_OTSU);
    Canny(thresh_img,edge_img,100,200);
    imshow("edge_img",edge_img);
    waitKey(0); 

    Mat kernel = getStructuringElement(MORPH_RECT, Size(10,10));
    morphologyEx(edge_img, edge_img1, CV_MOP_CLOSE, kernel);
    morphologyEx(edge_img1, edge_img2, CV_MOP_OPEN, kernel);
    imshow("edge_img2",edge_img2);
    waitKey(0); 

    vector<vector<Point>> contours;
    vector<Vec4i> hierarchy;
    findContours( edge_img2, contours, hierarchy,  CV_RETR_TREE, 
    CHAIN_APPROX_NONE, Point(0, 0));
    //轮廓个数
    cout<<contours.size()<<endl;

    return contours;
}

准确车牌

代码语言:javascript
复制
//返回车牌拟合矩形点集
vector<vector<Point>> find_car_plate(vector<vector<Point>>& contours)
{   
    vector<vector<Point>> tmp_contours(contours.size());
    int n=0;
    for(unsigned int i=0;i<contours.size();i++)
    {    
        //计算面积
        if(contourArea(contours[i]) > MIN_Area)
        {   
            for(unsigned int j=0;j<contours[i].size();j++)
                tmp_contours[n].push_back(contours[i][j]);
            n++;
            //cout<<contourArea(tmp_contours[i])<<endl;
        }
    }

     vector<vector<Point>> car_plate(1);//返回的车牌轮廓
     vector<RotatedRect> Rect_plate(n);
     Point2f rect_points[4];
     for( int i=0;i<n;i++)
     {    
            cout<<contourArea(tmp_contours[i])<<endl;
         //得到包裹的最小外切矩形,依次返回矩形 中心坐标,长和宽,倾斜角度
            Rect_plate[i]=minAreaRect(Mat(tmp_contours[i]));
            
            Rect_plate[i].points(rect_points);
            //外接矩形的四个顶点坐标
           // for(int j=0;j<n;j++)
              //  cout<<rect_points[j]<<endl;
            

            // 数据类型 Point2f center;  Size2f size;  float angle;//
          //  cout<< Rect_plate[i].center<<endl;//中心坐标
          //  cout<< Rect_plate[i].size<<endl;//长和宽
           // cout<< Rect_plate[i].center.x<<endl;//中心坐标
            //cout<< Rect_plate[i].size.width<<endl;//长和宽
          //  cout<< Rect_plate[i].angle<<endl;//倾斜角度

            int leng=Rect_plate[i].size.height;
            int width=Rect_plate[i].size.width;
            if(leng < width)
            {
                int tmp=leng;
                leng=width;
                width=tmp;
            }
           cout << leng <<"  "<<width<<endl;

           double ratio=double(leng)/double(width);

           cout<<"ratio "<<ratio<<endl;

           if( (ratio > 3) && (ratio < 5.5))
           {
               for(unsigned int j=0;j<tmp_contours[i].size();j++)
                  car_plate[0].push_back(tmp_contours[i][j]);
           }         
     }
       cout<<contourArea(car_plate[0])<<endl; 

       return car_plate;
}

保存车牌

代码语言:javascript
复制
//画出车牌矩形框,并输出车牌矩形
Mat draw_car_plate(vector<vector<Point>>& car_plate)
{
   //车牌四个顶点,找到对角顶点就好
    Point2f rect[4];
    RotatedRect rect_plate;
    rect_plate=minAreaRect(Mat(car_plate[0])); 
    rect_plate.points(rect);
    cout<<"draw car plate"<<endl;
    
    for(int i=0;i<4;i++)
        cout<<rect[i]<<endl;

    //找对角顶点
    int row_min=min(min(rect[0].x,rect[1].x),min(rect[1].x,rect[2].x));
    int col_min=min(min(rect[0].y,rect[1].y),min(rect[1].y,rect[2].y));
    int row_max=max(max(rect[0].x,rect[1].x),max(rect[1].x,rect[2].x));
    int col_max=max(max(rect[0].y,rect[1].y),max(rect[1].y,rect[2].y));
    
    cvtColor(resize_img,resize_img,CV_GRAY2BGR);
    rectangle(resize_img,Point(row_min,col_min), Point(row_max, col_max), Scalar(0,255,0),3);
    imshow("draw_img",resize_img);
    waitKey(0); 
    
    int l=row_max-row_min;
    int w=col_max-col_min;
    Mat car_plate_img=resize_img(Rect(row_min,col_min,l,w));
    imshow("car_plate_img",car_plate_img);
    waitKey(0); 
    imwrite("car_plate_img.jpg",car_plate_img);
    cout<<row_min<<" "<<col_min<<" "<<row_max<<" "<<col_max<<endl;

    return car_plate_img;

}

图像分割部分

分割车牌前奏

代码语言:javascript
复制
//根据设定的阈值和图片直方图,找出波峰,用于分隔字符
vector<double> find_waves(double threshold, vector<double> sum_each_row)
{
    double up_point=-1;//上升点
    bool is_peak=false;
    if (sum_each_row[0] > threshold)
    {
        up_point=0;
        is_peak=true;
    }
    vector<double> wave_peaks(sum_each_row.size());
    
    unsigned int i;
    for( i=0;i<sum_each_row.size();i++)
    {
        if(is_peak && sum_each_row[i] < threshold)
        {
            if (i-up_point >2)
            {
                is_peak=false;
                wave_peaks.push_back(up_point);//??????????
            }
        }
        else if(!is_peak && sum_each_row[i] >= threshold)
        {
            is_peak = true;
      up_point = i;
        }
    }

    if( is_peak && up_point !=-1 && (i-up_point > 4))
    {
         wave_peaks.push_back(up_point);
    }

    return wave_peaks;
}

分割字符二值

代码语言:javascript
复制

//返回车牌的 二值 图像 用于分割字符
Mat car_plate_binary_img(Mat& car_plate_img)
{   
    Mat gray_car_plate,binary_car_plate;
    cvtColor(car_plate_img,gray_car_plate,CV_BGR2GRAY);
    threshold(car_plate_img,binary_car_plate, 0, 255, THRESH_BINARY && CV_THRESH_OTSU);
    imshow("binary_car_plate",binary_car_plate);
    waitKey(0); 
    
    //存储每一行对应列的像素值
    vector<vector<double>> row_hists(binary_car_plate.rows);//必须设置大小,不然出错

    //cout<<binary_car_plate.rows<<"   "<<binary_car_plate.cols<<endl;

    for(int i=0;i<binary_car_plate.rows;i++)
    {
        for(int j=0;j<binary_car_plate.cols;j++)
        {   
            //cout<<binary_car_plate.at<double>(i,j)<<endl;
            row_hists[i].push_back(binary_car_plate.at<double>(i,j));
            cout<<row_hists[i][j]<<"  "<<
            binary_car_plate.at<double>(i,j)<<endl;
        }
    }

    vector<double> sum_each_row(binary_car_plate.cols);
    double sum=0;
    unsigned int n=sum_each_row.size();
    for(unsigned int j=0;j<n;j++)
    {
        for(unsigned int i=0;i<row_hists.size();i++)
        {
            sum+=row_hists[i][0];
        }
        sum_each_row.push_back(sum);
        sum=0;  
    }

    cout<<"***************"<<endl;
    
    // std::vector<double>::iterator biggest 
    //           = std::max_element(std::begin(v), std::end(v));
    //找到vector中的最小值
    auto row_min = std::min_element(std::begin(sum_each_row), std::end(sum_each_row));
    cout<<"row_min "<<*row_min<<endl;
     
     //对vector中元素求和
    double sum_sum_each_row=accumulate(sum_each_row.begin(),sum_each_row.end(),0);
    cout<<"sum_sum_each_row "<<sum_sum_each_row<<endl;

    //row的平均值
    double row_average=sum_sum_each_row/double(binary_car_plate.cols);
    double row_threshold=double(double(*row_min)+row_average)/2.0;
    cout<<"row_threshold "<<row_threshold<<endl;
    
    vector<double> wave_peaks=find_waves(row_threshold,sum_each_row);
    
   // for(auto iter=wave_peaks.cbegin();iter!=wave_peaks.cend();iter++)
  //  {
   //     cout<<*iter<<endl;
   // }

    //挑选跨度最大的波峰
    double wave_span=0.0;
    vector<double> select_span;
    for(unsigned int i=0;i<wave_peaks.size();i++)
    {
        double span=wave_peaks[1]-wave_peaks[0];
        if( span >= wave_span)
        {
            wave_span=span;
            select_span.push_back(wave_peaks[i]);
            
        }
    }

    Mat  plate_binary_img= binary_car_plate(Rect(select_span[0],select_span[1], 
    binary_car_plate.cols, binary_car_plate.rows));
    
    imshow("plate_binary_img",plate_binary_img);
    waitKey(0); 

    return  plate_binary_img;

}

图像聚类部分

计算街区距离

代码语言:javascript
复制
//进行字符分割
//计算曼哈顿距离,即街区距离
int dist_Vec( vector<double>& A,vector<double>& B )
{   
    int dist=0;
   
    for(unsigned i=0, n=A.size();i<n;i++)
    {
        dist+=abs(A[i]-B[i]);
    }

    return dist;
}

可能聚类中心

代码语言:javascript
复制
//返回中心矩阵
//功能:随机选取k个质心
//输出:centroids #返回一个m*n的质心矩阵
Mat cent_coord(Mat data, double k)
{
    int n=data.cols;//宽,shape[1]
    Mat __data=cv::Mat::zeros(Size(k,n),CV_8UC1);
    
    //double row_min=1000;
    //double row_max=0;
    for(int j=0;j<n;j++)
    {
        /*
        for( int i=0;i<data.rows;i++)
        {
            if(row_min < data.at<double>(i,j))
            {
                row_min=data.at<double>(i,j);
            }
        }
        double row_min_J=row_min;

        for( int i=0;i<data.rows;i++)
        {
            if(row_max > data.at<double>(i,j))
            {
                row_max=data.at<double>(i,j);
            }
        }
       double range_J=row_max-row_min_J;
        
        for(int i=0;i< k;i++)
       {
           __data.at<float>(i,j)=row_min_J+range_J*(i+1)/k;
       }*/

       //尝试以下代替上面注释的
       
       //取出第j列
       Mat data_row;
       for(int i=0;i<data.rows;i++)
       {
         data_row=data.colRange(i,j);
       }
       //取出mat 中最大值和最小值
       double minVal=0,maxVal=0;
       Point minPt,maxPt;
       minMaxLoc(data_row,&minVal,&maxVal,&minPt,&maxPt);
       double row_min_J=minVal;
       double range_J=maxVal-row_min_J;

       for(int i=0;i< k;i++)
       {
           __data.at<float>(i,j)=row_min_J+range_J*(i+1)/k;
       }

    }

    return __data;

}

K-均值聚类

代码语言:javascript
复制
//c++返回多个函数值
//设定一个结构体就好
struct Mul_Mat
{
    Mat center;
    Mat cluster;
};

Mul_Mat K_means;

//K均值聚类算法分割字符
Mul_Mat kMeans(Mat data, double k)
{
    int m=data.rows;//高 ,shape[0]
    K_means.cluster=cv::Mat::zeros(Size(m,2),CV_8UC1);

    K_means.center= cent_coord( data,  k);
    bool clusterChanged=true;
    
    vector<double> center,dataset;
    
    while(clusterChanged)
    {
        clusterChanged = false;
        for(int i=0;i<m;i++)
        {
            double min_dist=DBL_MAX;
            double min_index=-1;

            for(int j=0;j<k;j++)
            {   
                center.push_back(K_means.center.at<double>(i,j));
                dataset.push_back(K_means.center.at<double>(j,j));
            }

            for(int j=0;j<k;j++)
            {
                 double dist_means=dist_Vec(center,dataset);
                 if(dist_means < min_dist)
                 {
                     min_dist=dist_means;
                     min_index=j;
                 }
            }
            
            if(K_means.cluster.at<double>(i,0) != min_index)
                 clusterChanged = true;

            for(int j=0;j<k;j++)
                K_means.cluster.at<double>(i,j)=min_dist*min_dist;
        }
        
        vector<double> pts_cluster;
        for(int i=0;i<k;i++)
        {   
            for(int i=0;i<m;i++)
            {
            if(K_means.cluster.at<double>(i,0)!=0)
                pts_cluster.push_back(K_means.cluster.at<double>(i,0));
            }

            //vector 求均值
            double sum = std::accumulate(std::begin( pts_cluster), std::end( pts_cluster), 0.0);  
            double mean =  sum /  pts_cluster.size(); //均值  
            
            for(int j=0;j<m;j++)
                K_means.center.at<double>(i,j)=mean; 
        }

    }

    return K_means;
}

改进的K聚类

代码语言:javascript
复制
//bik 均值,降低SSE, 二分K均值算法
//返回一个包含质心的列表 和 数组
 pair<vector<pair<double,double>>,Mat> bk_means(Mat& data,double& k)
{
    int m=data.rows;
    Mat cluster_=cv::Mat::zeros(Size(m,2),CV_8UC1);
    vector<pair<double,double>> centList(m);

    vector<double> centroid;
    double sum=0.0;
    //对所有列取均值
    for(int i=0;i<data.cols;i++)
    {
        for(int j=0;j<m;j++)
        {
            sum+=data.at<double>(i,j);
        }
        double mean_row=sum/double(m);
        centroid.push_back(mean_row);
        sum=0;
    }


    vector<double> row_data;
    //计算每个点到质心的距离差
    for(int i=0;i<m;i++)
    {
        for( int j=0;j<data.cols;j++)
        {
            row_data.push_back(data.at<double>(i,j));
        }
       
        
         cout<<"  "<<dist_Vec(centroid,row_data)<<endl;

       // cluster_.at<double>(i,1)=dist_Vec(centroid,row_data);

         cluster_.at<double>(i,1)=255;
    }
 

   vector<double> row_cluster_;
   cout<<"cluster.row "<<cluster_.rows<<"cluster "<<cluster_.cols<<endl;

   for(int j=0;j<cluster_.cols;j++)
         row_cluster_.push_back(cluster_.at<double>(j,0));
   vector<double> is_i;

   cout<<"centList.size() "<<centList.size()<<endl;

    while(k)
    {
        double lowest_SSE=DBL_MAX;
        for(unsigned int i=0;i<centList.size();i++)
        {
                if(cluster_.at<double>(i,0)==i)
                    is_i.push_back(1);
                else
                    is_i.push_back(0);
        }
      
        Mat is_no0_cluster_;
        for(unsigned int i=0;i<is_i.size();i++)
        {
            if(is_i[i]!=0)
                is_no0_cluster_.at<double>(0,i)=i;
            is_no0_cluster_.at<double>(1,i)=0;
        }
      
        Mat _data;
        for(unsigned int i=0;i<is_i.size();i++)
        {
            int n=is_no0_cluster_.at<double>(0,i);

            for(int j=0;j<data.rows;j++)
                _data.at<double>(i,j)=data.at<double>(n,j);
        }

       

        Mul_Mat bik=kMeans( _data, 2.0);

         

        double sum_cluster_row1=0;
        cout<<bik.cluster.rows<<" bik.cluster.rows"<<endl;

       // for(int i=0;i<bik.cluster.rows;i++)
          //  sum_cluster_row1+=bik.cluster.at<double>(i,1);
       
        //
        

        vector<double> is_no_i;
        for(unsigned int i=0;i<centList.size();i++)
        {
                if(cluster_.at<double>(i,0)!=i)
                    is_no_i.push_back(1);
                else
                    is_no_i.push_back(0);
        }
       
        double sum_no_splite=0;
        for(unsigned int i=0;i<is_no_i.size();i++)
            sum_no_splite+=cluster_.at<double>(is_no_i[i],1);
        
      
        
        //
        unsigned int best_cent_splite=0;
        unsigned int v;
        Mat best_cents;
        Mat best_cluster;
        for(v=0;v<centList.size();v++)
        {
             if(sum_cluster_row1 + sum_no_splite < lowest_SSE )
          {
                 best_cent_splite=v;
                 best_cents=bik.center;
                best_cluster=bik.cluster;
                lowest_SSE=sum_cluster_row1 + sum_no_splite;
          }
        }

    
      //
       vector<double> is_1,is_0;
       for(unsigned int i=0;i<centList.size();i++)
        {
                if(cluster_.at<double>(i,0)==1)
                {
                      is_1.push_back(1);
                      is_0.push_back(0);
                }
                else
                {
                     is_1.push_back(0);
                     is_0.push_back(1);
                }
        }


        Mat is_1_, is_0_;
        for(unsigned int i=0;i<is_1.size();i++)
        {
            if(is_1[i]!=0)
                is_1_.at<double>(0,i)=i;
            is_1_.at<double>(1,i)=0;

            if(is_0[i] !=0)
                is_0_.at<double>(0,i)=i;
            is_0_.at<double>(1,i)=0;
        }
 

        for(unsigned int i=0;i<is_i.size();i++)
        {
            int n=is_1_.at<double>(0,i);
            int m=best_cluster.at<double>(i,0);

            int k=is_0_.at<double>(0,i);

            for(int j=0;j<best_cluster.cols;j++)
            {
                  best_cluster.at<double>(n,m)=centList.size();
                  best_cluster.at<double>(k,m)=best_cent_splite;
            }     
        }

       //返回值

       cluster_= best_cluster;
       for(unsigned int i=0;i<centList.size();i++)
       {
          // centList.push_back(make_pair(best_cents.at<double>(0,i),
           // best_cents.at<double>(1,i)));

         centList.push_back(make_pair(255,
            255));
       }

       k--;
    }
     

    pair<vector<pair<double,double>>, Mat> __data=std::make_pair(centList, cluster_);
    return __data;
}

分割字符

代码语言:javascript
复制
//此函数用来对车牌的二值图进行水平方向的切分,将字符分割出来
//输入:车牌的二值图,rows * cols的数组形式
//输出: 由分割后的车牌单个字符图像二值图矩阵组成的列表

//自定义pair排序
/*
bool cmp(pair<double,double> a, pair<double,double> b)
{
    if (a.first < b.first)
        return true;
    else
        return false;
}*/

//对vector 进行排序,并返回索引值
 struct node
    {
        pair<double,double> value;
        int index;
    };

    bool cmp(struct node a, struct node b)
{
        if (a.value.first< b.value.first)
        {
            return true;
        }
        return false;
    }

    void sort_indexes(vector<int> &idx, vector<pair<double,double>> &v)
{
        node* a = new node[v.size()];
        for (unsigned int i = 0; i < v.size(); i++)
        {
            a[i].value = v[i];
            a[i].index = i;
        }
        std::sort(a, a + v.size(), cmp);
        for (unsigned int i = 0; i < v.size(); i++) 
        {
            idx.push_back(a[i].index);
        }
       delete[] a;

    }


vector<double> row_list,col_list;

vector<Mat> split_carPlate_col(Mat& img)
{
     
    for(int i=0;i<img.cols;i++)
    {
        for(int j=0;j<img.rows;j++)
        {    

       // cout<<img.at<double>(i,j)<<endl;
            if(img.at<double>(i,j)==0)//????? 255
            {
                row_list.push_back(i);
                col_list.push_back(j);
                
            }
        }
    }
   
  
   cout<<row_list.size()<<" "<<col_list.size()<<endl;

   Mat dataArr(row_list.size(),col_list.size(), CV_8UC3);
   
   for(unsigned int i=0;i<col_list.size();i++)
   {
       dataArr.at<double>(col_list[i],row_list[i])=255;
   }
   vector<pair<double,double>> centroids;
   Mat clusterAssment;
   double k=7.0;

   std::tie(centroids,clusterAssment)=bk_means(dataArr,k);

   //sort(centroids.begin(),centroids.end(),cmp);
   
   vector<int> index;
   sort_indexes(index,centroids);
   
  
   vector<double> split_list;
   for(unsigned int i=0;i<centroids.size();i++)
   {
       int j=index[i];

         //nozero
        vector<int> is_index;
     for(unsigned int i=0;i<centroids.size();i++)
        {
                if(clusterAssment.at<double>(i,0)==j)
                    is_index.push_back(1);
                else
                    is_index.push_back(0);
        }
        
        Mat is_index_cluster_;
        for(unsigned int i=0;i<is_index.size();i++)
        {
            if(is_index[i]!=0)
                is_index_cluster_.at<double>(0,i)=i;
            is_index_cluster_.at<double>(1,i)=0;
        }
        
        Mat _data;
        for(unsigned int i=0;i<is_index.size();i++)
        {
            int n=is_index_cluster_.at<double>(0,i);

            for(int j=0;j<dataArr.rows;j++)
                _data.at<double>(i,j)=dataArr.at<double>(n,j);
        }

        //取出mat 中最大值和最小值
       double minVal=0,maxVal=0;
       Point minPt,maxPt;
       minMaxLoc(_data,&minVal,&maxVal,&minPt,&maxPt);
       
       split_list.push_back(minPt.y);
       split_list.push_back(maxPt.y);
        split_list.push_back(minPt.x);
       split_list.push_back(maxPt.x);

   }
   
   vector<Mat> character_list;
   for(unsigned int i=0;split_list.size();i++)
   {   
       //????????????
       Mat single_character_Arr=img(Rect(split_list[0],split_list[1],split_list[2],split_list[3]));
       character_list.push_back(single_character_Arr);
       imshow("single_character_Arr",single_character_Arr);
       waitKey(0);

   }

   return character_list;

}
本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2020-02-10,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 码出名企路 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
相关产品与服务
图像处理
图像处理基于腾讯云深度学习等人工智能技术,提供综合性的图像优化处理服务,包括图像质量评估、图像清晰度增强、图像智能裁剪等。
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档