验证码识别是3年前的一个小愿望了(当时是做一个自动回帖器,抽奖iphone),但自己这两年主要在做分布式架构,今年终于抽出了空,又战胜了对数学的恐惧,在coursera上学习了吴恩达的机器学习和深度学习,验证码识别也算是对部分课程的实践,下面就来整理一下这次识别的过程。
1. 验证码识别主流程
- 目标检测,检测出字符边距,主要是获得weight,并输出坐标
- 图片黑白处理
- cnn识别算法
2. 识别细节
2.1 目标检测,检测出验证码图片中的字符边距
这个部分使用了yolov2算法,下面介绍一下yolov2算法:
A. 理论:yolov2算法整体来说其实是把图片分成一个一个小格子,然后每个格子会有一个输出
2.1.1 labelImg使用
http://blog.youkuaiyun.com/dcrmg/article/details/78496002
首先,采用上述博客的方法对验证码进行手工的打标签,把验证码的目标边框转换为darknet使用的格式, 接下来,如果需要使用gpu,那么按照
2.1.2 darknet使用教程
darknet的使用主要有2个部分要注意,
第一块是:gpu训练darknet
如果使用gpu来训练darknet(比如我就是去百度租了5块1个小时的gpu进行训练,效率真的提高了很多),那么需要注意一些安装和配置:
1. 百度的gpu机器需要安装cudnn
a. 首先下载 https://developer.nvidia.com/rdp/cudnn-download , 请注意一定要下载cudnn-8.0-linux-x64-v5.1.tgz,别的版本可能会有问题
b. 安装cudnn
$ cd ~
$ sudo tar xvf cudnn-8.0-linux-x64-v5.1.tgz
$ cd cuda/include
$ sudo cp *.h /usr/local/include/
$ cd ../lib64
$ sudo cp lib* /usr/local/lib/
$ cd /usr/local/lib# sudo chmod +r libcudnn.so.5.1.5
$ sudo ln -sf libcudnn.so.5.1.5 libcudnn.so.5
$ sudo ln -sf libcudnn.so.5 libcudnn.so
$ sudo ldconfig
- 编译darknet:
由于要使用gpu的方式,所以我们需要修改一些配置文件后进行编译:
a. 修改makefile
GPU=1
CUDNN=1
b. 修改cuda的路径
ifeq ($(GPU), 1)
COMMON+= -DGPU -I/usr/local/cuda-8.0/include/
CFLAGS+= -DGPU
LDFLAGS+= -L/usr/local/cuda-8.0/lib64 -lcuda -lcudart -lcublas -lcurand
#########################
NVCC=/usr/local/cuda-8.0/bin/nvcc
c. make即可, 这就会产生gpu训练版本的darknet源码了,现在只需要参考:http://blog.youkuaiyun.com/dcrmg/article/details/78496002 进行运行即可。
d. 接下来就训练吧,训练产生的参数都会保存到一个文件backup下面的yolo-voc.backup, 这个文件可要好好的保存哦,可以说weight在手,一切都飞不走,我在百度的机器上跑一天,基本测试集最后能达到100%的iou,和90%的覆盖比,基本够用啦。
./darknet detector train cfg/voc.data cfg/yolo-voc.2.0.cfg cfg/yolo-voc.weight
f. 上面的训练结束之后(以测试集能达到100%的iou,90%的覆盖比为准),我们又需要改造一下代码,让darknet能够输出中心位置的坐标,而不仅仅是在图片上显示出来。打开darknet的代码: src/image.c, 在如下函数新增一句话:
void draw_detections(image im, int num, float thresh, box *boxes, float **probs, float **masks, char **names, image **alphabet, int classes)
{
int i,j;
for(i = 0; i < num; ++i){
char labelstr[4096] = {
0};
int class = -1;
for(j = 0; j < classes; ++j){
if (probs[i][j] > thresh){
if (class < 0) {
strcat(labelstr, names[j]);
class = j;
} else {
strcat(labelstr, ", ");
strcat(labelstr, names[j]);
}
}
}
if(class >= 0){
int width = im.h * .006;
/*
if(0){
width = pow(prob, 1./2.)*10+1;
alphabet = 0;
}
*/
//printf("%d %s: %.0f%%\n", i, names[class], prob*100);
int offset = class*123457 % classes;
float red = get_color(2,offset,classes);
float green = get_color(1,offset,classes);
float blue = get_color(0,offset,classes);
float rgb[3];
//width = prob*20+2;
rgb[0] = red;