2025年4月15日 星期二 乙巳(蛇)年 正月十六 设为首页 加入收藏
rss
您当前的位置:首页 > 计算机 > 编程开发 > Python

身份证号码识别

时间:03-27来源:作者:点击数:53

初次接触OCR技术,OCR技术在工业检测上有极大的用处,如工件上面得数字标号识别、印刷纸票识别、车牌识别、身份证号码识别等。但中文字体识别较难,如今百度OCR、谷歌tesseract等提供识别接口,可以取得较好的识别效果。

通过贾志刚老师的印刷字体识别课程和一些OpenCV函数的学习,用身份证号码识别检测一下所学知识。

主要步骤

  1. 使用OpenCV进行图像仿射变换或者透视变换,将图像摆正;
  2. 通过二值化和形态学处理,粗定位文本信息区域;
  3. 分割ROI区域,通过轮廓筛选,确定身份证号码区域;
  4. 字符分割和字符排序;
  5. 字符识别模型训练;
  6. 字符识别。

具体实现:

1、透视变换

先检测身份证外轮廓,获取身份证四个角点,然后利用OpenCV中getPerspectiveTransform()函数获取变换矩阵,warpPerspective()函数透视变换。特别需要注意角点坐标的顺序必须按顺序一一对应,按顺序找到对应坐标0123分别是 左上,右上,右下,左下。

  • #轮廓检测
  • cnts,hierarchy = cv.findContours(edged, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
  • # cnts = sorted(cnts, key = cv.contourArea, reverse = True)[:5]
  • for c in cnts:
  • peri = cv.arcLength(c,True)
  • approx = cv.approxPolyDP(c,0.02*peri,True) #轮廓多边形逼近,找到角点
  • if len(approx) == 4:
  • screenCnt = approx
  • #print(screenCnt)
  • break
  • cv.drawContours(gray, [screenCnt], -1, (0, 255, 0), 2)
  • pts = screenCnt.reshape(4, 2)
  • #print(a)
  • rect = np.zeros((4, 2), dtype = "float32")
  • # 按顺序找到对应坐标0123分别是 左上,右上,右下,左下
  • # 计算左上,右下
  • s = pts.sum(axis = 1)
  • rect[0] = pts[np.argmin(s)]
  • rect[2] = pts[np.argmax(s)]
  • # 计算右上和左下
  • diff = np.diff(pts, axis = 1)
  • rect[1] = pts[np.argmin(diff)]
  • rect[3] = pts[np.argmax(diff)]
  • (tl, tr, br, bl) = rect
  • # 计算输入的w和h值
  • widthA = np.sqrt(((br[0] - bl[0]) ** 2) + ((br[1] - bl[1]) ** 2))
  • widthB = np.sqrt(((tr[0] - tl[0]) ** 2) + ((tr[1] - tl[1]) ** 2))
  • maxWidth = max(int(widthA), int(widthB))
  • heightA = np.sqrt(((tr[0] - br[0]) ** 2) + ((tr[1] - br[1]) ** 2))
  • heightB = np.sqrt(((tl[0] - bl[0]) ** 2) + ((tl[1] - bl[1]) ** 2))
  • maxHeight = max(int(heightA), int(heightB))
  • # 变换后对应坐标位置
  • dst = np.array([
  • [0, 0],
  • [maxWidth - 1, 0],
  • [maxWidth - 1, maxHeight - 1],
  • [0, maxHeight - 1]], dtype = "float32")
  • # 计算变换矩阵
  • M = cv.getPerspectiveTransform(rect, dst)
  • warped = cv.warpPerspective(img, M, (maxWidth, maxHeight))
在这里插入图片描述
在这里插入图片描述

2、文本粗定位

通过Canny边缘检测,提取文本边缘,但此时边缘之间是断开的,需要通过形态学膨胀操作,将边缘之间连接起来。

  • canny = cv.Canny(gray,60,255)
  • # 形态学操作
  • kernel = cv.getStructuringElement(cv.MORPH_RECT,(11,5))
  • dilation = cv.dilate(canny,kernel,iterations = 1)
在这里插入图片描述

3、身份证号码ROI分割

观察到身份证号码区域的长度是最长的,因此可以通过轮廓检测,用boungdingRect()函数计算各轮廓的长宽值,设定合适阈值,找到ROI区域的轮廓进行分割。

  • #轮廓检测
  • cnts,hiri = cv.findContours(dilation, cv.RETR_LIST, cv.CHAIN_APPROX_SIMPLE)
  • for c in cnts:
  • x, y, w, h = cv.boundingRect(c)
  • if w < 250:
  • continue
  • cv.rectangle(gray, (x,y), (x+w,y+h), (255,0,0), 2)
  • dst = src[y-2:y + h, x:x + w]

4、字符分割与排序

因为数字字符是连接的,字符之间有间隙,可以直接使用轮廓提取,因此可以以此为据,分割字符。轮廓的检测不能保证是有序的,通过轮廓的X坐标进行排序,字符排序保证以正确顺序依次进行识别。

  • # 字符分割
  • contours, hireachy = cv.findContours(canny1, cv.RETR_EXTERNAL, cv.CHAIN_APPROX_SIMPLE)
  • #获取字符
  • rois = []
  • for c in range(len(contours)):
  • box = cv.boundingRect(contours[c])
  • if box[3] < 10:
  • continue
  • rois.append(box)
  • # 字符排序
  • num = len(rois)
  • for i in range(num):
  • for j in range(i+1, num, 1):
  • x1, y1, w1, h1 = rois[i]
  • x2, y2, w2, h2 = rois[j]
  • if x2 < x1:
  • temp = rois[j]
  • rois[j] = rois[i]
  • rois[i] = temp
在这里插入图片描述

5、识别模型训练

样本的准确采集与最后的结果准确度息息相关,将上述分割的字符进行样本拓展,因为数字像素特征比较明显,因此将像素特征作为输入,采用OpenCV封装的SVM网络进行训练与识别。

  • # 获取数据
  • train_data, train_labels = load_data()
  • # 网络构建
  • svm = cv.ml.SVM_create()
  • svm.setKernel(cv.ml.SVM_LINEAR)
  • svm.setType(cv.ml.SVM_C_SVC)
  • svm.setC(2.67)
  • svm.setGamma(5.383)
  • svm.train(train_data, cv.ml.ROW_SAMPLE, train_labels)
  • svm.save("svm_data.yml")
  • svm = cv.ml.SVM_load("svm_data.yml")
  • result = svm.predict(train_data)[1]
  • print(result)

6、身份证号码识别

  • #数字识别
  • svm = cv.ml.SVM_load("svm_data.yml")
  • result = svm.predict(digit_data)[1]
  • text = ""
  • for i in range(len(result)):
  • text += str(np.int32(result[i][0]))
  • print(text)
在这里插入图片描述
方便获取更多学习、工作、生活信息请关注本站微信公众号城东书院 微信服务号城东书院 微信订阅号
推荐内容
相关内容
栏目更新
栏目热门