2015/12/07

【JINS MEME × THETA S】流し目 or まばたきシャッターの実装

JINS MEME Advent Calendar 2015 7日目の記事です。昨日の記事は@mito_logさんによる JINS MEMEのデータをxx秒おきにGoogle SpreadSheetに流すでした。

今回はJinsMEME ESの3点眼位センサーをインプットとして使って、アウトプット先を最近購入したTHETA Sで遊んでみようかなと思い、実装してみました。
JINS MEMEは、どこを見ているかという視線判定とまばたきの早さ、強さというデータが取れるのが魅力的です。
今回は流し目(右か左)に視線移動するとTHETA Sのシャッターを押すという誰得なことをやります。

JINS MEMEってなに?

一日目の記事が非常にわかりやすく網羅されているかと思います。
http://qiita.com/mito_log/items/020a87996ed2b9d793e6

THETA Sってなに?

全天球の360度の写真、動画が取れるカメラデバイスです。
RICOH THETA Advent Calendarの一日目の記事を参考にしてください。
http://panora.tokyo/3945/
どんな写真をとっても360度で撮れるので、おもしろいです。
写真を見たほうがわかりやすいので、京都の紅葉の写真と市内一望の写真をあげていますのでこちらを見てグリグリ触って確認してみてください。下の写真にフォーカスを当てて、キーボードの上下左右を押しても360度、見ることができますよ!
京都は今年最後の紅葉風景です。 また来年。 #theta360 - Spherical Image - RICOH THETA

京都市内一望 #theta360 - Spherical Image - RICOH THETA



なんで流し目でシャッターなの?
  • THETA Sはボタンを押したらすぐ写真がとれるのですが、どうしても指が写ってしまう。
  • 上記の紅葉の写真みたいにスマホアプリからシャッター押せますがどうしてもうつむきがちになって顔が映らない
  • JinsMEMEでまばたき検出したら撮影でもいいが、連写しすぎちゃうので。まばたきの速さと強さで強くor弱くまばたきすればシャッターというのは実装しましたが、わかりやすいのは視線移動かなと思ったので。
という理由から流し目したら、シャッターを押すということに決めました。
それでもまぁまぁな頻度で流し目はするので、右向いて左向いたらシャッターとかでもいいと思います。

仕組み

ざっくり言うとJinsMEMEからiOSにBluetoothでデータを送信、
iOS側データを取得、iOSからwifi経由でTHETA Sを操作
THETAはHTTPサーバとして動作し、APIリクエストを送って操作ができます。
詳細はこちら。現時点(2015年12月6日)での
RICHO THETA API v2 Reference
https://developers.theta360.com/ja/docs/v2/api_reference/
言語はSwiftで以前の記事でサンプルを書きましたがそちらをベースに実装します。
http://qiita.com/you_matz/items/4f0f83289cb5f4ee5980

必要なもの、環境

実装 

下準備

こちらからJins Memeのサンプルをダウンロード
https://github.com/manchan/JinsMeme-Swift-Sample
JinsMEME developerサイトからアプリを作成
https://developers.jins.com/ja/login/?goto=/apps/create/
作成したAppCilentIDとClientSecretを以下の****の場所にセットします。
AppDelegate.swift
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {

        MEMELib.setAppClientId("*********************", clientSecret: "*********************")
        return true
    }
THETA Sのシャッターを押すために、
HTTPネットワークライブラリであるAlamofireを利用します。
use_frameworks!
pod 'Alamofire', '~> 2.0'

THETA Sとのセッションの開始

デバイスとのセッションを開始し、sessionIdを取得します。
MMDataViewController.swift
import Alamofire

    var ssid:String?

    override func viewDidLoad() {
        super.viewDidLoad()
        sessionStart()
    }

    // セッション開始
    private func sessionStart() {

        let parameters = [
            "name": "camera.startSession",
            "parameters": []
        ]

        Alamofire.request(.POST, "http://192.168.1.1/osc/commands/execute", parameters: parameters, encoding: .JSON)
            .responseJSON { _, _, result in

                switch result {
                case .Success(let data):
                    let data_dic = data as! NSDictionary
                    if let ssid = data_dic["results"]!["sessionId"] as? String{
                        self.ssid = ssid
                    }
                case .Failure(let data, let error):
                    print("Request failed with error: \(error)")
                }
        }
    }

THETA Sで静止画撮影

静止画撮影するためのメソッドです。
セッション開始して、取得したsessionIdを利用します。
撮影に成功すると、takePicture Success!!とアラートを出します。
MMDataViewController.swift
    // 静止画撮影
    private func takePicture(){

        let parameters = [
            "name": "camera.takePicture",
            "parameters": [
                "sessionId": self.ssid!
            ]
        ]

        Alamofire.request(.POST, "http://192.168.1.1/osc/commands/execute", parameters: parameters, encoding: .JSON)
            .responseJSON { _, _, result in

                switch result {
                case .Success(let data):
                    let data_dic = data as! NSDictionary
                    print("takePicture Success!!")
                    UIAlertView(title: "撮影test", message: "takePicture Success!!", delegate: nil, cancelButtonTitle: "OK").show()
                case .Failure(let data, let error):
                    print("Request failed with error: \(error)")
                }
        }
    }

JINS MEMEからリアルタイムデータの取得,視線移動からシャッター

memeRealTimeModeDataReceivedメソッドから取得したリアルタイムデータを利用します。
tableViewのcellForRowAtIndexPathデリゲートメソッドから視線を右に動かした箇所を抜粋
MMDataViewController.swift
            case 6:

                /****************
                視線が右に動いたかどうかを示す整数値
                0:なし
                1:移動検知ー小
                2:移動検知ー中
                3:逆移動検知ー大
                4:逆移動検知ー特大
                ****************/


                label = "Eye Move Right";
                value = NSString(format: "%d", data.eyeMoveRight) as String

                if value != "0"{

                    self.takePicture()
                    let alertController = UIAlertController(title: "右移動", message: "", preferredStyle: .Alert)
                    let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action:UIAlertAction!) in
                    }
                    alertController.addAction(cancelAction)
                    let OKAction = UIAlertAction(title: "OK", style: .Default) { (action:UIAlertAction!) in
                    }
                    alertController.addAction(OKAction)

                    self.presentViewController(alertController, animated: true, completion:nil)

                }

                break
視線が右に動いた場合、シャッターを押し、右移動というアラートを出しています。
これで、JINS MEME×THETA Sで静止画を撮影するということが可能となります。

ビルド実行

THETA Sの電源を入れ、iPhoneのWifi設定でTHETAのWifiに接続します。
その後、ビルド実行して、JINS MEMEデバイスとの接続で完了。
視線移動して、試してみてください。

シャッターを無音にしたい場合(2015/12/13追記)

_shutterVolumeを0にすることで、シャッター音なしも可能となります。
デフォルトで音量は100になっています。


MMDataViewController.swift
    func setShutterVolumeZero() {

        let parameters = [
            "name": "camera.setOptions",
            "parameters": [
                "sessionId": self.ssid!,
                "options": [
                     "_shutterVolume":0
                ]
            ]
        ]

        Alamofire.request(.POST, commandExecURL, parameters: parameters, encoding: .JSON)
            .responseJSON { _, _, result in

                switch result {
                case .Success(let data):
                    let data_dic = data as! NSDictionary
                    print(data_dic)
                case .Failure(let data, let error):
                    print("Request failed with error: \(error)")
                }
        }
    }

番外編(弱まばたきでシャッター)

まばたきのスピードでゆっくりまばたきしたら、撮影しますか?というアラートを出して、
OKを押すと、シャッターを押します。アラートは別になしでもいいかと思います。
MMDataViewController.swift
            case 8:

                /****************
                まばたきのスピード(Millisecond)
                ****************/

                label = "Blink Speed";
                value = NSString(format: "%d", data.blinkSpeed) as String
                // 50以下であれば
                if Int(value) != 0 &&  Int(value) < 50 {
                    let alertController = UIAlertController(title: "撮影しますか?", message: "", preferredStyle: .Alert)
                    let cancelAction = UIAlertAction(title: "Cancel", style: .Cancel) { (action:UIAlertAction!) in
                    }
                    alertController.addAction(cancelAction)
                    let OKAction = UIAlertAction(title: "OK", style: .Default) { (action:UIAlertAction!) in
                        if let sid = self.ssid as String? {
                            self.takePicture()
                        }
                    }
                    alertController.addAction(OKAction)
                    self.presentViewController(alertController, animated: true, completion:nil)

                }
                break

ぶっちゃけ反応どうなの?

視線を右に動かしてから、シャッターまでやはりタイムラグはあります。
そこはJINS MEME→iOS→THETA SでiOS経由なのでしょうがない部分ではあります。
そこがJINS MEMEのBluetooth→THETAとかに直接コマンド送れれば...もっと反応は早くなるでしょう。でも視線移動から体感一秒以内なので十分なスピードです。

おわりに

以上で【JINS MEME × THETA S】流し目シャッターの実装は終わりです。
まだまだJINS MEME × ハード連携は組み合わせはいくらでもあると思うので、これからもやっていきたいなと思っています。この投稿を読んでJINS MEME or THETA Sが欲しい!おもしろそうと思ってくれる方がいれば幸いです。
別でAppleWatchからTHETAをコントロールするようなことはしたので、またRICHO THETA Advent Calendarの方で書きます。