【深度学习模型】智云视图中文车牌识别源码解析(二)

【深度学习模型】智云视图中文车牌识别源码解析(二)


这里写图片描述


感受

HyperLPR可以识别多种中文车牌包括白牌,新能源车牌,使馆车牌,教练车牌,武警车牌等。 代码不可谓不混乱(别忘了这是职业公司的准产品级代码,如果是什么毕业论文我就不说了)测试驱动什么的别想了,所以可能这次更了就不更了!!


代码结构

本次是Pipeline.cpp部分;

  • CNNRecognizer.cpp:加载模型预测标签;
  • FastDeskew.cpp:快速旋转歪斜的车牌;
  • FineMapping.cpp:绘制车牌检测框;
  • Pipeline.cpp:这是串联程序,串联起了plateDetection、fineMapping、plateSegmentation、generalRecognizer、segmentationFreeRecognizer;
  • PlateDetection.cpp:探测车牌在图中何处位置;
  • PlateSegmentation.cpp:将每一个字符从车牌图像中分离;
  • Recognizer.cpp:挨个识别字符并返回识别结果;
  • SegmentationFreeRecognizer.cpp:检测单个图像并把结果保存在mapping_table;

这里写图片描述


源代码

头文件:

/*------------------------------------------------------------------*/
/*--------------用于串联多个程序实现检测识别(头文件)----------------*/
/*------------------------------------------------------------------*/

#ifndef SWIFTPR_PIPLINE_H
#define SWIFTPR_PIPLINE_H

#include "PlateDetection.h"
#include "PlateSegmentation.h"
#include "CNNRecognizer.h"
#include "PlateInfo.h"
#include "FastDeskew.h"
#include "FineMapping.h"
#include "Recognizer.h"
#include "SegmentationFreeRecognizer.h"

namespace pr{

    //可能出现的字符集合;
    const std::vector<std::string> CH_PLATE_CODE{"京", "沪", "津", "渝", "冀", "晋", "蒙", "辽", "吉", "黑", "苏", "浙", "皖", "闽", "赣", "鲁", "豫", "鄂", "湘", "粤", "桂",
                                        "琼", "川", "贵", "云", "藏", "陕", "甘", "青", "宁", "新", "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A",
                                        "B", "C", "D", "E", "F", "G", "H", "J", "K", "L", "M", "N", "P", "Q", "R", "S", "T", "U", "V", "W", "X",
                                        "Y", "Z","港","学","使","警","澳","挂","军","北","南","广","沈","兰","成","济","海","民","航","空"};

    //定义不同的分割方法的编号;                                        
    const int SEGMENTATION_FREE_METHOD = 0;
    const int SEGMENTATION_BASED_METHOD = 1;

    //声明主类;
    class PipelinePR{
        public:
            //创建车牌位置检测模型实例指针;
            GeneralRecognizer *generalRecognizer;
            //创建抠出车牌图像模型实例指针;
            PlateDetection *plateDetection;
            //创建车牌分割模型实例指针;
            PlateSegmentation *plateSegmentation;
            //创建识别模型实例指针;
            FineMapping *fineMapping;
            //创建识别单个车牌图像的模型实例指针;
            SegmentationFreeRecognizer *segmentationFreeRecognizer;

            //构造函数;
            PipelinePR(std::string detector_filename,
                       std::string finemapping_prototxt,std::string finemapping_caffemodel,
                       std::string segmentation_prototxt,std::string segmentation_caffemodel,
                       std::string charRecognization_proto,std::string charRecognization_caffemodel,
                       std::string segmentationfree_proto,std::string segmentationfree_caffemodel
                       );
            //析构函数;
            ~PipelinePR();
            //保存识别结果;
            std::vector<std::string> plateRes;
            //执行单个车牌的识别;
            std::vector<PlateInfo> RunPiplineAsImage(cv::Mat plateImage,int method);
    };
}
#endif //SWIFTPR_PIPLINE_H

主程序:

/*----------------------------------------------------------*/
/*--------------用于串联多个程序实现检测识别----------------*/
/*----------------------------------------------------------*/


#include "../include/Pipeline.h"


namespace pr {
    //定义水平方向填充参数;
    const int HorizontalPadding = 4;
    /*
    -----------串接多个功能(构造函数)------------

    @detector_filename            :                         识别模型所在路径;
    @finemapping_prototxt         :   抠出车牌图像模型的prototxt定义文件路径;
    @finemapping_caffemodel       :           抠出车牌图像模型的模型文件路径;
    @segmentation_prototxt        : 分割图像字符的模型的prototxt定义文件路径;
    @segmentation_caffemodel      :         分割图像字符的模型的模型文件路径;
    @charRecognization_proto      :     字符识别的模型的prototxt定义文件路径;  
    @charRecognization_caffemodel :             字符识别的模型的模型文件路径;
    @segmentationfree_proto       : 识别单个车牌的模型的prototxt定义文件路径;  
    @segmentationfree_caffemodel  :         识别单个车牌的模型的模型文件路径;
    */ 
    PipelinePR::PipelinePR(std::string detector_filename,
                           std::string finemapping_prototxt, std::string finemapping_caffemodel,
                           std::string segmentation_prototxt, std::string segmentation_caffemodel,
                           std::string charRecognization_proto, std::string charRecognization_caffemodel,
                           std::string segmentationfree_proto,std::string segmentationfree_caffemodel) {
        //创建车牌位置检测模型实例指针;
        plateDetection = new PlateDetection(detector_filename);
        //创建抠出车牌图像模型实例指针;
        fineMapping = new FineMapping(fnemapping_prototxt, finemapping_caffemodel);
        //创建车牌分割模型实例指针;
        plateSegmentation = new PlateSegmentation(segmentation_prototxt, segmentation_caffemodel);
        //创建识别模型实例指针;
        generalRecognizer = new CNNRecognizer(charRecognization_proto, charRecognization_caffemodel);
        //创建识别单个车牌图像的模型实例指针;
        segmentationFreeRecognizer =  new SegmentationFreeRecognizer(segmentationfree_proto,segmentationfree_caffemodel);

    }

    /*
    -----------析构函数------------

    */
    PipelinePR::~PipelinePR() {
        //删除创建的各个实例指针;
        delete plateDetection;
        delete fineMapping;
        delete plateSegmentation;
        delete generalRecognizer;
        delete segmentationFreeRecognizer;


    }

    /*
    -----------识别车牌中各个字符------------

    @plateImage :  包含一个/多个车牌图像;
    @method     :         分割方法的编码;
    */
    std::vector<PlateInfo> PipelinePR::RunPiplineAsImage(cv::Mat plateImage,int method) {
        //声明结果列表;
        std::vector<PlateInfo> results;
        //保存车牌中间信息;
        std::vector<pr::PlateInfo> plates;
        //执行车牌粗略探测位置(结果存在plates内);
        plateDetection->plateDetectionRough(plateImage,plates,36,700);

        //迭代图中每个车牌;
        for (pr::PlateInfo plateinfo:plates) {
            //获取该车牌图像(image_finemapping的finemapping是为了分割出尽量只包含单个车牌的图像);
            cv::Mat image_finemapping = plateinfo.getPlateImage();
            //对图像垂直处理;
            image_finemapping = fineMapping->FineMappingVertical(image_finemapping);
            //校正角度;
            image_finemapping = pr::fastdeskew(image_finemapping, 5);
            //选择分割车牌字符的方法;
            //方法一:基础方法;
            if(method==SEGMENTATION_BASED_METHOD)
            {
                //对图像水平处理;
                image_finemapping = fineMapping->FineMappingHorizon(image_finemapping, 2, HorizontalPadding);
                //大小调整;
                cv::resize(image_finemapping, image_finemapping, cv::Size(136+HorizontalPadding, 36));
                //展示;
                //cv::imshow("image_finemapping",image_finemapping);
                //cv::waitKey(0);
                //设定为调整后的图像;
                plateinfo.setPlateImage(image_finemapping);
                //定义矩形框列表;
                std::vector<cv::Rect> rects;
                //对车牌图像的字符分割,结果存在rects;
                plateSegmentation->segmentPlatePipline(plateinfo, 1, rects);
                //将每个rect中的字符子图又存到plateinfo中去;
                plateSegmentation->ExtractRegions(plateinfo, rects);
                //复制图像并且制作边界;处理边界卷积(将image_finemapping的黑色边界填充);
                cv::copyMakeBorder(image_finemapping, image_finemapping, 0, 0, 0, 20, cv::BORDER_REPLICATE);
                //录入plateinfo;
                plateinfo.setPlateImage(image_finemapping);
                //进行识别;
                generalRecognizer->SegmentBasedSequenceRecognition(plateinfo);
                //解码中文字符;
                plateinfo.decodePlateNormal(pr::CH_PLATE_CODE);
            }
            //方法二:Segmentation-free
            else if(method==SEGMENTATION_FREE_METHOD)
            {

                //对图像水平处理;
                image_finemapping = fineMapping->FineMappingHorizon(image_finemapping, 4, HorizontalPadding+3);
                //大小调整;
                cv::resize(image_finemapping, image_finemapping, cv::Size(136+HorizontalPadding, 36));
                //存储图像;
                //cv::imwrite("./test.png",image_finemapping);
                //显示图像;
                //cv::imshow("image_finemapping",image_finemapping);
                //cv::waitKey(0);
                //录入plateinfo;
                plateinfo.setPlateImage(image_finemapping);
                //定义矩形框列表;
                //std::vector<cv::Rect> rects;
                //对单个图像进行识别;
                std::pair<std::string,float> res = segmentationFreeRecognizer->SegmentationFreeForSinglePlate(plateinfo.getPlateImage(),pr::CH_PLATE_CODE);
                //获取置信度;
                plateinfo.confidence = res.second;
                //车牌识别字符结果;
                plateinfo.setPlateName(res.first);
            }
            //结果加入列表;
            results.push_back(plateinfo);
        }

        //遍历识别结果
        //for (auto str:results) {
            //输出;
            //std::cout << str << std::endl;
        //}
        //返回结果;
        return results;
    }//namespace pr
}

测试程序:

/*------------------------------------------------------*/
/*--------------用于串联多个程序(测试)----------*/
/*------------------------------------------------------*/

#include "../include/Pipeline.h"

using namespace std;


/*
    -----------Levenshtein距离(度量车牌识别效果)------------
    编辑距离Edit Distance,又称Levenshtein距离,是指两个字串之间,由一个转成另一个所需的最少编辑操作次数;
    Levenshtein距离算法可见:https://blog.csdn.net/qq844352155/article/details/38686549;
    @s1 : 字符串1;
    @s2 : 字符串2;
*/
template<class T>
static unsigned int levenshtein_distance(const T &s1, const T &s2){
    //获取s1、s2的字符长;
    const size_t len1 = s1.size(), len2 = s2.size();
    //声明col和prevCol辅助计算;
    //col记录的是最新的计数情况;
    //prevCol记录的是上一轮的计数情况;
    std::vector<unsigned int> col(len2 + 1), prevCol(len2 + 1);
    //迭代给prevCol赋值;
    for (unsigned int i = 0; i < prevCol.size(); i++) prevCol[i] = i;
    //迭代比较;扫描两字符串(n*m级的);
    for (unsigned int i = 0; i < len1; i++) {
        col[0] = i + 1;
        //迭代遍历s2[j] in s2;
        for (unsigned int j = 0; j < len2; j++)
            //比较是否不一样,交换代价最小方式优先;
            col[j + 1] = min(
                    min(prevCol[1 + j] + 1, col[j] + 1),
                    prevCol[j] + (s1[i] == s2[j] ? 0 : 1));
        //col与prevCol交换其内容(swap函数:prevCol的类型要与该vector一样,大小可以不同.);
        col.swap(prevCol);
    }
    //返回结果;
    return prevCol[len2];
}



/*
    -----------测试函数:测试识别准确率------------

*/
void TEST_ACC(){
    //声明PipelinePR实例;
    pr::PipelinePR prc("model/cascade.xml",
                       "model/HorizonalFinemapping.prototxt","model/HorizonalFinemapping.caffemodel",
                       "model/Segmentation.prototxt","model/Segmentation.caffemodel",
                       "model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel",
                       "model/SegmenationFree-Inception.prototxt","model/SegmenationFree-Inception.caffemodel"
    );
    //申明文件流;
    ifstream file;
    //图片路径;
    string imagename;
    //声明计数变量;
    int n = 0,correct = 0,j = 0,sum = 0;
    //图片名文本文件路径;
    char filename[] = "/Users/yujinke/Downloads/general_test/1.txt";
    //图片文件根目录;
    string pathh = "/Users/yujinke/Downloads/general_test/";
    //打开图片名文本文件;
    file.open(filename, ios::in);
    //读取图片名文本文件;
    while (!file.eof())
    {
        //读取一个图片名到imagename;
        file >> imagename;
        //该图片绝对路径;
        string imgpath = pathh + imagename;
        //输出信息;
        std::cout << "------------------------------------------------" << endl;
        cout << "图片名:" << imagename << endl;
        //读入图片;
        cv::Mat image = cv::imread(imgpath);
        //显示图片;
        //cv::imshow("image", image);
        //cv::waitKey(0);
        //申明一个存储该图片车牌信息检测结果的列表,读取结果;
        std::vector<pr::PlateInfo> res = prc.RunPiplineAsImage(image,pr::SEGMENTATION_FREE_METHOD);

        //置信度变量;
        float conf = 0;
        //置信度列表;
        vector<float> con ;
        //车牌名列表;
        vector<string> name;
        //遍历结果res;
        for (auto st : res) {
            //置信度大于0.1;
            if (st.confidence > 0.1) {
                //输出检测信息及置信度;
                //std::cout << st.getPlateName() << " " << st.confidence << std::endl;
                //加入置信度列表con;
                con.push_back(st.confidence);
                //加入车牌名列表name;
                name.push_back(st.getPlateName());
                //置信度综述累计;
                //conf += st.confidence;
            }
            //置信度小于0.1;
            else
                cout << "no string" << endl;
        }
        //输出置信度总数;
        //std::cout << conf << std::endl;
        //置信度列表大小;
        int num = con.size();
        //置信度最大值变量;
        float max = 0;
        //声明存储车牌字符、车牌前两个字符、车牌正确的前两个字符的变量;
        string platestr, chpr, ch;
        //声明度量车牌识别误差的变量;
        int diff = 0,dif = 0;
        //遍历置信度列表;
        for (int i = 0; i < num; i++) {
            //寻找最大的置信度下标;
            if (con.at(i) > max)
            {
                //最大置信度max;
                max = con.at(i);
                //platestr取"最可信"(最大置信度)的结果;
                platestr = name.at(i);
            }

        }
        //输出最大置信度;
        //cout << "max:"<<max << endl;
        //输出车牌字符;
        cout << "string:" << platestr << endl;
        //车牌前两个字符;
        chpr = platestr.substr(0, 2);
        //车牌正确的前两个字符的变量;
        ch = imagename.substr(0, 2);
        //度量距离;
        diff = levenshtein_distance(imagename, platestr);
        //dif值;
        dif = diff - 4;
        //输出;
        cout << "差距:" <<dif << endl;
        //累计;
        sum += dif;
        //错误计数;
        if (ch != chpr) n++;
        //正确计数;
        if (diff == 0) correct++;
        //总数计数;
        j++;
    }
    //计算汉字识别准确率;
    float cha = 1 - float(n) / float(j);
    //输出;
    std::cout << "------------------------------------------------" << endl;
    //输出车牌总数;
    cout << "车牌总数:" << j << endl;
    //输出汉字识别准确率;
    cout << "汉字识别准确率:"<<cha << endl;
    //计算字符识别准确率;
    float chaccuracy = 1 - float(sum - n * 2) /float(j * 8);
    //输出;
    cout << "字符识别准确率:" << chaccuracy << endl;
}


/*
    -----------测试函数:画框------------

*/
void TEST_PIPELINE(){
    //声明PipelinePR实例;
    pr::PipelinePR prc("model/cascade.xml",
                      "model/HorizonalFinemapping.prototxt","model/HorizonalFinemapping.caffemodel",
                      "model/Segmentation.prototxt","model/Segmentation.caffemodel",
                      "model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel",
                       "model/SegmentationFree.prototxt","model/SegmentationFree.caffemodel"
                    );
    //读取一张图片;
    cv::Mat image = cv::imread("/Users/yujinke/ClionProjects/cpp_ocr_demo/test.png");
    //申明一个存储该图片车牌信息检测结果的列表,读取结果;
    std::vector<pr::PlateInfo> res = prc.RunPiplineAsImage(image,pr::SEGMENTATION_FREE_METHOD);
    //遍历结果;
    for(auto st:res) {
        //选择置信度大于0.75的结果;
        if(st.confidence>0.75) {
            //输出结果;
            std::cout << st.getPlateName() << " " << st.confidence << std::endl;
            //矩形框位置;
            cv::Rect region = st.getPlateRect();
            //绘制框;
            cv::rectangle(image,cv::Point(region.x,region.y),cv::Point(region.x+region.width,region.y+region.height),cv::Scalar(255,255,0),2);
        }
    }
    //显示结果;
    cv::imshow("image",image);
    //等待;
    cv::waitKey(0);
}

/*
    -----------测试函数:视频流------------

*/
void TEST_CAM()
{
    //获取视频;
    cv::VideoCapture capture("test1.mp4");
    //申明帧变量;
    cv::Mat frame;
    //声明PipelinePR实例;
    pr::PipelinePR prc("model/cascade.xml",
                       "model/HorizonalFinemapping.prototxt","model/HorizonalFinemapping.caffemodel",
                       "model/Segmentation.prototxt","model/Segmentation.caffemodel",
                       "model/CharacterRecognization.prototxt","model/CharacterRecognization.caffemodel",
                       "model/SegmentationFree.prototxt","model/SegmentationFree.caffemodel"
    );
    //循环读取帧;
    while(1) {
        //读取下一帧
        //异常处理;
        if (!capture.read(frame)) {
            std::cout << "读取视频失败" << std::endl;
            exit(1);
        }
        //图像旋转;
        //cv::transpose(frame,frame);
        //图像翻转;
        //cv::flip(frame,frame,2);
        //大小调整;
        //cv::resize(frame,frame,cv::Size(frame.cols/2,frame.rows/2));
        //识别结果;
        std::vector<pr::PlateInfo> res = prc.RunPiplineAsImage(frame,pr::SEGMENTATION_FREE_METHOD);
        //遍历识别结果
        for(auto st:res) {
            if(st.confidence>0.75) {
                //输出结果;
                std::cout << st.getPlateName() << " " << st.confidence << std::endl;
                //矩形框位置;
                cv::Rect region = st.getPlateRect();
                //绘制框;
                cv::rectangle(image,cv::Point(region.x,region.y),cv::Point(region.x+region.width,region.y+region.height),cv::Scalar(255,255,0),2);
            }
        }
        //显示;
        cv::imshow("image",frame);
        //等待;
        cv::waitKey(1);
   }
}

/*
    -----------主函数:运行------------

*/
int main()
{
    //准确率测试;
    TEST_ACC();
    //视频画框测试;
    //TEST_CAM();
    //图片画框测试;
    //TEST_PIPELINE();
    return 0 ;
}

猜你喜欢

转载自blog.csdn.net/hanss2/article/details/81101580