1. 程式人生 > >TensorFlow車牌識別完整版(含車牌資料集)

TensorFlow車牌識別完整版(含車牌資料集)

在之前釋出的一篇博文《MNIST資料集實現車牌識別--初步演示版》中,我們演示瞭如何使用TensorFlow進行車牌識別,但是,當時採用的資料集是MNIST數字手寫體,只能分類0-9共10個數字,無法分類省份簡稱和字母,侷限性較大,無實際意義。

經過影象定位分割處理,博主收集了相關省份簡稱和26個字母的圖片資料集,結合前述博文中貼出的python+TensorFlow程式碼,實現了完整的車牌識別功能。本著分享精神,在此送上全部程式碼和車牌資料集。

省份簡稱訓練+識別程式碼(儲存檔名為train-license-province.py)(拷貝程式碼請務必注意python文字縮排,只要有一處縮排錯誤,就無法得到正確結果,或者出現異常

):

  1. #!/usr/bin/python3.5

  2. # -*- coding: utf-8 -*-

  3. import sys

  4. import os

  5. import time

  6. import random

  7. import numpy as np

  8. import tensorflow as tf

  9. from PIL import Image

  10. SIZE = 1280

  11. WIDTH = 32

  12. HEIGHT = 40

  13. NUM_CLASSES = 6

  14. iterations = 300

  15. SAVER_DIR = "train-saver/province/"

  16. PROVINCES = ("京","閩","粵","蘇","滬","浙")

  17. nProvinceIndex = 0

  18. time_begin = time.time()

  19. # 定義輸入節點,對應於圖片畫素值矩陣集合和圖片標籤(即所代表的數字)

  20. x = tf.placeholder(tf.float32, shape=[None, SIZE])

  21. y_ = tf.placeholder(tf.float32, shape=[None, NUM_CLASSES])

  22. x_image = tf.reshape(x, [-1, WIDTH, HEIGHT, 1])

  23. # 定義卷積函式

  24. def conv_layer(inputs, W, b, conv_strides, kernel_size, pool_strides, padding):

  25. L1_conv = tf.nn.conv2d(inputs, W, strides=conv_strides, padding=padding)

  26. L1_relu = tf.nn.relu(L1_conv + b)

  27. return tf.nn.max_pool(L1_relu, ksize=kernel_size, strides=pool_strides, padding='SAME')

  28. # 定義全連線層函式

  29. def full_connect(inputs, W, b):

  30. return tf.nn.relu(tf.matmul(inputs, W) + b)

  31. if __name__ =='__main__' and sys.argv[1]=='train':

  32. # 第一次遍歷圖片目錄是為了獲取圖片總數

  33. input_count = 0

  34. for i in range(0,NUM_CLASSES):

  35. dir = './train_images/training-set/chinese-characters/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  36. for rt, dirs, files in os.walk(dir):

  37. for filename in files:

  38. input_count += 1

  39. # 定義對應維數和各維長度的陣列

  40. input_images = np.array([[0]*SIZE for i in range(input_count)])

  41. input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])

  42. # 第二次遍歷圖片目錄是為了生成圖片資料和標籤

  43. index = 0

  44. for i in range(0,NUM_CLASSES):

  45. dir = './train_images/training-set/chinese-characters/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  46. for rt, dirs, files in os.walk(dir):

  47. for filename in files:

  48. filename = dir + filename

  49. img = Image.open(filename)

  50. width = img.size[0]

  51. height = img.size[1]

  52. for h in range(0, height):

  53. for w in range(0, width):

  54. # 通過這樣的處理,使數字的線條變細,有利於提高識別準確率

  55. if img.getpixel((w, h)) > 230:

  56. input_images[index][w+h*width] = 0

  57. else:

  58. input_images[index][w+h*width] = 1

  59. input_labels[index][i] = 1

  60. index += 1

  61. # 第一次遍歷圖片目錄是為了獲取圖片總數

  62. val_count = 0

  63. for i in range(0,NUM_CLASSES):

  64. dir = './train_images/validation-set/chinese-characters/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  65. for rt, dirs, files in os.walk(dir):

  66. for filename in files:

  67. val_count += 1

  68. # 定義對應維數和各維長度的陣列

  69. val_images = np.array([[0]*SIZE for i in range(val_count)])

  70. val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])

  71. # 第二次遍歷圖片目錄是為了生成圖片資料和標籤

  72. index = 0

  73. for i in range(0,NUM_CLASSES):

  74. dir = './train_images/validation-set/chinese-characters/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  75. for rt, dirs, files in os.walk(dir):

  76. for filename in files:

  77. filename = dir + filename

  78. img = Image.open(filename)

  79. width = img.size[0]

  80. height = img.size[1]

  81. for h in range(0, height):

  82. for w in range(0, width):

  83. # 通過這樣的處理,使數字的線條變細,有利於提高識別準確率

  84. if img.getpixel((w, h)) > 230:

  85. val_images[index][w+h*width] = 0

  86. else:

  87. val_images[index][w+h*width] = 1

  88. val_labels[index][i] = 1

  89. index += 1

  90. with tf.Session() as sess:

  91. # 第一個卷積層

  92. W_conv1 = tf.Variable(tf.truncated_normal([8, 8, 1, 16], stddev=0.1), name="W_conv1")

  93. b_conv1 = tf.Variable(tf.constant(0.1, shape=[16]), name="b_conv1")

  94. conv_strides = [1, 1, 1, 1]

  95. kernel_size = [1, 2, 2, 1]

  96. pool_strides = [1, 2, 2, 1]

  97. L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')

  98. # 第二個卷積層

  99. W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 16, 32], stddev=0.1), name="W_conv2")

  100. b_conv2 = tf.Variable(tf.constant(0.1, shape=[32]), name="b_conv2")

  101. conv_strides = [1, 1, 1, 1]

  102. kernel_size = [1, 1, 1, 1]

  103. pool_strides = [1, 1, 1, 1]

  104. L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')

  105. # 全連線層

  106. W_fc1 = tf.Variable(tf.truncated_normal([16 * 20 * 32, 512], stddev=0.1), name="W_fc1")

  107. b_fc1 = tf.Variable(tf.constant(0.1, shape=[512]), name="b_fc1")

  108. h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])

  109. h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)

  110. # dropout

  111. keep_prob = tf.placeholder(tf.float32)

  112. h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

  113. # readout層

  114. W_fc2 = tf.Variable(tf.truncated_normal([512, NUM_CLASSES], stddev=0.1), name="W_fc2")

  115. b_fc2 = tf.Variable(tf.constant(0.1, shape=[NUM_CLASSES]), name="b_fc2")

  116. # 定義優化器和訓練op

  117. y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

  118. cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))

  119. train_step = tf.train.AdamOptimizer((1e-4)).minimize(cross_entropy)

  120. correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))

  121. accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

  122. # 初始化saver

  123. saver = tf.train.Saver()

  124. sess.run(tf.global_variables_initializer())

  125. time_elapsed = time.time() - time_begin

  126. print("讀取圖片檔案耗費時間:%d秒" % time_elapsed)

  127. time_begin = time.time()

  128. print ("一共讀取了 %s 個訓練影象, %s 個標籤" % (input_count, input_count))

  129. # 設定每次訓練op的輸入個數和迭代次數,這裡為了支援任意圖片總數,定義了一個餘數remainder,譬如,如果每次訓練op的輸入個數為60,圖片總數為150張,則前面兩次各輸入60張,最後一次輸入30張(餘數30)

  130. batch_size = 60

  131. iterations = iterations

  132. batches_count = int(input_count / batch_size)

  133. remainder = input_count % batch_size

  134. print ("訓練資料集分成 %s 批, 前面每批 %s 個數據,最後一批 %s 個數據" % (batches_count+1, batch_size, remainder))

  135. # 執行訓練迭代

  136. for it in range(iterations):

  137. # 這裡的關鍵是要把輸入陣列轉為np.array

  138. for n in range(batches_count):

  139. train_step.run(feed_dict={x: input_images[n*batch_size:(n+1)*batch_size], y_: input_labels[n*batch_size:(n+1)*batch_size], keep_prob: 0.5})

  140. if remainder > 0:

  141. start_index = batches_count * batch_size;

  142. train_step.run(feed_dict={x: input_images[start_index:input_count-1], y_: input_labels[start_index:input_count-1], keep_prob: 0.5})

  143. # 每完成五次迭代,判斷準確度是否已達到100%,達到則退出迭代迴圈

  144. iterate_accuracy = 0

  145. if it%5 == 0:

  146. iterate_accuracy = accuracy.eval(feed_dict={x: val_images, y_: val_labels, keep_prob: 1.0})

  147. print ('第 %d 次訓練迭代: 準確率 %0.5f%%' % (it, iterate_accuracy*100))

  148. if iterate_accuracy >= 0.9999 and it >= 150:

  149. break;

  150. print ('完成訓練!')

  151. time_elapsed = time.time() - time_begin

  152. print ("訓練耗費時間:%d秒" % time_elapsed)

  153. time_begin = time.time()

  154. # 儲存訓練結果

  155. if not os.path.exists(SAVER_DIR):

  156. print ('不存在訓練資料儲存目錄,現在建立儲存目錄')

  157. os.makedirs(SAVER_DIR)

  158. saver_path = saver.save(sess, "%smodel.ckpt"%(SAVER_DIR))

  159. if __name__ =='__main__' and sys.argv[1]=='predict':

  160. saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))

  161. with tf.Session() as sess:

  162. model_file=tf.train.latest_checkpoint(SAVER_DIR)

  163. saver.restore(sess, model_file)

  164. # 第一個卷積層

  165. W_conv1 = sess.graph.get_tensor_by_name("W_conv1:0")

  166. b_conv1 = sess.graph.get_tensor_by_name("b_conv1:0")

  167. conv_strides = [1, 1, 1, 1]

  168. kernel_size = [1, 2, 2, 1]

  169. pool_strides = [1, 2, 2, 1]

  170. L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')

  171. # 第二個卷積層

  172. W_conv2 = sess.graph.get_tensor_by_name("W_conv2:0")

  173. b_conv2 = sess.graph.get_tensor_by_name("b_conv2:0")

  174. conv_strides = [1, 1, 1, 1]

  175. kernel_size = [1, 1, 1, 1]

  176. pool_strides = [1, 1, 1, 1]

  177. L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')

  178. # 全連線層

  179. W_fc1 = sess.graph.get_tensor_by_name("W_fc1:0")

  180. b_fc1 = sess.graph.get_tensor_by_name("b_fc1:0")

  181. h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])

  182. h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)

  183. # dropout

  184. keep_prob = tf.placeholder(tf.float32)

  185. h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

  186. # readout層

  187. W_fc2 = sess.graph.get_tensor_by_name("W_fc2:0")

  188. b_fc2 = sess.graph.get_tensor_by_name("b_fc2:0")

  189. # 定義優化器和訓練op

  190. conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

  191. for n in range(1,2):

  192. path = "test_images/%s.bmp" % (n)

  193. img = Image.open(path)

  194. width = img.size[0]

  195. height = img.size[1]

  196. img_data = [[0]*SIZE for i in range(1)]

  197. for h in range(0, height):

  198. for w in range(0, width):

  199. if img.getpixel((w, h)) < 190:

  200. img_data[0][w+h*width] = 1

  201. else:

  202. img_data[0][w+h*width] = 0

  203. result = sess.run(conv, feed_dict = {x: np.array(img_data), keep_prob: 1.0})

  204. max1 = 0

  205. max2 = 0

  206. max3 = 0

  207. max1_index = 0

  208. max2_index = 0

  209. max3_index = 0

  210. for j in range(NUM_CLASSES):

  211. if result[0][j] > max1:

  212. max1 = result[0][j]

  213. max1_index = j

  214. continue

  215. if (result[0][j]>max2) and (result[0][j]<=max1):

  216. max2 = result[0][j]

  217. max2_index = j

  218. continue

  219. if (result[0][j]>max3) and (result[0][j]<=max2):

  220. max3 = result[0][j]

  221. max3_index = j

  222. continue

  223. nProvinceIndex = max1_index

  224. print ("概率: [%s %0.2f%%] [%s %0.2f%%] [%s %0.2f%%]" % (PROVINCES[max1_index],max1*100, PROVINCES[max2_index],max2*100, PROVINCES[max3_index],max3*100))

  225. print ("省份簡稱是: %s" % PROVINCES[nProvinceIndex])

城市代號訓練+識別程式碼(儲存檔名為train-license-letters.py):

  1. #!/usr/bin/python3.5

  2. # -*- coding: utf-8 -*-

  3. import sys

  4. import os

  5. import time

  6. import random

  7. import numpy as np

  8. import tensorflow as tf

  9. from PIL import Image

  10. SIZE = 1280

  11. WIDTH = 32

  12. HEIGHT = 40

  13. NUM_CLASSES = 26

  14. iterations = 500

  15. SAVER_DIR = "train-saver/letters/"

  16. LETTERS_DIGITS = ("A","B","C","D","E","F","G","H","J","K","L","M","N","P","Q","R","S","T","U","V","W","X","Y","Z","I","O")

  17. license_num = ""

  18. time_begin = time.time()

  19. # 定義輸入節點,對應於圖片畫素值矩陣集合和圖片標籤(即所代表的數字)

  20. x = tf.placeholder(tf.float32, shape=[None, SIZE])

  21. y_ = tf.placeholder(tf.float32, shape=[None, NUM_CLASSES])

  22. x_image = tf.reshape(x, [-1, WIDTH, HEIGHT, 1])

  23. # 定義卷積函式

  24. def conv_layer(inputs, W, b, conv_strides, kernel_size, pool_strides, padding):

  25. L1_conv = tf.nn.conv2d(inputs, W, strides=conv_strides, padding=padding)

  26. L1_relu = tf.nn.relu(L1_conv + b)

  27. return tf.nn.max_pool(L1_relu, ksize=kernel_size, strides=pool_strides, padding='SAME')

  28. # 定義全連線層函式

  29. def full_connect(inputs, W, b):

  30. return tf.nn.relu(tf.matmul(inputs, W) + b)

  31. if __name__ =='__main__' and sys.argv[1]=='train':

  32. # 第一次遍歷圖片目錄是為了獲取圖片總數

  33. input_count = 0

  34. for i in range(0+10,NUM_CLASSES+10):

  35. dir = './train_images/training-set/letters/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  36. for rt, dirs, files in os.walk(dir):

  37. for filename in files:

  38. input_count += 1

  39. # 定義對應維數和各維長度的陣列

  40. input_images = np.array([[0]*SIZE for i in range(input_count)])

  41. input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])

  42. # 第二次遍歷圖片目錄是為了生成圖片資料和標籤

  43. index = 0

  44. for i in range(0+10,NUM_CLASSES+10):

  45. dir = './train_images/training-set/letters/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  46. for rt, dirs, files in os.walk(dir):

  47. for filename in files:

  48. filename = dir + filename

  49. img = Image.open(filename)

  50. width = img.size[0]

  51. height = img.size[1]

  52. for h in range(0, height):

  53. for w in range(0, width):

  54. # 通過這樣的處理,使數字的線條變細,有利於提高識別準確率

  55. if img.getpixel((w, h)) > 230:

  56. input_images[index][w+h*width] = 0

  57. else:

  58. input_images[index][w+h*width] = 1

  59. #print ("i=%d, index=%d" % (i, index))

  60. input_labels[index][i-10] = 1

  61. index += 1

  62. # 第一次遍歷圖片目錄是為了獲取圖片總數

  63. val_count = 0

  64. for i in range(0+10,NUM_CLASSES+10):

  65. dir = './train_images/validation-set/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  66. for rt, dirs, files in os.walk(dir):

  67. for filename in files:

  68. val_count += 1

  69. # 定義對應維數和各維長度的陣列

  70. val_images = np.array([[0]*SIZE for i in range(val_count)])

  71. val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])

  72. # 第二次遍歷圖片目錄是為了生成圖片資料和標籤

  73. index = 0

  74. for i in range(0+10,NUM_CLASSES+10):

  75. dir = './train_images/validation-set/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  76. for rt, dirs, files in os.walk(dir):

  77. for filename in files:

  78. filename = dir + filename

  79. img = Image.open(filename)

  80. width = img.size[0]

  81. height = img.size[1]

  82. for h in range(0, height):

  83. for w in range(0, width):

  84. # 通過這樣的處理,使數字的線條變細,有利於提高識別準確率

  85. if img.getpixel((w, h)) > 230:

  86. val_images[index][w+h*width] = 0

  87. else:

  88. val_images[index][w+h*width] = 1

  89. val_labels[index][i-10] = 1

  90. index += 1

  91. with tf.Session() as sess:

  92. # 第一個卷積層

  93. W_conv1 = tf.Variable(tf.truncated_normal([8, 8, 1, 16], stddev=0.1), name="W_conv1")

  94. b_conv1 = tf.Variable(tf.constant(0.1, shape=[16]), name="b_conv1")

  95. conv_strides = [1, 1, 1, 1]

  96. kernel_size = [1, 2, 2, 1]

  97. pool_strides = [1, 2, 2, 1]

  98. L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')

  99. # 第二個卷積層

  100. W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 16, 32], stddev=0.1), name="W_conv2")

  101. b_conv2 = tf.Variable(tf.constant(0.1, shape=[32]), name="b_conv2")

  102. conv_strides = [1, 1, 1, 1]

  103. kernel_size = [1, 1, 1, 1]

  104. pool_strides = [1, 1, 1, 1]

  105. L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')

  106. # 全連線層

  107. W_fc1 = tf.Variable(tf.truncated_normal([16 * 20 * 32, 512], stddev=0.1), name="W_fc1")

  108. b_fc1 = tf.Variable(tf.constant(0.1, shape=[512]), name="b_fc1")

  109. h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])

  110. h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)

  111. # dropout

  112. keep_prob = tf.placeholder(tf.float32)

  113. h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

  114. # readout層

  115. W_fc2 = tf.Variable(tf.truncated_normal([512, NUM_CLASSES], stddev=0.1), name="W_fc2")

  116. b_fc2 = tf.Variable(tf.constant(0.1, shape=[NUM_CLASSES]), name="b_fc2")

  117. # 定義優化器和訓練op

  118. y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

  119. cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))

  120. train_step = tf.train.AdamOptimizer((1e-4)).minimize(cross_entropy)

  121. correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))

  122. accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

  123. sess.run(tf.global_variables_initializer())

  124. time_elapsed = time.time() - time_begin

  125. print("讀取圖片檔案耗費時間:%d秒" % time_elapsed)

  126. time_begin = time.time()

  127. print ("一共讀取了 %s 個訓練影象, %s 個標籤" % (input_count, input_count))

  128. # 設定每次訓練op的輸入個數和迭代次數,這裡為了支援任意圖片總數,定義了一個餘數remainder,譬如,如果每次訓練op的輸入個數為60,圖片總數為150張,則前面兩次各輸入60張,最後一次輸入30張(餘數30)

  129. batch_size = 60

  130. iterations = iterations

  131. batches_count = int(input_count / batch_size)

  132. remainder = input_count % batch_size

  133. print ("訓練資料集分成 %s 批, 前面每批 %s 個數據,最後一批 %s 個數據" % (batches_count+1, batch_size, remainder))

  134. # 執行訓練迭代

  135. for it in range(iterations):

  136. # 這裡的關鍵是要把輸入陣列轉為np.array

  137. for n in range(batches_count):

  138. train_step.run(feed_dict={x: input_images[n*batch_size:(n+1)*batch_size], y_: input_labels[n*batch_size:(n+1)*batch_size], keep_prob: 0.5})

  139. if remainder > 0:

  140. start_index = batches_count * batch_size;

  141. train_step.run(feed_dict={x: input_images[start_index:input_count-1], y_: input_labels[start_index:input_count-1], keep_prob: 0.5})

  142. # 每完成五次迭代,判斷準確度是否已達到100%,達到則退出迭代迴圈

  143. iterate_accuracy = 0

  144. if it%5 == 0:

  145. iterate_accuracy = accuracy.eval(feed_dict={x: val_images, y_: val_labels, keep_prob: 1.0})

  146. print ('第 %d 次訓練迭代: 準確率 %0.5f%%' % (it, iterate_accuracy*100))

  147. if iterate_accuracy >= 0.9999 and it >= iterations:

  148. break;

  149. print ('完成訓練!')

  150. time_elapsed = time.time() - time_begin

  151. print ("訓練耗費時間:%d秒" % time_elapsed)

  152. time_begin = time.time()

  153. # 儲存訓練結果

  154. if not os.path.exists(SAVER_DIR):

  155. print ('不存在訓練資料儲存目錄,現在建立儲存目錄')

  156. os.makedirs(SAVER_DIR)

  157. # 初始化saver

  158. saver = tf.train.Saver()

  159. saver_path = saver.save(sess, "%smodel.ckpt"%(SAVER_DIR))

  160. if __name__ =='__main__' and sys.argv[1]=='predict':

  161. saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))

  162. with tf.Session() as sess:

  163. model_file=tf.train.latest_checkpoint(SAVER_DIR)

  164. saver.restore(sess, model_file)

  165. # 第一個卷積層

  166. W_conv1 = sess.graph.get_tensor_by_name("W_conv1:0")

  167. b_conv1 = sess.graph.get_tensor_by_name("b_conv1:0")

  168. conv_strides = [1, 1, 1, 1]

  169. kernel_size = [1, 2, 2, 1]

  170. pool_strides = [1, 2, 2, 1]

  171. L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')

  172. # 第二個卷積層

  173. W_conv2 = sess.graph.get_tensor_by_name("W_conv2:0")

  174. b_conv2 = sess.graph.get_tensor_by_name("b_conv2:0")

  175. conv_strides = [1, 1, 1, 1]

  176. kernel_size = [1, 1, 1, 1]

  177. pool_strides = [1, 1, 1, 1]

  178. L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')

  179. # 全連線層

  180. W_fc1 = sess.graph.get_tensor_by_name("W_fc1:0")

  181. b_fc1 = sess.graph.get_tensor_by_name("b_fc1:0")

  182. h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])

  183. h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)

  184. # dropout

  185. keep_prob = tf.placeholder(tf.float32)

  186. h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

  187. # readout層

  188. W_fc2 = sess.graph.get_tensor_by_name("W_fc2:0")

  189. b_fc2 = sess.graph.get_tensor_by_name("b_fc2:0")

  190. # 定義優化器和訓練op

  191. conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

  192. for n in range(2,3):

  193. path = "test_images/%s.bmp" % (n)

  194. img = Image.open(path)

  195. width = img.size[0]

  196. height = img.size[1]

  197. img_data = [[0]*SIZE for i in range(1)]

  198. for h in range(0, height):

  199. for w in range(0, width):

  200. if img.getpixel((w, h)) < 190:

  201. img_data[0][w+h*width] = 1

  202. else:

  203. img_data[0][w+h*width] = 0

  204. result = sess.run(conv, feed_dict = {x: np.array(img_data), keep_prob: 1.0})

  205. max1 = 0

  206. max2 = 0

  207. max3 = 0

  208. max1_index = 0

  209. max2_index = 0

  210. max3_index = 0

  211. for j in range(NUM_CLASSES):

  212. if result[0][j] > max1:

  213. max1 = result[0][j]

  214. max1_index = j

  215. continue

  216. if (result[0][j]>max2) and (result[0][j]<=max1):

  217. max2 = result[0][j]

  218. max2_index = j

  219. continue

  220. if (result[0][j]>max3) and (result[0][j]<=max2):

  221. max3 = result[0][j]

  222. max3_index = j

  223. continue

  224. if n == 3:

  225. license_num += "-"

  226. license_num = license_num + LETTERS_DIGITS[max1_index]

  227. print ("概率: [%s %0.2f%%] [%s %0.2f%%] [%s %0.2f%%]" % (LETTERS_DIGITS[max1_index],max1*100, LETTERS_DIGITS[max2_index],max2*100, LETTERS_DIGITS[max3_index],max3*100))

  228. print ("城市代號是: 【%s】" % license_num)

車牌編號訓練+識別程式碼(儲存檔名為train-license-digits.py):

  1. #!/usr/bin/python3.5

  2. # -*- coding: utf-8 -*-

  3. import sys

  4. import os

  5. import time

  6. import random

  7. import numpy as np

  8. import tensorflow as tf

  9. from PIL import Image

  10. SIZE = 1280

  11. WIDTH = 32

  12. HEIGHT = 40

  13. NUM_CLASSES = 34

  14. iterations = 1000

  15. SAVER_DIR = "train-saver/digits/"

  16. LETTERS_DIGITS = ("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")

  17. license_num = ""

  18. time_begin = time.time()

  19. # 定義輸入節點,對應於圖片畫素值矩陣集合和圖片標籤(即所代表的數字)

  20. x = tf.placeholder(tf.float32, shape=[None, SIZE])

  21. y_ = tf.placeholder(tf.float32, shape=[None, NUM_CLASSES])

  22. x_image = tf.reshape(x, [-1, WIDTH, HEIGHT, 1])

  23. # 定義卷積函式

  24. def conv_layer(inputs, W, b, conv_strides, kernel_size, pool_strides, padding):

  25. L1_conv = tf.nn.conv2d(inputs, W, strides=conv_strides, padding=padding)

  26. L1_relu = tf.nn.relu(L1_conv + b)

  27. return tf.nn.max_pool(L1_relu, ksize=kernel_size, strides=pool_strides, padding='SAME')

  28. # 定義全連線層函式

  29. def full_connect(inputs, W, b):

  30. return tf.nn.relu(tf.matmul(inputs, W) + b)

  31. if __name__ =='__main__' and sys.argv[1]=='train':

  32. # 第一次遍歷圖片目錄是為了獲取圖片總數

  33. input_count = 0

  34. for i in range(0,NUM_CLASSES):

  35. dir = './train_images/training-set/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  36. for rt, dirs, files in os.walk(dir):

  37. for filename in files:

  38. input_count += 1

  39. # 定義對應維數和各維長度的陣列

  40. input_images = np.array([[0]*SIZE for i in range(input_count)])

  41. input_labels = np.array([[0]*NUM_CLASSES for i in range(input_count)])

  42. # 第二次遍歷圖片目錄是為了生成圖片資料和標籤

  43. index = 0

  44. for i in range(0,NUM_CLASSES):

  45. dir = './train_images/training-set/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  46. for rt, dirs, files in os.walk(dir):

  47. for filename in files:

  48. filename = dir + filename

  49. img = Image.open(filename)

  50. width = img.size[0]

  51. height = img.size[1]

  52. for h in range(0, height):

  53. for w in range(0, width):

  54. # 通過這樣的處理,使數字的線條變細,有利於提高識別準確率

  55. if img.getpixel((w, h)) > 230:

  56. input_images[index][w+h*width] = 0

  57. else:

  58. input_images[index][w+h*width] = 1

  59. input_labels[index][i] = 1

  60. index += 1

  61. # 第一次遍歷圖片目錄是為了獲取圖片總數

  62. val_count = 0

  63. for i in range(0,NUM_CLASSES):

  64. dir = './train_images/validation-set/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  65. for rt, dirs, files in os.walk(dir):

  66. for filename in files:

  67. val_count += 1

  68. # 定義對應維數和各維長度的陣列

  69. val_images = np.array([[0]*SIZE for i in range(val_count)])

  70. val_labels = np.array([[0]*NUM_CLASSES for i in range(val_count)])

  71. # 第二次遍歷圖片目錄是為了生成圖片資料和標籤

  72. index = 0

  73. for i in range(0,NUM_CLASSES):

  74. dir = './train_images/validation-set/%s/' % i # 這裡可以改成你自己的圖片目錄,i為分類標籤

  75. for rt, dirs, files in os.walk(dir):

  76. for filename in files:

  77. filename = dir + filename

  78. img = Image.open(filename)

  79. width = img.size[0]

  80. height = img.size[1]

  81. for h in range(0, height):

  82. for w in range(0, width):

  83. # 通過這樣的處理,使數字的線條變細,有利於提高識別準確率

  84. if img.getpixel((w, h)) > 230:

  85. val_images[index][w+h*width] = 0

  86. else:

  87. val_images[index][w+h*width] = 1

  88. val_labels[index][i] = 1

  89. index += 1

  90. with tf.Session() as sess:

  91. # 第一個卷積層

  92. W_conv1 = tf.Variable(tf.truncated_normal([8, 8, 1, 16], stddev=0.1), name="W_conv1")

  93. b_conv1 = tf.Variable(tf.constant(0.1, shape=[16]), name="b_conv1")

  94. conv_strides = [1, 1, 1, 1]

  95. kernel_size = [1, 2, 2, 1]

  96. pool_strides = [1, 2, 2, 1]

  97. L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')

  98. # 第二個卷積層

  99. W_conv2 = tf.Variable(tf.truncated_normal([5, 5, 16, 32], stddev=0.1), name="W_conv2")

  100. b_conv2 = tf.Variable(tf.constant(0.1, shape=[32]), name="b_conv2")

  101. conv_strides = [1, 1, 1, 1]

  102. kernel_size = [1, 1, 1, 1]

  103. pool_strides = [1, 1, 1, 1]

  104. L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')

  105. # 全連線層

  106. W_fc1 = tf.Variable(tf.truncated_normal([16 * 20 * 32, 512], stddev=0.1), name="W_fc1")

  107. b_fc1 = tf.Variable(tf.constant(0.1, shape=[512]), name="b_fc1")

  108. h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])

  109. h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)

  110. # dropout

  111. keep_prob = tf.placeholder(tf.float32)

  112. h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

  113. # readout層

  114. W_fc2 = tf.Variable(tf.truncated_normal([512, NUM_CLASSES], stddev=0.1), name="W_fc2")

  115. b_fc2 = tf.Variable(tf.constant(0.1, shape=[NUM_CLASSES]), name="b_fc2")

  116. # 定義優化器和訓練op

  117. y_conv = tf.matmul(h_fc1_drop, W_fc2) + b_fc2

  118. cross_entropy = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=y_, logits=y_conv))

  119. train_step = tf.train.AdamOptimizer((1e-4)).minimize(cross_entropy)

  120. correct_prediction = tf.equal(tf.argmax(y_conv, 1), tf.argmax(y_, 1))

  121. accuracy = tf.reduce_mean(tf.cast(correct_prediction, tf.float32))

  122. sess.run(tf.global_variables_initializer())

  123. time_elapsed = time.time() - time_begin

  124. print("讀取圖片檔案耗費時間:%d秒" % time_elapsed)

  125. time_begin = time.time()

  126. print ("一共讀取了 %s 個訓練影象, %s 個標籤" % (input_count, input_count))

  127. # 設定每次訓練op的輸入個數和迭代次數,這裡為了支援任意圖片總數,定義了一個餘數remainder,譬如,如果每次訓練op的輸入個數為60,圖片總數為150張,則前面兩次各輸入60張,最後一次輸入30張(餘數30)

  128. batch_size = 60

  129. iterations = iterations

  130. batches_count = int(input_count / batch_size)

  131. remainder = input_count % batch_size

  132. print ("訓練資料集分成 %s 批, 前面每批 %s 個數據,最後一批 %s 個數據" % (batches_count+1, batch_size, remainder))

  133. # 執行訓練迭代

  134. for it in range(iterations):

  135. # 這裡的關鍵是要把輸入陣列轉為np.array

  136. for n in range(batches_count):

  137. train_step.run(feed_dict={x: input_images[n*batch_size:(n+1)*batch_size], y_: input_labels[n*batch_size:(n+1)*batch_size], keep_prob: 0.5})

  138. if remainder > 0:

  139. start_index = batches_count * batch_size;

  140. train_step.run(feed_dict={x: input_images[start_index:input_count-1], y_: input_labels[start_index:input_count-1], keep_prob: 0.5})

  141. # 每完成五次迭代,判斷準確度是否已達到100%,達到則退出迭代迴圈

  142. iterate_accuracy = 0

  143. if it%5 == 0:

  144. iterate_accuracy = accuracy.eval(feed_dict={x: val_images, y_: val_labels, keep_prob: 1.0})

  145. print ('第 %d 次訓練迭代: 準確率 %0.5f%%' % (it, iterate_accuracy*100))

  146. if iterate_accuracy >= 0.9999 and it >= iterations:

  147. break;

  148. print ('完成訓練!')

  149. time_elapsed = time.time() - time_begin

  150. print ("訓練耗費時間:%d秒" % time_elapsed)

  151. time_begin = time.time()

  152. # 儲存訓練結果

  153. if not os.path.exists(SAVER_DIR):

  154. print ('不存在訓練資料儲存目錄,現在建立儲存目錄')

  155. os.makedirs(SAVER_DIR)

  156. # 初始化saver

  157. saver = tf.train.Saver()

  158. saver_path = saver.save(sess, "%smodel.ckpt"%(SAVER_DIR))

  159. if __name__ =='__main__' and sys.argv[1]=='predict':

  160. saver = tf.train.import_meta_graph("%smodel.ckpt.meta"%(SAVER_DIR))

  161. with tf.Session() as sess:

  162. model_file=tf.train.latest_checkpoint(SAVER_DIR)

  163. saver.restore(sess, model_file)

  164. # 第一個卷積層

  165. W_conv1 = sess.graph.get_tensor_by_name("W_conv1:0")

  166. b_conv1 = sess.graph.get_tensor_by_name("b_conv1:0")

  167. conv_strides = [1, 1, 1, 1]

  168. kernel_size = [1, 2, 2, 1]

  169. pool_strides = [1, 2, 2, 1]

  170. L1_pool = conv_layer(x_image, W_conv1, b_conv1, conv_strides, kernel_size, pool_strides, padding='SAME')

  171. # 第二個卷積層

  172. W_conv2 = sess.graph.get_tensor_by_name("W_conv2:0")

  173. b_conv2 = sess.graph.get_tensor_by_name("b_conv2:0")

  174. conv_strides = [1, 1, 1, 1]

  175. kernel_size = [1, 1, 1, 1]

  176. pool_strides = [1, 1, 1, 1]

  177. L2_pool = conv_layer(L1_pool, W_conv2, b_conv2, conv_strides, kernel_size, pool_strides, padding='SAME')

  178. # 全連線層

  179. W_fc1 = sess.graph.get_tensor_by_name("W_fc1:0")

  180. b_fc1 = sess.graph.get_tensor_by_name("b_fc1:0")

  181. h_pool2_flat = tf.reshape(L2_pool, [-1, 16 * 20*32])

  182. h_fc1 = full_connect(h_pool2_flat, W_fc1, b_fc1)

  183. # dropout

  184. keep_prob = tf.placeholder(tf.float32)

  185. h_fc1_drop = tf.nn.dropout(h_fc1, keep_prob)

  186. # readout層

  187. W_fc2 = sess.graph.get_tensor_by_name("W_fc2:0")

  188. b_fc2 = sess.graph.get_tensor_by_name("b_fc2:0")

  189. # 定義優化器和訓練op

  190. conv = tf.nn.softmax(tf.matmul(h_fc1_drop, W_fc2) + b_fc2)

  191. for n in range(3,8):

  192. path = "test_images/%s.bmp" % (n)

  193. img = Image.open(path)

  194. width = img.size[0]

  195. height = img.size[1]

  196. img_data = [[0]*SIZE for i in range(1)]

  197. for h in range(0, height):

  198. for w in range(0, width):

  199. if img.getpixel((w, h)) < 190:

  200. img_data[0][w+h*width] = 1

  201. else:

  202. img_data[0][w+h*width] = 0

  203. result = sess.run(conv, feed_dict = {x: np.array(img_data), keep_prob: 1.0})

  204. max1 = 0

  205. max2 = 0

  206. max3 = 0

  207. max1_index = 0

  208. max2_index = 0

  209. max3_index = 0

  210. for j in range(NUM_CLASSES):

  211. if result[0][j] > max1:

  212. max1 = result[0][j]

  213. max1_index = j

  214. continue

  215. if (result[0][j]>max2) and (result[0][j]<=max1):

  216. max2 = result[0][j]

  217. max2_index = j

  218. continue

  219. if (result[0][j]>max3) and (result[0][j]<=max2):

  220. max3 = result[0][j]

  221. max3_index = j

  222. continue

  223. license_num = license_num + LETTERS_DIGITS[max1_index]

  224. print ("概率: [%s %0.2f%%] [%s %0.2f%%] [%s %0.2f%%]" % (LETTERS_DIGITS[max1_index],max1*100, LETTERS_DIGITS[max2_index],max2*100, LETTERS_DIGITS[max3_index],max3*100))

  225. print ("車牌編號是: 【%s】" % license_num)

儲存好上面三個python指令碼後,我們首先進行省份簡稱訓練。在執行程式碼之前,需要先把資料集解壓到訓練指令碼所在目錄。然後,在命令列中進入指令碼所在目錄,輸入執行如下命令:

python train-license-province.py train

訓練結果如下:

然後進行省份簡稱識別,在命令列輸入執行如下命令:

python train-license-province.py predict

執行城市代號訓練(相當於訓練26個字母):

python train-license-letters.py train


識別城市代號:

python train-license-letters.py predict

執行車牌編號訓練(相當於訓練24個字母+10個數字,我國交通法規規定車牌編號中不包含字母I和O):

python train-license-digits.py train


識別車牌編號:

python train-license-digits.py predict

可以看到,在測試圖片上,識別準確率很高。識別結果是閩O-1672Q。

下圖是測試圖片的車牌原圖: