2018年7月7日 18:52:17
画像からテキストを抽出する場合、PHPで独自アルゴリズムを実装することも可能だが、その場合の最適化は煩雑であり、ライブラリの選定や精度調整が必要になる。
一般的なプロジェクトにおいて、OCRサービスを構築しない限り、専用ツールやAPIを構築する必要はない。
一、
ツールについて:tesseract-ocr/tesseractは現在検証中だが、完全なセットアップには時間がかかり、学習データが必要で、複数言語に対応している。
https://github.com/ShuiPingYang/tesseract-ocr-for-php
公式サイト:http://tesseract-ocr.repairfaq.org
実際のプロジェクトに導入するには少なくとも1ヶ月程度、成熟した運用には数ヶ月のオンラインデータトレーニングが必要となる。
二、
既存の成熟したプラットフォームのAPIを利用する方法もある。現在テスト済みの例として、百度(Baidu)の画像認識APIの「一般文字認識(高精度版)」があり、複雑な画像でも一定の精度を維持しており、戻り値は配列形式。不規則なテーブルやテキストに対しては別途処理が必要だが、現状のプロジェクトではこれを使用。公式サイト:http://ai.baidu.com/ 500回/日まで無料。テスト用途には便利。
他にもhttps://ai.qq.com/(騰訊)なども存在するが、登録のみで未テスト。
またアリババクラウドのAPIも存在するが未確認。
いずれも言語別のデモパッケージが提供されている。
以下は百度APIのデモコード:
/*
* 百度OCR
* $image は画像のバイナリデータ
* $type = 1 一般文字認識 50000回/日無料
* $type = 2 一般文字認識(高精度)500回/日無料
* $type = 3 ネットワーク画像文字認識 500回/日無料
* $type = 4 身分証明書認識 500回/日無料
* $type = 5 銀行カード認識 500回/日無料
*
* 備考:一般文字認識(高精度)の精度が高いので推奨
*/
public static function baidu_ai($image = '', $type = 2) {
if (empty($image)) {
throw new \Exception('画像のバイナリデータは空にできません');
}
$AppID = '';
$API_Key = '';
$Secret_Key = '';
if ($type == 1) {
// 一般文字認識
$url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/general_basic';
} elseif ($type == 2) {
//一般文字認識(高精度版)
$url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/accurate_basic';
} elseif ($type == 3) {
//ネットワーク画像文字認識
$url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/webimage';
} elseif ($type == 4) {
//身分証明書認識
$url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/idcard';
} elseif ($type == 5) {
//銀行カード認識
$url = 'https://aip.baidubce.com/rest/2.0/ocr/v1/bankcard';
} else {
throw new \Exception('$type のタイプが誤っています');
}
//トークン取得(有効期限30日)、Redisに保存
$baidu_key = 'BAIDU_TOKET';
if (Redis::ttl($baidu_key) < 360) {
$data['grant_type'] = 'client_credentials';
$data['client_id'] = $API_Key;
$data['client_secret'] = $Secret_Key;
$access_token_url = 'https://aip.baidubce.com/oauth/2.0/token';
$result = self::post($access_token_url, $data);
if ($result['code'] != 200) {
throw new \Exception('認証エラー');
}
Redis::set($baidu_key, $result['content']);
Redis::expire($baidu_key, 29 * 24 * 3600);
}
$baidu_token = Redis::get($baidu_key);
$baidu_token = json_decode($baidu_token, true);
$access_token = $baidu_token['access_token'];
$request_data['image'] = base64_encode($image);
$request_url = $url . "?access_token={$access_token}&aipSdk=php&aipSdkVersion=2_2_2";
$data_result = self::post($request_url, $request_data);
if ($data_result['code'] != 200) {
throw new \Exception('APIリクエストエラー');
}
return $data_result['content'];
}
public static function post($url = '', $data = [], $headers = []) {
if (empty($url) || empty($data)) {
throw new \Exception('リクエストURLとデータは空にできません');
}
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_HEADER, false);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, false);
curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
curl_setopt($ch, CURLOPT_POSTFIELDS, is_array($data) ? http_build_query($data) : $data);
curl_setopt($ch, CURLOPT_TIMEOUT_MS, 60000);
curl_setopt($ch, CURLOPT_CONNECTTIMEOUT_MS, 60000);
$content = curl_exec($ch);
$code = curl_getinfo($ch, CURLINFO_HTTP_CODE);
if ($code === 0) {
throw new \Exception(curl_error($ch));
}
curl_close($ch);
return array(
'code' => $code,
'content' => $content,
);
}
三、
純粋なアルゴリズムによる抽出は見送る。
PDFの内容抽出
テキストベースのPDFをPHPで処理する場合、いくつかのライブラリが存在し、効果的な結果を得られる。ただし、出力されるのは非構造化されたテキスト。
https://github.com/smalot/pdfparser PHPライブラリ
精度と効率ともに良好。
プロジェクトのデモコード:
composer require smalot/pdfparser
use Smalot\PdfParser\Parser;
public static function parsing_pdf() {
$parser = new Parser();
$pdf = $parser->parseFile('http://www.yafco.com.hk/asp_bin/newimg/1523872385.pdf');
// pp($pdf);
$text = $pdf->getText();
$result = json_encode($text, JSON_UNESCAPED_UNICODE);
}
画像形式のPDFはページごとに画像に分割してからOCR処理が必要。
データ抽出に関する考察
たとえば特定のウェブサイトから公開されたPDFファイルをダウンロードし、一部のデータを抽出する場合、まず元のPDFテキストをJSON形式に変換し、タブ文字や改行文字を適切に処理する。日本語はエンコードしないこと。
json_encode('日本語', JSON_UNESCAPED_UNICODE);
タブ文字(\t)や改行(\n)などの区切り文字を用いて、視覚的に規則正しいPDFであっても、解析後は不規則な形式になる。複数の要素を位置で特定するには、全体を走査する方法は非効率的である。
方法1:
PDFのデータ配置が概ね固定されている場合、大枠を小さな塊に分割し、それぞれの塊内での位置を細かく指定することで、多数のマッチング条件を減らすことが可能。全体走査よりも効率的。
方法2:
自然言語処理(NLP)APIを利用。百度AIや騰訊AIのテキスト解析機能はアルゴリズムを実装したインターフェースを提供しているが、完全な機能は提供されていない。また、1回の処理で扱える文字数が制限されているため、1ページあたり数千文字ある場合は不向き。
方法3:
Hadoopを使用する。しかし、直接的なデータクリーニング・抽出ツールは存在せず、大量データ分析を行う場合はこの生態系が必要。
今後の更新予定あり。