master
maizhijian 3 years ago
parent d18a336b3e
commit 18b2550975
  1. 1
      app/Http/Controllers/Home/Controller.php
  2. 30
      app/Http/Controllers/IdcardController.php
  3. 1
      app/Http/Controllers/TestController.php
  4. 828
      app/Service/RecogService.php
  5. BIN
      public/idcard/0.jpg
  6. BIN
      public/idcard/1.jpg
  7. BIN
      public/idcard/10.jpg
  8. BIN
      public/idcard/11.jpg
  9. BIN
      public/idcard/12.jpg
  10. BIN
      public/idcard/13.jpg
  11. BIN
      public/idcard/14.jpg
  12. BIN
      public/idcard/15.jpg
  13. BIN
      public/idcard/16.jpg
  14. BIN
      public/idcard/17.jpg
  15. BIN
      public/idcard/2.jpg
  16. BIN
      public/idcard/3.jpg
  17. BIN
      public/idcard/4.jpg
  18. BIN
      public/idcard/5.jpg
  19. BIN
      public/idcard/6.jpg
  20. BIN
      public/idcard/7.jpg
  21. BIN
      public/idcard/8.jpg
  22. BIN
      public/idcard/9.jpg
  23. BIN
      public/idcard/cut.jpg
  24. BIN
      public/idcard/id.jpg
  25. BIN
      public/idcard/id_bw.jpg
  26. BIN
      public/idcard/id_screen.jpg
  27. BIN
      public/idcard/sobel.jpg
  28. 1
      routes/api.php
  29. BIN
      storage/app/pic/4Gl2XeQPwDeuIWFo0Ctz8d5zmVaUkVmnKCUL54zk.jpeg
  30. BIN
      storage/app/pic/HznAr1FG39egLCKBUy6jPdSOhz7wbgOycrlzC033.jpeg
  31. BIN
      storage/app/pic/LcLastS6R9RMWMHjv1rHz5kPkWU6a675lnVfsllj.jpeg
  32. BIN
      storage/app/pic/NN48H140aSBnzelL5G1SeWRqnirJCow0MVin9g6e.jpeg
  33. BIN
      storage/app/pic/Nmh0zJ6osSBSxqpQeYptVfDuoK0Cy7QokcFpf6qh.jpeg
  34. BIN
      storage/app/pic/cKzoxqzu8yrVOp05IGnLQQan5ZVj07e7GGyrZobC.png
  35. BIN
      storage/app/pic/gcH235v4oBjzQTmf2mwWBEqD6WAUFZDWXOqeEWYN.jpeg
  36. BIN
      storage/app/pic/pKUAPabd4EqMtNBewiFREHyvxS4TN38rNC5QE6G9.jpeg
  37. BIN
      storage/app/pic/po6LSIZxEu9G3pYaeMf6xTdqZ8n6fNxcRmeDmikw.jpeg

@ -15,6 +15,7 @@ class Controller extends BaseController
public function __construct()
{
$this->ag();
//test
app()->extend(BaseViewFactory::class, function ($view, $app) {
/** @var \Illuminate\View\Factory $view */

@ -0,0 +1,30 @@
<?php
namespace App\Http\Controllers;
use App\Models\TestModel;
use App\Service\RecogService;
use Illuminate\Http\Request;
use Illuminate\Support\Facades\DB;
class IdcardController extends Controller
{
public function index (Request $request)
{
$path = storage_path().'/app/'.$request->file('pic')->store('pic');
// $path = 'D:\phpEnv\www\blog\storage\app\pic\WUluCVHloihXEyMPrQh1c41DmLK9q21Zfywcx2Xw.jpeg';
$recog = new RecogService($path);
try {
$id_no = $recog->recognize_id_number();
} catch (recog_exception $e) {
return ['code' => 999, 'message' => '图片不合规范', 'data' => ''];
}
return ['code' => 0, 'message' => '', 'data' => $id_no];
}
}

@ -17,4 +17,5 @@ class TestController extends Controller
// TestModel::create(['name' => mt_rand(111111,999999), 'time' => time()]);
// return 1;
}
}

@ -0,0 +1,828 @@
<?php
namespace App\Service;
use Throwable;
/**
* 从身份证照片识别身份证号。
*
* 用法:
*
* <?php
*
* require_once('recog.php');
*
* $recog = new recog('图片路径');
*
* try {
* $id_no = $recog->recognize_id_number();
* } catch (recog_exception $e) {
* // 出错处理
* }
*
* 注意:
* 1、目前只支持 .jpg 格式图片;
* 2、如果图片过大,请用 ini_set('memory_limit', '1024M'); 将 php 允许内容设大;
* 3、照片背景最好单色,无线状条纹,与身份证本身颜色区别大些。拍摄时尽量端正。
*
*/
class RecogService {
/**
* @var string 身份证照片图像路径。
*/
private $image_path;
/**
* @var resource 身份证处理图像资源句柄。
*/
private $img;
/**
* @var array 协方差矩阵逆矩阵。
*/
static private $pooled_cov_inv = array(
array(0.00710845672853204, 0.0039446711283805, 0.00116497399796239, 0.00221331994831941, 0.00131940301426123, 0.00157461941628086, 0.00319800005056393, 0.00226498196158488, 0.00110922438962248, 0.00135182886257559, -0.00384480752553024, -0.00350175038073242, -0.00284470183449717, -0.00244184054418538, -0.0046703286465042),
array(0.0039446711283805, 0.00941449553607874, -0.00156678041438081, 0.00514021650842889, 0.00117317575829987, 0.000659537873258636, 0.00327163646216167, 0.00260263349006258, 0.000803336226523026, 0.00085103599011712, -0.00348208011375717, -0.00412489047687407, -0.00358665867806154, -0.00349444355315733, -0.00408154889941768),
array(0.00116497399796239, -0.00156678041438082, 0.0199445265460817, -0.00832546458964705, 0.00224531038062372, 0.00183773872494236, 0.00169440537533953, -0.00490795483205157, 0.00141747856551286, 0.000304442062153529, -0.00126404065000818, -0.00203083032124226, -0.00197310838038498, -0.00322238699379759, -0.002088018954331),
array(0.00221331994831941, 0.00514021650842889, -0.00832546458964704, 0.0225448802702108, -0.00307013556528412, -0.000382540187844993, 0.000312101204709124, 0.00334047311403818, -0.00161315213556878, 0.000753379347939048, -0.00174524623795882, -0.00206802194006454, -0.0037351611351895, -0.00194125513622051, -0.00276588300177873),
array(0.00131940301426122, 0.00117317575829987, 0.00224531038062372, -0.00307013556528413, 0.0159689117962463, -0.00149784355781548, 0.000346755378208585, 0.00170551077094237, 0.00241677990664607, 0.0018156746165767, -0.00285346285054146, -0.00364189419805494, -0.00303367801308524, -0.00283257545452448, -0.00288254849789108),
array(0.00157461941628086, 0.000659537873258648, 0.00183773872494236, -0.00038254018784498, -0.00149784355781547, 0.0212200137877075, -0.00568659059697146, 0.000922350403276974, 0.0030194458906049, 0.000344557189141248, -0.00246199024852513, -0.00245003453136874, -0.00360268396078748, -0.0035880817445959, -0.00401352671993961),
array(0.00319800005056392, 0.00327163646216166, 0.00169440537533953, 0.000312101204709112, 0.000346755378208579, -0.00568659059697147, 0.0213137819888336, -0.00584294827133668, 0.00214888264527886, 0.000976689675983129, -0.00280682353001263, -0.00292750177420907, -0.00329472240040449, -0.00266977074601445, -0.00322435982757423),
array(0.00226498196158488, 0.00260263349006258, -0.00490795483205157, 0.00334047311403819, 0.00170551077094238, 0.000922350403276972, -0.00584294827133667, 0.0222397895289423, -0.00065479959464453, 0.00296404543556003, -0.00288649193719865, -0.00253915179101506, -0.00358665812129274, -0.00244322713719601, -0.00333458160257149),
array(0.00110922438962248, 0.000803336226523022, 0.00141747856551286, -0.00161315213556878, 0.00241677990664606, 0.00301944589060489, 0.00214888264527887, -0.00065479959464453, 0.00971671675951504, 0.00278394413831865, -0.0027536516250455, -0.0034828211357343, -0.00285855387168301, -0.0029942294883606, -0.00331270782662298),
array(0.00135182886257559, 0.000851035990117119, 0.000304442062153529, 0.000753379347939048, 0.0018156746165767, 0.000344557189141241, 0.000976689675983133, 0.00296404543556003, 0.00278394413831865, 0.0087138260201973, -0.00338791565853486, -0.00280225824296423, -0.00210533988784246, -0.00136656282124493, -0.00388098814575398),
array(-0.00384480752553024, -0.00348208011375718, -0.00126404065000818, -0.00174524623795882, -0.00285346285054146, -0.00246199024852512, -0.00280682353001264, -0.00288649193719865, -0.0027536516250455, -0.00338791565853487, 0.00572958017723214, 0.0044486557337598, 0.00325637872134912, 0.00252256677411048, 0.00453513197852907),
array(-0.00350175038073242, -0.00412489047687407, -0.00203083032124226, -0.00206802194006453, -0.00364189419805494, -0.00245003453136873, -0.00292750177420907, -0.00253915179101506, -0.0034828211357343, -0.00280225824296423, 0.0044486557337598, 0.00694332472854774, 0.00328322918478732, 0.00279312313862495, 0.00370520475329374),
array(-0.00284470183449717, -0.00358665867806154, -0.00197310838038499, -0.0037351611351895, -0.00303367801308524, -0.00360268396078747, -0.0032947224004045, -0.00358665812129273, -0.00285855387168301, -0.00210533988784246, 0.00325637872134912, 0.00328322918478732, 0.00639019798613423, 0.00364943113202557, 0.00454627780146313),
array(-0.00244184054418538, -0.00349444355315733, -0.00322238699379759, -0.00194125513622051, -0.00283257545452448, -0.00358808174459589, -0.00266977074601445, -0.002443227137196, -0.0029942294883606, -0.00136656282124493, 0.00252256677411048, 0.00279312313862495, 0.00364943113202557, 0.0056909340972842, 0.00417245977402912),
array(-0.0046703286465042, -0.00408154889941768, -0.00208801895433101, -0.00276588300177872, -0.00288254849789108, -0.00401352671993959, -0.00322435982757424, -0.00333458160257149, -0.00331270782662298, -0.00388098814575398, 0.00453513197852907, 0.00370520475329374, 0.00454627780146313, 0.00417245977402912, 0.00757805238875424),
);
/**
* @var array 均值向量。
*/
static private $means = array(
'0' => array(82.0967741935484, 94.1612903225806, 75.9032258064516, 79.258064516129, 80.6612903225806, 79.3064516129032, 78.0645161290323, 77.741935483871, 86.5322580645161, 93.5322580645161, 142.790322580645, 130.774193548387, 63.4032258064516, 88.8709677419355, 171.516129032258),
'1' => array(50.8076923076923, 79.9102564102564, 78.5, 49.7307692307692, 41.2435897435897, 40.7051282051282, 40.7179487179487, 39.6666666666667, 38.525641025641, 34.2692307692308, 0.423076923076923, 34.6923076923077, 82.6794871794872, 225.641025641026, 9.41025641025641),
'2' => array(82.9111111111111, 76.5111111111111, 36.7777777777778, 39.5777777777778, 50.7111111111111, 54.9111111111111, 49.2888888888889, 37.8666666666667, 53.6444444444444, 127.2, 37.8222222222222, 118.2, 100.266666666667, 110.688888888889, 71.8666666666667),
'3' => array(137.114285714286, 85.3142857142857, 52.2571428571429, 57, 80.6571428571429, 52.1142857142857, 38.0857142857143, 39.3142857142857, 75.9142857142857, 106.485714285714, 49.2571428571429, 81.4, 133.342857142857, 157.828571428571, 103.571428571429),
'4' => array(31.08, 37.04, 36.92, 41.36, 57.8, 78.72, 121.16, 140.16, 46.56, 32.68, 48.48, 110.24, 110.6, 117.2, 60.92),
'5' => array(104.227272727273, 73.4090909090909, 39.7727272727273, 74.6363636363636, 89.2272727272727, 43.6363636363636, 36.1363636363636, 39.9545454545455, 56.9090909090909, 79.1363636363636, 45.4545454545455, 122.590909090909, 102.045454545455, 125.090909090909, 59.7272727272727),
'6' => array(27.4285714285714, 39, 43.1428571428571, 70.4285714285714, 117.428571428571, 79.2857142857143, 71.7142857142857, 75.2857142857143, 86.5714285714286, 79.8571428571429, 59.7142857142857, 125, 112.857142857143, 107.857142857143, 93.1428571428571),
'7' => array(151.75, 86.0833333333333, 43.4166666666667, 46.0833333333333, 43.9166666666667, 43.8333333333333, 42, 38.8333333333333, 44.3333333333333, 33.25, 26.75, 114.333333333333, 130.75, 88.8333333333333, 56.75),
'8' => array(72.6538461538462, 105.615384615385, 90.2692307692308, 104.653846153846, 99.5384615384615, 109.346153846154, 94.0769230769231, 90.7692307692308, 107.461538461538, 93.8076923076923, 90.2307692307692, 182.230769230769, 131.576923076923, 175.461538461538, 116.923076923077),
'9' => array(80.0666666666667, 96.5111111111111, 75.2222222222222, 75.4222222222222, 87.8222222222222, 117.911111111111, 76.7555555555556, 44.9555555555556, 43.4888888888889, 40.6666666666667, 72.6, 108.022222222222, 115.355555555556, 124.288888888889, 112.933333333333),
'x' => array(55.125, 78.875, 87.5, 84.5, 67, 64.125, 86.625, 88, 81.375, 65.75, 67.125, 140.25, 116.875, 146.625, 79.2),
);
/**
* @var bool 是否输出消息。
*/
static private $show_message = true;
/**
* @var int 紧缩图像时认为是前景部分的阈值(百分比,例如 3 代表该行/列前景色超过 3% 则被认为是前景部分)。
*/
static private $screening_threshold = 1;
/**
* @var int 霍夫参数空间大小(粒度)。100 表示 PI 被分成 100 等分。
*/
static private $hough_space_size = 100;
/**
* 构造函数。
*
* @param $img_path string 身份证照片图像路径。
*/
public function __construct($img_path) {
$this->image_path = $img_path;
}
/**
* 输出消息。
*
* @param $message string 消息。
*/
private function show_message($message) {
if (self::$show_message) {
// echo "{$message}\n";
}
}
/**
* 识别身份证照片中的身份证号。
*
* @return string 身份证号。
*
* @throws recog_exception
*/
public function recognize_id_number() {
error_reporting(0);
$this->show_message('Start processing. Image: ' . $this->image_path . '.');
$this->get_image();
$this->show_message('Image got. ' . imagesx($this->img) . ' x ' . imagesy($this->img) . '.');
$tmp = $this->sobel_edge();
$this->show_message('Find edge by SOBEL filter.');
$this->output("sobel", $tmp);
list($left, $right, $top, $bottom) = $this->hough($tmp);
$this->show_message("Find card by Hough Transform. Left: {$left} Right: {$right} Top: {$top} Bottom: {$bottom}.");
$this->cut_card($left, $right, $top, $bottom);
$this->show_message("Cut card.");
$this->output("cut", $this->img);
$this->cut_id_number();
$this->show_message("Cut id number.");
$this->output("id", $this->img);
$this->black_and_white();
$this->show_message("Black & White id number.");
$this->output("id_bw", $this->img);
$this->img = $this->screening($this->img);
$this->show_message("Screening id number.");
$this->output("id_screen", $this->img);
$c_arr = $this->split();
$this->show_message("Split numbers.");
for ($i = 0; $i < count($c_arr); $i++) {
$this->output("{$i}", $c_arr[$i]);
}
$id_number = '';
foreach ($c_arr as $c) {
$id_number .= $this->recognize($c);
}
$this->show_message("The result is {$id_number}.");
return $id_number;
}
/**
* 从原始图像中切割出身份证部分。
*
* @param $left int 身份证左边缘。
* @param $right int 身份证右边缘。
* @param $top int 身份证上边缘。
* @param $bottom int 身份证下边缘。
*
* @throws recog_exception
*/
private function cut_card($left, $right, $top, $bottom) {
if ($right <= $left || $bottom <= $top) {
return;
}
$dst_width = $right - $left;
$dst_height = $bottom - $top;
if (!$tmp = imagecreatetruecolor($dst_width, $dst_height)) {
throw new recog_exception('cut_card: Allocate temp image failed');
}
if (!imagecopy($tmp, $this->img, 0, 0, $left, $top, $dst_width, $dst_height)) {
throw new recog_exception('cut_card: Copy image failed');
}
$gabbage = $this->img;
$this->img = $tmp;
imagedestroy($gabbage);
}
/**
* 将身份证号部分切割出来。
*
* @throws recog_exception
*/
private function cut_id_number() {
$width = imagesx($this->img);
$height = imagesy($this->img);
$left = 0.31 * $width;
$top = 0.8 * $height;
$dst_width = 2.0 / 3.3 * $width;
$dst_height = 0.15 * $height;
if (!$tmp = imagecreatetruecolor($dst_width, $dst_height)) {
throw new recog_exception('cut: Allocate temp image failed');
}
if (!imagecopy($tmp, $this->img, 0, 0, $left, $top, $dst_width, $dst_height)) {
throw new recog_exception('screening: Copy image failed');
}
$gabbage = $this->img;
$this->img = $tmp;
imagedestroy($gabbage);
}
/**
* 获取分割的身份证数字。
*
* @return array 身份证数字图像对象资源句柄数组。
*
* @throws recog_exception
*/
private function split() {
$width = imagesx($this->img);
$height = imagesy($this->img);
$histgram = array();
for ($i = 0; $i < $width; $i++) {
$number = 0;
for ($j = 0; $j < $height; $j++) {
$rgb = imagecolorat($this->img, $i, $j);
$r = ($rgb >> 16) & 0xFF;
if ($r === 0) {
$number++;
}
}
$histgram[$i] = round((float)$number / $height * 100);
}
$in = false;
$left = 0;
$c_arr = array();
$w = ceil($width / 18);
foreach ($histgram as $i => $n) {
if ($n > 10 && !$in) {
$in = true;
$left = $i;
}
if ($in && $n < 10) {
$in = false;
$c_width = $i - $left;
if (!$tmp = imagecreatetruecolor($w, $height)) {
throw new recog_exception('split: Allocate image failed');
}
if (!imagefill($tmp, 0, 0, imagecolorallocate($tmp, 255, 255, 255))) {
throw new recog_exception('split: Fill image failed');
}
if (!imagecopy($tmp, $this->img, ($w - $c_width) / 2, 0, $left, 0, $c_width, $height)) {
throw new recog_exception('split: Copy image failed');
}
$tmp = $this->screening($tmp);
$c_arr[] = $tmp;
}
}
// 最后一个字符。
if ($in) {
$c_width = $width - $left;
if (!$tmp = imagecreatetruecolor($w, $height)) {
throw new recog_exception('split: Allocate image failed');
}
if (!imagefill($tmp, 0, 0, imagecolorallocate($tmp, 255, 255, 255))) {
throw new recog_exception('split: Fill image failed');
}
if (!imagecopy($tmp, $this->img, ($w - $c_width) / 2, 0, $left, 0, $c_width, $height)) {
throw new recog_exception('split: Copy image failed');
}
$tmp = $this->screening($tmp);
$c_arr[] = $tmp;
}
foreach ($c_arr as $key => $i) {
$width = imagesx($i);
$height = imagesy($i);
$w = 0.7 * $height;
$h = $height;
if (!$t = imagecreatetruecolor($w, $h)) {
throw new recog_exception('split: Allocate image failed');
}
if (!imagefill($t, 0, 0, imagecolorallocate($tmp, 255, 255, 255))) {
throw new recog_exception('split: Fill image failed');
}
if (!imagecopy($t, $i, ($w - $width) / 2, ($h - $height) / 2, 0, 0, $width, $height)) {
throw new recog_exception('split: Copy image failed');
}
$t = $this->resize($t);
$c_arr[$key] = $t;
}
return $c_arr;
}
/**
* 将图像二值化。
*
* @throws recog_exception
*/
private function black_and_white() {
$width = imagesx($this->img);
$height = imagesy($this->img);
for ($i = 0; $i < $width; $i++) {
for ($j = 0; $j < $height; $j++) {
$rgb = imagecolorat($this->img, $i, $j);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$m = ($r + $g + $b) / 3;
$grey = $m < 100 ? 0 : 255;
if (!imagesetpixel($this->img, $i, $j, imagecolorallocate($this->img, $grey, $grey, $grey))) {
throw new recog_exception("Black and white. Set pixcel failed. x={$i}, y={$j}");
}
}
}
}
/**
* hough 算法检测直线。
*
* @param $img resource sobel 算子查找边缘之后的二值图像对象。
*
* @return array 身份真边缘,分别是左、右、上、下。
*
* @throws recog_exception
*/
private function hough($img) {
$width = imagesx($img);
$height = imagesy($img);
$centerX = $width / 2;
$centerY = $height / 2;
$hough_interval = (M_PI / 2.0) / (float)self::$hough_space_size;
$hough_2d = array();
for ($i = 0; $i < $width; $i++) {
for ($j = 0; $j < $height; $j++) {
$rgb = imagecolorat($img, $i, $j);
if (!$grey = ($rgb >> 16) & 0xFF) {
continue;
}
for ($cell = 0; $cell < self::$hough_space_size; $cell++) {
$r = (int)(($i - $centerX) * cos($cell * $hough_interval) + ($j - $centerY) * sin($cell * $hough_interval));
$hough_2d["{$cell}_{$r}"] += 1;
}
}
}
arsort($hough_2d);
$max_freq = 0;
$lines = array();
foreach ($hough_2d as $param => $freq) {
// 如果频数小于最大频数的 20% ,忽略之。
if ($max_freq === 0) {
$max_freq = $freq;
} elseif ($freq < $max_freq * 0.2) {
break;
}
list($cell, $r) = explode('_', $param);
$theta = $cell * $hough_interval / (M_PI / 2.0) * 90.0;
if (count($lines)) {
$has_neighbor = false;
foreach ($lines as $idx => $l) {
$ptheta = $l['t'];
$pr = $l['r'];
$pc = $l['c'];
if (abs($theta - $ptheta) < 2 && abs($r - $pr) < 2) {
$lines[$idx]['t'] = ($ptheta * $pc + $theta) / ($pc + 1);
$lines[$idx]['r'] = ($pr * $pc + $r) / ($pc + 1);
$lines[$idx]['c']++;
$has_neighbor = true;
break;
}
}
if (!$has_neighbor) {
$lines[] = array('t' => $theta, 'r' => $r, 'c' => 1);
}
} else {
$lines[] = array('t' => $theta, 'r' => $r, 'c' => 1);
}
}
$horizental = array();
$vertical = array();
foreach ($lines as $line) {
if($line['t'] <= 3) {
$horizental[] = $line['r'];
}
if($line['t'] >= 87.5) {
$vertical[] = $line['r'];
}
}
if(count($horizental) < 2 || count($vertical) < 2) {
throw new recog_exception('Failed to find edges');
}
$left = round(min($horizental) + $width / 2.0);
$right = round(max($horizental) + $width / 2.0);
$top = round(min($vertical) + $height / 2.0);
$bottom = round(max($vertical) + $height / 2.0);
return array($left, $right, $top, $bottom);
}
/**
* sobel 算子边缘检测。
*
*
* @return resource Sobel 算子检测边缘后的黑白而至图像资源句柄。
*/
private function sobel_edge() {
$width = imagesx($this->img);
$height = imagesy($this->img);
if (!$tmp = imagecreatetruecolor($width, $height)) {
throw new recog_exception('sobel_edge: Allocate image failed');
}
if (!imagefill($tmp, 0, 0, imagecolorallocate($tmp, 255, 255, 255))) {
throw new recog_exception('sobel_edge: Fill image failed');
}
// 首先将图像灰度化,存副本。
$sdata = array();
for ($i = 0; $i < $width; $i++) {
for ($j = 0; $j < $height; $j++) {
$rgb = imagecolorat($this->img, $i, $j);
$r = ($rgb >> 16) & 0xFF;
$g = ($rgb >> 8) & 0xFF;
$b = $rgb & 0xFF;
$grey = ($r + $g + $b) / 3;
$grey = ($r);
$sdata[$i][$j] = $grey;
}
}
for ($i = 0; $i < $width; $i++) {
for ($j = 0; $j < $height; $j++) {
if ($i > 0 && $j > 0) {
$n0 = $sdata[$i - 1][$j - 1];
} else {
$n0 = 0;
}
if ($j > 0) {
$n1 = $sdata[$i][$j - 1];
} else {
$n1 = 0;
}
if ($j > 0 && $i < $width - 1) {
$n2 = $sdata[$i + 1][$j - 1];
} else {
$n2 = 0;
}
if ($i > 0) {
$n3 = $sdata[$i - 1][$j];
} else {
$n3 = 0;
}
if ($i < $width - 1) {
$n5 = $sdata[$i + 1][$j];
} else {
$n5 = 0;
}
if ($j < $height - 1 && $i > 0 && isset($sdata[$i - 1][$j - 1])) {
$n6 = $sdata[$i - 1][$j - 1];
} else {
$n6 = 0;
}
if ($j < $height - 1) {
$n7 = $sdata[$i][$j + 1];
} else {
$n7 = 0;
}
if ($j < $height - 1 && $i < $width - 1) {
$n8 = $sdata[$i + 1][$j + 1];
} else {
$n8 = 0;
}
$g0 = $n0 + 2 * $n1 + $n2 - $n6 - 2 * $n7 - $n8;
$g1 = $n1 + 2 * $n2 - $n3 + $n5 - 2 * $n6 - $n7;
$g2 = -$n0 + $n2 - 2 * $n3 + 2 * $n5 - $n6 + $n8;
$g3 = -2 * $n0 - $n1 - $n3 - $n5 + $n7 + 2 * $n8;
$g4 = -$n0 - 2 * $n1 - $n2 + $n6 + 2 * $n7 + $n8;
$g5 = -$n1 - 2 * $n2 + $n3 - $n5 + 2 * $n6 + $n7;
$g6 = $n0 - $n2 + 2 * $n3 - 2 * $n5 + $n6 - $n8;
$g7 = 2 * $n0 + $n1 + $n3 - $n5 - $n7 - 2 * $n8;
$g = max($g0, $g1, $g2, $g3, $g4, $g5, $g6, $g7);
$grey = $g > 177 ? 255 : 0;
if (!imagesetpixel($tmp, $i, $j, imagecolorallocate($tmp, $grey, $grey, $grey))) {
throw new recog_exception("find_card. Set pixcel failed. x={$i}, y={$j}");
}
}
}
return $tmp;
}
/**
* 紧缩图像。
*
* @param $img resource 图像句柄。
*
* @return resource 紧缩完成后的图像资源句柄。
*
* @throws recog_exception
*/
private function screening($img) {
$width = imagesx($img);
$height = imagesy($img);
$target = 0;
$histgram = array();
for ($i = 0; $i < $width; $i++) {
$number = 0;
for ($j = 0; $j < $height; $j++) {
$rgb = imagecolorat($img, $i, $j);
$r = ($rgb >> 16) & 0xFF;
if ($r === $target) {
$number++;
}
}
$histgram[$i] = round((float)$number / $height * 100);
}
$left = 0;
foreach ($histgram as $key => $n) {
if ($n > self::$screening_threshold) {
$left = $key;
break;
}
}
$right = $width;
foreach (array_reverse($histgram, true) as $key => $n) {
if ($n > self::$screening_threshold) {
$right = $key;
break;
}
}
$histgram = array();
for ($j = 0; $j < $height; $j++) {
$number = 0;
for ($i = 0; $i < $width; $i++) {
$rgb = imagecolorat($img, $i, $j);
$r = ($rgb >> 16) & 0xFF;
if ($r === $target) {
$number++;
}
}
$histgram[$j] = round((float)$number / $width * 100);
}
$top = 0;
foreach ($histgram as $key => $n) {
if ($n > self::$screening_threshold) {
$top = $key;
break;
}
}
$bottom = $height;
foreach (array_reverse($histgram, true) as $key => $n) {
if ($n > self::$screening_threshold) {
$bottom = $key;
break;
}
}
$dst_width = $right + 1 - $left > 0 ? $right + 1 - $left : 1;
$dst_height = $bottom + 1 - $top > 0 ? $bottom + 1 - $top : 1;
if (!$tmp = imagecreatetruecolor($dst_width, $dst_height)) {
throw new recog_exception("screening: Allocate temp image failed. [{$dst_width}/{$dst_height}]");
}
if (!imagefill($tmp, 0, 0, imagecolorallocate($tmp, 255, 255, 255))) {
throw new recog_exception("screening: Fill image failed");
}
if (!imagecopy($tmp, $img, 0, 0, $left, $top, $dst_width, $dst_height)) {
throw new recog_exception('screening: Copy image failed');
}
imagedestroy($img);
return $tmp;
}
/**
* 获取输入图片,转成 GD 对象。只支持 jpg 。
*
* @throws recog_exception
*/
private function get_image() {
if (!file_exists($this->image_path)) {
throw new recog_exception('image file dosen\'t exist. path: ' . $this->image_path);
}
if (!$this->img = imagecreatefromjpeg($this->image_path)) {
throw new recog_exception('get image failed. path: ' . $this->image_path);
}
}
/**
* 缩放图像。
*
* @param $img resource 图像对象
* @param $width int 缩放后的宽度。
* @param $height int 缩放后的高度。
*
* @return resource 缩放后的图像对象。
*
* @throws recog_exception
*/
private function resize($img, $width = 35, $height = 50) {
if (!$tmp = imagecreatetruecolor($width, $height)) {
throw new recog_exception("resize: Allocate image failed");
}
if (!imagefill($tmp, 0, 0, imagecolorallocate($tmp, 255, 255, 255))) {
throw new recog_exception("resize: Fill image failed");
}
$src_width = imagesx($img);
$src_height = imagesy($img);
for ($i = 0; $i < $width; $i++) {
for ($j = 0; $j < $height; $j++) {
$rgb = imagecolorat($img, floor($i * $src_width / $width), floor($j * $src_height / $height));
$r = ($rgb >> 16) & 0xFF;
$grey = $r < 50 ? 0 : 255;
imagesetpixel($tmp, $i, $j, imagecolorallocate($tmp, $grey, $grey, $grey));
}
}
return $tmp;
}
/**
* 识别图像中的数字。
*
* @param $img resource 包含一个数字的图像对象。
*
* @return string
*
* @throws recog_exception
*/
public function recognize($img) {
$width = imagesx($img);
$height = imagesy($img);
$v = array();
for ($j = 0; $j < $height; $j += 5) {
$b_count = 0;
for ($k = 0; $k < 5; $k++) {
for ($i = 0; $i < $width; $i++) {
$rgb = imagecolorat($img, $i, $j + $k);
$r = ($rgb >> 16) & 0xFF;
if ($r < 10) {
$b_count++;
}
}
}
$v[] = $b_count;
}
for ($i = 0; $i < $width; $i += 7) {
$b_count = 0;
for ($k = 0; $k < 5; $k++) {
for ($j = 0; $j < $height; $j++) {
$rgb = imagecolorat($img, $i + $k, $j);
$r = ($rgb >> 16) & 0xFF;
if ($r < 10) {
$b_count++;
}
}
}
$v[] = $b_count;
}
$result = null;
$min_dis = 10000000;
foreach (self::$means as $c => $mean) {
$dis = self::distance($v, $mean);
if ($dis <= $min_dis) {
$min_dis = $dis;
$result = $c;
}
}
return $result;
}
/**
* 计算 Mahalanobis 距离。
*
* @param $v array 向量 1
* @param $mean array 向量 2
*
* @return float
*/
private static function distance($v, $mean) {
$cov_inv = self::$pooled_cov_inv;
$md = array();
for ($i = 0; $i < count($mean); $i++) {
$md[] = $v[$i] - $mean[$i];
}
$tmp = array();
for ($i = 0; $i < count($cov_inv); $i++) {
$tmp[$i] = 0;
for ($j = 0; $j < count($cov_inv[$i]); $j++) {
$tmp[$i] += $md[$j] * $cov_inv[$i][$j];
}
}
$distance = 0;
for ($i = 0; $i < count($tmp); $i++) {
$distance += $tmp[$i] * $md[$i];
}
return $distance;
}
/**
* 输出图像。
*
* @param $name string 图像文件名。
* @param $img resource 图像资源句柄。
*/
private function output($name, $img) {
$this->show_message('Image Save: ' . "{$name}.jpg");
imagejpeg($img, "./idcard/{$name}.jpg");
}
}
class recog_exception extends \Exception {
public function __construct($message = "", $code = 0, Throwable $previous = null)
{
return false;
parent::__construct($message, $code, $previous);
}
}
;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 145 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 45 KiB

@ -15,6 +15,7 @@ use Illuminate\Support\Facades\Route;
*/
//test3
Route::get('maizhijian', 'TestController@index');
Route::post('idcard', 'IdcardController@index');
Route::namespace('Auth')->prefix('auth')->as('auth.')->group(function () {
Route::prefix('passport')->as('passport.')->middleware('auth:api')->group(function () {

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 340 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 394 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 65 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 456 KiB

Loading…
Cancel
Save