python opencv 身份证照片识别,包含正面、反面(附完整代码,可直接使用)

您所在的位置:网站首页 如何将身份证照片的正反面一起拍下来 python opencv 身份证照片识别,包含正面、反面(附完整代码,可直接使用)

python opencv 身份证照片识别,包含正面、反面(附完整代码,可直接使用)

2024-07-03 17:31| 来源: 网络整理| 查看: 265

环境

提示:硬件环境Mac m1 python:3.8 opencv-python: 3.4.8.29 pytesseract: 0.3.9 cnocr: 2.1.2.1

文章目录 环境前言正面识别背面识别源码下载

前言

这里利用opencv 做身份证照片处理,为识别增加准确度。利用pytesseract 做文字提取,由于是识别身份证,需要提前下载好中文字体包。

正面识别

代码如下(示例):

# -*- coding: utf-8 -*- import os, cv2 import sys, numpy as np import math import include.binaryzation as bz import include.functions as func import copy import fileutil DEBUG = False CARD_NAME = '' CARD_SEX = '' CARD_ETHNIC = '' CARD_YEAR = '' CARD_MON = '' CARD_DAY = '' CARD_ADDR = '' # 身份证号码 CARD_NUM = '' # from imutils.perspective import four_point_transform # parser = argparse.ArgumentParser() # parser.add_argument('image', help='path to image file') # args = parser.parse_args() # _localDir = os.path.dirname(__file__) _curpath = os.path.normpath(os.path.join(os.getcwd(), _localDir)) curpath = _curpath def show(image, window_name): cv2.namedWindow(window_name, 0) cv2.imshow(window_name, image) # 0任意键终止窗口 cv2.waitKey(0) cv2.destroyAllWindows() def getCardNum(img, kenalRect): """ 识别并提取身份证号码 :return: """ gray = cv2.cvtColor(img, cv2.COLOR_RGB2GRAY) thr = bz.myThreshold().getMinimumThreshold(gray) ret, binary = cv2.threshold(gray, thr, 255, cv2.THRESH_BINARY) # 3. 膨胀和腐蚀操作的核函数 kenal = cv2.getStructuringElement(cv2.MORPH_RECT, (kenalRect[0], kenalRect[1])) dilation = cv2.dilate(binary, kenal, iterations=1) erosion = cv2.erode(dilation, kenal, iterations=1) # OCR识别 cardNum = func.ocr(erosion) cardNum = func.is_identi_number(cardNum) if cardNum: return cardNum return False def getChineseChar(img, kenalRect): """ 分析汉字区域,并识别提取 :return: """ global CARD_NAME global CARD_SEX global CARD_ETHNIC global CARD_YEAR global CARD_MON global CARD_DAY global CARD_ADDR global CARD_NUM CARD_NAME = CARD_SEX = CARD_ETHNIC = CARD_YEAR = CARD_MON = CARD_DAY = CARD_ADDR = '' # 图片大小比例缩小处理,为了加快性能 h, w, _ = img.shape min_w = 200 scale = 1 # min(1., w * 1. / min_w) h_proc = int(h * 1. / scale) w_proc = int(w * 1. / scale) im_dis = cv2.resize(img, (w_proc, h_proc)) # 灰度处理 gray = cv2.cvtColor(im_dis, cv2.COLOR_BGR2GRAY) # 2. 形态学变换的预处理,得到可以查找矩形的图片 mybz = bz.myThreshold() algos = mybz.getAlgos() for i in algos: # 获取阈值 thr = getattr(mybz, algos[i])(gray) # thr = mybz.getMinimumThreshold(gray) # func.showImg(gray, 'gray') # 膨胀和腐蚀 ret, binary = cv2.threshold(gray, thr, 255, cv2.THRESH_BINARY) # 获取行起始坐标 boundaryCoors = func.horizontalProjection(binary) if not boundaryCoors: continue # print(boundaryCoors) # 垂直投影对行内字符进行切割 erosion = cb = copy.copy(binary) # show(binary, 'binary') coors = {} # 信息所对应的坐标 textLine = 0 # 有效文本行序号 for LineNum, boundaryCoor in enumerate(boundaryCoors): if textLine == 2: kenal1 = cv2.getStructuringElement(cv2.MORPH_RECT, (1, 1)) kenal2 = cv2.getStructuringElement(cv2.MORPH_RECT, (2, 2)) dilation = cv2.dilate(cb, kenal1, iterations=1) erosion = cv2.erode(dilation, kenal2, iterations=1) vertiCoors, text = func.verticalProjection(erosion, boundaryCoor, textLine, img) if len(vertiCoors) == 0: continue if textLine == 0: CARD_NAME = text elif textLine == 1: if text[0] != '男' and text[0] != '女': CARD_SEX = func.getSexByCardNum(CARD_NUM) else: CARD_SEX = text[0] CARD_ETHNIC = text[1] elif textLine == 2: # 为了获取更加精准的值,通过身份证号码规则直接取得出生年月 CARD_YEAR = text[0] CARD_MON = text[1] CARD_DAY = text[2] pass else: CARD_ADDR += text if DEBUG: fator = 2 for verticoo in vertiCoors: box = [[verticoo[0] * scale - fator, boundaryCoor[0] * scale - fator], [verticoo[1] * scale + fator, boundaryCoor[0] * scale - fator], [verticoo[1] * scale + fator, boundaryCoor[1] * scale + fator], [verticoo[0] * scale - fator, boundaryCoor[1] * scale + fator], ] cv2.drawContours(img, [np.int0(box)], 0, (0, 255, 0), 2) textLine += 1 return return False def findChineseCharArea(cardNumPoint1, width, hight): """ 根据身份证号码的位置推断姓名、性别、名族、出生年月、住址的位置 :param cardNumPoint1: tuple 身份证号码所处的矩形的左上角坐标 :param width: int 身份证号码所处的矩形的宽 :param hight: int 身份证号码所处的矩形的高 :return: """ # new_x = int(cardNumPoint1[0] - (width / 18) * 6) new_x = cardNumPoint1[0] - (width / 18) * 5.5 new_width = int(width / 5 * 4) box = [] # new_y = cardNumPoint1[1] - hight * 6.5 card_hight = hight / (0.9044 - 0.7976) # 身份证高度 card_y_start = cardNumPoint1[1] - card_hight * 0.7976 # 粗略算出图像中身份证上边界的y坐标 # 为了保证不丢失文字区域,姓名的相对位置保留,以身份证上边界作为起始切割点 # new_y = card_y_start# + card_hight * 0.0967 # 容错因子,防止矩形存在倾斜导致区域重叠 factor = 20 new_y = card_y_start if card_y_start > factor else factor new_hight = card_hight * (0.7616 - 0.0967) + card_hight * 0.0967 # 文字下边界坐标 new_y_low = (new_y + new_hight) if (new_y + new_hight) b: width = a hight = b pts2 = np.float32([[0, hight], [0, 0], [width, 0], [width, hight]]) else: width = b hight = a angle = 90 + angle pts2 = np.float32([[width, hight], [0, hight], [0, 0], [width, 0]]) # 透视变换 box = cv2.boxPoints(rect) pts1 = np.float32(box) M = cv2.getPerspectiveTransform(pts1, pts2) cropImg = cv2.warpPerspective(img, M, (int(width), int(hight))) # show(cropImg, 'cropImg') # 计算核大小 kenalx = kenaly = int(math.ceil((hight / 100.0))) CARD_NUM = getCardNum(cropImg, (kenalx, kenaly)) if CARD_NUM: notFound = False # 找到身份证号码,然后根据号码区域的倾斜角度,对原图进行旋转变换 if abs(angle) > 10: sp = img.shape H = sp[0] W = sp[1] M = cv2.getRotationMatrix2D((W / 2, H / 2), angle, 1) cropImg = cv2.warpAffine(img, M, (W, H)) # cv2.namedWindow("倾斜矫正", cv2.WINDOW_NORMAL) # cv2.imshow("倾斜矫正", cropImg) # cv2.waitKey(0) # 矫正图片地址 global curpath path = 'tilt_correction.jpg' newFile = os.path.join(curpath, path) cv2.imwrite(newFile, cropImg) return True, '', newFile # 寻找汉字区域 # 裁剪后的图片 box = cv2.boxPoints(rect) box = np.int0(box) cropImg, point, width, hight = func.cropImgByBox(img, box) box = findChineseCharArea(point, width, hight) # cv2.drawContours(img, [box], 0, (0, 255, 0), 3) chiCharArea, point, width, hight = func.cropImgByBox(img, box) getChineseChar(chiCharArea, (kenalx, kenaly)) # if DEBUG: # show(cropImg,'cropImg') # winname = "身份证号码: %s" % (CARD_NUM) # cv2.namedWindow(winname, cv2.WINDOW_NORMAL) # cv2.imshow(winname, cropImg) # cv2.waitKey(0) break if notFound: continue else: break if notFound: # win32api.MessageBox(0, "无法识别,请换一个分辨率高点的照片~", "错误提示") return False, '无法识别,请换一个分辨率高点的照片~", "错误提示', '' # 带轮廓的图片 if DEBUG: # cv2.namedWindow("img", cv2.WINDOW_NORMAL) # cv2.imshow("img", img) key = cv2.waitKey(0) # # cv2.destroyAllWindows() if key != 32: sys.exit() if CARD_NUM != '': # 为了获取更加精准的值,通过身份证号码规则直接取得出生年月 CARD_YEAR, CARD_MON, CARD_DAY = func.getBirthByCardNum(CARD_NUM) if DEBUG: info = """ 姓名:%s 性别:%s 民族:%s 出生:%s 年 %s 月 %s 日 住址:%s 公民身份号码:%s """ % (CARD_NAME, CARD_SEX, CARD_ETHNIC, CARD_YEAR, CARD_MON, CARD_DAY, CARD_ADDR, CARD_NUM) # ret = [CARD_NAME, CARD_SEX, CARD_ETHNIC, CARD_YEAR, CARD_MON, CARD_DAY, CARD_ADDR, CARD_NUM] ret = {"card_name": CARD_NAME, "CARD_SEX": CARD_SEX, "card_ethnic": CARD_ETHNIC, "date_time": CARD_YEAR + "-" + CARD_MON + "-" + CARD_DAY, "card_addr": CARD_ADDR, "card_num": CARD_NUM} return True, ret, '' else: return True, '', '' def calculateElement(img): # 根据图片大小粗略计算腐蚀 或膨胀所需核的大小 sp = img.shape width = sp[1] # width(colums) of image kenaly = math.ceil((width / 400.0) * 12) kenalx = math.ceil((kenaly / 5.0) * 4) a = (int(kenalx), int(kenaly)) return a def preprocess(gray, algoFunc): # 1. Sobel算子,x方向求梯度 # sobel = cv2.Sobel(gray, cv2.CV_8U, 1, 0, ksize = 3) # 获取二值化阈值 thr = bz.myThreshold() # threshold = thr.get1DMaxEntropyThreshold(gray) threshold = getattr(thr, algoFunc)(gray) if threshold


【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3