OcrApi.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515
  1. <?php
  2. /**
  3. * 重庆赤晓店信息科技有限公司
  4. * https://www.chixiaodian.com
  5. * Copyright (c) 2023 赤店商城 All rights reserved.
  6. */
  7. namespace app\utils\Ocr;
  8. use app\models\Option;
  9. use app\utils\Ocr\Constant\ContentType;
  10. use app\utils\Ocr\Constant\HttpHeader;
  11. use app\utils\Ocr\Constant\HttpMethod;
  12. use app\utils\Ocr\Constant\SystemHeader;
  13. use app\utils\Ocr\Http\HttpClient;
  14. use app\utils\Ocr\Http\HttpRequest;
  15. use yii\db\Exception;
  16. use app\utils\CurlHelper;
  17. use yii\helpers\Json;
  18. class OcrApi
  19. {
  20. /**
  21. * 营业执照
  22. */
  23. const TYPE_LICENSE = 1;
  24. /**
  25. * 身份证信息
  26. */
  27. const TYPE_ID_CARD = 2;
  28. /**
  29. * 银行卡信息
  30. */
  31. const TYPE_BANK = 3;
  32. /**
  33. * 通用文字信息
  34. */
  35. const TYPE_TEXT = 4;
  36. /**
  37. * 校验数组
  38. * @var int[]
  39. */
  40. public static $validType = [
  41. self::TYPE_LICENSE,
  42. self::TYPE_ID_CARD,
  43. self::TYPE_BANK,
  44. self::TYPE_TEXT
  45. ];
  46. /**
  47. * 身份证正面
  48. */
  49. const SIDE_FACE = 'face';
  50. /**
  51. * 身份证反面
  52. */
  53. const SIDE_BACK = 'back';
  54. /**
  55. * 调用app_key
  56. * @var string
  57. */
  58. private $app_key;
  59. /**
  60. * 调用app_secret
  61. * @var string
  62. */
  63. private $app_secret;
  64. /**
  65. * 营业执照
  66. * @var string
  67. */
  68. private $business_license_api = 'https://bizlicense.market.alicloudapi.com/rest/160601/ocr/ocr_business_license.json';
  69. /**
  70. * 身份证
  71. * @var string
  72. */
  73. private $id_card_api = 'https://cardnumber.market.alicloudapi.com/rest/160601/ocr/ocr_idcard.json';
  74. //https://market.aliyun.com/apimarket/detail/cmapi010401#sku=yuncode4401000018开通地址
  75. /**
  76. * 通用文字识别
  77. */
  78. private $text_recognition = "https://tysbgpu.market.alicloudapi.com/api/predict/ocr_general";
  79. //https://market.aliyun.com/apimarket/detail/cmapi020020#sku=yuncode1402000000开通地址
  80. /**
  81. * 银行卡
  82. * @var string
  83. */
  84. private $bank_card_api = 'https://yhk.market.alicloudapi.com/rest/160601/ocr/ocr_bank_card.json';
  85. //https://market.aliyun.com/apimarket/detail/cmapi016870#sku=yuncode10870000013开通地址
  86. public function __construct($is_platform = false) {
  87. if ($is_platform) {
  88. $saas_ocr = Option::get('ocr', 0, 'saas', '{"app_key": "", "app_secret": ""}')['value'];
  89. $ocr_setting_info = Json::decode($saas_ocr);
  90. $app_key = $ocr_setting_info['app_key'];
  91. $app_secret = $ocr_setting_info['app_secret'];
  92. } else {
  93. $store_ocr = Option::get('ocr', get_store_id(), 'store', '{"app_key": "", "app_secret": ""}')['value'];
  94. $ocr_setting_info = Json::decode($store_ocr);
  95. if(empty($ocr_setting_info['app_key']) || empty($ocr_setting_info['app_secret'])){
  96. $saas_ocr = Option::get('ocr', 0, 'saas', '{"app_key": "", "app_secret": ""}')['value'];
  97. $ocr_setting_info = Json::decode($saas_ocr);
  98. }
  99. $app_key = $ocr_setting_info['app_key'];
  100. $app_secret = $ocr_setting_info['app_secret'];
  101. }
  102. if (empty($app_key) || empty($app_secret)) {
  103. throw new \Exception('获取配置信息失败');
  104. }
  105. $this->app_key = $app_key;
  106. $this->app_secret = $app_secret;
  107. }
  108. /**
  109. * @param $url
  110. * @param $appKey
  111. * @param $appSecret
  112. * @param $bodyContent
  113. * @return Http\HttpResponse
  114. */
  115. private function doPost($url, $appKey, $appSecret, $bodyContent)
  116. {
  117. // 域名后、query前的部分
  118. $urlEles = parse_url($url);
  119. $host = $urlEles["scheme"] . "://" . $urlEles["host"];
  120. $path = $urlEles["path"];
  121. $request = new HttpRequest($host, $path, HttpMethod::POST, $appKey, $appSecret);
  122. // 设定Content-Type,根据服务器端接受的值来设置
  123. $request->setHeader(HttpHeader::HTTP_HEADER_CONTENT_TYPE, ContentType::CONTENT_TYPE_JSON);
  124. // 设定Accept,根据服务器端接受的值来设置
  125. $request->setHeader(HttpHeader::HTTP_HEADER_ACCEPT, ContentType::CONTENT_TYPE_JSON);
  126. // 注意:业务body部分,不能设置key值,只能有value
  127. if (0 < strlen($bodyContent)) {
  128. $request->setHeader(HttpHeader::HTTP_HEADER_CONTENT_MD5, base64_encode(md5($bodyContent, true)));
  129. $request->setBodyString($bodyContent);
  130. }
  131. // 指定参与签名的header
  132. $request->setSignHeader(SystemHeader::X_CA_TIMESTAMP);
  133. $response = HttpClient::execute($request);
  134. return $response;
  135. }
  136. /**
  137. * 营业执照识别
  138. * @param $image_url
  139. */
  140. public function getBusinessLicense($image_url, $type = 0)
  141. {
  142. $cache_key = 'ocr_license_' . md5($image_url);
  143. if ($license = cache()->get($cache_key)) {
  144. return [
  145. 'code' => 0,
  146. 'msg' => 'success',
  147. 'data' => $this->handleLicenseData($license)
  148. ];
  149. }
  150. $url = $this->business_license_api;
  151. $file = $image_url;
  152. // 如果没有configure字段,configure设为空
  153. $configure = [];
  154. if (substr($file, 0, 4) == "http") {
  155. if ($type) {
  156. $file = $this->imgToBase64($file);
  157. }
  158. $base64 = $file;
  159. } else if ($fp = fopen($file, "rb", 0)) {
  160. $binary = fread($fp, filesize($file)); // 文件读取
  161. fclose($fp);
  162. $base64 = base64_encode($binary); // 转码
  163. }
  164. $request = [
  165. 'image' => "$base64"
  166. ];
  167. if (count($configure) > 0) {
  168. $request["configure"] = json_encode($configure);
  169. }
  170. $body = json_encode($request);
  171. $response = $this->doPost($url, $this->app_key, $this->app_secret, $body);
  172. $stat = $response->getHttpStatusCode();
  173. if ($stat == 200) {
  174. $result_str = $response->getBody();
  175. $data = Json::decode($result_str);
  176. cache()->set($cache_key, $data, 30 * 60);
  177. return [
  178. 'code' => 0,
  179. 'msg' => 'success',
  180. 'data' => $this->handleLicenseData($data)
  181. ];
  182. } elseif ($stat == 462) {
  183. return $this->getBusinessLicense($image_url, 1);
  184. } else {
  185. return [
  186. 'code' => $stat,
  187. 'msg' => 'fail'
  188. ];
  189. // printf("Http error code: %d\n", $stat);
  190. // printf("Error msg in body: %s\n", $response->getBody());
  191. // printf("header: %s\n", $response->getHeader());
  192. }
  193. }
  194. /**
  195. * 身份证识别
  196. * @param string $image_url
  197. * @param string $side
  198. */
  199. public function getIDCard($image_url, $side = self::SIDE_FACE, $type = 0)
  200. {
  201. $cache_key = 'ocr_id_card_' . md5($image_url) . '_' . $side;
  202. if ($card = cache()->get($cache_key)) {
  203. return [
  204. 'code' => 0,
  205. 'msg' => 'success',
  206. 'data' => $this->handleIDCardData($card, $side)
  207. ];
  208. }
  209. $url = $this->id_card_api;
  210. $file = $image_url;
  211. // 如果没有configure字段,configure设为空
  212. $configure = [
  213. 'side' => $side
  214. ];
  215. if (substr($file, 0, 4) == "http") {
  216. if ($type) {
  217. $file = $this->imgToBase64($file);
  218. }
  219. $base64 = $file;
  220. } else if ($fp = fopen($file, "rb", 0)) {
  221. $binary = fread($fp, filesize($file)); // 文件读取
  222. fclose($fp);
  223. $base64 = base64_encode($binary); // 转码
  224. }
  225. $request = [
  226. 'image' => "$base64"
  227. ];
  228. if (count($configure) > 0) {
  229. $request["configure"] = json_encode($configure);
  230. }
  231. $body = json_encode($request);
  232. $response = $this->doPost($url, $this->app_key, $this->app_secret, $body);
  233. $stat = $response->getHttpStatusCode();
  234. if ($stat == 200) {
  235. $result_str = $response->getBody();
  236. $data = Json::decode($result_str);
  237. cache()->set($cache_key, $data, 30 * 60);
  238. return [
  239. 'code' => 0,
  240. 'msg' => 'success',
  241. 'data' => $this->handleIDCardData($data, $side)
  242. ];
  243. } elseif ($stat == 462) {
  244. return $this->getIDCard($image_url, $side, 1);
  245. } else {
  246. return [
  247. 'code' => $stat,
  248. 'msg' => 'fail'
  249. ];
  250. // printf("Http error code: %d\n", $stat);
  251. // printf("Error msg in body: %s\n", $response->getBody());
  252. // printf("header: %s\n", $response->getHeader());
  253. }
  254. }
  255. /**
  256. * 银行卡
  257. * @param string $image_url
  258. * @param string $side
  259. */
  260. public function getBankCard($image_url, $type = 0)
  261. {
  262. $cache_key = 'ocr_bank_card_' . md5($image_url);
  263. if ($bank = cache()->get($cache_key)) {
  264. return [
  265. 'code' => 0,
  266. 'msg' => 'success',
  267. 'data' => $this->handleBankCardData($bank)
  268. ];
  269. }
  270. $url = $this->bank_card_api;
  271. $file = $image_url;
  272. // 如果没有configure字段,configure设为空
  273. $configure = [
  274. 'card_type' => true
  275. ];
  276. if (substr($file, 0, 4) == "http") {
  277. if ($type) {
  278. $file = $this->imgToBase64($file);
  279. }
  280. $base64 = $file;
  281. } else if ($fp = fopen($file, "rb", 0)) {
  282. $binary = fread($fp, filesize($file)); // 文件读取
  283. fclose($fp);
  284. $base64 = base64_encode($binary); // 转码
  285. }
  286. $request = [
  287. 'image' => "$base64"
  288. ];
  289. if (count($configure) > 0) {
  290. $request["configure"] = json_encode($configure);
  291. }
  292. $body = json_encode($request);
  293. $response = $this->doPost($url, $this->app_key, $this->app_secret, $body);
  294. $stat = $response->getHttpStatusCode();
  295. if ($stat == 200) {
  296. $result_str = $response->getBody();
  297. $data = Json::decode($result_str);
  298. cache()->set($cache_key, $data, 30 * 60);
  299. return [
  300. 'code' => 0,
  301. 'msg' => 'success',
  302. 'data' => $this->handleBankCardData($data)
  303. ];
  304. } elseif ($stat == 462) {
  305. return $this->getBankCard($image_url, 1);
  306. } else {
  307. return [
  308. 'code' => $stat,
  309. 'msg' => 'fail'
  310. ];
  311. // printf("Http error code: %d\n", $stat);
  312. // printf("Error msg in body: %s\n", $response->getBody());
  313. // printf("header: %s\n", $response->getHeader());
  314. }
  315. }
  316. /**
  317. * 处理文字识别数据
  318. */
  319. public function getTextRecognition($image_url, $type = 0) {
  320. $url = $this->text_recognition;
  321. $file = $image_url;
  322. $configure = [
  323. 'output_prob' => false,
  324. 'output_keypoints' => false,
  325. 'skip_detection' => false,
  326. 'dir_assure' => false,
  327. 'language' => 'sx'
  328. ];
  329. if (substr($file, 0, 4) == "http") {
  330. if ($type) {
  331. $file = $this->imgToBase64($file);
  332. }
  333. $base64 = $file;
  334. } else if ($fp = fopen($file, "rb", 0)) {
  335. $binary = fread($fp, filesize($file)); // 文件读取
  336. fclose($fp);
  337. $base64 = base64_encode($binary); // 转码
  338. }
  339. $request = [
  340. 'image' => "$base64"
  341. ];
  342. if (count($configure) > 0) {
  343. $request["configure"] = $configure;
  344. }
  345. $body = json_encode($request);
  346. $response = $this->doPost($url, $this->app_key, $this->app_secret, $body);
  347. $stat = $response->getHttpStatusCode();
  348. $result_str = $response->getBody();
  349. $str = "";
  350. if ($stat == 200) {
  351. $result = json_decode($result_str, true);
  352. if (!empty($result['ret'])) {
  353. foreach ($result['ret'] as $ret) {
  354. $str .= $ret['word'] . " ";
  355. }
  356. }
  357. return [
  358. 'code' => 0,
  359. 'msg' => '识别成功',
  360. 'data' => [
  361. 'result' => $result,
  362. 'str' => $str
  363. ]
  364. ];
  365. }
  366. return [
  367. 'code' => 1,
  368. 'msg' => '识别失败',
  369. 'data' => $result_str
  370. ];
  371. }
  372. /**
  373. * 处理拿到的营业执照数据
  374. */
  375. private function handleLicenseData($res) {
  376. if (empty($res)) {
  377. return [];
  378. }
  379. $data = [
  380. 'address' => $res['address'],
  381. 'business' => $res['business'],
  382. 'capital' => $res['capital'],
  383. 'establish_date' => $this->handleDate($res['establish_date']),
  384. 'name' => $res['name'],
  385. 'person' => $res['person'],
  386. 'reg_num' => $res['reg_num'],
  387. 'type' => $res['type'],
  388. 'valid_period' => $this->handleDate($res['valid_period'])
  389. ];
  390. return $data;
  391. }
  392. /**
  393. * 处理拿到的身份证信息数据
  394. */
  395. private function handleIDCardData($res, $side = self::SIDE_FACE) {
  396. if (empty($res)) {
  397. return [];
  398. }
  399. if ($side == self::SIDE_FACE) {
  400. $data = [
  401. 'address' => $res['address'],
  402. 'birth' => $this->handleDate($res['birth']),
  403. 'name' => $res['name'],
  404. 'nationality' => $res['nationality'],
  405. 'num' => $res['num'],
  406. 'sex' => $res['sex']
  407. ];
  408. } else {
  409. $data = [
  410. 'end_date' => $this->handleDate($res['end_date']),
  411. 'issue' => $res['issue'],
  412. 'start_date' => $this->handleDate($res['start_date'])
  413. ];
  414. }
  415. return $data;
  416. }
  417. /**
  418. * 处理拿到的银行卡信息数据
  419. */
  420. private function handleBankCardData($res) {
  421. if (empty($res)) {
  422. return [];
  423. }
  424. $data = [
  425. 'bank_name' => $res['bank_name'],
  426. 'card_num' => $res['card_num'],
  427. 'card_type' => $res['card_type'],
  428. 'valid_date' => $res['valid_date']
  429. ];
  430. return $data;
  431. }
  432. /**
  433. * 处理日期20220101 to 2020-01-01
  434. * @param $date
  435. */
  436. private function handleDate($date) {
  437. if (is_numeric($date)){
  438. return substr($date, 0 , 4) . '-' . substr($date, 4, 2) . '-' . substr($date, 6, 2);
  439. } else {
  440. return $date;
  441. }
  442. }
  443. public function imgToBase64($img_file) {
  444. $img_file = $this->saveTempImage($img_file);
  445. $img_base64 = '';
  446. if (file_exists($img_file)) {
  447. $app_img_file = $img_file; // 图片路径
  448. $img_info = getimagesize($app_img_file); // 取得图片的大小,类型等
  449. //echo '<pre>' . print_r($img_info, true) . '</pre><br>';
  450. $fp = fopen($app_img_file, "r"); // 图片是否可读权限
  451. if ($fp) {
  452. $filesize = filesize($app_img_file);
  453. $content = fread($fp, $filesize);
  454. $file_content = chunk_split(base64_encode($content)); // base64编码
  455. switch ($img_info[2]) { //判读图片类型
  456. case 1: $img_type = "gif";
  457. break;
  458. case 2: $img_type = "jpg";
  459. break;
  460. case 3: $img_type = "png";
  461. break;
  462. }
  463. $img_base64 = 'data:image/' . $img_type . ';base64,' . $file_content;//合成图片的base64编码
  464. }
  465. fclose($fp);
  466. }
  467. return $img_base64; //返回图片的base64
  468. }
  469. //获取网络图片到临时目录
  470. public function saveTempImage($url)
  471. {
  472. if (strpos($url,'http') === false) {
  473. $url = 'http:'. trim($url);
  474. }
  475. if (!is_dir(\Yii::$app->runtimePath . '/image')) {
  476. mkdir(\Yii::$app->runtimePath . '/image');
  477. }
  478. $save_path = \Yii::$app->runtimePath . '/image/' . md5($url) . '.jpg';
  479. CurlHelper::download($url, $save_path);
  480. return $save_path;
  481. }
  482. }