天天看點

身份證号碼識别(python)

分割

适用于背景較暗的照片,光亮時需要調整參數。

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)           

複制

身份證号碼識别(python)

num1

自制資料集的預處理

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("圖檔位置"):
    convertjpg(jpgfile,"處理後的圖檔位置")           

複制

識别

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)           

複制

身份證号碼識别(python)

識别

資料集的收集是準确率的關鍵,MNIST根本不适用,需要自己收集資料集。