[JAVA] Ich habe einen THETA API-Client erstellt, der für die Plug-Entwicklung verwendet werden kann

All-Sky-Kamera RICOH THETA-Plug-In Ich habe einen Client für THETA API v2.1 erstellt, der für die Entwicklung verwendet werden kann, daher werde ich den Inhalt vorstellen.

Der Quellcode ist auf GitHub verfügbar. https://github.com/theta4j/theta-web-api

Motivation zur Entwicklung

Wie der Titel schon sagt, habe ich es für die Entwicklung von RICOH THETA-Steckern entwickelt.

RICOH THETA V läuft auf einem Android-basierten Betriebssystem, und Sie können Apps frei entwickeln und verwenden, indem Sie sich als Entwickler registrieren. Die für THETA entwickelte Android-App wird als Plug-In bezeichnet.

Auf die bekannte THETA-API als THETA-API kann auch über das Plug-In zugegriffen werden. Da der API-Server in THETA ausgeführt wird, greifen Sie mit 127.0.0.1 darauf zu. Mit anderen Worten, Sie können über das Plug-In auf die THETA-API zugreifen, um zu fotografieren und zu konfigurieren.

Da das Plug-In eigentlich eine Android-App ist, ist die Entwicklungssprache Kotlin oder Java. Ich konnte jedoch die THETA-API-Bibliothek für Java nicht finden, und das offizielle SDK enthielt nur ein wenig Beispielcode. Möglicherweise ist die THETA-API eine einfache API, die nur JSON über HTTP austauscht, sodass keine Motivation besteht, eine Clientbibliothek zu implementieren.

Also habe ich eine Java-Implementierung des THETA API-Clients gemacht.

Wie benutzt man

Die Einführung ist lang geworden, aber ich werde Ihnen zeigen, wie man sie benutzt. Das Repository-Beispiel ist in Java geschrieben, daher werde ich es hier in Kotlin schreiben.

Zum Projekt hinzufügen

Diese Bibliothek wird von MavenCentral und JCenter veröffentlicht, sodass Gradle installiert werden kann, indem nur zwei Zeilen zu "build.gradle" hinzugefügt werden.

repositories {
    ...
    jcenter() //Fügen Sie diese Zeile hinzu
}

dependencies {
    ...
    implementation 'org.theta4j:theta-web-api:1.4.0' //Fügen Sie diese Zeile hinzu
}

Erstellen eines THETA-Objekts

Erstellen Sie grundsätzlich eine Instanz der Klasse "org.theta4j.webapi.Theta" und rufen Sie die dort wachsenden Methoden auf, um sie zu verwenden.

Wie Sie ein Theta-Objekt erstellen, hängt davon ab, wie Sie eine Verbindung zu THETA herstellen.

//Bei Verwendung über das THETA-Plug-In
//Der Endpunkt ist http://127.0.0.1:Werden Sie 8080
val theta = Theta.createForPlugin()

//THETA ist Wi-Im Falle des Fi-Master-Modus(AP-Modus)
//Der Endpunkt ist http://192.168.1.Werden Sie 1
val theta = Theta.create()

//THETA Wi-Wenn sich Fi im Slave-Modus befindet und die Digest-Authentifizierung deaktiviert ist(CL-Modus)
//* Die IP-Adresse variiert je nach Umgebung
val theta = Theta.create("http://192.168.100.34")

//THETA Wi-Wenn sich Fi im Slave-Modus befindet und die Digest-Authentifizierung aktiviert ist(CL-Modus)
//* Die IP-Adresse variiert je nach Umgebung
val theta = Theta.create("http://192.168.100.34", "username", "password")

Standbilder aufnehmen

Das Aufnehmen eines Standbilds ist sehr einfach. Rufen Sie einfach die Methode takePicture auf.

theta.takePicture()

Standbilder aufnehmen und Ergebnisse erzielen

Der Aufnahmebefehl wird jedoch nicht sofort ausgeführt. Wenn Sie also das Ergebnis erzielen möchten, gehen Sie wie folgt vor.

//Nicht synchron abgeschlossen!!
// res.res bis der Status FERTIG ist.Ergebnis ist null
var res = theta.takePicture()

//Abfrage in Intervallen von 100 ms
while(res.state != CommandState.DONE) {
    res = theta.commandStatus(res)
    Thread.sleep(100)
}

println(res.result) //Die URL des Aufnahmeergebnisses wird angezeigt

Es ist nützlich, eine Hilfsfunktion wie folgt zu definieren:

fun <R> waitForDone(response: CommandResponse<R>): CommandResponse<R> {
    var res = response //Da das formale Argument val ist, definieren Sie die Variable var neu
    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) //Die URL des Aufnahmeergebnisses wird angezeigt
}

Optionen abrufen und festlegen

Optionseinstellungen und Unterstützungswerte können mit der Methode "getOptions" aufgerufen werden.

Im Folgenden finden Sie ein Beispiel für die Einstellung des Maximalwerts bei gleichzeitiger Erfassung des Verschlussgeräusch-Einstellwerts und des Unterstützungswerts.

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 und SHUTTER_VOLUME_SUPPORT sind Konstanten, die in der Klasse org.theta4j.webapi.Options definiert sind.

Verwenden Sie die Methode "getOption" / "setOption", um einen einzelnen Optionswert abzurufen und festzulegen.

val shutterVolume = theta.getOption(SHUTTER_VOLUME)
theta.setOption(SHUTTER_SPEED, ShutterSpeed._1_100)

Optionseinstellungen und Standbildaufnahmen

Bei der Aufnahme eines Standbilds mit Verschlusszeitpriorität und Belichtung von 10 Sekunden lautet der Code wie folgt.

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()
}

Holen Sie sich Live-Ansicht

Sie können das Video auch für die Live-Vorschau erhalten. Da Sie eine JPEG-Byte-Zeichenfolge erhalten können, können Sie diese mit BitmapFactory unter Android dekodieren.

theta.livePreview.use { stream ->
    while(true) {
        val frame = stream.nextFrame()              //JPEG-Byte-Zeichenfolge für ein Blatt(InputStream)
        val bmp = BitmapFactory.decodeStream(frame) //Dekodieren(Android Beispiel)
        //Hier bmp Zeichenprozess usw.
    }
}

Weitere Informationen finden Sie unter Javadoc oder stellen Sie im Kommentarbereich eine Frage.

Verwendung mit Android- oder THETA-Plug-In

Seien Sie vorsichtig, wenn Sie es mit dem Android- oder THETA-Plug-In verwenden.

Fügen Sie zunächst die folgende Zeile zu "AndroidManifest.xml" hinzu, da Sie Internetberechtigungen benötigen.

<uses-permission android:name="android.permission.INTERNET"/>

Außerdem können Methoden mit E / A-Zugriff wie "Theta # takePicture" und "Theta # getOptions" nicht in einem UI-Thread ausgeführt werden.

override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent) {
    //Schlüsselereignisse usw. werden im UI-Thread ausgeführt
    theta.takePicture() //Dies führt zu einem Fehler
}

Verwenden Sie "ExecutorService" usw., um es in einem anderen Thread auszuführen.

private val executor = Executors.newSingleThreadExecutor()

override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent) {
    executor.submit {
        theta.takePicture() //OK, da es sich nicht um einen UI-Thread handelt
    }
}

Über Design

Von hier an werden wir über Design sprechen.

Designziele

Zunächst setzen wir uns einige Entwicklungsziele.

Grundsätzlich war ich mir bewusst, dass es für andere Zwecke als die Plug-In-Entwicklung weit verbreitet ist.

Bibliothek verwendet

Wie oben erwähnt, ist die THETA-API im Grunde eine API zum Austausch von JSON über HTTP. Das heißt, Sie benötigen eine HTTP-Bibliothek und eine JSON-Bibliothek.

Ich dachte, dass "HttpURLConnection" für HTTP ausreichen würde, aber THETA V erfordert eine Digest-Authentifizierung, wenn es im Client-Modus ausgeführt wird. Ich habe eine Looking Digest Authentication Library für okhttp gefunden und mich daher für okhttp entschieden. Die Dokumentation ist auch umfangreich und sieht gut aus.

Ich wollte JSON-B für JSON verwenden, aber es funktionierte nicht unter Android. Ich habe mich entschieden, GSON zu verwenden, anstatt tief zu jagen.

Verfassung

THETA API ist [Open Spherical Camera API](https://developers.google.com/streetview/open-spherical- Basierend auf camera /) (OSC API) verfügt es über eigene Erweiterungen. Daher werden dieses Mal das Paket für die OSC-API und das Paket für die ursprüngliche Erweiterung von THETA getrennt.

Paket Überblick
org.theta4j.osc Paket für Open Spherical Camera API
org.theta4j.webapi OSC API THETA proprietäres Erweiterungspaket

Wir haben uns jedoch entschlossen, das Paket "org.theta4j.osc" zu minimieren und alle Funktionen zu implementieren, die Drittanbieter in "org.theta4j.webapi" erweitern dürfen.

Beispielsweise ist "caemra.startCapture" ein Befehl, der in der OSC-API definiert ist, aber die THETA-API fügt einen Erweiterungsparameter mit dem Namen "_mode" hinzu. Definieren Sie es daher im webapi -Paket.

Andererseits ist der Befehl camera.takePicture auch ein Befehl, der von der OSC-API definiert wird, aber es gibt keine erweiterte Spezifikation in der THETA-API. Daher könnte "camera.takeCapture" im "osc" -Paket definiert werden.

Es ist jedoch verwirrend, je nach Vorhandensein oder Fehlen von Erweiterungen unterschiedliche Pakete zu haben, und es ist nicht immer so, dass Erweiterungen in Zukunft nicht mehr enthalten sein werden. Daher habe ich beschlossen, alle Funktionen zu definieren, die im webapi-Paket erweitert werden dürfen.

Typ sicher

Die OSC-API verfügt optional über eine Einstell- / Erfassungsfunktion. Es setzt / ruft Optionswerte mit verschiedenen Typen gleichzeitig ab.

Wenn Sie beispielsweise das Bluetooth-Netzteil und die Verschlusszeit gleichzeitig einstellen möchten, senden Sie den folgenden JSON. In diesem Beispiel werden String und Number gemischt.

{
    "options": {
        "_bluetoothPower": "ON",
        "shutterSpeed": 0.01
    }
}

Dies ist eine Kombination aus einem Schlüssel vom Typ "java.lang.String" und einem Wert eines beliebigen Typs. In Java wäre dies also "Map <String, Object>". Wenn der Werttyp jedoch "java.lang.Object" ist, ist beim Abrufen des Werts ein Downcasting erforderlich, wodurch er überhaupt typsicher ist.

Daher habe ich die folgende Klasse definiert, die eine Reihe von Optionswerten enthält. Wenn Sie ein geeignetes Klassenobjekt für das Argument "Typ" angeben, tritt ein Kompilierungsfehler auf, wenn zwischen den Typen "Typ" und "Wert" eine Diskrepanz besteht und Sie das Problem beim Kompilieren finden. Dies ist das Muster, das in Effective Java 2nd Edition als "typsichere heterogene Container" eingeführt wurde.

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") //Kompilierungsfehler aufgrund von Typinkongruenz
}

Die Kombination von "Typ" und "Name" ist jedoch konstant. Zum Beispiel ist der Typ von "shutterSpeed" immer "Number", niemals "String" oder "Boolean".

Daher werden in der Realität die folgenden "Typ" und "Name" kombiniert, um den Typ "Option " zu definieren und zu verwenden.

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") //Kompilierungsfehler aufgrund von Typinkongruenz
}

Die Befehlsausführungsfunktion ist auch typsicher nach dem Muster "typsicherer heterogener Container".

Zusammenfassung

Wir haben kurz die Verwendung und das Design der Client-Implementierung der THETA-API vorgestellt. Wir hoffen, es hilft denen, die Apps und Plug-Ins entwickeln möchten, die die THETA-API verwenden.

RICOH THETA Plugins ist einfach zu entwickeln. Insbesondere wenn Sie Kenntnisse über Android-Apps haben, können Sie diese sofort entwickeln. Probieren Sie es also aus.

Der folgende Artikel fasst kurz zusammen, wie Sie mit der Plug-Entwicklung beginnen.

[THETA-Plug-In-Entwicklung] Ausführen des RICOH THETA-Plug-In-SDK mit THETA

Recommended Posts

Ich habe einen THETA API-Client erstellt, der für die Plug-Entwicklung verwendet werden kann
Ich habe ein Plug-In für IntelliJ IDEA erstellt
Ich habe einen API-Client für Nature Remo erstellt
[Ruby] Ich habe einen einfachen Ping-Client erstellt
Ich habe eine Klasse erstellt, die JUMAN und KNP aus Java verwenden kann
Technologie-Auszug, mit dem EC-Sites in Java-Schulungen erstellt werden können
Ich habe ein Diff-Tool für Java-Dateien erstellt
Ich habe eine Viewer-App erstellt, die PDF anzeigt
Organisieren Sie Methoden, die mit StringUtils verwendet werden können
RSpec-Sammlung, die ich häufig verwendet habe
Ich habe Docker als Anfänger für mein Portfolio verwendet, daher hoffe ich, dass sogar 1 mm für jemanden hilfreich ist.
Ich habe einen Testcode (Junit & mockit) für den Code geschrieben, der die AWS-API (Java) aufruft.
Ich habe ein Docker-Image für die japanische Version von SDAPS erstellt
Ich habe ein Check-Tool für das Release-Modul erstellt
Ich habe eine Methode entwickelt, um nach Premium Friday zu fragen
[Swift] API für Apps, die die Auswahl bestanden haben
Ich habe im Frühjahr einen Restful-Server und -Client erstellt.
Ich habe eine Bibliothek erstellt, die in Safari wie ein Tab funktioniert !!
Ich habe eine Bibliothek zum Anzeigen von Tutorials auf Android erstellt.
Ich habe einen Wrapper erstellt, der KNP von Java aus aufruft
Über die Sache, dass hidden_field wahnsinnig benutzt werden kann
Praktische Tastenkombinationen für Eclipse
Ich habe ein Rätsel ausprobiert, das nur von den unteren 10% der schlechten Ingenieure gelöst werden kann
Ich habe eine Antwortfunktion für die Erweiterung Rails Tutorial (Teil 4) erstellt: Eine Funktion, die den Benutzer einzigartig macht
Ich habe eine Funktion zum Registrieren von Bildern bei der API in Spring Framework erstellt. Teil 2 (Client Edition)
Ich habe eine Entwicklungsumgebung mit Rails6 + Docker + PostgreSQL + Materialise erstellt.
Erstellen Sie eine JAR-Datei, die auf Gradle ausgeführt werden kann
Ich habe ein Plug-In erstellt, das Jextract mit Gradle-Aufgaben ausführt
Probleme, die leicht mit Java und JavaScript verwechselt werden können
Suchen Sie eine Switch-Anweisung, die in einen Switch-Ausdruck konvertiert werden kann
Ich habe einen MOD erstellt, der sofort ein Fahrzeug mit Minecraft anruft
Ich kann keine Verbindung mehr zu einer VM mit einem Docker-Container herstellen, der eine Verbindung über SSH herstellen kann