Microsoft Bing API Speech to Text で音声ファイルをテキスト化

音声ファイルをテキスト化する Microsoft Bing Speech to Text の試用をしてみました。STT にはクライアントライブラリとREST API が提供されています。お手軽な REST API の方を試してみます。シェルスクリプトのサンプルを元にPHPで書いてみました。

<?php
//
//  Microsoft Bing API Speech to Text
//

define("BING_TOKEN_URL", "https://api.cognitive.microsoft.com/sts/v1.0/issueToken");
define("BING_BASE_URL", "https://speech.platform.bing.com/speech/recognition");
define("BING_SERVICE", "/cognitiveservices");
define("BING_VERSION", "/v1");
define("BING_SUBSCRIPTION_KEY", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");

define("BING_LANG", "ja-JP");
define("BING_LOCALE", "ja-JP");
define("AUDIO_TYPE", "audio/wav");

/*
 * Recognition modes:
 * interactive: a user makes short requests and expects the application
 *              to perform an action in response.
 * conversation: users are engaged in a human-to-human conversation.
 *
 * dictation: users recite longer utterances to the application
 *            for further processing.
 */
define("RECOGNITION_MODE", "conversation");
/*
 * Output format:
 * simple: A simplified phrase result containing the recognition status 
 *         and the recognized text in display form.
 * detailed: A recognition status and N-best list of phrase results
 *           where each phrase result contains all four recognition forms
 *           and a confidence score.
 */
define("OUTPUT_FORMAT", "simple");


function Get_Token($url, $subscriptionKey) {
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        "Content-type: application/x-www-form-urlencoded",
        "Content-Length: 0",
        "Ocp-Apim-Subscription-Key: {$subscriptionKey}"
    ));
    $token = @curl_exec($ch);
    return $token;
}

function Speech_to_Text($url, $token, $audioFile, $audioType) {
    $size = filesize($audioFile);
    $data = file_get_contents($audioFile);
    $ch = curl_init();
    curl_setopt($ch, CURLOPT_POST, 1);
    curl_setopt($ch, CURLOPT_HEADER, 0);
    curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, 0);
    curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
    curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);
    curl_setopt($ch, CURLOPT_URL, $url);
    curl_setopt($ch, CURLOPT_HTTPHEADER, array(
        "Transfer-Encoding: chunked",
        "Content-Type: {$audioType}; codec=\"audio/pcm\"; samplerate=16000",
        "Authorization: Bearer {$token}"
    ));
    curl_setopt($ch, CURLOPT_POSTFIELDS, $data);
    curl_setopt($ch, CURLOPT_INFILESIZE, $size);
    $res = @curl_exec($ch);
    return $res;
}

// Main

if (empty($argv[1])) {
	echo "Please, specify a file to transcribe.".PHP_EOL;
	exit;
}

$filename = trim($argv[1]);
if (!file_exists($filename)) {
	echo "The file specified doesn't exist.".PHP_EOL;
	exit;
}

$token = Get_Token(BING_TOKEN_URL, BING_SUBSCRIPTION_KEY);

if (!empty($token)) {
	$url = BING_BASE_URL."/".RECOGNITION_MODE;
	$url .= BING_SERVICE.BING_VERSION;
	$url .= "?language=".BING_LANG;
	$url .= "&locale=".BING_LOCALE;
	$url .= "&format=".OUTPUT_FORMAT;
	$url .= "&requestid=rest_sample_request_id";
	$res = Speech_to_Text($url, $token, $filename, AUDIO_TYPE);
	var_dump($res);
} else {
	echo "Failed to get token.".PHP_EOL;
}
?>

Bing Speech to Text REST API では 15秒以下という制限があるため、音声の始まりの部分しかテキスト化できませんが、ちゃんと日本語テキストとして出力してくれています。いくつか試してみました。

出力結果:

outbound_only.wav:「お電話ありがとうございます。発信専用ダイアルのためおつなぎすることが出来ません・・・」

# php sample.php outbound_only.wav 
string(125) "{"RecognitionStatus":"Success","DisplayText":"お電話ありがとうございます","Offset":19100000,"Duration":19100000}"
busy.wav: 「ただいま電話が大変混み合っております。・・・」

# php sample.php busy.wav 
string(141) "{"RecognitionStatus":"Success","DisplayText":"ただいま電話 ga 大変混み合っております","Offset":12500000,"Duration":34500000}"
outoofservice.wav: 「ただいまのお時間は受付を終了しております。・・・」

# php sample.php outofservice.wav 
string(146) "{"RecognitionStatus":"Success","DisplayText":"ただいまのお時間は受付を終了しております","Offset":16600000,"Duration":40700000}"

PHP で複数ページのTIFFファイルをJPGに変換

ひとつのファイルに複数のページを格納したTIFFフォーマットの画像ファイルをそれぞれのページごとにJPGファイルとして保存する方法をPHPで実現します。

$path = "./src/foo.tif";
$dest = "./dst/";
$type = "jpg";

# ファイル名の取得
$extract = pathinfo($path);
$file = $extract['filename'];

# TIFF画像ファイルを読み込み
$images = new Imagick($path);

foreach($images as $i => $image) {
    $n = $i + 1;
    $image->writeImage("{$dest}{$file}_page{$n}.{$type}");
}
$images->clear(); 

例:

ソースファイル: foo.tif
出力ファイル: foo_page1.jpg  foo_page2.jpg foo_page3.jpg

以上です。

Composer – PHP 依存関係管理

Composerは、PHPの依存性管理のためのツールです。プロジェクトが依存するライブラリを管理することができます。ライブラリの管理をプロジェクトごと行うことができるので、システム全体に影響することなく利用できます。

Composer のシステム要件は PHP 5.3.2以上ですが、PHP 5.3.4 以上を推奨しています。古いバージョンではエッジケースがあるかもしれないと注意事項があります。

1. インストール

Composer の公式ページのダウンロードのやり方を紹介します。

php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"
php -r "if (hash_file('SHA384', 'composer-setup.php') === 'e115a8dc7871f15d853148a7fbac7da27d6c0030b848d9b3dc09e2a0388afed865e6a3d6b3c0fad45c48e2b5fc1196ae') { echo 'Installer verified'; } else { echo 'Installer corrupt'; unlink('composer-setup.php'); } echo PHP_EOL;"
php composer-setup.php --install-dir=/usr/local/bin --filename=composer
php -r "unlink('composer-setup.php');"

インストールを確認してみます。

# which composer
/usr/local/bin/composer

これで composer がコマンドラインから使えるようになりました。

2. パッケージリスト

composer で利用できるパッケージは何があるのしょうか?
https://packagist.org/explore/ で確認できます。

私は ocr 関連のライブラリが必要なので “ocr” で検索しみるとたくさんできてきました。
今回は、”thiagoalessio/tesseract_ocr” を利用してみます。

3. パッケージのインストール

パッケージのインストールには、”composer.json” というファイルに依存関係を記述してインストールフォルダに配置する必要があります。(https://packagist.org/ 参照)

今回利用する “thiagoalessio/tesseract_ocr” に composer.json の依存関係の記述があります。バージョンを “1.0.0-RC” から “0.2.1” に変更して使用します。
(追記:実は後でわかったことですが、1.0.0-RC のtesseract_ocr をインストールすると PHP 5.3.3 では文法エラーになる構文が使われています。)

# vi /project/lib/composer.json
--- 以下の内容を記述して保存 ---
{
    "require": {
        "thiagoalessio/tesseract_ocr": "0.2.1"
    }
}

そして、次に以下のコマンドを実行します。

# composer install
Your version of PHP, 5.3.3, is affected by CVE-2013-6420 and cannot safely perform certificate validation, we strongly suggest you upgrade.
You are running composer with xdebug enabled. This has a major impact on runtime performance. See https://getcomposer.org/xdebug
Loading composer repositories with package information
Updating dependencies (including require-dev)
  - Installing thiagoalessio/tesseract_ocr (0.2.1)
    Downloading: 100%         

Writing lock file
Generating autoload files

「使用しているPHPのバージョンが 5.3.3 なのでCVE-2013-6420の影響があるので安全に証明書の確認ができないためアップグレードすることを強くお勧めします。」と注意されました。さっそくエッジケースに出くわしましたね。
取り急ぎ、影響ないので無視します。
(追記:xdebug を有効にして composer を実行しています。。。という注意は /etc/php.d/xdebug.ini の zend_extension=/usr/lib64/php/modules/xdebug.so の行頭に ; セミコロンを付けてコメントアウトすると xdebug が無効になります。)

インストールフォルダをみると以下のファイルとフォルダが出来ています。

composer.json
composer.lock
vendor
 - autoload.php

これがあればインストールは成功です。

5. PHPからパッケージの利用

インストールした “tesseract_ocr” は TesseractOCR のラッパーライブラリなので TesseractOCR をインストールします。

# yum install tesseract

https://packagist.org/packages/thiagoalessio/tesseract_ocr を参考にして画像をOCRしてみます。
画像はこちらを使用させていただきます。

# vi test.php
--- 以下の内容を記述して保存 ---
<?php
include "lib/vendor/autoload.php";

$tocr = new TesseractOCR("./text.jpeg");
$tocr-<setLanguage("eng");
$text = $tocr->recognize();

cho "Output ==>".PHP_EOL;
echo $text;
?>

さっそく実行してみます。

# php test.php
Tesseract Open Source OCR Engine v3.04.00 with Leptonica
Output ==>
The quick brown fox
jumps over the lazy
dog,

しっかり認識できています!

以上です。

PHPテキストファイルの改行コード検出

テキストファイルの改行コードの検出方法を考えてみました。
単純にファイルの先頭を数MB 読んで改行コードを判定する方法です。
改行コードが混在してたり、バイナリファイルだと正しく検出できません。

echo detectEOL("/tmp/test.txt");

function detectEOL($file) {
    if (!file_exists($file)) {
        return null;
    }

    $fpsrc = fopen($file, "r");
    if (!$fpsrc) {
        return null;
    }

    $chunk = fread($fpsrc, 10*1024);
    fclose($fpsrc);

    if (empty($chunk)) {
        return null;
    }

    if (substr_count($chunk, "\r\n")) {
        return '\r\n';
    }

    if (substr_count($chunk, "\r")) {
        return '\r';
    }

    return '\n';
}

PHPファイルエンコード変換

ファイルのエンコードを UTF-8 > Shift-JIS へ変換して、別ファイルへ出力するサンプルです。

ファイルの内容をすべて読み込んでエンコード変換する方法:

$srcfile = "/tmp/test.csv";
// 一時ファイルディレクトリにファイルを作成します
$dstfile = tempnam(sys_get_temp_dir(), 'prefix');

$contents = file_get_contents($srcfile);
$contents = mb_convert_encoding($contents, "Shift-JIS", "UTF-8");

$fp = fopen($dstfile , "w");

if ($fp) {
    fwrite($fp, $contents);
    fclose($fp);
}

上記方法だとファイルの内容が多いとメモリの使用上限に達することがあったので
少し改良した版:

$srcfile = "/tmp/test.csv";
// 一時ファイルディレクトリにファイルを作成します
$dstfile = tempnam(sys_get_temp_dir(), 'prefix');

$fpsrc = fopen($srcfile , "r");
$fpdst = fopen($dstfile , "w");

if ($fpsrc && $fpdst) {
    while(($chunk = fgets($fpsrc)) !== false) {
        $chunk = mb_convert_encoding($chunk, "Shift-JIS", "UTF-8");
        fwrite($fpdst, $chunk);
    }
}
if ($fpsrc) {
    fclose($fpsrc);
}
if ($fpdst) {
    fclose($fpdst);
}