PHP OCR實戰:用Tesseract從影像中讀取文字

2016-01-15    分類:WEB開發、程式設計開發、首頁精華1人評論發表於2016-01-15

本文由碼農網 – 邱康原創翻譯,轉載請看清文末的轉載要求,歡迎參與我們的付費投稿計劃

Optical Character Recognition (OCR)即光學字元辨識是把列印文字轉換成一個數字表示的過程。它有各種各樣的實際應用–從數字化印刷書籍、建立收據的電子記錄,到車牌識別甚至破解基於影像的驗證碼。

Robotic eye

Tesseract是一個能實現OCR的開源專案。你能在*Nix系統,Mac系統和Windows系統上執行這個專案,但是隻要使用一個庫,我們就能在PHP專案中使用它了。本教程的目的是教你如何使用。

安裝

準備

為了讓事情變得簡單和一致的, 我們將使用虛擬機器(本文使用Vagrant)來執行應用程式,這會涉及到安裝PHP和Nginx,我們將安裝Tesseract來分別演示過程。如果你想自己基於現有Debian-based系統安裝Tesseract,你可以跳過下一部分—或者檢視the README來獲得在其他*nix上,Mac系統或者Windows的安裝指導.

配置Vagrant

為了配置Vagrant以跟上本教程,完成如下步驟。或者你也可以簡單的從Github獲得程式碼。

 

輸入以下命令來下載Homestead Improved Vagrant配置到一個名為orc的資料夾:

git clone https://github.com/Swader/homestead_improved ocr

將Nginx配置檔案Homestead.yml中的以下程式碼:

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”。

所以我們採用兩步處理。

  1. 嘗試提取可能是電話號碼的數字串。
  2. 用一個庫輪流評估每一個候選字元,一旦找到一個有效電話號碼則停止。

第一步,我們可以用一個基本的正規表示式。可以用谷歌電話庫來確定一個數字串是否是合法電話號碼。

備註:我已在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。

譯文連結:http://www.codeceo.com/article/php-ocr-tesseract-get-text.html
英文原文:OCR in PHP: Read Text from Images with Tesseract
翻譯作者:碼農網 – 邱康
轉載必須在正文中標註並保留原文連結、譯文連結和譯者等資訊。]

相關文章