分割部分的参数适用身份证与背景色差较大的情况,反之需要自行调参。
import cv2
import numpy as np
def binarize(img, threshould=180):
ret, binary = cv2.threshold(img,threshould,255,cv2.THRESH_BINARY)
return binary
def morphology(img, mode, kernel_size):
kernel = cv2.getStructuringElement(cv2.MORPH_RECT, (kernel_size,kernel_size))
if mode == 'dilate':
img = cv2.dilate(img, kernel)
elif mode == 'erode':
img = cv2.erode(img, kernel)
elif mode == 'open':
img = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
elif mode == 'close':
img = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
return img
# 画线(测试用)
def draw(img, box, color=(0, 255, 0)):
draw_img = cv2.line(img, (box[0][0], box[0][1]), (box[1][0], box[1][1]), color, 10)
draw_img = cv2.line(img, (box[1][0], box[1][1]), (box[2][0], box[2][1]), color, 10)
draw_img = cv2.line(img, (box[2][0], box[2][1]), (box[3][0], box[3][1]), color, 10)
draw_img = cv2.line(img, (box[3][0], box[3][1]), (box[0][0], box[0][1]), color, 10)
return draw_img
# 身份证轮廓
def getRectanglecard(img):
cnt, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
if len(cnt) > 1:
max_distance = -99999
target_index = -1
for i in range(len(cnt)):
c = cnt[i].squeeze()
if(len(c) < 3):
continue
rect = cv2.minAreaRect(np.array(c))
box = np.int0(cv2.boxPoints(rect))
point1 = np.array([box[0][0], box[0][1]])
point2 = np.array([box[1][0], box[1][1]])
point3 = np.array([box[2][0], box[2][1]])
distance = np.linalg.norm(point2 - point1) + np.linalg.norm(point3- point2)
if distance > max_distance:
max_distance = distance
target_index = i
cnt = cnt[target_index].squeeze()
else:
cnt = cnt[0].squeeze()
rect = cv2.minAreaRect(np.array(cnt))
box = np.int0(cv2.boxPoints(rect))
return box
# 左上,右上,右下,左下
def newbox(box):
newbox = []
rebox = []
min_num = 99999
for i in range(len(box)):
rebox.append([box[i][0], box[i][1], box[i][0]+box[i][1]])
for i in range(4):
if rebox[i][2] < min_num:
min_index = i
min_num = rebox[i][2]
newbox.append([rebox[min_index][0], rebox[min_index][1]])
del (rebox[min_index])
min_num = 99999
for i in range(3):
if abs(rebox[i][1] - newbox[0][1]) < min_num:
min_index = i
min_num = abs(rebox[i][1] -newbox[0][1])
newbox.append([rebox[min_index][0], rebox[min_index][1]])
del (rebox[min_index])
min_num = 99999
for i in range(2):
if abs(rebox[i][0] - newbox[1][0]) < min_num:
min_index = i
min_num = abs(rebox[i][0] -newbox[1][0])
newbox.append([rebox[min_index][0], rebox[min_index][1]])
del (rebox[min_index])
newbox.append([rebox[0][0], rebox[0][1]])
return newbox
# 新尺寸
def size(box):
(tl, tr, br, bl) = box
width1 = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
width2 = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
width = max(int(width1), int(width2))
height1 = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
height2 = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
height = max(int(height1), int(height2))
return width, height
# 身份证号轮廓
def getRectanglenum(img):
cnt, hierarchy = cv2.findContours(img, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
rect2 = []
for i in cnt:
rect = cv2.minAreaRect(i)
width = rect[1][0] + 0.0000001
height = rect[1][1] + 0.0000001
mul = width / height
if 17 < mul < 19:
rect2 = rect
box = np.int0(cv2.boxPoints(rect2))
return box
# 找身份证
img = cv2.imread('身份证原照片')
ini_img = img
img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
img = binarize(img)
#cv2.imwrite('binarize.jpeg', img)
img = cv2.medianBlur(img, 9)
#cv2.imwrite('img1.jpeg', img)
img = morphology(img, mode='close', kernel_size=21)
img = morphology(img, mode='dilate', kernel_size=11)
img = morphology(img, mode='open', kernel_size=101)
#cv2.imwrite('img2.jpeg', img)
edge = cv2.Canny(img, 50, 150)
box = getRectanglecard(edge)
#cv2.imwrite('edge.jpeg', edge)
box = newbox(box)
box = np.float32((box[0], box[1], box[2], box[3]))
width, height = size(box)
dst = np.array([[0, 0],[width - 1, 0],[width - 1, height - 1],[0, height - 1]], dtype="float32")
m = cv2.getPerspectiveTransform(box, dst)
warped = cv2.warpPerspective(ini_img, m, (width, height))
cv2.imwrite('warped.jpeg', warped)
# 找身份证号码
img_card = warped
img_card = cv2.cvtColor(img_card,cv2.COLOR_BGR2GRAY)
img_card = binarize(img_card,threshould=70)
#cv2.imwrite('img_card_binarize.jpeg', img_card)
img_card = morphology(img_card, mode='open', kernel_size=90)
img_card = cv2.bitwise_not(img_card)
#cv2.imwrite('img_card.jpeg', img_card)
edge1 = cv2.Canny(img_card, 50, 150)
#cv2.imwrite('edge1.jpeg', edge1)
box1 = getRectanglenum(edge1)
box1 = newbox(box1)
box1 = np.float32((box1[0], box1[1], box1[2], box1[3]))
width1, height1 = size(box1)
dst1 = np.array([[0, 0],[width1 - 1, 0],[width1 - 1, height1 - 1],[0, height1 - 1]], dtype="float32")
m1 = cv2.getPerspectiveTransform(box1, dst1)
warped1 = cv2.warpPerspective(warped, m1, (width1, height1))
cv2.imwrite('warped1.jpeg', warped1)
# 分割身份证号
num1_width = width1//18
num2_width = 2*width1//18
num3_width = 3*width1//18
num4_width = 4*width1//18
num5_width = 5*width1//18
num6_width = 6*width1//18
num7_width = 7*width1//18
num8_width = 8*width1//18
num9_width = 9*width1//18
num10_width = 10*width1//18
num11_width = 11*width1//18
num12_width = 12*width1//18
num13_width = 13*width1//18
num14_width = 14*width1//18
num15_width = 15*width1//18
num16_width = 16*width1//18
num17_width = 17*width1//18
num1 = cv2.resize((warped1[0:height1, 0:num1_width]), (32,32), cv2.INTER_AREA)
num2 = cv2.resize((warped1[0:height1, num1_width:num2_width]), (32,32), cv2.INTER_AREA)
num3 = cv2.resize((warped1[0:height1, num2_width:num3_width]), (32,32), cv2.INTER_AREA)
num4 = cv2.resize((warped1[0:height1, num3_width:num4_width]), (32,32), cv2.INTER_AREA)
num5 = cv2.resize((warped1[0:height1, num4_width:num5_width]), (32,32), cv2.INTER_AREA)
num6 = cv2.resize((warped1[0:height1, num5_width:num6_width]), (32,32), cv2.INTER_AREA)
num7 = cv2.resize((warped1[0:height1, num6_width:num7_width]), (32,32), cv2.INTER_AREA)
num8 = cv2.resize((warped1[0:height1, num7_width:num8_width]), (32,32), cv2.INTER_AREA)
num9 = cv2.resize((warped1[0:height1, num8_width:num9_width]), (32,32), cv2.INTER_AREA)
num10 = cv2.resize((warped1[0:height1, num9_width:num10_width]), (32,32), cv2.INTER_AREA)
num11 = cv2.resize((warped1[0:height1, num10_width:num11_width]), (32,32), cv2.INTER_AREA)
num12 = cv2.resize((warped1[0:height1, num11_width:num12_width]), (32,32), cv2.INTER_AREA)
num13 = cv2.resize((warped1[0:height1, num12_width:num13_width]), (32,32), cv2.INTER_AREA)
num14 = cv2.resize((warped1[0:height1, num13_width:num14_width]), (32,32), cv2.INTER_AREA)
num15 = cv2.resize((warped1[0:height1, num14_width:num15_width]), (32,32), cv2.INTER_AREA)
num16 = cv2.resize((warped1[0:height1, num15_width:num16_width]), (32,32), cv2.INTER_AREA)
num17 = cv2.resize((warped1[0:height1, num16_width:num17_width]), (32,32), cv2.INTER_AREA)
num18 = cv2.resize((warped1[0:height1, num17_width:width1]), (32,32), cv2.INTER_AREA)
import os
from PIL import Image
import numpy as np
import tensorflow as tf
data_dir = '数据集'
model_path = '模型'
num_dir = '分割后的数字'
def read_data(data_dir):
datas = []
labels = []
fpaths = []
for fname in os.listdir(data_dir):
fpath = os.path.join(data_dir, fname)
fpaths.append(fpath)
image = Image.open(fpath)
data = np.array(image) / 255.0
label = int(fname.split("_")[0])
datas.append(data)
labels.append(label)
datas = np.array(datas)
labels = np.array(labels)
print("shape of datas: {}\tshape of labels: {}".format(datas.shape,labels.shape))
return fpaths, datas, labels
def read_num(num_dir):
num_datas = []
num_fpaths = []
for fname in os.listdir(num_dir):
num_fpath = os.path.join(num_dir, fname)
num_fpaths.append(num_fpath)
num_fpaths.sort()
image = Image.open(num_fpath)
num_data = np.array(image) / 255.0
num_datas.append(num_data)
num_datas = np.array(num_datas)
num_fpaths.sort()
print("shape of num_datas: {}".format(num_datas.shape))
return num_fpaths, num_datas
fpaths, datas, labels = read_data(data_dir)
num_fpaths, num_datas = read_num(num_dir)
num_classes = len(set(labels))
datas_placeholder = tf.placeholder(tf.float32, [None, 32, 32, 3])
labels_placeholder = tf.placeholder(tf.int32, [None])
dropout_placeholdr = tf.placeholder(tf.float32)
conv0 = tf.layers.conv2d(datas_placeholder, 20, 5, activation=tf.nn.relu)
pool0 = tf.layers.max_pooling2d(conv0, [2, 2], [2, 2])
conv1 = tf.layers.conv2d(pool0, 40, 4, activation=tf.nn.relu)
pool1 = tf.layers.max_pooling2d(conv1, [2, 2], [2, 2])
flatten = tf.layers.flatten(pool1)
fc = tf.layers.dense(flatten, 400, activation=tf.nn.relu)
dropout_fc = tf.layers.dropout(fc, dropout_placeholdr)
logits = tf.layers.dense(dropout_fc, num_classes)
predicted_labels = tf.argmax(logits, 1)
losses = tf.nn.softmax_cross_entropy_with_logits_v2(labels=tf.one_hot(labels_placeholder, num_classes),logits=logits)
mean_loss = tf.reduce_mean(losses)
optimizer = tf.train.AdamOptimizer(learning_rate=1e-2).minimize(losses)
type = 3
#type = 1,2,3 训练模式,测试模式,实测模式
saver = tf.train.Saver()
with tf.Session() as sess:
if (type == 1):
print("训练模式")
sess.run(tf.global_variables_initializer())
train_feed_dict = {
datas_placeholder: datas,
labels_placeholder: labels,
dropout_placeholdr: 0.1
}
for step in range(150):
_, mean_loss_val = sess.run([optimizer, mean_loss],feed_dict=train_feed_dict)
if step % 10 == 0:
print("step = {}\tmean loss = {}".format(step,mean_loss_val))
saver.save(sess, model_path)
print("训练结束,保存模型到{}".format(model_path))
elif (type == 2):
print("测试模式")
saver.restore(sess, model_path)
label_name_dict = {0:"0",1:"1",2:"2",3:"3",4:"4",5:"5",6:"6",7:"7",8:"8",9:"9"}
test_feed_dict = {
datas_placeholder: datas,
labels_placeholder: labels,
dropout_placeholdr: 0
}
predicted_labels_val = sess.run(predicted_labels,feed_dict=test_feed_dict)
for fpath, real_label, predicted_label in zip(fpaths, labels,predicted_labels_val):
real_label_name = label_name_dict[real_label]
predicted_label_name = label_name_dict[predicted_label]
print("{}\t{} => {}".format(fpath, real_label_name,predicted_label_name))
elif (type == 3):
prenum = []
print("实测模式")
saver.restore(sess, model_path)
label_name_dict = {0: "0", 1: "1", 2: "2", 3: "3", 4: "4", 5: "5", 6: "6", 7: "7", 8: "8", 9: "9"}
num_feed_dict = {
datas_placeholder: num_datas,
dropout_placeholdr: 0
}
predicted_labels_val = sess.run(predicted_labels, feed_dict=num_feed_dict)
for num_fpath, predicted_label in zip(num_fpaths, predicted_labels_val):
predicted_label_name = label_name_dict[predicted_label]
prenum.append(predicted_label_name)
print(prenum)
准确率的关键取决于数据集,MNIST不适用于身份证字体的识别,需要自制数据集。
关于自制数据集的处理
import os
dirName = "原始数据集"
li=os.listdir(dirName)
for filename in li:
newname = filename
newname = newname.split(".")
if newname[-1]=="png":
newname[-1]="jpeg"
newname = str.join(".",newname)
filename = dirName+filename
newname = dirName+newname
os.rename(filename,newname)
print(newname,"updated successfully")
from PIL import Image
import os.path
import glob
def convertjpg(jpgfile,outdir,width=32,height=32):
img=Image.open(jpgfile)
try:
new_img=img.resize((width,height),Image.BILINEAR)
new_img.save(os.path.join(outdir,os.path.basename(jpgfile)))
except Exception as e:
print(e)
for jpgfile in glob.glob("原始数据集位置/*.jpeg"):
convertjpg(jpgfile,"最终数据集的位置")