QRコードのディスプレイ表示

| |バージョン|
|:---|:---|
|NAOqi|2.5.5.5|
|Choregraphe|2.5.5.5|
----
# この記事の内容
　本記事では、PepperにQRコードを読み込ませ、読取結果をディスプレイに表示させるサンプルアプリを紹介します。

# QRコード読み込み・表示アプリ
## サンプルアプリ
　サンプルプログラムはGitHubよりダウンロードしてください。
　https://github.com/Atelier-Akihabara/pepper-qrio-example
 
　サンプルプログラムqr_io_appを実行しQRコードを認識すると以下のように表示されます。（qr_io_app.pmlを開いてプログラムをPepperに転送してください）

<img width="353" alt="fig1.png" src="https://qiita-image-store.s3.amazonaws.com/0/54394/2fc77b29-9879-9dda-1761-95ba94792d21.png">

## サンプルアプリの基本動作
- PepperにQRコードをかざす。
- QRコードを認識すると、ディスプレイに認識したQRコードと同じ内容のQRコードが表示される。QRコードの仕様上、かざしたQRコードと表示されたQRコード画像が図形的に必ずしも一致するわけではありません。
- バックバンパーを押すと終了する。

# 解説
　処理の流れは、1. 初期化→2. 画像化→3. 表示となります。

## 1. 初期化
<img width="297" alt="fig2.png" src="https://qiita-image-store.s3.amazonaws.com/0/54394/e9de8e55-3b3a-ec71-2b42-a88e3b73e275.png">

### SetPathLibFldボックス
　libフォルダにパスを通し、アプリがlibフォルダのライブラリを使えるようにします詳細については次の記事を参照してください（[拡張ライブラリの組み込み方法](http://qiita.com/Atelier-Akihabara/items/d150185ed28fdba6ef20)）。libフォルダにはQRコードライブラリqrcodeがインストールされています。

### Initializeボックス
　ディスプレイを有効化し、PepperのBasic Awarenessを停止させ、顔を前向きにする基本的な初期化行います。

## 2. 画像化
<img width="146" alt="fig3.png" src="https://qiita-image-store.s3.amazonaws.com/0/54394/89ec0487-88cc-0c96-b3ed-0d508b62657c.png">

　PepperがQRコードを認識すると、ALMemoryから認識結果がボックスに飛んできます。
　ALMemoryからQRコード文字列を受け取るとReadWrite QRボックスは文字列を画像に変換し、ALMemory経由でディスプレイに画像データDataUriを送信します。

## ReadWrite QRボックス
　本プログラムの中心的役割を担うボックスです。処理の内容をみていきます。

### 処理のメインコード
1. 受け取った文字列pを画像化関数makeQRに渡します。
2. makeQR関数で変換された画像データをconvertToDataUriに渡しDataUri化します。
3. raiseEventでDataUri化されたデータをディスプレイに送信します。

```py:
def onInput_QRinput(self, p):
    #QR検知中確認
    if not self.isDetecting :
        return
    #検知開始後、初回のみ実行
    self.isDetecting = False
    #読み取ったQRコードの文字列を取得
    inputCode = p[0][0]
    #QR生成
    img = self.makeQR(inputCode) # 1.
    #ファイルオブジェクト化
    s = StringIO.StringIO()
    img.save(s, "png")
    #画像をdataURIスキームに変換
    dataUri = self.convertToDataUri(s.getvalue()) # 2.
    s.close()
    #タブレットに送信
    self.memory.raiseEvent(self.getParameter("key"), [inputCode, dataUri]) # 3.
    #一定時間待機
    time.sleep(2)
    #QR検知再開
    self.isDetecting = True
```

　続いて、makeQR内で呼び出されているサブルーチンについて解説いたします。

### 画像生成コード
1. ライブラリqrcodeを利用しQRコードライブラリに画像作成のためのパラメータを設定します。
2. QRコード画像生成を実行します。
3. 画像を目的のサイズにリサイズします。

```py:
def makeQR(self, str):
    #QR生成
    import qrcode
    #エラー訂正レベル
    ec_level = [qrcode.constants.ERROR_CORRECT_L,    # 7%以下の誤差を修正
                qrcode.constants.ERROR_CORRECT_M,    #15%以下の誤差を修正
                qrcode.constants.ERROR_CORRECT_Q,    #25%以下の誤差を修正
                qrcode.constants.ERROR_CORRECT_H]    #30%以下の誤差を修正
    qr = qrcode.QRCode(
        #QRコードのバージョン
        version=self.getParameter("version"),
        #エラー訂正レベル
        error_correction=ec_level[self.getParameter("error_correction")],
        #セルサイズ設定
        box_size=self.getParameter("box_size"),
        #境界線のサイズ設定
        border=self.getParameter("border")
    ) # 1., 2.
    qr.add_data(str)
    qr.make(fit=True)
    img = qr.make_image()
    #リサイズ
    imgSize = self.getParameter("imgSize")
    img = img.resize((imgSize, imgSize)) # 3.
    return img
```

### DataUrl変換コード
　画像データをDataUriに変換するメンバメソッドです。onInput_QRinput内で利用しています。

```py:
def convertToDataUri(self, img):
    #画像をdataURIスキームに変換
    img_b64 = img.encode("base64").replace("\n", "")
    dataUri = 'data:image/png;base64,{0}'.format(img_b64)
    return dataUri
```

### ReadWrite QRボックスのパラメータ
　参考までにパラメータの設定内容を解説いたします。下記のパラメータは上記コード内のself.getParameterにて取得されています。

|バラメータ|内容|
|:--|:--|
|key|認識データの送信先です。認識されたデータは、ALMemoryにDataUri形式で送信されます。|
|version|QRコードの型番を指定します。型番は1〜40まであり、データサイズが増えるに従い大きくする必要があります。詳細については、QRコードの規格を参照しください。|
|error_correction|QRコードのデータ訂正コード比率を4段階で設定します。エラー訂正比率が高ければ、信頼性は高くなりますが、保存できるデータ容量は少なくなります。<br>- 0 ERROR_CORRECT_L7%まで<br>- 1 ERROR_CORRECT_M15%まで<br>- 2 ERROR_CORRECT_Q25%まで<br>- 3 ERROR_CORRECT_H30%まで|
|box_size|ピクセルの構成数を設定します。|
|border|境界線のサイズを設定します。|
|imgSize|生成されたデータの目標サイズですこのサイズにリサイズされます|

## 3. 表示
　ALMemoryに飛んできたデータを受け取りディスプレイに画像データを表示します。

### HTMLコード
 　画像を表示するためのスペース箱 `id="qr_display"` を用意しています。中にはテキストを表示するための<p>要素と画像を表示するための<img>要素を記述しています。

```html:
<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width,initial-scale=1.0,minimum-scale=1.0,maximum-scale=1.0,user-scalable=no">
    
    <title>no title</title>
    
    <link rel="stylesheet" href="css/normalize.css" type="text/css" />
    <link rel="stylesheet" href="css/main.css" type="text/css" />
    <script src="/libs/qimessaging/2/qimessaging.js"></script>
    <script src="js/jquery-3.0.0.min.js"></script>
    <script src="js/adjust23.js"></script>
    <script src="js/main.js"></script>
  </head>
  <body>
    <div id="qr_display">
      <p></p>
      <img src="">
    </div>
  </body>
</html>
```

### JavaScriptコード
1. ALMemoryキー `"qr_io_app/toTablet_sendQR"` を受け取るサブスクラバを作成します。
2. ALMemoryからデータを受け取るとHTML側に用意した画像を表示するためのスペース箱 `id="qr_display"` 内の\<p\>要素と\<img\>要素にデータを書き込みます。

```js:
$(function(){
	QiSession(function( s ) {
		session = s;
		session.service("ALMemory").then(function (ALMemory) {
			//待受け
			ALMemory.subscriber("qr_io_app/toTablet_sendQR").then(function(subscriber) {
				subscriber.signal.connect(displayQR);
			});
			
			//QR表示画面
			function displayQR(value) {
				$("#qr_display p").text("「"+value[0]+"」");
				if (value[0] == null) {
					$("#qr_display p").text("「None」");
				}
				$("#qr_display img").attr("src", value[1]);
			}
		});
	});
});
```

## 活用例
　本サンプルでは掲載してませんが、アプリに[カメラプレビュー](http://qiita.com/Atelier-Akihabara/items/b58901802eaaa1ef0df3)を搭載し、読み取った結果をQRコード画像にしてディスプレイに表示させることもできます。
　また、利用者がPepperにQRコードをかざし、読み取った結果をもとにPepperが次のトリガーとなるQRコードを表示する使い方も考えられます。

　例えば、展示会場では次のような動作ができるのではないでしょうか。

1. 展示会場等で入り口のPepperに入場用のQRコードをかざす
2. Pepperに入場者の情報使った固有のQRコードが表示される
3. 入場者は表示されたQRコードを読み取る
4. 展示の説明とともに、次の順路を案内するまたは、次の場所にいるPepperに誘導する等

　QRコードは応用範囲が広いため、プレビュー・表示のテクニックを駆使したアプリ開発に挑戦してみてください。

**記事協力企業株式会社システナ**