3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

PythonistaAdvent Calendar 2017

Day 8

RICOH Theta S に Pythonistaからアクセスしたい

Last updated at Posted at 2017-12-07

この記事は Pythonista Advent Calendar 2017 の8日目の記事です。
Theta S に Pythonista 経由でアクセスして写真を撮る方法を紹介します。

📷 概要

iPhoneを持ち歩くようになってから、コンデジを連れ出す機会が少なくなりましたが、
ThetaやGoProなど単機能特化型カメラはiPhoneでカバーできない写真が撮れるので、よく持ち歩いています。

Theta Sには公式のアプリもあって使いやすいのですが、
ファイル共有に難があり、写真の加工は他のアプリに任せる必要があることの2点に、不便さを感じます。
そこで、PythonistaとTheta APIを使って必要最低限の機能を実装して見ました。

APIは下記を参考にしています。
https://developers.theta360.com/ja/docs/v2.1/api_reference/

🎒 準備

  • Pythonista
  • RICOH Theta S以降 (無印のThetaとapiが異なります)

最近発売されたTheta Vも同じ構成で動くと思います。
requests モジュールに依存しますが、親切な Pythonista にはプリインストールされているので、
準備はこれだけ。

💻 コードの中身

コードを書き始める前に、流れを把握します。

静止画を1枚撮るまでの流れ

  1. APIバージョンの指定
  2. 撮影前状態の取得
  3. プロパティの取得・設定
  4. 静止画撮影
  5. ファイル保存確認
  6. ファイル取得

例えば、静止画のプロパティ設定をPOSTするには、

property
POST /osc/commands/execute
{
    "name": "camera.setOptions",
    "parameters": {
        "options": {
            "fileFormat": {
                "type": "jpeg",
                "width": 2048,
                "height": 1024
            }
        }
    }
}

と書きますが、これを Python と request で書くと

property
import request

param = {
    "name": "camera.setOptions",
    "parameters": {
        "options": {
            "fileFormat": {
                "type": "jpeg",
                "width": 2048,
                "height": 1024
            }
        }
    }
}
request.post("http://192.168.1.1:80/osc/commands/execute", data=param) 

になります。

ラッピングしたコード

流れに沿って、Python でラップしていきます。

wrapper

import io
import json
import inspect

import requests
from PIL import Image

cam = 'http://192.168.1.1:80'
url = cam+'/osc/'
exe = url+'commands/execute'


def info():
	r=requests.get(url+inspect.currentframe().f_code.co_name)
	return r.json()
	
	
def state():
	r=requests.post(url+inspect.currentframe().f_code.co_name)
	return r.json()
		
		
def startSession():
	j={'name':'camera.{}'.format(inspect.currentframe().f_code.co_name),
'parameters':{}}
	
	r=requests.post(exe, data=json.dumps(j))
	return r.json()
	

def closeSession(id):
	j={'name':'camera.{}'.format(inspect.currentframe().f_code.co_name),
'parameters':{'sessionId':'{}'.format(id)}}
	
	r=requests.post(exe, data=json.dumps(j))
	return r.json()
	
	
def setOptionsid(id):
	j={'name': 'camera.setOptions',
	'parameters':{'sessionId':id,'options':{'clientVersion':2}}}
	r=requests.post(exe,data=json.dumps(j))
	return r.json()
	
	
def getCameraOption():
	j={'name': 'camera.{}'.format(inspect.currentframe().f_code.co_name),
	'parameters':{'optionNames':['fileFormat','fileFormatSupport']}}
	r=requests.post(exe,data=json.dumps(j))
	return r.json()


def listFiles():
	j={'name':'camera.{}'.format(inspect.currentframe().f_code.co_name)}
	r=requests.post(exe,data=json.dumps(j))
	return r.json()
	

def getLivePreview():
	j={'name':'camera.{}'.format(inspect.currentframe().f_code.co_name)}
	r=requests.post(exe,data=json.dumps(j))
	return r.json()
	

def setOptions(type,width,height):
	j={'name': 'camera.{}'.format(inspect.currentframe().f_code.co_name),
	'parameters':{'options':{'fileFormat':{'type':type,'width':width,'height':height}}}}
	
	r=requests.post(exe, data=json.dumps(j))
	return r.json()
	
	
def takePicture():
	j={'name':'camera.{}'.format(inspect.currentframe().f_code.co_name)}
	
	r=requests.post(exe, data=json.dumps(j))
	return r.json()
	

def startCapture():
	j={'name':'camera.{}'.format(inspect.currentframe().f_code.co_name)}
	
	r=requests.post(exe,data=json.dumps(j))
	return r.json()
	
			
def stopCapture():
	j={'name':'camera.{}'.format(inspect.currentframe().f_code.co_name)}
	r=requests.post(exe,data=json.dumps(j))
	return r.json()
			

def checkForUpdates(fig):
	j={'stateFingerprint':fig}
	r=requests.post(url+inspect.currentframe().f_code.co_name, data=json.dumps((j)))
	return r.json()
	
	
def reset():
	r=requests.post(exe+inspect.currentframe().f_code.co_name)
	
	
def image():
	while len(state()['state']['_latestFileUrl']) > 0:
		break
	
	r = requests.get(state()['state']['_latestFileUrl'])
	return Image.open(io.BytesIO(r.content))
	
	
def main():
	
	# session
	d = startSession()
	if d['state'] == 'error':
		return closeSession('SID_001')
		
	# api and options
	k=d['results']['sessionId']
	r=setOptionsid(k)
	
	while r['state'] == 'done':
		break	
	setOptions('jpeg',2048,1024)

	# take pictures
	d = checkForUpdates(state()['fingerprint'])
	tmp = d['stateFingerprint']
	
	t = takePicture()
	while tmp != state()['fingerprint']:
		break

	# image
	img=image()
	
	# session
	closeSession(k)
	
	return img

		
if __name__ == '__main__':
	main()

ui モジュールでカメラ設定を入力したり、撮った写真を PIL で画像加工したりする前の
最小限のコードになりました。

📚 まとめ

バッテリー同梱型の Python を生活に持ち込んで、日常の小さな問題を解決できると楽しいです。

今回はカメラのコントロールにiPhoneとPythonistaを使って見ましたが、
IOT 機器のコントロールに応用出来るだろうし、Pythonista には Bluetooth モジュールが入っているので、
iPhoneを汎用リモコン化することもできそうですね。機会があれば試して見たいです。

最後までお読みいただきありがとうございました。どなたかの参考になれば嬉しいです。

3
2
0

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
3
2

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?