光学文字認識 (OCR) は、印刷されたテキストをデジタル表現に変換するプロセスです。印刷された書籍のデジタル化や領収書の電子記録の作成から、ナンバー プレートの認識、さらには画像ベースのキャプチャの解読まで、さまざまな実用的な用途があります。
Tesseract は、OCR を実装できるオープンソース プロジェクトです。このプロジェクトは *Nix システム、Mac システム、Windows システムで実行できますが、ライブラリを使用することで PHP プロジェクトでも使用できます。このチュートリアルの目的は、その使用方法を教えることです。
インストール
準備する
物事をシンプルかつ一貫性を保つために、仮想マシン (この記事では Vagrant を使用します) を使用してアプリケーションを実行します。これには、PHP と Nginx のインストールが含まれ、それぞれのプロセスを説明します。既存の Debian ベースのシステムに Tesseract を自分でインストールしたい場合は、次のセクションをスキップするか、他の *nix、Mac システム、または Windows
でのインストール手順について README を確認してください。Vagrant を構成する
このチュートリアルに従うように Vagrant を設定するには、以下の手順を実行します。または、単に Github からコードを取得することもできます。
次のコマンドを入力して、Homestead Enhanced Vagrant 構成を orc という名前のフォルダーにダウンロードします。
git クローン https://github.com/Swader/homestead_improved ocr次のコードを Nginx 構成ファイル Homestead.yml に追加します。 リーリー
次のように変更されました:
リーリーhosts ファイルにも追加します
リーリー Tesseractをインストールする次のステップは Tesseract をインストールすることです
Homestead 改良版は debian を使用しているため、vagrant ssh を使用して仮想マシンにログインした後、apt-get を使用してインストールできます。次のコマンドを実行するだけです。 リーリー
上記のように、README には他のオペレーティング システムに対応するチュートリアルがあります。
インストールのテストとカスタマイズ
PHP ラッパーを使用しますが、その前にコマンドラインで Tesseract をテストできます。
まずこの画像sign.pngを保存してください仮想マシンで次のコマンドを実行して画像からテキストを読み取ります
リーリー
これにより、現在のフォルダーにファイル out.txt が作成されます。このファイルには、次の単語が含まれるはずです: CAUTION
今すぐお試しください
sign2.jpg リーリー 今回は、Einbahnstral’ie という単語が生成されます。近いですが不正解です。画像内のテキストは非常に鮮明ですが、文字 ß を認識できません。
Tesseract が文字列を正しく読み取るためには、いくつかの新しい言語ファイル (この場合はドイツ語) をインストールする必要があります。利用可能な言語ファイルの包括的なリストがここにありますが、必要なものだけをダウンロードしましょう:
リーリー
解凍:リーリー
次に、ファイルを次のディレクトリにコピーします:リーリー
例えば
リーリー ここで、元のコマンドを –l を使用して再度実行します
リーリー
今回は、テキストは正しい Einbahnstraße である必要があります)。上記のプロセスを繰り返すことで、任意の言語を使用することができます。
設定アプリ
PHP で Tesseract を使用するためにこのライブラリを使用します。
最小限の Web アプリケーションを構築します。ユーザーは画像をアップロードし、OCR 処理の結果を表示します。これを実現するために、Silex マイクロフレームワークを使用します。アプリ自体はシンプルなので、慣れていなくても心配する必要はありません。このチュートリアルのコードはすべて Github で入手できることに注意してください。
最初のステップは、Composer を使用して依存関係ファイルをインストールすることです:
リーリー 次に 3 つのフォルダーを作成します: リーリー
フォーム (vi
ewsindex.twig) をアップロードする必要があります:リーリー 結果表示ページ (viewsresults.twig) が必要です:: リーリー
次に、スケルトン Silex アプリ (publicindex.php) を作成します:リーリー
ブラウザでこのアプリにアクセスすると、ファイルアップロードフォームが表示されます。 Homestead 改良版 Vagrant を使用している場合は、以下のリンクからアプリケーションにアクセスできます。
リーリー
次のステップは、ファイルのアップロードを実装することです。 Silex はこのジョブを非常に単純にします。$request には、アップロードされたファイルを取得できるファイル コンポーネントが含まれています。コード:リーリー
ご覧のとおり、ファイル名の競合を減らすためにランダムなファイル名が生成されますが、このアプリケーションでは、ファイルにどのような名前を付けるかは重要ではありません。ファイルのコピーをローカルに取得したら、Tessearct ライブラリのインスタンスを生成して分析できます。リーリー
画像への OCR の実装は非常に簡単で、メソッド accept() を呼び出すだけです。リーリー
最後に、結果ページに結果を表示します:リーリー
いくつかの写真で試して、どのように機能するかを確認してください。困ったときはこちらを参考にしてください
一个实际的例子
让我们来看OCR一个更实用的例子。在本例中,我们尝试在图像中找到一个格式化的电话号码。
看看下面一幅图,上传到你的应用:
结果应该如下:
<ol class="dp-c"><li class="alt"><span><span>:ii‘i </span></span></li><li><span>Customer Service Helplines </span></li><li class="alt"><span> </span></li><li><span>British Airways Helpline </span></li><li class="alt"><span> </span></li><li><span>09040 490 541 </span></li></ol>
它没有挑出正文文本,这是我们能料到的,因为图片质量太差。虽然识别了号码但是也有一些“噪声”。
为了提取相关信息,有如下几件事我们可以做。
你可以让Tesseract 把它的结果限制在一定的字符集内,所以我们告诉它只返回数字型的内容代码如下:
<ol class="dp-c"><li class="alt"><span><span class="vars">$tesseract</span><span>->setWhitelist(range(0,9)); </span></span></li></ol>
但这样有个问题。它常常把非数字字符解释成数字而非忽略它们。比如“Bob”可能被解释称数字“808”。
所以我们采用两步处理。
第一步,我们可以用一个基本的正则表达式。可以用谷歌电话库来确定一个数字串是否是合法电话号码。
备注:我已在Sitepoint 写过关于谷歌电话库的内容。
让我们给谷歌电话库添加一个PHP 端口,修改composer.json,添加:
<ol class="dp-c"><li class="alt"><span><span class="string">"giggsey/libphonenumber-for-php"</span><span>: </span><span class="string">"~7.0"</span><span> </span></span></li></ol>
别忘了升级:
<ol class="dp-c"><li class="alt"><span><span>composer update </span></span></li></ol>
现在我们可以写一个函数,输入为一个字符串,尝试提取一个合法的电话号码
<ol class="dp-c"><li class="alt"><span><span class="comment">/**</span> </span></li><li><span><span class="comment">* Parse a string, trying to find a valid telephone number. As soon as it finds a</span> </span></li><li class="alt"><span><span class="comment">* valid number, it'll return it in E1624 format. If it can't find any, it'll</span> </span></li><li><span><span class="comment">* simply return NULL.</span> </span></li><li class="alt"><span><span class="comment">*</span> </span></li><li><span><span class="comment">* @param string $text The string to parse</span> </span></li><li class="alt"><span><span class="comment">* @param string $country_code The two digit country code to use as a "hint"</span> </span></li><li><span><span class="comment">* @return string | NULL</span> </span></li><li class="alt"><span><span class="comment">*/</span><span> </span></span></li><li><span><span class="keyword">function</span><span> findPhoneNumber(</span><span class="vars">$text</span><span>, </span><span class="vars">$country_code</span><span> = </span><span class="string">'GB'</span><span>) { </span></span></li><li class="alt"><span> </span></li><li><span> <span class="comment">// Get an instance of Google's libphonenumber</span><span> </span></span></li><li class="alt"><span> <span class="vars">$phoneUtil</span><span> = \libphonenumber\PhoneNumberUtil::getInstance(); </span></span></li><li><span> </span></li><li class="alt"><span> <span class="comment">// Use a simple regular expression to try and find candidate phone numbers</span><span> </span></span></li><li><span> preg_match_all(<span class="string">'/(\+\d+)?\s*(\(\d+\))?([\s-]?\d+)+/'</span><span>, </span><span class="vars">$text</span><span>, </span><span class="vars">$matches</span><span>); </span></span></li><li class="alt"><span> </span></li><li><span> <span class="comment">// Iterate through the matches</span><span> </span></span></li><li class="alt"><span> <span class="keyword">foreach</span><span> (</span><span class="vars">$matches</span><span> </span><span class="keyword">as</span><span> </span><span class="vars">$match</span><span>) { </span></span></li><li><span> </span></li><li class="alt"><span> <span class="keyword">foreach</span><span> (</span><span class="vars">$match</span><span> </span><span class="keyword">as</span><span> </span><span class="vars">$value</span><span>) { </span></span></li><li><span> </span></li><li class="alt"><span> try { </span></li><li><span> </span></li><li class="alt"><span> <span class="comment">// Attempt to parse the number</span><span> </span></span></li><li><span> <span class="vars">$number</span><span> = </span><span class="vars">$phoneUtil</span><span>->parse(trim(</span><span class="vars">$value</span><span>), </span><span class="vars">$country_code</span><span>); </span></span></li><li class="alt"><span> </span></li><li><span> <span class="comment">// Just because we parsed it successfully, doesn't make it vald - so check it</span><span> </span></span></li><li class="alt"><span> <span class="keyword">if</span><span> (</span><span class="vars">$phoneUtil</span><span>->isValidNumber(</span><span class="vars">$number</span><span>)) { </span></span></li><li><span> </span></li><li class="alt"><span> <span class="comment">// We've found a telephone number. Format using E.164, and exit</span><span> </span></span></li><li><span> <span class="keyword">return</span><span> </span><span class="vars">$phoneUtil</span><span>->format(</span><span class="vars">$number</span><span>, \libphonenumber\PhoneNumberFormat::E164); </span></span></li><li class="alt"><span> </span></li><li><span> } </span></li><li class="alt"><span> </span></li><li><span> } catch (\libphonenumber\NumberParseException <span class="vars">$e</span><span>) { </span></span></li><li class="alt"><span> </span></li><li><span> <span class="comment">// Ignore silently; getting here simply means we found something that isn't a phone number</span><span> </span></span></li><li class="alt"><span> </span></li><li><span> } </span></li><li class="alt"><span> </span></li><li><span> } </span></li><li class="alt"><span> } </span></li><li><span> </span></li><li class="alt"><span> <span class="keyword">return</span><span> null; </span></span></li><li><span> </span></li><li class="alt"><span>} </span></li></ol>
希望注释能解释这个函数在干什么。注意如果这个库没能从字符串中解析出一个合法的电话号码它会抛出一个异常。这不是什么问题;我们直接忽略它并继续下一个候选字符。
如果我们找到一个电话号码,我们以E.164的形式返回它。这提供了一个国际化的号码,我们可以用来打电话或者发送SMS。
现在我们可以如下使用:
<ol class="dp-c"><li class="alt"><span><span class="vars">$text</span><span> = </span><span class="vars">$tesseract</span><span>->recognize(); </span></span></li><li><span><span class="vars">$number</span><span> = findPhoneNumber(</span><span class="vars">$text</span><span>, </span><span class="string">'GB'</span><span>); </span></span></li></ol>
我们需要给谷歌电话库提供一个提示来说明这个号码是哪个国家的。你也可以改成你自己的国家。
我们把所有的这些打包在一个新的路由中:
<ol class="dp-c"><li class="alt"><span><span class="vars">$app</span><span>->post(</span><span class="string">'/identify-telephone-number'</span><span>, </span><span class="keyword">function</span><span>(Request </span><span class="vars">$request</span><span>) </span><span class="keyword">use</span><span> (</span><span class="vars">$app</span><span>) { </span></span></li><li><span> </span></li><li class="alt"><span> <span class="comment">// Grab the uploaded file</span><span> </span></span></li><li><span> <span class="vars">$file</span><span> = </span><span class="vars">$request</span><span>->files->get(</span><span class="string">'upload'</span><span>); </span></span></li><li class="alt"><span> </span></li><li><span> <span class="comment">// Extract some information about the uploaded file</span><span> </span></span></li><li class="alt"><span> <span class="vars">$info</span><span> = </span><span class="keyword">new</span><span> SplFileInfo(</span><span class="vars">$file</span><span>->getClientOriginalName()); </span></span></li><li><span> </span></li><li class="alt"><span> <span class="comment">// Create a quasi-random filename</span><span> </span></span></li><li><span> <span class="vars">$filename</span><span> = sprintf(</span><span class="string">'%d.%s'</span><span>, time(), </span><span class="vars">$info</span><span>->getExtension()); </span></span></li><li class="alt"><span> </span></li><li><span> <span class="comment">// Copy the file</span><span> </span></span></li><li class="alt"><span> <span class="vars">$file</span><span>->move(__DIR__.</span><span class="string">'/../uploads'</span><span>, </span><span class="vars">$filename</span><span>); </span></span></li><li><span> </span></li><li class="alt"><span> <span class="comment">// Instantiate the Tessearct library</span><span> </span></span></li><li><span> <span class="vars">$tesseract</span><span> = </span><span class="keyword">new</span><span> TesseractOCR(__DIR__ . </span><span class="string">'/../uploads/'</span><span> . </span><span class="vars">$filename</span><span>); </span></span></li><li class="alt"><span> </span></li><li><span> <span class="comment">// Perform OCR on the uploaded image</span><span> </span></span></li><li class="alt"><span> <span class="vars">$text</span><span> = </span><span class="vars">$tesseract</span><span>->recognize(); </span></span></li><li><span> </span></li><li class="alt"><span> <span class="vars">$number</span><span> = findPhoneNumber(</span><span class="vars">$text</span><span>, </span><span class="string">'GB'</span><span>); </span></span></li><li><span> </span></li><li class="alt"><span> <span class="keyword">return</span><span> </span><span class="vars">$app</span><span>->json( </span></span></li><li><span> [ </span></li><li class="alt"><span> <span class="string">'number'</span><span> => </span><span class="vars">$number</span><span>, </span></span></li><li><span> ] </span></li><li class="alt"><span> ); </span></li><li><span> </span></li><li class="alt"><span>}); </span></li></ol>
我们现在有简单的API的基础—-也就是JSON响应-—我们可以用来作为一个简单的移动应用的后端,这款应用可以用来从一幅图中添加联系人,打电话。
总结
OCR有许多应用——并且很容易整合进你的应用超过你的预期)。本文中,我们安装了开源OCR包;并使用一个包装器库,把它整合进一个非常简单的PHP应用。我们只是触及到了所有可能性的表面,希望这能给你一些想法,帮你想想怎么在你自己的应用中使用OCR。
译文链接:http://www.codeceo.com/article/php-ocr-tesseract-get-text.html
英文原文:OCR in PHP: Read Text from Images with Tesseract