Node.js + Express + LPR API でナンバープレート認識(解説・後編)

前回はNode.jsとExpressを利用してナンバープレート認識を行う車番認識メモアプリのサンプルの解説で端末カメラのブラウザ表示と画像取得方法をご紹介しました。

サンプルアプリのソースコードは随時GitHubにて公開しています。

第2回は取得した画像をLPR APIにリクエストを送信し、結果を表示させる部分を紹介します。

4.取得した画像をサーバー側に渡す。

前回取得した画像をサーバー側に渡す部分を作っていきます。
Formdataでサーバー側にPOSTして画像データを渡します。レスポンスを受け取った後の処理もここに記載します。

const upload = function() {
 const formData = new FormData ();
  formData.append('file1', blob, 'image1.jpg');
  const action = "/lpr-api";
  const options = {
   method: 'POST',
    body: formData,
  }
  fetch(action,options).then(response => {
   return response.json();
  })
  .then((json) => {
   this.response_txt = JSON.stringify(json, null , "\t")
    console.log(this.response_txt);
  })
 .catch(error => {
   console.log(error);
  })
}

5.APIリクエスト

まず、APIにリクエストするために必要な設定を行っていきます。以下4つのモジュールを追加します。

"express-form-data","form-data","formdata-node","node-fetch@2"

expressでmultipart/form-data形式でHTTPリクエストを送信する際に必要になります。

次にLPR APIのAPIトークンとリクエストURLを取得します。SensingAPIの管理画面で確認することができます。管理画面には以下よりログインします。

LPR APIのAPIトークンの発行がお済みでない方は、「SensingAPI 初めの一歩」の①~③までの記事をご参照ください。
※APIのご利用に応じて利用料金が発生します。あらかじめご了承ください。

準備ができましたら、管理画面からAPIトークンとAPIのリクエストURLをコピーして、app.js内の文字列に代入します。

//Jsonファイル読み込み
const path = require('path');


//Token設定
const API_TOKEN = "***PUT_YOUR_TOKEN***";
const API_URL = "***PUT_YOUR_TOKEN_PATH***";

//Formdata
const ExpressformData = require('express-form-data');
const fetch = require('node-fetch');
const FormData = require('form-data');

const fs = require('fs');

//画像データクリーンアップ
app.use(ExpressformData.parse({autoClean:true}));

API_TOKENにAPIトークンを、API_URLにリクエストURLをそれぞれ代入してださい
実際のWebサービスの開発でコードを共有しながら利用する場合には、環境変数などにしてAPIトークンが外部に漏れない様に取り扱いにご注意ください。

次にフロントから、画像データの取得とLPR APIへのリクエストを行う機能を追加します。
const action = “/lpr-api”で指定しているサーバ側の”/lpr-api”を作成していきます。

app.post('/lpr-api', async(req,res)=>{

  //画像ファイル取得
  const path1 = req.files.file1.path;

  if(path1){
    const form = new FormData();
    const buffer =fs.createReadStream(path1);

    //Formdataに画像データとJSONパラメータを追加する
    form.append('image1',buffer);
    form.append('meta','{"psizemax":800,"psizelimit":0}',{
     contentType:'application/json',filename:'param.json'
    });

    url = `${API_URL}?token=${API_TOKEN}`;

    const response = await fetch(url,{method:'PUT',body:form});
    const json = await response.json();
    

  }else{
    res.render('/',{message:"error:アップロードできませんでした"})
  }
})

アップロードされた画像データとパラメータをFormDataに追加します。
パラメータは、サンプルアプリの様に別途JSONファイルを読み込む方法と直接FormDataにJSON文字列を記入する方法等ありますが、今回は後者の方で記載しました。

TokenとAPIのリクエスト先URL情報を持つurl変数を作成し、先程のFormDataと合わせてfetchでPUTします。

 const response = await fetch(url,{method:'PUT',body:form});

ここでAPIからのレスポンスがサーバー側で正しく受信できているか、下記のコードを追加して動作と受信内容をコンソールで確認します。

//camera.ejs 
 SubmitButton.onclick = () => {  
        ~省略~
    }
      this.blob = new Blob([barr], { type: "image/jpeg" });
      upload(); //*追加* upload関数呼び出し
  } 

//app.js json変数の下に追加してください
console.log(json);

アプリ起動後、ブラウザのボタンアクションでナンバープレートを撮影すると、コンソールで結果を確認することができます。

正常動作が確認できましたら、サーバーからクライアント側にレスポンスを返す下記のコードをconsole.logの上に配置して、再度ブラウザのボタンアクションで画像を撮影してください。

await res.send(json);

ブラウザ側のコンソールで認識結果が受信できたことが確認できました。最後に取得したデータが表示されています。

6.認識結果表示

結果表示用のUIを作成します。
id=”resps”の<div>内に出力結果を追加していきます。

<div id="app">
  <section class="section">
    <div class="container" style="text-align:center;flex-direction: column">
      <video ref="video" id="video" width="800" height="450" muted autoplay playsinline ></video>
        <div class="container" style="margin-bottom: 10px;">
          <button id="submit-button"  type="button" width="200" height="30">撮影ボタン</button>
          //ここから
          <div class="hidden_box">
            <hr />
            <div class="result">
              <p class="feedback">
                <div class="resfont" contenteditable="true" id="resps">
                </div>
              </p>
            </div>
            <hr />
          </div>
     //ここまで追加
       </div>
       <canvas ref="canvas" id="canvas" width="0" height="0" name="file1" class="nonedisp"></canvas> 
    </div>
  </section>
</div>

表示用の関数を作成します。
下記のコードはレスポンスのJSON内の’PLATES’の有無で認識できたかをtry-catchで切り分けています。ナンバープレートがあった場合、地名と、分類番号(3ケタ英数字)、かな文字、一連番号(4ケタ数字)、種類を表示させるようにしています。
spanタグとその内に、innerHTMLで文字列を生成して”id = resps”内に子要素として追加しています。
ナンバープレートが無かった場合(”RES:0”やアップロード時、リクエストエラー等)の処理をcatchに追記しておきます。

const TextPush = function(){
 const j = JSON.parse(this.response_txt);
  try{
   const cam = j['PLATES'][0];
    const alertrep = `${cam['AREA']} ${cam['CLASS']} ${cam['KANA']} ${cam['DIGITS']} ${cam['KIND']}`;
    const textres1 = document.createElement("span");
    textres1.innerHTML =  alertrep;
    const x = document.getElementById('resps');
    x.appendChild(textres1);
  }catch{
    const textres2 = document.createElement("span");
    textres2.innerHTML = "ナンバープレートが見つかりませんでした。" ;
    const y =  document.getElementById('resps');
    y.appendChild(textres2);
  } 
}

以上でサンプルアプリの開発の主要部分の手順、と詳細設定方法についての説明は以上になります。
最後に、このままだとレイアウトがあまり良くないので、<head>内に<style>を追加して微調整し、アプリっぽくしていきます。

<style>
 //出力結果レイアウト調整
 span {
  display:block;
   font-size: medium;
 }
 //canvas費用時
 .nonedisp {
  display: none;
 }
</style>

実際に起動すると以下のようになります。

まとめ

前回に引き続き、サンプルアプリの取得した画像データを用いてLPR APIへリクエストを送る手順を紹介しました。

EyeTech Sensing APIは、簡単に無料で会員登録ができる画像認識APIサービスです。最先端のナンバープレート認識API「LPR API」を、Webからメールアドレスで会員登録するだけで、初期費用・維持費不要で、すぐに利用できますので、ぜひお試しください。