【Azure】ComputerVisionを利用したOCRページを作ってみた

#概要

AzureのOCR機能であるComputerVisionを利用したOCR Webページを作成しました
初めてAuzreを触ったり Web環境の構築を行ったので備忘録として書いていきたいと思います
また どなたかの参考になれば幸いです

こちらの記事及び関連サイトを参考に作成しました (コードはほぼコピペです)
[[AzureのComputer Vision API＆Face API ] WEBカメラでパシャリと感情分析やOCRしてみる](https://qiita.com/shima-07/items/26b71e96cbf87376bcb0)

画面構成はこんな感じです。
![画面イメージ2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/423144f5-3f3d-f141-6cb2-93335f5e05c4.png)



#1．Azure無料アカウント作成

[アカウントを作成する](https://azure.microsoft.com/ja-jp/free/search/?&ef_id=EAIaIQobChMI65Lgw-vs6gIVA7eWCh1e5gkvEAAYASAAEgKEqfD_BwE:G:s&OCID=AID2100066_SEM_EAIaIQobChMI65Lgw-vs6gIVA7eWCh1e5gkvEAAYASAAEgKEqfD_BwE:G:s&dclid=CLze_MTr7OoCFf7BTAIdshAA8Q)

Azureのアカウントを持ってない人はまずアカウントを作成してください
1年間 特定のサービスを無料で利用できたり30日間¥22,500分の利用が無料になります

#2．リソースグループ作成
####1．①左上のメニューバー → ②[リソースグループ] → ③[作成]　をクリック
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/e087551b-2eeb-c6f5-66ef-c7b1fd32ccee.png)

####2．各項目を入力し、[確認および作成]をクリック
①サブスクリプション：無料枠で使用したかったので[FreeTrial]を選択
②リソースグループ：任意の名前を入力
③リージョン：日本で使用するので[東日本]を選択
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/87f2fd04-b11d-e1e1-3d1c-58e449064dc2.png)

####3．内容を確認し、[作成]をクリック
![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/5ca5d0ab-9f3b-fd9c-80eb-334cc5923825.png)

####4．右上に表示されるポップアップの[リソースグループに移動]をクリック
![図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/70375773-c59d-fba8-92f1-1005ade8b866.png)

####5．リソースグループの確認
先ほど付けた名前でリソースグループが作成されていることが確認できます
![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/b86b411b-8dbb-4422-2338-ad9c8838f6d1.png)



#3．ComputerVision作成
####1．検索ボックスでCognitive Serviceを検索
![図6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/38b5181f-1147-eb77-c868-a37856cca080.png)

####2．Cognitive Seviceのページで[＋追加]をクリック
![図7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/575e1bd7-5277-0823-c417-560894264696.png)

####3．検索ボックスでComputer Visionを検索し、クリック
![図8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/37481d24-5db3-d4ae-de6e-bd14300a01e0.png)

####4．[作成]をクリック
![図9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/9bfb0bc9-4768-aa1c-322d-82337ed5e8a0.png)

####5．各項目を入力し、[作成]をクリック
リソースグループの作成時と同様に各項目を入力する

①名前：任意の名前を入力
②サブスクリプション：無料枠で使用したかったので[FreeTrial]を選択
③場所：日本で使用するので[東日本]を選択
④価格レベル：無料枠で使用したかったので[Free F0]を選択
⑤リソースグループ：先ほど作成した[test-rg]を選択
![図10.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/43dcc4aa-91b5-501f-b94d-d9f478c28d4f.png)

※注意※
①[名前]はAzure上で一意の名前である必要があります
入力した名前が既に使用されている場合は以下のような警告が表示されます
![図11.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/827f9c86-5e03-927e-4be8-462038cd9d23.png)

####6．リソースのデプロイ
デプロイの完了を待つ
![図12.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/51bf1148-1aca-517f-d8ea-9c86a9456f8a.png)
↓
完了したら[リソースに移動]をクリック
![図13.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/74dcc708-edfa-f049-933f-b745c7c4eb04.png)

####7．Computer Visionの確認
Computer Visionのリソースが作成されていることが確認できます
![図14.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/ad2ad1c8-e6fc-f835-e843-80e91ae64d9c.png)

#4．環境構築
PCのカメラを使って文字を読み取るOCRサイトを作成するにあたり 環境構築を行います

必要なフレームワーク・ライブラリは以下の通りです
・Node.js
・Vue.js
・axios

ちなみに すでに環境がある方は **[8．コードを書く](https://qiita.com/asamin_na/private/fbbfb82d7eed46276c25#8%E3%82%B3%E3%83%BC%E3%83%89%E3%82%92%E6%9B%B8%E3%81%8F)**まで飛ばしてください

#5．Node.jsインストール
Vue.jsをインストールするために必要なnpmをインストールするために
まずはNode.jsをインストールします

**1．[Node.jsのHP](https://nodejs.org/ja/)で[推奨版]を選択**
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/50a4cc9b-21f8-af3d-d723-c46d42e80842.png)

**2．インストーラーを起動し [Next]をクリック**
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/08904b81-a95d-73aa-4af4-e973aba4cad0.png)


**3．チェックボックスにチェックを入れ [Next]をクリック**
![図3.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/d2d761f1-9513-17e6-3b9b-029889255dac.png)


**4．インストール先を確認し [Next]をクリック (今回はデフォルトのまま進む)**
![図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/6a229253-5849-5703-35c3-aab1fbf7cc50.png)


**5．インストールするモジュールを確認し [Next]をクリック (今回はデフォルトのまま進む)**
![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/795290ce-ca0f-705d-f578-8b23f5e8c19c.png)


**6．チェックを外した状態で [Next]をクリック (今回はデフォルトのまま進む)**
オプションのモジュールをインストールしたい場合はチェックを入れて進んでください
![図6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/442c4609-609c-9c68-4774-4717675e1c80.png)


**7．[Install]をクリック**
![図7.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/bb537824-8849-70a6-9b9b-09a9090100cc.png)


**8．インストールが完了するまで待つ**
![図8.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/7f8f7fcc-8ecd-d6c4-2015-bd9bbd64733f.png)


**9．[Finish]をクリック**
![図9.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/bbca9681-4faa-9653-7456-88c02748956d.png)


これでNode.js及びnpmをインストールできました


#6．Vue.jsインストール
PCのカメラを使用するためにVue.jsをインストールします
コマンドプロンプトで以下を実行します

```
npm install -g vue-cli
```

#7．axiosインストール
OCR結果を受け取るためにaxiosをインストールします
コマンドプロンプトで以下を実行します

```
npm install axios
```



#8．コードを書く
任意の名前でhtmlファイルを作成し コードを書きます
長いのでサンプルコードは**[12．サンプルコード](https://qiita.com/asamin_na/private/fbbfb82d7eed46276c25#12%E3%82%B5%E3%83%B3%E3%83%97%E3%83%AB%E3%82%B3%E3%83%BC%E3%83%89)**に記載します


#9．ComputerVisionのキーを取得

サンプルコード内にある
```ここにキーを記載```と```ここにエンドポイントを記載```に自分のComputerVisionの情報を記載します

まずはキーを取得します

####1．[キーとエンドポイント]画面に遷移
3．Computer Visionの作成の**[7．Computer Visionの確認](https://qiita.com/asamin_na/private/fbbfb82d7eed46276c25#7computer-vision%E3%81%AE%E7%A2%BA%E8%AA%8D)**の画面を表示し、左のメニューから[リソースの管理]>[キーとエンドポイント]をクリック
![図1.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/567c4046-89ae-6cca-2610-bffba211bad9.png)

####2．[キー1]をクリップボードにコピー
![図2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/5c4ee93b-ce42-7a90-5904-3402dcc5dda7.png)


####3．コード内の「ここにキーを記載」を[キー1]に置き換える
```
headers: {
              'Content-type': 'application/octet-stream',
              'Ocp-Apim-Subscription-Key':'ここにキーを記載'
            },
```

#10．ComputerVisionのエンドポイントURLを取得
####1．以下のサイトにアクセスし、リージョンを選択
[Computer Vision API - v3.0](https://japaneast.dev.cognitive.microsoft.com/docs/services/computer-vision-v3-ga/operations/56f91f2e778daf14a499f20d)

2．リソースグループ作成の**[2．各項目を入力し、[確認および作成]をクリック](https://qiita.com/asamin_na/private/fbbfb82d7eed46276c25#2%E5%90%84%E9%A0%85%E7%9B%AE%E3%82%92%E5%85%A5%E5%8A%9B%E3%81%97%E7%A2%BA%E8%AA%8D%E3%81%8A%E3%82%88%E3%81%B3%E4%BD%9C%E6%88%90%E3%82%92%E3%82%AF%E3%83%AA%E3%83%83%E3%82%AF)**で選択したリージョンをクリックする
　(この記事では[Japan East]を選択)



![スクリーンショット (286).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/14d02f63-44e7-4faa-638e-8f18250ed95f.png)

####2．各項目を選択
①[japaneast.api~~]と[[resouce name].cognitive~~]のどちらかを選択
(この記事では[japaneast.api~~]を選択)
![図4.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/215a2e95-81f8-6391-5a3c-8ba60c0f2ae4.png)
②読みたい文字の言語を選択
　デフォルトはunk (ComputerVisionが言語を判断する)
　日本語の場合はja
　英語の場合はen　　を選択
③画像のテキストの向きを検出するかどうかを選択
④APIに送信される本文のタイプを選択
　(この記事では[application/octet-stream]を選択)
⑤10．ComputerVisionのキーを取得　で取得したキーを入力
![図5.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/0a4e958f-3969-7343-9115-9721c507f918.png)
####3．エンドポイントURLをコピー
[Request URL]に書かれているURLをコピーする
![図6.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/c97f1322-b162-bf48-f99b-8b4c991e0155.png)

####4．コード内の「ここにキーを記載」を[Request URL]に置き換える
```
const COMPUTER_VISION_API_ENDPOINT_URL = 'ここにエンドポイントを記載';
```

#11．実行してみる
htmlファイルをブラウザーで開きます

[カメラON]ボタンを押してPCのカメラの前に読み取り対象を持っていき
[写真を撮る!]ボタンを押して写真を撮ると
[読み込み結果 :]に結果が返ってきます

![スクリーンショット (297).png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/8fc11dd1-eddb-4bd8-356c-35023f65ebe2.png)

![スクリーンショット (297)2.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/615615/7cf975be-506d-089b-c5cf-7cde6c7e3d4a.png)

【読み取り対象】Cloud Architect Powered by A.R.I. Group
【読み取り結果】CloudArchitectA.R.I.Group

「Powered by」が読み取れてないですね
文字が小さすぎるでしょうか…

PCのカメラの精度も影響してくるかと思いますが 他の文字はきちんととれてるみたいです

色んな言語を読み込ませてみたいですね


🌷🌷🌷🌷🌷
長くなってしまいましたが お付き合いいただきありがとうございました



#12．サンプルコード

```qiita.html

<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>TestPageForOCR</title>

  <!-- Load required Bootstrap and BootstrapVue CSS -->
  <link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap/dist/css/bootstrap.min.css" />
  <link type="text/css" rel="stylesheet" href="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.css" />

  <!-- Load polyfills to support older browsers -->
  <script src="https://polyfill.io/v3/polyfill.min.js?features=es2015%2CIntersectionObserver"
    crossorigin="anonymous"></script>

  <!-- Load Vue followed by BootstrapVue -->
  <script src="https://unpkg.com/vue@latest/dist/vue.min.js"></script>
  <script src="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue.min.js"></script>

  <!-- Load the following for BootstrapVueIcons support -->
  <script src="https://unpkg.com/bootstrap-vue@latest/dist/bootstrap-vue-icons.min.js"></script>

</head>

<body>
  <br>
  <br>
  <div class="container">

    <div id="appFaceAPI-File">

      <div class="row">
        <div class="col">
            <h2>①判定したい画像を選んでください</h2>
        </div>
      </div>

      <div class="row">
        <div class="col">
          <div class="form-group">
            <!-- https://bootstrap-vue.org/docs/components/form-file -->
            <div>
              <!-- Styled -->
              <b-form-file
                v-model="file"
                :state="Boolean(file)"
                placeholder="Choose a file or drop it here..."
                drop-placeholder="Drop file here..."
                @change="handlerFileChange"
              ></b-form-file>              
              <div class="mt-3">ファイルネーム: {{ file ? file.name : '' }}</div>
            </div>

            <div>
                <br>
                <h2><p>②カメラで撮影します。</p></h2>
                <p>※ カメラで撮影する前にカメラをONにしてください。</p> 
                <p>※ カメラOFFで消えない時はリロードするとカメラを停止できます。</p> 
            </div>

            <div>
                <table>
                    <tr>
                        <td><button id="on" v-on:click="btnON()">カメラON</button></td>
                        <td><button id="off" v-on:click="btnOFF()">カメラOFF</button></td>
                        <td><b-button v-on:click="hanlderCapture">写真を撮る！</button></td>
                    </tr>
                </table>

            </div>

          </div>
        </div>
      </div>

      <div>
        <table>
            <tr>
                <td>
                    <!-- キャプチャした画像 -->
                          <h3>カメラの映像</h3>
                          <video ref="video" id="video" width="320" height="240" autoplay></video>
                </td>

                <td>
                          <h3>写真のキャプチャ</h3>
                          <canvas ref="canvas" id="canvas" width="320" height="240"></canvas>
                </td>

                <td>
                    <!-- 選択したサムネイル -->

                        <h3>選択した画像のサムネ</h3>
                        <img id="output"  width="320" height="240" >
                </td>


            </tr>
        </table>
    </div>

    <div class="row">
      <div class="col">
        <h2>読み込み結果 : </h2>
        <pre><code>{{ letter }}</code></pre>
      </div>
    </div>
    <br>

    </div>

  </div>

  <!-- axios -->
  <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>

  <script>

    const app = new Vue({
      el: '#appFaceAPI-File',
      data: {
        response: '',
        file: null,
        feel:'',
        score:'',
        letter:'',
        oarray:'',

        /// ここから下はカメラ用
        video: {},
        canvas: {},
        captures: []


      },
      methods: {


        handlerFileChange: async function (e) {
          //console.log('handlerFileChange');

          // 1. ファイルアップロードの入力から、まずファイル情報を取り出す
          const files = e.target.files || e.dataTransfer.files;
          const file = files[0];

          /// サムネイルを取得するための記述
          // https://web.dev/read-files/ 参照
          const output = document.getElementById('output');
          const reader = new FileReader();
               reader.addEventListener('load', event => {
                 output.src = event.target.result;
               });
               reader.readAsDataURL(file);

          ////

          // 2. ファイル参照の中から ArrayBuffer としてデータを取り出す
          let contentBuffer = await this.readFileAsync(file);
          console.log('bbbbbb' + file);

          this.sendCognitiveAsFile(contentBuffer);
        },

        // ファイル情報の中から ArrayBuffer としてデータを取り出す
        // 本来 onnload で取り出すが await / async で呼び出せるようにしている
        readFileAsync: function (file) {
          return new Promise((resolve, reject) => {
            let reader = new FileReader();
            //console.log('aaaaaa' + reader.result);
            reader.onload = () => {
              resolve(reader.result);
            };
            reader.onerror = reject;
            reader.readAsArrayBuffer(file);


          })
        },

///////////////////////// 写真
       // キャプチャボタンの挙動
       hanlderCapture: async function () {
          this.canvas = this.$refs.canvas
          this.canvas.getContext('2d').drawImage(this.video, 0, 0, 320, 240);

          // まず、CanvasからBlogデータを取得
          const blob = await this.getBlogData(this.canvas);
          // BlogデータをArrayBufferに変換
          const contentBuffer = await this.readBlobToArrayBuffTo(blob);
          // console.log(contentBuffer);

          this.sendCognitiveAsFile(contentBuffer);

        },

        getBlogData: function (canvas) {
          return new Promise((resolve, reject) => {
            try {
              // 標準はPNG 読み込みなので、ファイルサイズが重くなりがち
              /*
              canvas.toBlob(function(blob){
                resolve(blob);
              });
              */
              // JPEG画質も指定できる
              canvas.toBlob(function(blob){
                resolve(blob);
              },"image/jpeg", 0.8);
            } catch( e ){
              reject(e);
            }
          })
        },

        // Blob の中から ArrayBuffer としてデータを取り出す
        // Blob : Binary Large Object バイナリデータを格納する場合のデータ型
        // 
        // 本来 onnload で取り出すが await / async で呼び出せるようにしている
        readBlobToArrayBuffTo: function (file) {
          return new Promise((resolve, reject) => {
            let reader = new FileReader();
            reader.onload = () => {
              resolve(reader.result);
            };
            reader.onerror = reject;
            reader.readAsArrayBuffer(file);
          })
        },

/////////////////////////////////

        // このあたりは以下の記事を参考に。
        // https://www.1ft-seabass.jp/memo/2020/05/07/azure-face-api-application-octet-stream-using-axios/

        sendCognitiveAsFile: async function(contentBuffer) {

          const COMPUTER_VISION_API_ENDPOINT_URL = 'ここにエンドポイントを記載';
                                                  
          // サブスクリプションをOcp-Apim-Subscription-Keyヘッダーに
          // JSONで送るのでContent-typeヘッダーにapplication/octet-stream指定

          const config = {
            url: COMPUTER_VISION_API_ENDPOINT_URL,
            method: 'post',
            headers: {
              'Content-type': 'application/octet-stream',
              'Ocp-Apim-Subscription-Key':'ここにキーを記載'
            },
            data: contentBuffer
          };

          var alltext = '';
          var ocrarray = [];

          // axios
          try {
              // POSTリクエストで送る
              const responseAzure = await axios.request(config);
              console.log('post OK');
              // データ送信が成功するとレスポンスが来る
              console.log(responseAzure.data);

              for(let i = 0; i <= responseAzure.data.regions[0].lines.length -1; i++){
                  ocrarray.push(responseAzure.data.regions[0].lines[i].words);
                  for(let j = 0; j <= responseAzure.data.regions[0].lines[i].words.length - 1 ; j++){
                      alltext = alltext + responseAzure.data.regions[0].lines[i].words[j].text ;

                      //console.log(responseAzure.data.regions[0].lines[i].words.length);

                  }
              }

              this.letter = alltext ;
              this.oarray = ocrarray ;

          } catch (error) {
              console.log('post Error');
              // ダメなときはエラー
              console.error(error);
          }

        },

        // ストリームの停止
        btnOFF(){
            this.video = this.$refs.video;
            if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
                navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
                    this.video.srcObject = stream ;
                    stream.getTracks().forEach(track => track.stop()); // ストリームの停止
                })
                }
            },

        // ストリームの開始
        btnON(){
            this.video = this.$refs.video;
            if (navigator.mediaDevices && navigator.mediaDevices.getUserMedia) {
                navigator.mediaDevices.getUserMedia({ video: true }).then(stream => {
                    this.video.srcObject = stream ;
                    this.video.play() ;
                })
                }
            }



      }
      ,
      mounted() {
        console.log('mounted');
      }
    })
  </script>
```