Spherical camera RICOH THETA plug-in I made a client for THETA API v2.1 that can be used for development, so I will introduce the contents.
The source code is available on GitHub. https://github.com/theta4j/theta-web-api
As the title suggests, I made it for use in RICOH THETA plug-in development.
RICOH THETA V runs on an Android-based OS, and you can freely develop and use apps by registering as a developer. The Android app developed for THETA is called a plug-in.
And the familiar THETA API as the THETA API can also be accessed from the plugin. Since the API server is running inside THETA, access it with 127.0.0.1
. In other words, you can access the THETA API from the plugin to shoot and configure.
Since the plugin is actually an Android app, the development language is Kotlin or Java. However, I couldn't find the THETA API library for Java, and the official SDK just contained a bit of sample code. Perhaps the THETA API is a simple API that just exchanges JSON over HTTP, so there is no motivation to implement a client library.
That's why I created a Java implementation of the THETA API client.
The introduction has become long, but I will show you how to use it. Repository sample is written in Java, so I will write it in Kotlin here.
This library is published by MavenCentral and JCenter, so Gradle can be installed by just adding two lines to build.gradle
.
repositories {
...
jcenter() //Add this line
}
dependencies {
...
implementation 'org.theta4j:theta-web-api:1.4.0' //Add this line
}
Basically, create an instance of the ʻorg.theta4j.webapi.Theta` class and call the method that grows there to use it.
How to create a theta object depends on how you connect to THETA.
//When using from THETA plug-in
//The endpoint is http://127.0.0.1:Become 8080
val theta = Theta.createForPlugin()
//THETA is Wi-In the case of Fi master mode(AP mode)
//The endpoint is http://192.168.1.Become 1
val theta = Theta.create()
//THETA Wi-When Fi is in slave mode and Digest authentication is disabled(CL mode)
//* IP address varies depending on the environment
val theta = Theta.create("http://192.168.100.34")
//THETA Wi-If Fi is in slave mode and Digest authentication is enabled(CL mode)
//* IP address varies depending on the environment
val theta = Theta.create("http://192.168.100.34", "username", "password")
Taking a still image is very easy. Just call the takePicture
method.
theta.takePicture()
However, the shooting command is not actually completed immediately, so if you want the result, do as follows.
//Not completed synchronously!!
// res.res until state is DONE.result is null
var res = theta.takePicture()
//Polling at 100ms intervals
while(res.state != CommandState.DONE) {
res = theta.commandStatus(res)
Thread.sleep(100)
}
println(res.result) //The URL of the shooting result is displayed
It is useful to define a helper function like this:
fun <R> waitForDone(response: CommandResponse<R>): CommandResponse<R> {
var res = response //Since the formal argument is val, redefine the variable of var
while (res.state != CommandState.DONE) {
res = theta.commandStatus(res)
Thread.sleep(100)
}
return res
}
fun main(args : Array<String>) {
val res = waitForDone(theta.takePicture())
println(res.result) //The URL of the shooting result is displayed
}
Option settings and support values can be called with the getOptions
method.
The following is an example of setting the maximum value while acquiring the shutter sound setting value and support value at the same time.
val opts = theta.getOptions(SHUTTER_VOLUME, SHUTTER_VOLUME_SUPPORT)
val volume opts.get(SHUTTER_VOLUME)
val support = opts.get(SHUTTER_VOLUME_SUPPORT)
println("Current Volume : $volume")
theta.setOption(SHUTTER_VOLUME, support.maxShutterVolume)
SHUTTER_VOLUME
and SHUTTER_VOLUME_SUPPORT
are constants defined in the ʻorg.theta4j.webapi.Options` class.
Use the getOption
/ setOption
methods to get and set a single option value.
val shutterVolume = theta.getOption(SHUTTER_VOLUME)
theta.setOption(SHUTTER_SPEED, ShutterSpeed._1_100)
When shooting a still image with shutter speed priority and 10-second exposure, the code is as follows.
import org.theta4j.webapi.*
import org.theta4j.webapi.Options.*
val theta = Theta.createForPlugin()
fun main(args : Array<String>) {
val opts = OptionSet.Builder()
.put(CAPTURE_MODE, CaptureMode.IMAGE)
.put(EXPOSURE_PROGRAM, ExposureProgram.SHUTTER_SPEED)
.put(SHUTTER_SPEED, ShutterSpeed._10)
.build()
theta.setOptions(opts)
theta.takePicture()
}
You can also get the video for live preview. Since you can get a JPEG byte string, you can decode it with BitmapFactory
on Android.
theta.livePreview.use { stream ->
while(true) {
val frame = stream.nextFrame() //JPEG byte string for one sheet(InputStream)
val bmp = BitmapFactory.decodeStream(frame) //Decode(Android example)
//Here, bmp drawing process, etc.
}
}
For other usage, please refer to Javadoc or ask a question in the comment section.
Be careful when using it with Android or THETA plugins.
First, add the following line to ʻAndroidManifest.xml` because you need internet permissions.
<uses-permission android:name="android.permission.INTERNET"/>
Also, methods with I / O access, such as Theta # takePicture
and Theta # getOptions
, cannot be executed in the UI thread.
override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent) {
//Key events etc. are executed in the UI thread
theta.takePicture() //This will result in an error
}
ʻExecute it in another thread using ExecutorService` etc.
private val executor = Executors.newSingleThreadExecutor()
override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent) {
executor.submit {
theta.takePicture() //OK as it is not a UI thread
}
}
From here on, we will talk about design.
First, we set some goals for development.
Basically, I was conscious of having it widely used for purposes other than plug-in development.
As mentioned above, THETA API is basically an API for exchanging JSON over HTTP. That is, you need an HTTP library and a JSON library.
I thought that HttpURLConnection
would be enough for HTTP, but THETA V requires Digest authentication when running in client mode. I found a good looking Digest authentication library for okhttp, so I decided to use okhttp. The documentation is also substantial and looks good.
I wanted to use JSON-B for JSON, but it didn't work on Android. I decided to use GSON instead of chasing deeply.
THETA API is [Open Spherical Camera API](https://developers.google.com/streetview/open-spherical- It is based on camera /) (OSC API) with its own extensions. Therefore, this time, the package for OSC API and the package for THETA's original extension are separated.
package | Overview |
---|---|
org.theta4j.osc | Package for Open Spherical Camera API |
org.theta4j.webapi | Package for THETA's own extension of OSC API |
However, we decided to minimize the ʻorg.theta4j.osc package and implement all the features that third vendors are allowed to extend in ʻorg.theta4j.webapi
.
For example, caemra.startCapture
is a command defined in the OSC API, but the THETA API adds an extension parameter called _mode
. Therefore, define it in the webapi
package.
On the other hand, the camera.takePicture
command is also a command defined by the OSC API, but there is no extended specification in the THETA API. Therefore, camera.takeCapture
could be defined in the ʻosc` package.
However, it is confusing that the packages to which the extension belongs are separated depending on the presence or absence of the extension, and it is not always the case that the extension will not be included in the future, so I decided to define all the functions that are allowed to be extended in the webapi
package.
The OSC API has an optional setting / acquisition function. It sets / retrieves option values with different types at once.
For example, if you want to set the Bluetooth power and shutter speed at the same time, send the following JSON. In this example, String and Number are mixed.
{
"options": {
"_bluetoothPower": "ON",
"shutterSpeed": 0.01
}
}
This is a combination of a key of type java.lang.String
and a value of any type, so in Java it would beMap <String, Object>
. However, if the value type is java.lang.Object
, then downcasting is required when retrieving the value, which makes it type-safe at all.
Therefore, I defined the following class that holds a set of option values. If you specify an appropriate Class object for the argument type
, a compile error will occur if there is a discrepancy between the types of type
and value
, and you can find the problem at compile time. This is the pattern introduced in Effective Java 2nd Edition as "Type-Safe Heterogeneous Containers".
public final class OptionSet {
public <T> void put(Class<T> type, String name, T value);
public <T> T get(Class<T> type, String name);
}
public static void main(String[] args) {
OptionSet opts = ...;
opts.put(BigDecimal.class, "shutterSpeed", "foo") //Compile error due to type mismatch
}
However, the combination of type
and name
is constant. For example, the type of shutterSpeed
is always Number, never String or Boolean.
Therefore, in reality, the following ʻOption type, which is a combination of
type and
name`, is defined and used.
public interface Option<T> {
Class<T> getType();
String getName();
}
public final class Options {
public static final Option<BigDecimal> SHUTTER_SPEED = OptionImpl.create("shutterSpeed", BigDecimal.class)
}
public final class OptionSet {
public <T> void put(Option<T> option, T value);
...
}
public static void main(String[] args) {
OptionSet opts = ...;
opts.put(Options.SHUTTER_SPEED, "foo") //Compile error due to type mismatch
}
The command execution function is also designed to be type-safe using the pattern of "type-safe heterogeneous containers".
We briefly introduced the usage and design of the client implementation of THETA API. We hope it helps those who want to develop apps and plugins that use the THETA API.
RICOH THETA Plugins is easy to develop. Especially if you have knowledge of Android apps, you can develop it immediately, so please try it.
The following article briefly summarizes how to get started with plugin development.
[THETA plug-in development] How to run the RICOH THETA Plug-in SDK with THETA
Recommended Posts