Provides an automatic extraction of ID card information through the combination of face recognition and OCR technology. Through the avatar on the ID card, locate the information location on the ID card, and return the ID number.
Conventional idea: Use machine learning to train the object recognition of ID cards with opencv or coffe, and then use cv2.CascadeClassifier of opencv to detect ID cards, and then use ocr to extract its internal information.
The idea of this paper: Borrow the existing wheels, face detection + ocr for ID number extraction.
Although there is a suspicion of using a knife to kill chickens, the technology is ready-made, and there is no need to do machine learning training yourself.
The principle is very simple: the face on the ID card can be detected by face-detection, and the face-rectangle is returned,
The position of the face rect, the ratio is calculated with the width and height, and the general position of the ID card number can be calculated.
Because the faces in the photos are relatively large or small, the regular judgment of the ID number is added, and the general/smaller/larger face ratio is used to regain the ID number area.
In addition, the quality of the picture itself and the loss of information in the process of binarization and denoising, there are understandable errors in the recognition results, so the recognized letters are subjected to manual digital conversion, such as:
(" ","")
("O","0")
("U","0")
("D","0")
("Z","2")
("S","5")
("s","5")
("o","6")
("f","7")
("H","11")
The overall process can be described as:
Perform face detection on the picture to obtain a face list;
Traverse this list for the position and size of each face:
ID number positioning and ocr recognition (partial magnification, binarization, denoising, ocr) under the general face ratio, and fine-tuning processing;
若得到的不是身份证号(正则验证),则进行较小面部比例下的识别;
若得到的不是身份证号(正则验证),则进行较大面部比例下的识别;
若得到的不是身份证号(正则验证),则返回无法识别;
由人脸位置和大小进行身份证号码的定位思路见下图:
算法中的ocr识别过程及效果为:
1.局部放大:
2.灰度图:
3.二值化:
3.去噪声:
4.ocr:
432930194901170013
代码:
#-*- coding: utf-8 -*- import cv2 import sys from PIL import Image import pytesseract import time import re import sys reload(sys) sys.setdefaultencoding('utf-8') print(sys.getdefaultencoding()) #身份证号 r=r'^([1-9]\d{5}[12]\d{3}(0[1-9]|1[012])(0[1-9]|[12][0-9]|3[01])\d{3}[0-9xX])$' ###########根据比例和偏移算出号码位置 #一般面部比例 def CalcIdRectByFaceRect_normal(x,y,w,h): #print(x,y,w,h) scale = float(w) / 95 #print(scale) x1 = int(x + (( 0 - 159) ) * scale) y1 = int( y + (0 + (149 ) ) * scale) x2 = int( x + (0 - 159 + ( 275 ) ) * scale) y2 = int(y +(0 + (149 ) + (45 ) ) * scale) #print( x1,y1,x2, y2) return ( x1,y1,x2, y2) #较大面部比例 def CalcIdRectByFaceRect_big(x,y,w,h): scale = float(w) / 95 x1 = int(x + (( 0 - 159) + 10) * scale) y1 = int( y + (0 + (149 - 3) ) * scale) x2 = int( x + (0 - 159 + ( 275 - 10) ) * scale) y2 = int(y +(0 + (149 - 3) + (45 - 10) ) * scale) return ( x1,y1,x2, y2) #较小面部比例 def CalcIdRectByFaceRect_small(x,y,w,h): scale = float(w) / 95 x1 = int(x + (( 0 - 159) - 10) * scale) y1 = int( y + (0 + (149 + 3) ) * scale) x2 = int( x + (0 - 159 + ( 275+ 10) ) * scale) y2 = int(y +(0 + (149 + 5) + (45 + 10) ) * scale) return ( x1,y1,x2, y2) ###########二值化算法 def binarizing(img,threshold): pixdata = img.load() w, h = img.size for y in range(h): for x in range(w): if pixdata[x, y] < threshold: pixdata[x, y] = 0 else: pixdata[x, y] = 255 return img ###########去除干扰线算法 def depoint(img): #input: gray image pixdata = img.load() w,h = img.size for y in range(1,h-1): for x in range(1,w-1): count = 0 if pixdata[x,y-1] > 245: count = count + 1 if pixdata[x,y+1] > 245: count = count + 1 if pixdata[x-1,y] > 245: count = count + 1 if pixdata[x+1,y] > 245: count = count + 1 if count > 2: pixdata[x,y] = 255 return img ######## 通过头像的位置 身份证号码识别 def identity_OCR_byFaceRect(oImg,faceRect): (x,y,w,h) = faceRect iw,ih = oImg.size ##将身份证放大3倍 largeImg = oImg.resize((iw*3,ih*3),Image.ANTIALIAS) #largeImg.save('1_large.png') #print(x,y,w,h) (x1,y1,x2,y2) = CalcIdRectByFaceRect_normal(x,y,w,h) print("id pos normal: %s,%s,%s,%s"%(x1,y1,x2,y2)) region = (x1*3,y1*3,x2*3,y2*3) code = GetRegionString(largeImg,region) print("code:%s"%code) if not re.match(r,code): (x1,y1,x2,y2) = CalcIdRectByFaceRect_small(x,y,w,h) print("id pos small: %s,%s,%s,%s"%(x1,y1,x2,y2)) region = (x1*3,y1*3,x2*3,y2*3) code = GetRegionString(largeImg,region) print("code:%s"%code) if not re.match(r,code): (x1,y1,x2,y2) = CalcIdRectByFaceRect_big(x,y,w,h) print("id pos big: %s,%s,%s,%s"%(x1,y1,x2,y2)) region = (x1*3,y1*3,x2*3,y2*3) code = GetRegionString(largeImg,region) print("code:%s"%code) if not re.match(r,code): code = 'no match detect' return code, (x1,y1,x2,y2) def GetRegionString(img,region): #裁切身份证号码图片 cropImg = img.crop(region) #cropImg.save('2_crop.png') # 转化为灰度图 grayImg = cropImg.convert('L') #grayImg.save('3_grey.png') # 把图片变成二值图像。 bImg =binarizing(grayImg,100) #bImg.save('4_bin.png') dImg =depoint(bImg) #dImg.save('5_depoint.png') code = pytesseract.image_to_string(dImg) code = PostProc(code) return code ######## 号码后处理 def PostProc(s): res = s res = res.replace(" ","") res = res.replace("O","0") res = res.replace("U","0") res = res.replace("D","0") res = res.replace("Z","2") res = res.replace("S","5") res = res.replace("s","5") res = res.replace("o","6") res = res.replace("f","7") res = res.replace("H","11") return res ######## 检测身份证 def DetectFacesAndIDs(window_name, pic_path): frame =cv2.imread(pic_path) oImg =Image.open(pic_path) ih,iw = frame.shape[:2] print("image shape:%s,%s"%(ih,iw)) #人脸识别分类器 classfier = cv2.CascadeClassifier("haarcascade_frontalface_alt.xml") #识别出人脸后要画的边框的颜色,RGB格式 color = (0, 255, 0) color2 = (255, 0, 0) #将当前帧转换成灰度图像 gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) #人脸检测,1.2和2分别为图片缩放比例和需要检测的有效点数 faceRects = classfier.detectMultiScale(gray, scaleFactor = 1.2, minNeighbors = 3, minSize = (32, 32)) if len(faceRects) > 0: #大于0则检测到人脸 for faceRect in faceRects: #单独框出每一张人脸 x, y, w, h = faceRect print("face: %s,%s,%s,%s"%(x, y, w, h)) cv2.rectangle(frame, (x, y), (x + w, y + h), color, 2) code,(x1,y1,x2,y2) = identity_OCR_byFaceRect(oImg,faceRect) cv2.rectangle(frame, (x1,y1), (x2,y2), color2, 2) #code = code.encode("utf-8") #code = PostProc(code) #print(u"code:%s"%code) print("----------------- detect result: %s"%code) #cv2.imshow(window_name, frame) cv2.imwrite("%s.iddet.png"% pic_path,frame) if __name__ == '__main__': pic_path =sys.argv[1] time1 = time.time() DetectFacesAndIDs("detect face area", pic_path) time2 = time.time() print u'time:' + str(time2 - time1) + 's'
整体输入图像:
识别结果图像:
图片来源于baidu
识别结果:
(py27) C:\ws\test\ocr>python idbyface.py ids.png utf-8 image shape:925,1360 [ INFO:0] Initialize OpenCL runtime... face: 1160,667,75,75 id pos normal: 1034,784,1251,820 code:ll211'1'|l(n'-41?.211?.'1'|X id pos small: 1026,787,1259,832 code:5‘ll211'1'|l(n'-41?.211?.'1'|X id pos big: 1042,782,1243,809 code:ll211'1'|l(n'-41?.211?.'1'|X ----------------- detect result: no match detect face: 211,71,58,58 id pos normal: 113,161,281,189 code:a50725198601156011 id pos small: 107,163,287,198 code:430725198601156011 ----------------- detect result: 430725198601156011 face: 658,73,95,95 id pos normal: 499,222,774,267 code:431023199205297212 ----------------- detect result: 431023199205297212 face: 211,350,66,66 id pos normal: 100,453,291,484 code:37030519820727311X ----------------- detect result: 37030519820727311X face: 651,352,70,70 id pos normal: 533,461,736,494 code:53038119‘ id pos small: 526,464,743,506 code:53038119‘ id pos big: 541,459,729,485 code:;znxa116u ----------------- detect result: no match detect face: 1141,103,92,92 id pos normal: 987,247,1253,290 code:32622196188496 id pos small: 977,250,1263,305 code:32622196188496 id pos big: 996,244,1243,278 code:32622196188496 ----------------- detect result: no match detect face: 334,617,112,112 id pos normal: 146,792,470,845 code:9:432930194901170013 id pos small: 134,796,482,863 code:69:432930194901170013 id pos big: 158,789,458,830 code:432930194901170013 ----------------- detect result: 432930194901170013 time:7.49099993706s
代码和测试文件见: https://github.com/kissmett/idcard-face-detect-number
以上身份证图片均来自baidu,不知道侵犯肖像权没有,如有冒犯请第一时间联系我!