LoginSignup
4
3

More than 1 year has passed since last update.

THETA プラグインで QR コードを読む

Last updated at Posted at 2019-09-17

※改善版の記事を公開中です。ぜひ合わせてお読みください。
【改善版】THETA プラグインで QR コードを読む - Qiita

QR コードスキャナ

はじめに

こんにちは、リコーの @shrhdk_ です。

リコーでは RICOH THETA という全周囲360度撮れるカメラをつくっています。

実は、最近の THETA V や THETA Z1 といった機種は、OS に Android を採用していて、本体内部で Android アプリを動かすことができます。

THETA 向けに開発した Android アプリのことを THETA プラグインと呼んでいて、開発したプラグインは 公式プラグインストア にて配布することができます。

今回は、QR コードを読み取るプラグインを作ってみたので、作り方を紹介します。

RICOH THETA プラグインパートナープログラムについて

THETA プラグインをご存じない方は こちら をご覧ください。

興味を持たれた方は Twitter のフォローと THETAプラグイン開発コミュニティ (Slack) への参加もよろしくお願いします。

今回作ったプラグイン

今回作ったプラグインは、実用性はありません。以下のような動きをします。

  1. 起動するとすぐにスキャンが開始される
  2. 色名の QR コードをスキャンする
    • シャッターボタンの反対側のレンズにむける
    • 対応する色名は RED, GREEN, BLUE の3種類
  3. スキャン結果が表示される
    • THETA V では Wi-Fi LED がスキャンした色で点灯する
    • THETA Z1 では OLED にスキャンした色名が表示される
  4. シャッターボタンを押すとスキャンが再開される

ビルド方法

ソースコードは以下のリポジトリで公開しています。

ソースコードをダウンロードして、Android Studio でインポートしてビルドしてください。

ライブラリ

THETA Plug-in Library

THETA プラグイン特有の機能を利用するために、プロジェクトに SDK を取り込む必要があります。

プロジェクトルート/build.gradle に以下の行を追加して、

/build.gradle
allprojects {
    repositories {
        google()
        jcenter()
+       maven { url 'https://github.com/ricohapi/theta-plugin-library/raw/master/repository' } // この行を追加
    }
}

プロジェクトルート/app/build.gradle に以下の行を追加します。

/app/build.gradle
dependencies {
    ...
+   implementation 'com.theta360:pluginlibrary:2.0.0' // この行を追加
    ...
}

ZXing

QR コードのデコードには ZXing というライブラリを使います。「ゼブラ・クロッシング」と読むらしいです。洒落てますね。

このライブラリは Android に対応しているので、Android ベースの THETA プラグイン開発にも使えます。

プロジェクトルート/app/build.gradle に以下の行を追加して、プロジェクトに取り込みます。

/app/build.gradle
dependencies {
    ...
+   implementation 'com.google.zxing:javase:3.3.3'       // この行を追加
+   implementation 'com.google.zxing:android-core:3.3.0' // この行を追加
    ...
}

処理の流れ

大まかな処理の流れは以下のようになります。

  1. カメラ初期化
  2. キャプチャ開始
  3. コールバックで画像データ取得
  4. 画像変換
  5. ZXing でデコード (できなかったら 3 へ戻る)
  6. キャプチャ停止
  7. 結果表示

それぞれ順番に解説していきます。プロジェクト全体は以下のリポジトリで公開しています。

0. THETA Plug-in Library の利用

THETA Plug-in Library の API は com.theta360.pluginlibrary.activity.PluginActivity を継承して使います。

以後、断りなく出てくるメソッドは PluginActivity のメソッドだと考えてください。

MainActivity.kt
class MainActivity : PluginActivity(), Camera.PreviewCallback {
    ...
}

1. カメラ初期化 (と後処理)

カメラ初期化は onResume でやります。

SurfaceTexture の生成と設定、カメラの設定をしています。

THETA のカメラ API は Android Camera API v1 をベースにいくつかの独自パラメータを追加したものとなっています。

あわせて、ボタンのイベントハンドラの登録と、キャプチャ開始のメソッドも呼んでいます。

MainActivity.kt
private var mTexture: SurfaceTexture? = null
private var mCamera: Camera? = null

override fun onResume() {
    super.onResume()

    notificationCameraClose() // THETA 本体アプリにカメラを開放してもらう

    mTexture = SurfaceTexture(10)
    mCamera = Camera.open().apply {
        parameters = parameters.apply {
            setPreviewSize(1920, 960) 
            set("RIC_SHOOTING_MODE", "RicMoviePreview1920") // 1920x960 (THETA 独自パラメータ)
            set("RIC_PROC_STITCHING", "RicStaticStitching") // Equirectangular 形式にする (THETA 独自パラメータ)
            previewFrameRate = 5
            previewFormat = ImageFormat.YV12
        }
        setPreviewTexture(mTexture)
    }

    // ボタンのイベントハンドラの登録
    setKeyCallback(object : KeyCallback {
        override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent?) {
            if (keyCode == KeyReceiver.KEYCODE_CAMERA) { // シャッターボタンが押された
                start() // キャプチャ再開
            }
        }
        // ...
    })

    start() // キャプチャ開始
}

2. キャプチャ開始

キャプチャ開始のメソッドは次のようになっています。

まず、フレームデータのコールバックを登録して、Camera#startPreview でキャプチャを開始します。

キャプチャ開始後に V では Wi-Fi LED を消灯し、Z1 では OLED に Scanning に表示しています。

最後に開始音を鳴らします。

MainActivity.kt
private fun start() {
    mCamera?.apply {
        setPreviewCallback(this@MainActivity) // MainActivity が Camera.Callback を実装している
        startPreview()

        notificationLedHide(LedTarget.LED3) // for V
        showOledTextMiddle("Scanning")      // for Z1
        notificationAudioMovStart()         // 開始音を鳴らす
    }
}

showOledTextMiddle メソッドは次のような実装で、ブロードキャストインテントを投げています。

MainActivity.kt
private fun showOledTextMiddle(text: String) {
    sendBroadcast(Intent("com.theta360.plugin.ACTION_OLED_TEXT_SHOW").apply {
        putExtra("text-middle", text)
    })
}

このあたりの API の仕様は以下のページで解説されています。

3. コールバックで画像データ取得

Camera#startPreview を呼ぶと、Camera#setPreviewCallback メソッドで登録したオブジェクトのメソッドがフレーム毎に呼ばれます。

MainActivity.kt
override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
    // 毎フレーム呼ばれる
    // data にフレームデータが入っている
}

4. 画像変換

まず、取得した画像をトリミングしつつ、ZXing で処理可能な形式に変換します。

data には Equirectangular 形式で大きさは 1920x960 の画像データが入っています。RGB 形式ではなく、YUV の YV12 形式となっています。

Equirectangular 形式は画像端の歪みが大きいため、中央部分をくり抜いて使います。本来は射影方式を変換して歪みを取り除くべきですが、今回は簡易的に処理しています。

MainActivity.kt
override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
    val width = 1920 / 5
    val height = 960 / 3
    val left = width * 2
    val top = height * 1
    val src = PlanarYUVLuminanceSource(data, 1920, 960, left, top, width, height, false)
    val bmp = BinaryBitmap(HybridBinarizer(src))

    // ...
    // ... ZXing でデコード
    // ...
}

5. ZXing でデコード

QRCodeReader クラスを使って、QR コード画像をデコードします。

見つからなかったり、符号訂正できなかったりした場合は例外が発生するので、キャッチしてログを出しています。

QR コードのデコードに成功したら、カメラのキャプチャを停止します。(シャッターボタン押下で再開)

MainActivity.kt
private val mReader: Reader = QRCodeReader()

override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
    // ...
    // ... 画像変換処理
    // ...    

    val result: Result
    try {
        result = mReader.decode(bmp)
    } catch (e: Exception) {
        Log.d(TAG, "NOT FOUND")
        return
    }
    Log.d(TAG, "FOUND ${result.text}")

    stop() // キャプチャを停止

    // ...
    // ... 結果表示
    // ...
}

6. キャプチャ停止

Camera#stopPreview でキャプチャを停止して、終了音を鳴らします。

MainActivity.kt
private fun stop() {
    mCamera?.apply {
        stopPreview() // 停止
        notificationAudioMovStop() // 終了音を鳴らす
    }
}

7. 結果表示

THETA V の場合は Wi-Fi LED の点灯色を変更し、THETA Z1 では OLED の色名を表示します。

MainActivity.kt
override fun onPreviewFrame(data: ByteArray?, camera: Camera?) {
    // ...
    // ... ZXing でデコード
    // ...

    when (result.text) { // for V
        "RED" -> notificationLed3Show(LedColor.RED)
        "GREEN" -> notificationLed3Show(LedColor.GREEN)
        "BLUE" -> notificationLed3Show(LedColor.BLUE)
    }
    showOledTextMiddle(result.text) // for Z1
}

まとめ

THETA プラグインで QR コードを読み込む方法を紹介しました。

今回は数バイト程度の情報を QR コードで読み取れました。もう少し多くの情報量を読めるかもしれませんが試せていません。興味を持たれた方がお試しくれるとうれしいです。

QR コードは Bluetooth や Wi-Fi よりも手軽にデータを受け渡せるので、プラグインの可能性が広がるのではないでしょうか。

興味を持たれた方は Twitter のフォローと THETAプラグイン開発コミュニティ (Slack) への参加もよろしくお願いします。

4
3
1

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3