gd库多点画图 php_用 PHP 实现身份证号码识别

本文介绍了一种使用PHP的GD库处理身份证照片以识别号码的方法。涉及的关键步骤包括:身份证照片的边缘检测,号码位置的固定切割,二值化处理,数字与'x'的分割,以及通过计算像素分布进行数字识别。虽然存在一定的局限性,如对照片质量、角度的要求,但整体流程提供了一个实用的身份证号码识别方案。
摘要由CSDN通过智能技术生成

4479e9a79da87193760114b8a7832544.png

首先做一下广告:


多年以前,闲得淡疼,曾经用 PHP 实现了一个从身份证照片识别身份证号码的程序。读/写 jpg 图片使用了 PHP 的 GD 库,其余全部手写 PHP 。在整个识别过程中有这样几个问题:

  1. 照片中身份证在什么位置?如何提取出来?
  2. 身份证号码在证上的什么位置?如何提取出来?
  3. 如何将号码切分成(18 个)数字和 x ?(身份证最后一位校验位有可能是 x 。
  4. 如何识别 10 个数字和 x ?

本文分别将我采用的解决办法陈述一下。本文代码 github:

https://github.com/zhangjuefei/php_id_card_number​github.com

1、照片中身份证在什么位置?如何提取出来?

例如下图的身份证(为保护隐私,以下示例图片都将部分身份信息涂灰):

eae894348548bdb128076df746d37e83.png
身份证照片(背景色要单一,且与身份证本身分界清晰)

要想将身份证部分切割出来,要先找到身份证上、下、左、右四个边缘。我用 Sobel 算子检测图像中的“边缘”,得到结果如下:

0da05285188b2e9e372da2ecb7e0e7c0.png
Sobel 滤波器检测出身份证图像中的边缘

这里存在一个不健壮之处:就是需要背景和身份证本身颜色尽量差别大些,这样才能检测出明显的边界,否则在这一步有可能失败。可以看到例图的边界比较清晰,因为我拍照时将身份证放在黑色哑光的椅子上。
接下来需要定位身份证的上、下、左、右四边缘。我使用了 Hough 变换来检测图像中的直线。由于实现较简单,这里存在另一个不鲁棒之处:如果图中除了身份证边缘之外还存在明显且长的直线,则会对边缘的识别形成干扰。
本来,用 Hough 变换可以得到四个边缘的方程,继而确定四角坐标。再通过映射变换,可以把扭歪放置拍照的身份证“扶正”。但为简单起见,我退而求其次:我要求身份证拍照时平放桌上、端正拍照。也就是说,要求身份证的上、下边界基本水平;左、右边界基本竖直。照得过于歪斜就有可能导致识别失败。通过 Hough 变换找到身份证的边框如下:

a715d781b8145d9b1de96aae8b176a74.png
霍夫变换找到身份证的四条边界

接下来只要根据这四条边缘将身份证切割出来即可,得到:

54a627abb255601a96c41d2bef288f58.png
将身份证从背景中剪裁出来

至此,第一个问题解决(虽然妥协甚多,但是仍可改进),接下来是第二个问题。


2、身份证号码在证上的什么位置?如何提取出来?

这个问题要简单一些,我默认身份证号码在证上的位置是固定的,于是只是要按照定死的位置和长宽比例把号码部分切割出来即可,得到:

7839925ec5df327634f8c9789dadec4c.png
把身份证号码部分截取出来

但是这里也有坑。我见过有来自内蒙古自治区的身份证,汉字的旁边还有蒙文,导致号码不在通常位置上。这个问题我选择忽视。可以看到为保险起见,切割时在号码四周保留了比较宽的边缘。进入下一步——分割数字之前,需要将图像二值化并将周围的边缘去掉。
这里使用了一个土办法:首先根据 RGB 值的方差和均值进行二值化。这样背景的白色和红/蓝的纹路就都可以去掉了。如下:

9c84a769eb17c7a31a5b8a77b4d4c649.png
将背景花纹去掉

注意这个过程中的阈值都是写死的。如果要适应更广泛的情况,应该根据图片统计特性动态设置阈值。我为了简单,妥协之。

之后就是要去掉这串数字周围的白边。扫描上图的行和列,如果该行/列的前景色(黑色)像素所占比例低于阈值,则认为这行/列属于空白边缘。这里的阈值又是写死的。该过程的成果:

e6c6d27ef2dc9a9bdf19f2e947d733b0.png
剪裁

3、如何将号码切分成数字和 'x' ?

我使用了一个简单的切分方法:扫描图像的每一列,计数该列中前景色(黑色)的像素数量。高于阈值(又是写死的阈值)的列认为属于数字部分,反之认为属于数字间空隙。上述号码串图像的每列前景色(黑色)像素数如下图:

9c5b23cfb069e30c5c2eb34d048c053a.png
17 位身份证号纵向前景色(数字本身)的像素数

上图中的每一簇(峰)就是一个数字。比如第三个有两个尖的簇就是数字 0 。两个尖就是 0 的两侧。又窄又高的那四簇就是 4 个 1 。能看出前几个数字是 110108 。后面几位我还是隐去了,因为会看的能看出是什么数字,保护隐私。

前景色(黑色)像素数低于阈值的部分就是数字间的空隙。这样就可以把这一串数字切分成一个个单独的数字图片。每一个数字图片采用之前用过的方法去除空白边缘。然后再用映射的办法缩放成 35 x 50 像素大小的图片。截取出来一个个数字图片如下:

a65b190bc75fcf8ae75291a0253e2f05.png

57e96e71550e6f3b57e9f1d7947c14b9.png

8521c45824426b8ab2ccd02c83378f14.png

09cceb104ea10734e91895c809f0fd65.png

11d131154eeed4110312185b90bbeb03.png

45fb129fae5f4014477fc00276c949f0.png

7242586f47a342c1f9e13132b09abf21.png

5c3f128b997c488ef40b88d4696ae1fb.png

ad633d83d1dd25106012ebfc9cba70e4.png

d3747c73024e1002427e2a5836f0c843.png
后面的数字不贴了,保护隐私

我搜集了一些身份证照片,用上述方法取得了若干 0~9 数字和 x 的图片,作为训练集。需要指出,0~9 和 x 在身份证号中分布不均匀。有些较多,有些较少。我所能得到的身份证照片数量有限,不允许我抛弃样本以使 11 个总体数量均一。我忽略了这个问题。


4、如何识别 10 个数字和 'x' ?

至此,训练样本和待识别数字都被整理成 35x50 的二值图片。下一个问题是如何建模数字。我先将图片纵向分成 35x5 大小的 10 块,计数每一块中前景色(黑色)像素的个数,得到 10 个数值。再将图片横向分成 7x50 大小的 5 块,计数每一块中前景色(黑色)像素的个数,得到 5 个数值。这 15 个数值组成一个 15 维向量,作为该数字图像的建模。

把训练图片集中的数字图像建模,得到了来自 11 个总体(0~9 和 x)的一些训练样本。用均值估计每一个总体的期望。用加权平均(pooled)的方法估计了 11 个总体的共同协方差。这里我做了一个不太站得住脚的假设:就是 11 个总体的协方差矩阵相等。

识别时,把待识别的数字图像建模成 15 维向量,依次计算该向量与 11 个均值之间的Mahalanobis 距离:

取距离最小的均值所属的总体所代表的数字作为识别结果。

是 11 个数字(包含 x)中第 i 个的样本均值。
是全体样本(11 个数字都混在一块)的协方差矩阵的逆矩阵。算马氏距离取距离最小的数字类别,这本质上是最大似然(如果假设 11 个数字先验概率相同,那么这也是最大后验)。

5、代码

大家可以百度图片几张身份证(挑选背景颜色单纯、与身份证颜色反差大且拍照端正的)测试一下,正确率还不错。以下是源代码:

test.php

<?php
require_once(dirname(__FILE__) . DIRECTORY_SEPARATOR . 'recog.php');

ini_set('memory_limit', '2048M');

if ($argc < 2) {
    
	echo "usage: php test.php id_card_file_path";
	exit(1);
}

$recog = new recog($argv[1]);

try {
    
    echo $recog->recognize_id_number() . "n";
} catch (recog_exception $e) {
    
    echo "ERROR: " . $e->getMessage() . "n";
}

执行命令以及运行结果:

php test.php D:documentsstudy
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值