MyException - 我的异常网
当前位置:我的异常网» 开源软件 » 经过人脸检测来识别身份证上号码

经过人脸检测来识别身份证上号码

www.MyException.Cn  网友分享于:2018-03-12  浏览:0次
通过人脸检测来识别身份证上号码

提供一种通过人脸识别和ocr技术组合的身份证信息自动提取。通过身份证上的头像,定位身份证上的信息位置,并返回身份证号.

常规想法:采用机器学习的方式,用opencv或者coffe训练身份证的物体辨识,然后通过比如opencv的cv2.CascadeClassifier 来进行身份证的检测,然后用ocr提取其内部信息。

本文思路:借用现有的轮子,人脸检测+ocr进行身份证号提取。

虽有杀鸡用牛刀之嫌,但技术都是现成的,不用自己做机器学习训练。

原理很简单:通过人脸检测(face-detection)可以检测到身份证上的人脸,并返回面部识别框(face-rectangle),

face rect的位置,配合宽高计算出比例,可以计算出身份证号码的大体为位置,

因为照片中的面部有相对较大或者较小之分,故加入身份证号正则判断,并进行:一般/较小/较大面部比例 重获身份证号区域。

另,图片本身质量和在二值化和去噪过程中的信息损失,识别结果存在可理解的误差,故识别出的字母进行人工的数字转换,例如: 

(" ","") 

("O","0") 

("U","0") 

("D","0") 

("Z","2") 

("S","5") 

("s","5") 

("o","6") 

("f","7") 

("H","11") 

 

整体流程可以描述为:

针对图片进行人脸检测,得到人脸列表;

遍历这个列表,针对每一个人脸的位置和大小进行:

    一般面部比例下的身份证号定位和ocr识别(局部放大,二值化,去噪,ocr),并进行微调处理;

    若得到的不是身份证号(正则验证),则进行较小面部比例下的识别;

    若得到的不是身份证号(正则验证),则进行较大面部比例下的识别;

    若得到的不是身份证号(正则验证),则返回无法识别;

        

由人脸位置和大小进行身份证号码的定位思路见下图:



 

算法中的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,不知道侵犯肖像权没有,如有冒犯请第一时间联系我!

文章评论

我跳槽是因为他们的显示器更大
我跳槽是因为他们的显示器更大
“懒”出效率是程序员的美德
“懒”出效率是程序员的美德
我是如何打败拖延症的
我是如何打败拖延症的
老程序员的下场
老程序员的下场
为啥Android手机总会越用越慢?
为啥Android手机总会越用越慢?
我的丈夫是个程序员
我的丈夫是个程序员
中美印日四国程序员比较
中美印日四国程序员比较
那些争议最大的编程观点
那些争议最大的编程观点
要嫁就嫁程序猿—钱多话少死的早
要嫁就嫁程序猿—钱多话少死的早
当下全球最炙手可热的八位少年创业者
当下全球最炙手可热的八位少年创业者
做程序猿的老婆应该注意的一些事情
做程序猿的老婆应该注意的一些事情
程序员应该关注的一些事儿
程序员应该关注的一些事儿
科技史上最臭名昭著的13大罪犯
科技史上最臭名昭著的13大罪犯
总结2014中国互联网十大段子
总结2014中国互联网十大段子
聊聊HTTPS和SSL/TLS协议
聊聊HTTPS和SSL/TLS协议
如何区分一个程序员是“老手“还是“新手“?
如何区分一个程序员是“老手“还是“新手“?
Google伦敦新总部 犹如星级庄园
Google伦敦新总部 犹如星级庄园
初级 vs 高级开发者 哪个性价比更高?
初级 vs 高级开发者 哪个性价比更高?
程序员周末都喜欢做什么?
程序员周末都喜欢做什么?
10个调试和排错的小建议
10个调试和排错的小建议
看13位CEO、创始人和高管如何提高工作效率
看13位CEO、创始人和高管如何提高工作效率
“肮脏的”IT工作排行榜
“肮脏的”IT工作排行榜
写给自己也写给你 自己到底该何去何从
写给自己也写给你 自己到底该何去何从
十大编程算法助程序员走上高手之路
十大编程算法助程序员走上高手之路
5款最佳正则表达式编辑调试器
5款最佳正则表达式编辑调试器
程序员的一天:一寸光阴一寸金
程序员的一天:一寸光阴一寸金
程序员眼里IE浏览器是什么样的
程序员眼里IE浏览器是什么样的
程序员必看的十大电影
程序员必看的十大电影
程序员的鄙视链
程序员的鄙视链
2013年美国开发者薪资调查报告
2013年美国开发者薪资调查报告
10个帮程序员减压放松的网站
10个帮程序员减压放松的网站
2013年中国软件开发者薪资调查报告
2013年中国软件开发者薪资调查报告
亲爱的项目经理,我恨你
亲爱的项目经理,我恨你
程序员和编码员之间的区别
程序员和编码员之间的区别
每天工作4小时的程序员
每天工作4小时的程序员
那些性感的让人尖叫的程序员
那些性感的让人尖叫的程序员
编程语言是女人
编程语言是女人
代码女神横空出世
代码女神横空出世
程序猿的崛起——Growth Hacker
程序猿的崛起——Growth Hacker
团队中“技术大拿”并非越多越好
团队中“技术大拿”并非越多越好
什么才是优秀的用户界面设计
什么才是优秀的用户界面设计
程序员都该阅读的书
程序员都该阅读的书
一个程序员的时间管理
一个程序员的时间管理
老美怎么看待阿里赴美上市
老美怎么看待阿里赴美上市
为什么程序员都是夜猫子
为什么程序员都是夜猫子
程序员最害怕的5件事 你中招了吗?
程序员最害怕的5件事 你中招了吗?
Java 与 .NET 的平台发展之争
Java 与 .NET 的平台发展之争
Web开发人员为什么越来越懒了?
Web开发人员为什么越来越懒了?
Web开发者需具备的8个好习惯
Web开发者需具备的8个好习惯
软件开发程序错误异常ExceptionCopyright © 2009-2015 MyException 版权所有