车牌识别系统

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010122972/article/details/77453801

将车牌识别系统细分为三个部分:
1.车牌检测
2.字符分割
3.字符识别

1.车牌检测

为实现更稳定的检测性能,使用yolo来对车牌进行识别。具体的训练方法见darknet用自己的数据进行训练。数据由EasyPR提供的200多张和自己从网上爬的几百张组成,爬虫脚本和数据标注方法见自行准备深度学习训练数据。最终得到的效果不错,下面是一些测试结果:

这里写图片描述

2.字符分割

利用EasyPR开源的代码来实现字符分割,在源代码下修改如下:
1.src/core/plate_locate.cpp中注释掉int CPlateLocate::plateLocate(Mat src, vector &resultVec, int index)函数中的两行:

int CPlateLocate::plateLocate(Mat src, vector<Mat> &resultVec, int index) {
  vector<CPlate> all_result_Plates;
  plateColorLocate(src, all_result_Plates, index);
  //plateSobelLocate(src, all_result_Plates, index);
  //plateMserLocate(src, all_result_Plates, index);
  for (size_t i = 0; i < all_result_Plates.size(); i++) {
    CPlate plate = all_result_Plates[i];
    resultVec.push_back(plate.getPlateMat());
  }
  return 0;
}

2.将test中全部删除,只保留chars.hpp和main.cpp
chars.hpp:

#ifndef EASYPR_CHARS_HPP
#define EASYPR_CHARS_HPP
namespace easypr {
namespace demo {
int test_chars_segment() {
  cv::Mat src = cv::imread("resources/image/try_3.jpg");
  std::vector<cv::Mat> resultVec;
  CPlateLocate plate;
  int result = plate.plateLocate(src, resultVec);
  if(result==0)
  {
    size_t num = resultVec.size();
    for(size_t j=0;j<num;j++)
    {
       std::vector<cv::Mat> resultVec2;
       cv::Mat resultMat = resultVec[j];
       CCharsSegment plate2;
       int result2 = plate2.charsSegment(resultMat, resultVec2);
       if(result2==0)
       {
         size_t num2=resultVec2.size();
         for(size_t i=0;i<num2;i++)
         {
            cv::Mat resultMat2 = resultVec2[i];
            cv::imshow("chars_segment", resultMat2);
            cv::waitKey(0);
         }
         cv::destroyWindow("chars_segment");
       }     
     }
   }
  return result;
}
}
}
#endif  // EASYPR_CHARS_HPP

main.cpp:

#include "easypr.h"
#include "chars.hpp"

namespace easypr {
namespace demo {
int testMain() {
  assert(test_chars_segment() == 0);
  return 0;
}
}  // namespace demo
}  // namespace easypr

int main(int argc, const char* argv[]) {
  easypr::demo::testMain();
  return 0;
}

这样处理后,终端在EasyPR根目录下输入

./demo

即可显示车牌分割后的字符

3.字符识别

依然使用darknet来进行字符识别。
不同于检测,这次使用darknet的classfier函数进行训练和识别,并考虑到分割出来的字符很小,使用了很浅的网络。
利用EasyPR提供的字符识别训练集(即resource/train/ann.7z)进行训练。将数据集解压后,每个文件夹中都有着对应的字符数据,对每个文件夹进行重命名处理。每个图像的命名格式采用(图片序号)_(标签).(图片格式)的格式进行命名。
这里写图片描述

重命名方法可以参考自行准备深度学习训练数据中的第2点。

重命名完成后,在darknet/data文件夹下新建char文件夹,并在char文件夹下新建image文件夹,将重命名完成的图片统一复制到image文件夹中,再使终端进入char路径(即darknet/data/char),输入

find `pwd`/image -name \*.jpg > train.list

会在char文件夹下自动生成train.list文件,其中包含每个图像的绝对路径。

在darknet/cfg文件夹下新建cifar.data(名字cifar可以自定义),并进行如下改写:

classes=65
train  = data/char/train.list
labels = data/char/labels.txt
backup = backup/
top=2

class 为识别类别数量,train为train.list路径,label为label文件路径。如果需要训练完后的评估,请参照train.list的方法生成val.list,然后再cifar中增加一行

valid=(your path)

然后在char文件夹下新建labels.txt:
这里写图片描述
每行一个类别标签。

标签的注意事项
1.不区分大小写
2.不能有包含关系,如ji和jin,不能出现这样的形式,可改为ji1和jin

最后,cfg文件夹下新建 cifar_small.cfg

[net]
batch=128
subdivisions=1
height=28
width=28
channels=3
max_crop=32
min_crop=32

hue=.1
saturation=.75
exposure=.75

learning_rate=0.01
policy=poly
power=4
max_batches = 5000000
momentum=0.9
decay=0.0005

[convolutional]
batch_normalize=1
filters=32
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=2

[convolutional]
batch_normalize=1
filters=64
size=3
stride=1
pad=1
activation=leaky

[maxpool]
size=2
stride=2

[convolutional]
batch_normalize=1
filters=128
size=3
stride=1
pad=1
activation=leaky

[convolutional]
filters=65
size=1
stride=1
pad=1
activation=leaky

[avgpool]

[softmax]
groups=1

[cost]
type=sse

avgpool上面那个convolutional中的filter为自己的类别数。max_batches和学习率之类的自己按照自己的需要来调整。

万事俱备,开始训练:
终端进入darknet根目录,输入

./darknet classifier train cfg/cifar.data cfg/cifar_small.cfg

然后坐等结果吧

猜你喜欢

转载自blog.csdn.net/u010122972/article/details/77453801