[PYTHON] RICOH THETA S API in several languages.

At first

RICOH THETA API V2 There is a Developer Page (https://developers.theta360.com/en/), a Reference (https://developers.theta360.com/en/docs/v2/api_reference/) and a Forum. ](Https://developers.theta360.com/ja/forums/) is open to the public.

This page shows an example of actually hitting the following API based on Getting Started.

  1. Get information about the camera body GET / osc / info
  2. Start Session POST /osc/commands/execute camera.startSession
  3. Still image shooting POST / osc / commands / execute camera.takePicture
  4. Get shooting status POST / OSC / state
  5. Get File POST /osc/commands/execute camera.getImage
  6. End Session POST /osc/commands/execute camera.closeSession

Basically, JSON format parameters and responses are exchanged by POST.

Prerequisite

Shell Script First, in order to get an image of the processing flow, let's hit from Shell while checking the response. This time, we will use the "curl" command that is installed by default on Mac OS X.

#Get information about the camera body
$ curl http://192.168.1.1:80/osc/info
{"manufacturer":"RICOH","model":"RICOH THETA S","serialNumber":"XXXXXXXX","firmwareVersion":"01.11","supportUrl":"https://theta360.com/en/support/","endpoints":{"httpPort":80,"httpUpdatesPort":80},"gps":false,"gyro":false,"uptime":583,"api":["/osc/info","/osc/state","/osc/checkForUpdates","/osc/commands/execute","/osc/commands/status"]}

Information on the camera body has been acquired. This value will not be used after this.

#Session start
$ curl -X POST http://192.168.1.1:80/osc/commands/execute -d '{"name": "camera.startSession"}'
{"name":"camera.startSession","state":"done","results":{ "sessionId":"SID_0001","timeout":180}}

The "sessionId" obtained here will be used later. You need to manually put it in the place corresponding to "SID_0001" on the next line.

#Still image shooting
$ curl -X POST http://192.168.1.1:80/osc/commands/execute -d '{"name": "camera.takePicture", "parameters": {"sessionId": "SID_0001"}}'
{"name":"camera.takePicture","state":"inProgress","id":"2","progress":{"completion":0.0}}

The camera is called a cure. It will take some time to finish shooting the image.

#Acquisition of shooting status
$ curl -X POST http://192.168.1.1:80/osc/state
{"fingerprint":"FIG_0006","state":{"sessionId":"SID_0001","batteryLevel":1.0,"storageChanged":false,"_captureStatus":"idle","_recordedTime":0,"_recordableTime":0,"_latestFileUri":"100RICOH/R0010174.JPG","_batteryState":"charging"}}

Here, use "_latestFileUri" that can be obtained when the shooting process is completed with the following API. If there is a change in "fingerprint", the image processing is finished. You may need to run it several times until you get the "_latestFileUri".

#File acquisition
$ curl -X POST http://192.168.1.1:80/osc/commands/execute -d '{"name": "camera.getImage", "parameters": {"fileUri": "100RICOH/R0010174.JPG"}}' > image.jpg && open image.jpg

I have taken the file and saved it in image.jpg in the execution directory. It is open in the viewer after picking up.

#End of session
$ curl -X POST http://192.168.1.1:80/osc/commands/execute -d '{"name": "camera.closeSession", "parameters": {"sessionId": "SID_0001"}}'
{"name":"camera.closeSession","state":"done"}

The cleanup is complete.

It takes some time to specify the parameters, but I think it was confirmed that you can easily take pictures and take back images with HTTP.

Ruby

require 'net/http'
require 'json'

http = Net::HTTP.new('192.168.1.1', 80)
JSON.parse(http.get("/osc/info").body)

res = http.post('/osc/commands/execute', {name: "camera.startSession"}.to_json)
sessionId = JSON.parse(res.body)["results"]["sessionId"]

params = {name: "camera.takePicture", parameters: {sessionId: sessionId}}.to_json
http.post('/osc/commands/execute', params)

fileUri = JSON.parse(http.post('/osc/state',"").body)["state"]["_latestFileUri"] while fileUri.empty?

params = {name: "camera.getImage", parameters: {fileUri: fileUri}}.to_json
res = http.post('/osc/commands/execute', params)

open("image.jpg ", "wb") {|f| f.write(res.body)}
`open image.jpg`

params = {name: "camera.closeSession", parameters: {sessionId: sessionId}}.to_json
http.post('/osc/commands/execute', params)

I think you can read it intuitively. I like Ruby.

Python

import urllib
import json

urllib.urlopen("http://192.168.1.1/osc/info").read()

data = json.dumps({"name":"camera.startSession"})
res = urllib.urlopen('http://192.168.1.1/osc/commands/execute', data)
sessionId = json.loads(res.read())["results"]["sessionId"]

data = json.dumps({"name":"camera.takePicture", "parameters": {"sessionId": sessionId}})
urllib.urlopen('http://192.168.1.1/osc/commands/execute', data)

fileUri = ""
while not fileUri:
    res = urllib.urlopen('http://192.168.1.1/osc/state', urllib.urlencode({}))
    fileUri = json.loads(res.read())["state"]["_latestFileUri"]

data = json.dumps({"name":"camera.getImage", "parameters": {"fileUri": fileUri}})
res = urllib.urlopen('http://192.168.1.1/osc/commands/execute', data)
with open("image.jpg ", "wb") as file:
    file.write(res.read())

data = json.dumps({"name":"camera.closeSession", "parameters": {"sessionId": sessionId}})
urllib.urlopen('http://192.168.1.1/osc/commands/execute', data)

I think you can read this intuitively as well.

I wanted to open the image, but when I paste the following code together after the above code, the behavior of Popen was not stable. If it is executed at any time, the image could be opened with the following code even from the REPL environment.

import subprocess
subprocess.Popen(['open', 'image.jpg'])

Swift

import UIKit

// for asynchronous use in Playground
import XCPlayground 
XCPlaygroundPage.currentPage.needsIndefiniteExecution = true 

let session = NSURLSession.sharedSession()

// osc/info
var url = NSURL(string: "http://192.168.1.1/osc/info")
var task = session.dataTaskWithURL(url!) {
    (data, response, error) in
    print(NSString(data: data!, encoding: NSUTF8StringEncoding))
}
task.resume()

// camera.startSession
var sessionId = "";
var jsonDic = NSDictionary()
url = NSURL(string: "http://192.168.1.1/osc/commands/execute")
var req = NSMutableURLRequest(URL: url!)
var params: [String: AnyObject] = ["name": "camera.startSession"]
req.HTTPMethod = "POST"
req.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params, options: NSJSONWritingOptions.PrettyPrinted)
task = session.dataTaskWithRequest(req) {
    (data, response, error) in
    print(NSString(data: data!, encoding: NSUTF8StringEncoding))
    do {
        jsonDic = try NSJSONSerialization.JSONObjectWithData(data!, options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
    } catch {}
    let result = jsonDic["results"]  as! NSDictionary
    sessionId = result["sessionId"] as! String
}
task.resume()
while sessionId.isEmpty { sleep(1) }

// camera.takePicture
url = NSURL(string: "http://192.168.1.1/osc/commands/execute")
req = NSMutableURLRequest(URL: url!)
params = ["name": "camera.takePicture", "parameters": ["sessionId": sessionId]]
req.HTTPMethod = "POST"
req.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params,
    options: NSJSONWritingOptions.PrettyPrinted)
task = session.dataTaskWithRequest(req) {
    (data, response, error) in
    print(NSString(data: data!, encoding: NSUTF8StringEncoding))
}
task.resume()

// osc/state
var fileUri = "";
url = NSURL(string: "http://192.168.1.1/osc/state")
req = NSMutableURLRequest(URL: url!)
req.HTTPMethod = "POST"
repeat {
    sleep(1)
    print("try osc/state")
    task = session.dataTaskWithRequest(req) {
        (data, response, error) in
        print(NSString(data: data!, encoding: NSUTF8StringEncoding))
        do {
            jsonDic = try NSJSONSerialization.JSONObjectWithData(data!,
                options: NSJSONReadingOptions.MutableContainers) as! NSDictionary
        } catch {}
        let result = jsonDic["state"]  as! NSDictionary
        fileUri = result["_latestFileUri"] as! String
    }
    task.resume()
} while fileUri.isEmpty
print(fileUri)

// camera.getImage
var img :UIImage?
url = NSURL(string: "http://192.168.1.1/osc/commands/execute")
req = NSMutableURLRequest(URL: url!)
params = ["name": "camera.getImage", "parameters": ["fileUri": fileUri]]
req.HTTPMethod = "POST"
req.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params,
    options: NSJSONWritingOptions.PrettyPrinted)
task = session.dataTaskWithRequest(req) {
    (data, response, error) in
//    NSData.init(data: data!)
    img = UIImage(data: data!)
}
task.resume()
while (img == nil) { sleep(1) }

// camera.closeSession
url = NSURL(string: "http://192.168.1.1/osc/commands/execute")
req = NSMutableURLRequest(URL: url!)
params = ["name": "camera.closeSession", "parameters": ["sessionId": sessionId]]
req.HTTPMethod = "POST"
req.HTTPBody = try NSJSONSerialization.dataWithJSONObject(params,
    options: NSJSONWritingOptions.PrettyPrinted)
task = session.dataTaskWithRequest(req) {
    (data, response, error) in
    print(NSString(data: data!, encoding: NSUTF8StringEncoding))
}
task.resume()

Since NSURLSession is an API that executes asynchronously, waiting processing is inserted here and there. When the value is entered in img, you can see the image by previewing the variable in the Results Sidebar of the Playground environment. Where to process JSON, I want to write it a little more beautifully.

Unity

C#

using UnityEngine;
using UnityEngine.UI;
using System.Text;
using System.Collections;
using System.Collections.Generic;

public class THETAOSC : MonoBehaviour {

	void Start () {
		StartCoroutine(StartThetaS());
	}

	IEnumerator StartThetaS () {
		string url = "http://192.168.1.1/osc/info";
		WWW www = new WWW(url);
		yield return www;
		Debug.Log(www.text);

		Dictionary<string, string> header = new Dictionary<string, string>();
		url = "http://192.168.1.1/osc/commands/execute";
		string jsonStr = "{\"name\": \"camera.startSession\"}";
		byte[] postBytes = Encoding.Default.GetBytes (jsonStr);
		www = new WWW (url, postBytes, header);
		yield return www;
		JsonNode json = JsonNode.Parse(www.text);
		string sessionId = json["results"]["sessionId"].Get<string>();
		Debug.Log(sessionId);

		jsonStr = "{\"name\": \"camera.takePicture\", \"parameters\": {\"sessionId\": \"" + sessionId + "\"}}";
		postBytes = Encoding.Default.GetBytes (jsonStr);
		www = new WWW (url, postBytes, header);
		yield return www;
		Debug.Log(www.text);

		string fileUri = "";
		url = "http://192.168.1.1/osc/state";
		jsonStr = "{}";
		postBytes = Encoding.Default.GetBytes (jsonStr);
		while(fileUri == "") {
			www = new WWW (url, postBytes, header);
			yield return www;
			Debug.Log(www.text);
			json = JsonNode.Parse(www.text);
			fileUri = json["state"]["_latestFileUri"].Get<string>();
		}
		Debug.Log(fileUri);

		//Even if fileUri is obtained, the process may not be completed, so wait for a while
		yield return new WaitForSeconds(3); 

		url = "http://192.168.1.1/osc/commands/execute";
		jsonStr = "{\"name\": \"camera.getImage\", \"parameters\": {\"fileUri\": \"" + fileUri + "\"}}";
		postBytes = Encoding.Default.GetBytes (jsonStr);
		www = new WWW (url, postBytes, header);
		yield return www;

		//Show in Raw Image for confirmation
		RawImage rawImage = GetComponent<RawImage>();
		rawImage.texture = www.textureNonReadable;
		rawImage.SetNativeSize();

		jsonStr = "{\"name\": \"camera.closeSession\", \"parameters\": {\"sessionId\": \"" + sessionId + "\"}}";
		postBytes = Encoding.Default.GetBytes (jsonStr);
		www = new WWW (url, postBytes, header);
		yield return www;
		Debug.Log(www.text);
	}
}

Unreal Engine 4 (planned)

Summary Using the OSC-compliant API of THETA S, I actually tried to move the flow of shooting and image collection and how to hit the API in each environment. You can set shooting options for the camera in the same way, so I think you can play around with it. I hope it will be a reference and an opportunity!

Recommended Posts

RICOH THETA S API in several languages.
The story around the time acquisition API in programming languages
Evernote API in Python
HMAC in various languages
C API in Python 3