# 基于Tensorflow的验证码识别

## 步骤

1. 采集验证码图片
2. 处理图片：灰度、去噪、分割
3. 分类图片，准备训练数据：将0-9数字图片放入对应文件夹，转化成数据
4. 编写训练模型
5. 调用模型，形成识别系统

## 处理图片

### 灰度化

`int gray = (int) (0.3 * r + 0.59 * g + 0.11 * b);`

`gray = gray < 127 ? 0 : 255;`

### 分割图片

1. 扫描出每个数字左右边界，即找到最左最右的点
2. 根据这个边界进行横向裁剪
3. 如果发现最后裁剪出来不是4个数字而是3个或者更少，查看是否存在某个裁剪块的宽度比一般的要大，然后进行平均分割
4. 对每个裁剪下来的图片再进行纵向裁剪，将上下多余的空白区裁剪掉

## 分类图片，准备训练数据

`0,0,0,0,0,0,0,1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,....`

```[1, 0, 0, 0, 0, 0, 0, 0, 0, 0]  // 表示0
[0, 0, 0, 0, 0, 1, 0, 0, 0, 0]  // 表示5```

## 编写训练模型

### CNN

TF提供2维卷积函数

`tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')`

`tf.nn.max_pool(x, ksize=[1,2,2,1], strides=[1,2,2,1], padding='SAME')`

x是输入；ksize表示使用2x2池化，即将2x2的色块降为1x1，最大池化会保留灰度值最高的那个像素点；因为需要整体缩小图片所以这里strides在横向纵向的步长都为2。

### 构建处理层

```def weight_variable(shape, name):
initial = tf.truncated_normal(shape, stddev=0.1)
return tf.Variable(initial, name=name)

def bias_variable(shape, name):
initial = tf.constant(0.1, shape=shape)
return tf.Variable(initial, name=name)

def conv2d(x, W):
# stride [1, x_movement, y_movement, 1]
return tf.nn.conv2d(x, W, strides=[1, 1, 1, 1], padding='SAME')

def max_pool_2x2(x):
# stride [1, x_movement, y_movement, 1]

#define weights & biases
weights = {
"h1": weight_variable([5, 5, 1, 32], "w_h1"),
"h2": weight_variable([5, 5, 32, 64], "w_h2"),
"f1": weight_variable([9*9*64, 1024], "w_f1"),
"f2": weight_variable([1024, 10], "w_f2")
}

biases = {
"h1": bias_variable([32], "b_h1"),
"h2": bias_variable([64], "b_h2"),
"f1": bias_variable([1024], "b_f1"),
"f2": bias_variable([10], "b_f2")
}

x_image = tf.reshape(xs, [-1, 36, 36, 1]) # [n_samples, 28,28,1]

## conv1 layer ##
W_conv1 = weights["h1"] # patch 5x5, in size 1, out size 32
b_conv1 = biases["h1"]
h_conv1 = tf.nn.relu(conv2d(x_image, W_conv1) + b_conv1) # output size 36x36x32
h_pool1 = max_pool_2x2(h_conv1)                          # output size 18x18x32

## conv2 layer ##
W_conv2 = weights["h2"] # patch 5x5, in size 32, out size 64
b_conv2 = biases["h2"]
h_conv2 = tf.nn.relu(conv2d(h_pool1, W_conv2) + b_conv2) # output size 18x18x64
h_pool2 = max_pool_2x2(h_conv2)                          # output size 9x9x64

## fc1 layer ##
W_fc1 = weights["f1"]
b_fc1 = biases["f1"]
# [n_samples, 9, 9, 64] ->> [n_samples, 9*9*64]
h_pool2_flat = tf.reshape(h_pool2, [-1, 9*9*64])
h_fc1 = tf.nn.relu(tf.matmul(h_pool2_flat, W_fc1) + b_fc1)
h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

## fc2 layer ##
W_fc2 = weights["f2"]
b_fc2 = biases["f2"]
prediction = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)```

### 定义Loss和Optimizer

```cross_entropy = tf.reduce_mean(-tf.reduce_sum(ys * tf.log(prediction),
reduction_indices=[1]))       # loss

### 执行训练

```for i in range(150):
sess.run(train_step, feed_dict={xs: datas, ys: labels, keep_prob: 0.5})```

### 计算正确率

```#y_pre 是每个数据对应各个类别（0-9）的概率如[[0.1,0.2,0.3,0.4...],[0.2,0.1,0.9....]]
y_pre = sess.run(prediction, feed_dict={xs: test_datas, keep_prob: 1})
#tf.argmax返回最大数的索引，再比较预测数据索引和测试数据真实索引
correct_prediction = tf.equal(tf.argmax(y_pre,1), tf.argmax(test_labels,1))
#将上面比较结果True转为1，False转为0，相加计算平均值
accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))
result = sess.run(accuracy)```

## 调用模型

### 保存模型

```saver = tf.train.Saver()
save_path = saver.save(sess, "./models/save_net.ckpt")```

### 恢复模型

```saver = tf.train.Saver()
saver.restore(sess, "./final_models/save_net.ckpt")```

19 篇文章24 人订阅

0 条评论

## 相关文章

2705

1532

3958

### 谈谈Tensorflow的dropout

Dropout这个概念已经推出4年了，它的详细描述见论文（https://arxiv.org/abs/1207.0580）。可是呢，它仿佛是个犹抱琵琶半遮面的美...

3467

### 【干货】用PyTorch进行RNN语言建模 - Packed Batching和Tied Weight

【导读】PyTorch是一个日益流行的神经网络框架，自然支持RNN。但是关于RNN，Pytorch的官方教程描述的不怎么详细，这篇文章将介绍使用Pytorch实...

5132

4546

1404

### Github上一些精致且实用的TensorFlow项目及相关论文

【导读】Github上有许多成熟的TensorFlow代码和模型，可以直接用于科研和工程中。本文会介绍其中一些与自动问答、机器翻译、风格转换等相关的精致且实用的...

1443

1473

5275