Optical Character Recognition (OCR)即光學字符辨識是把打印文本轉換成一個數字表示的過程。它有各類各樣的實際應用–從數字化印刷書籍、建立收據的電子記錄,到車牌識別甚至破解基於圖像的驗證碼。javascript
Tesseract是一個能實現OCR的開源項目。你能在*Nix系統,Mac系統和Windows系統上運行這個項目,可是隻要使用一個庫,咱們就能在PHP項目中使用它了。本教程的目的是教你如何使用。php
安裝html
準備java
爲了讓事情變得簡單和一致的, 咱們將使用虛擬機(本文使用Vagrant)來運行應用程序,這會涉及到安裝PHP和Nginx,咱們將安裝 Tesseract來分別演示過程。若是你想本身基於現有Debian-based系統安裝Tesseract,你能夠跳過下一部分—或者查看 the README來得到在其餘*nix上,Mac系統或者Windows的安裝指導.git
配置Vagrantgithub
爲了配置Vagrant以跟上本教程,完成以下步驟。或者你也能夠簡單的從Github得到代碼。web
輸入如下命令來下載Homestead Improved Vagrant配置到一個名爲orc的文件夾:正則表達式
git clone https://github.com/Swader/homestead_improved ocrexpress
將Nginx配置文件Homestead.yml中的如下代碼:json
sites: - map: homestead.app to: /home/vagrant/Code/Project/public
修改爲:
sites: - map: homestead.app to: /home/vagrant/Code/public
一樣要在hosts文件中添加
192.168.10.10 homestead.app
安裝Tesseract
下一步是安裝Tesseract
由於Homestead Improved 使用debian,咱們能夠在使用vagrant ssh登錄虛擬機後使用apt-get 來安裝它,簡單運行以下命令:
sudo apt-get install tesseract-ocr
正如上文提到的,在the README中有其餘的操做系統對應教程。
測試並定製安裝
咱們將使用PHP包裝,可是以前咱們能夠在命令行測試Tesseract。
首先保存這個圖片sign.png
在虛擬機中,執行以下命令來從圖片中讀取文字
tesseract sign.png out
這將在當前文件夾建立一個文件:out.txt裏面應該有單詞:CAUTION
如今嘗試sign2.jpg
tesseract sign2.jpg out
此次產生單詞Einbahnstral’ie。很接近但不正確—雖然圖像中的文字至關清晰,它沒能識別字符ß。
爲了獲使Tesseract正常讀取字符串,咱們須要安裝一些新的語言文件—就本例來講,德語。
這裏有一個全面的可用語言文件列表,但咱們直接下載所需的文件:
wget https://tesseract-ocr.googlecode.com/files/tesseract-ocr-3.02.deu.tar.gz
解壓:
tar zxvf tesseract-ocr-3.02.deu.tar.gz
而後把文件複製到以下目錄:
/usr/share/tesseract-ocr/tessdata
例如
cp deu-frak.traineddata /usr/share/tesseract-ocr/tessdata cp deu.traineddata /usr/share/tesseract-ocr/tessdata
如今咱們再次執行原來的命令可是要用 –l
tesseract sign2.jpg out -l deu 「deu」 是德語的 ISO 639-3碼.
此次,文字應該是Einbahnstraße(正確的)。
能夠經過重複上述過程來使用任意語言。
配置應用程序
咱們將使用這個庫來用PHP使用Tesseract。
咱們將創建一個極簡的web應用:用戶上傳圖片,並查看OCR處理結果。咱們將使用Silex microframework 來實現。不要擔憂你不熟悉它,這個應用自己很簡單。
記住這篇教程的全部代碼都能在Github上得到。
第一步是用Composer來安裝依賴文件:
composer require silex/silex twig/twig thiagoalessio/tesseract_ocr:dev-master
而後創建三個文件夾:
- public - uploads - views
咱們須要上傳表單(views\index.twig):
<html> <head> <title>OCR</title> </head> <body> <form action="" method="post" enctype="multipart/form-data"> <input type="file" name="upload"> <input type="submit"> </form> </body> </html>
須要一個結果展現頁面(views\results.twig)::
<html> <head> <title>OCR</title> </head> <body> <h2>Results</h2> <textarea cols="50" rows="10">{{ text }}</textarea> <hr> <a href="/">← Go back</a> </body> </html>
如今創建skeleton Silex app (public\index.php):
<php require __DIR__.'/../vendor/autoload.php'; use Symfony\Component\HttpFoundation\Request; $app = new Silex\Application(); $app->register(new Silex\Provider\TwigServiceProvider(), [ 'twig.path' => __DIR__.'/../views', ]); $app['debug'] = true; $app->get('/', function() use ($app) { return $app['twig']->render('index.twig'); }); $app->post('/', function(Request $request) use ($app) { // TODO }); $app->run();
若是你在瀏覽器訪問這個應用,你應該能看到一個文件上傳表單。若是你在使用Homestead Improved Vagrant,你能夠經過以下連接訪問該應用。
http://homestead.app/
下一步是實現文件上傳。Silex使得這項工做很是簡單;$request包含一個files組件,咱們能夠經過它來得到任意上傳的文件,代碼:
// Grab the uploaded file $file = $request->files->get('upload'); // Extract some information about the uploaded file $info = new SplFileInfo($file->getClientOriginalName()); // Create a quasi-random filename $filename = sprintf('%d.%s', time(), $info->getExtension()); // Copy the file $file->move(__DIR__.'/../uploads', $filename);
如你所見,咱們產生隨機文件名來減小文件名衝突—但在本應用中,咱們怎麼命名文件是不重要的。一旦咱們在本地有一份文件拷貝,咱們就能夠產生一個Tessearct庫的實例,而後進行分析:
// Instantiate the Tessearct library $tesseract = new TesseractOCR(__DIR__ . '/../uploads/' . $filename);
在圖像上實現OCR至關簡單,咱們只需調用方法recognize()。
// Perform OCR on the uploaded image $text = $tesseract->recognize();
最後咱們把結果展現到結果頁面:
return $app['twig']->render( 'results.twig', [ 'text' => $text, ] );
在一些圖片上嘗試,看看它效果怎樣。若是你有困難,能夠參考這個
一個實際的例子
讓咱們來看OCR一個更實用的例子。在本例中,咱們嘗試在圖像中找到一個格式化的電話號碼。
看看下面一幅圖,上傳到你的應用:
結果應該以下:
:ii‘i Customer Service Helplines British Airways Helpline 09040 490 541
它沒有挑出正文文本,這是咱們能料到的,由於圖片質量太差。雖然識別了號碼可是也有一些「噪聲」。
爲了提取相關信息,有以下幾件事咱們能夠作。
你可讓Tesseract 把它的結果限制在必定的字符集內,因此咱們告訴它只返回數字型的內容代碼以下:
$tesseract->setWhitelist(range(0,9));
但這樣有個問題。它經常把非數字字符解釋成數字而非忽略它們。好比「Bob」可能被解釋稱數字「808」。
因此咱們採用兩步處理。
嘗試提取多是電話號碼的數字串。
用一個庫輪流評估每個候選字符,一旦找到一個有效電話號碼則中止。
第一步,咱們能夠用一個基本的正則表達式。能夠用谷歌電話庫來肯定一個數字串是不是合法電話號碼。
備註:我已在Sitepoint 寫過關於谷歌電話庫的內容。
讓咱們給谷歌電話庫添加一個PHP 端口,修改composer.json,添加:
"giggsey/libphonenumber-for-php": "~7.0"
別忘了升級:
composer update
如今咱們能夠寫一個函數,輸入爲一個字符串,嘗試提取一個合法的電話號碼
/** * Parse a string, trying to find a valid telephone number. As soon as it finds a * valid number, it'll return it in E1624 format. If it can't find any, it'll * simply return NULL. * * @param string $text The string to parse * @param string $country_code The two digit country code to use as a "hint" * @return string | NULL */ function findPhoneNumber($text, $country_code = 'GB') { // Get an instance of Google's libphonenumber $phoneUtil = \libphonenumber\PhoneNumberUtil::getInstance(); // Use a simple regular expression to try and find candidate phone numbers preg_match_all('/(\+\d+)?\s*(\(\d+\))?([\s-]?\d+)+/', $text, $matches); // Iterate through the matches foreach ($matches as $match) { foreach ($match as $value) { try { // Attempt to parse the number $number = $phoneUtil->parse(trim($value), $country_code); // Just because we parsed it successfully, doesn't make it vald - so check it if ($phoneUtil->isValidNumber($number)) { // We've found a telephone number. Format using E.164, and exit return $phoneUtil->format($number, \libphonenumber\PhoneNumberFormat::E164); } } catch (\libphonenumber\NumberParseException $e) { // Ignore silently; getting here simply means we found something that isn't a phone number } } } return null; }
但願註釋能解釋這個函數在幹什麼。注意若是這個庫沒能從字符串中解析出一個合法的電話號碼它會拋出一個異常。這不是什麼問題;咱們直接忽略它並繼續下一個候選字符。
若是咱們找到一個電話號碼,咱們以E.164的形式返回它。這提供了一個國際化的號碼,咱們能夠用來打電話或者發送SMS。
如今咱們能夠以下使用:
$text = $tesseract->recognize(); $number = findPhoneNumber($text, 'GB');
咱們須要給谷歌電話庫提供一個提示來講明這個號碼是哪一個國家的。你也能夠改爲你本身的國家。
咱們把全部的這些打包在一個新的路由中:
$app->post('/identify-telephone-number', function(Request $request) use ($app) { // Grab the uploaded file $file = $request->files->get('upload'); // Extract some information about the uploaded file $info = new SplFileInfo($file->getClientOriginalName()); // Create a quasi-random filename $filename = sprintf('%d.%s', time(), $info->getExtension()); // Copy the file $file->move(__DIR__.'/../uploads', $filename); // Instantiate the Tessearct library $tesseract = new TesseractOCR(__DIR__ . '/../uploads/' . $filename); // Perform OCR on the uploaded image $text = $tesseract->recognize(); $number = findPhoneNumber($text, 'GB'); return $app->json( [ 'number' => $number, ] ); });
咱們如今有簡單的API的基礎—-也就是JSON響應-—咱們能夠用來做爲一個簡單的移動應用的後端,這款應用能夠用來從一幅圖中添加聯繫人,打電話。
總結
OCR有許多應用——而且很容易整合進你的應用(超過你的預期)。本文中,咱們安裝了開源OCR包;並使用一個包裝器庫,把它整合進一個很是簡單的PHP應用。咱們只是觸及到了全部可能性的表面,但願這能給你一些想法,幫你想一想怎麼在你本身的應用中使用OCR。