[JAVA] J'ai créé un client API THETA qui peut être utilisé pour le développement de plugins

Caméra All-sky plug-in RICOH THETA J'ai créé un client pour l'API THETA v2.1 qui peut être utilisé pour le développement, je vais donc en présenter le contenu.

Le code source est disponible sur GitHub. https://github.com/theta4j/theta-web-api

Motivation pour le développement

Comme le titre l'indique, je l'ai fait pour une utilisation dans le développement de plug-ins RICOH THETA.

RICOH THETA V fonctionne sur un système d'exploitation Android, et vous pouvez librement développer et utiliser des applications en vous inscrivant en tant que développeur. L'application Android développée pour THETA s'appelle un plug-in.

Et le familier API THETA comme l'API THETA est également accessible à partir du plug-in. Puisque le serveur d'API fonctionne dans THETA, accédez-y avec 127.0.0.1. En d'autres termes, vous pouvez accéder à l'API THETA à partir du plug-in pour filmer et configurer.

Puisque le plug-in est en fait une application Android, le langage de développement est Kotlin ou Java. Cependant, je n'ai pas trouvé la bibliothèque d'API THETA pour Java et le SDK officiel contenait juste un peu d'exemple de code. Peut-être que l'API THETA est une API simple qui échange simplement JSON sur HTTP, il n'y a donc aucune motivation pour implémenter une bibliothèque cliente.

J'ai donc réalisé une implémentation Java du client API THETA.

Comment utiliser

L'introduction est devenue longue, mais je vais vous montrer comment l'utiliser. L 'Exemple de référentiel est écrit en Java, je vais donc l'écrire en Kotlin ici.

Ajouter au projet

Cette bibliothèque est publiée par MavenCentral et JCenter, donc Gradle peut être installé en ajoutant simplement deux lignes à build.gradle.

repositories {
    ...
    jcenter() //Ajouter cette ligne
}

dependencies {
    ...
    implementation 'org.theta4j:theta-web-api:1.4.0' //Ajouter cette ligne
}

Créer un objet THETA

En gros, créez une instance de la classe ʻorg.theta4j.webapi.Theta` et appelez les méthodes qui s'y développent.

La manière de créer un objet thêta dépend de la manière dont vous vous connectez à THETA.

//Lors de l'utilisation à partir du plug-in THETA
//Le point final est http://127.0.0.1:Devenir 8080
val theta = Theta.createForPlugin()

//THETA est Wi-Dans le cas du mode maître Fi(Mode AP)
//Le point final est http://192.168.1.Devenir 1
val theta = Theta.create()

//THETA Wi-Lorsque Fi est en mode esclave et que l'authentification Digest est désactivée(Mode CL)
//* L'adresse IP varie en fonction de l'environnement
val theta = Theta.create("http://192.168.100.34")

//THETA Wi-Si Fi est en mode esclave et que l'authentification Digest est activée(Mode CL)
//* L'adresse IP varie en fonction de l'environnement
val theta = Theta.create("http://192.168.100.34", "username", "password")

Prise d'images fixes

Prendre une image fixe est très simple. Appelez simplement la méthode takePicture.

theta.takePicture()

Prendre des images fixes et obtenir des résultats

Cependant, la commande de prise de vue n'est pas exécutée immédiatement, donc si vous voulez le résultat, procédez comme suit.

//Non terminé de manière synchrone!!
// res.res jusqu'à ce que l'état soit DONE.le résultat est nul
var res = theta.takePicture()

//Interroger à intervalles de 100 ms
while(res.state != CommandState.DONE) {
    res = theta.commandStatus(res)
    Thread.sleep(100)
}

println(res.result) //L'URL du résultat de la prise de vue s'affiche

Il est utile de définir une fonction d'assistance comme celle-ci:

fun <R> waitForDone(response: CommandResponse<R>): CommandResponse<R> {
    var res = response //Puisque l'argument formel est val, redéfinissez la variable de 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) //L'URL du résultat de la prise de vue s'affiche
}

Obtenir et définir des options

Les paramètres d'options et les valeurs de prise en charge peuvent être appelés avec la méthode getOptions.

Voici un exemple de réglage de la valeur maximale tout en acquérant la valeur de réglage du son de l'obturateur et la valeur de prise en charge en même temps.

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 et SHUTTER_VOLUME_SUPPORT sont des constantes définies dans la classe ʻorg.theta4j.webapi.Options`.

Pour obtenir et définir une valeur d'option unique, utilisez la méthode getOption / setOption.

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

Paramètres des options et prise de vue d'images fixes

Lors de la prise de vue d'une image fixe avec la priorité à la vitesse d'obturation et une exposition de 10 secondes, le code est le suivant.

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

Obtenez une vue en direct

Vous pouvez également obtenir la vidéo pour un aperçu en direct. Puisque vous pouvez obtenir une chaîne d'octets JPEG, vous pouvez la décoder avec BitmapFactory sur Android.

theta.livePreview.use { stream ->
    while(true) {
        val frame = stream.nextFrame()              //Chaîne d'octets JPEG pour une feuille(InputStream)
        val bmp = BitmapFactory.decodeStream(frame) //Décoder(Exemple Android)
        //Ici, processus de dessin bmp, etc.
    }
}

Pour toute autre utilisation, veuillez vous référer à Javadoc ou poser une question dans la section commentaires.

Utiliser avec le plug-in Android ou THETA

Soyez prudent lorsque vous l'utilisez avec le plug-in Android ou THETA.

Tout d'abord, ajoutez la ligne suivante à ʻAndroidManifest.xml` car vous avez besoin d'autorisations Internet.

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

De plus, les méthodes avec accès E / S, telles que Theta # takePicture et Theta # getOptions, ne peuvent pas être exécutées dans un thread d'interface utilisateur.

override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent) {
    //Les événements clés, etc. sont exécutés dans le thread de l'interface utilisateur
    theta.takePicture() //Cela entraînera une erreur
}

Utilisez ʻExecutorService` etc. pour l'exécuter dans un autre thread.

private val executor = Executors.newSingleThreadExecutor()

override fun onKeyDown(keyCode: Int, keyEvent: KeyEvent) {
    executor.submit {
        theta.takePicture() //OK car ce n'est pas un fil d'interface utilisateur
    }
}

À propos du design

A partir de là, nous parlerons de design.

Objectifs de conception

Premièrement, nous fixons des objectifs de développement.

Fondamentalement, j'étais conscient de l'avoir largement utilisé à des fins autres que le développement de plug-ins.

Bibliothèque utilisée

Comme mentionné ci-dessus, l'API THETA est essentiellement une API permettant d'échanger JSON sur HTTP. Autrement dit, vous avez besoin d'une bibliothèque HTTP et d'une bibliothèque JSON.

Je pensais que HttpURLConnection serait suffisant pour HTTP, mais THETA V nécessite une authentification Digest lors de l'exécution en mode client. J'ai trouvé une Looking Digest Authentication Library pour okhttp, j'ai donc décidé d'utiliser okhttp. La documentation est également substantielle et semble bonne.

Je voulais utiliser JSON-B pour JSON, mais cela ne fonctionnait pas sur Android. J'ai décidé d'utiliser GSON au lieu de chasser profondément.

Constitution

API THETA est [API Open Spherical Camera](https://developers.google.com/streetview/open-spherical- Basé sur camera /) (API OSC), il possède ses propres extensions. Par conséquent, cette fois, le package pour l'API OSC et le package pour l'extension d'origine de THETA sont séparés.

paquet Aperçu
org.theta4j.osc Package pour l'API Open Spherical Camera
org.theta4j.webapi Package d'extension propriétaire OSC API THETA

Cependant, nous avons décidé de minimiser le paquet ʻorg.theta4j.osc et d'implémenter toutes les fonctionnalités que les fournisseurs tiers sont autorisés à étendre dans ʻorg.theta4j.webapi.

Par exemple, caemra.startCapture est une commande définie dans l'API OSC, mais l'API THETA ajoute un paramètre d'extension appelé _mode. Par conséquent, définissez-le dans le package webapi.

D'autre part, la commande camera.takePicture est également une commande définie par l'API OSC, mais il n'y a pas de spécification étendue dans l'API THETA. Par conséquent, camera.takeCapture pourrait être défini dans le paquet ʻosc`.

Cependant, il est déroutant d'avoir des packages différents en fonction de la présence ou de l'absence d'extensions, et ce n'est pas toujours le cas que les extensions ne seront pas incluses dans le futur, j'ai donc décidé de définir toutes les fonctions qui peuvent être étendues dans le package webapi.

Type coffre-fort

L'API OSC a une fonction optionnelle de réglage / acquisition. Il définit / récupère des valeurs d'option avec différents types à la fois.

Par exemple, si vous souhaitez définir l'alimentation Bluetooth et la vitesse d'obturation en même temps, envoyez le JSON suivant. Dans cet exemple, String et Number sont mélangés.

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

Il s'agit d'une combinaison d'une clé de type java.lang.String et d'une valeur de n'importe quel type, donc en Java ce serait Map <String, Object>. Cependant, si le type de valeur est java.lang.Object, la conversion descendante est requise lors de la récupération de la valeur, ce qui la rend sûre du tout.

Par conséquent, j'ai défini la classe suivante qui contient un ensemble de valeurs d'options. Si vous spécifiez un objet Class approprié pour l'argument type, une erreur de compilation se produira s'il y a une différence entre les types de type et de valeur, et vous pouvez trouver le problème au moment de la compilation. C'est le modèle introduit dans Effective Java 2nd Edition en tant que «Conteneurs hétérogènes de type sûr».

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") //Erreur de compilation due à une incompatibilité de type
}

Cependant, la combinaison de «type» et «nom» est constante. Par exemple, le type de shutterSpeed est toujours Number, jamais String ou Boolean.

Par conséquent, en réalité, le type «Option » suivant, qui est une combinaison de «type» et «nom», est défini et utilisé.

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") //Erreur de compilation due à une incompatibilité de type
}

La fonction d'exécution de commande est également conçue pour être de type sécurisé en utilisant le modèle de «conteneurs hétérogènes de type sécurisé».

Résumé

Nous avons brièvement présenté l'utilisation et la conception de l'implémentation client de l'API THETA. Nous espérons que cela aidera ceux qui souhaitent développer des applications et des plug-ins utilisant l'API THETA.

RICOH THETA Plugins est facile à développer. Surtout si vous connaissez les applications Android, vous pouvez les développer immédiatement, alors essayez-le.

L'article suivant résume brièvement comment démarrer avec le développement de plug-ins.

[Développement du plug-in THETA] Comment exécuter le SDK du plug-in RICOH THETA avec THETA

Recommended Posts

J'ai créé un client API THETA qui peut être utilisé pour le développement de plugins
J'ai posé une question qui peut être utilisée pour des entretiens techniques
J'ai créé un plug-in pour IntelliJ IDEA
J'ai créé un client API pour Nature Remo
[Ruby] J'ai créé un simple client Ping
J'ai créé une classe qui peut utiliser JUMAN et KNP de Java
Extrait technologique pouvant être utilisé pour créer des sites EC dans la formation Java
J'ai créé un outil Diff pour les fichiers Java
J'ai créé une application de visualisation qui affiche le PDF
Organiser les méthodes qui peuvent être utilisées avec StringUtils
Collection RSpec que j'ai fréquemment utilisée
J'ai utilisé Docker pour mon portfolio en tant que débutant, donc j'espère que même 1 mm sera utile à quelqu'un.
J'ai écrit un code de test (Junit & mockit) pour le code qui appelle l'API AWS (Java)
J'ai créé une image Docker pour la version japonaise de SDAPS
J'ai créé un outil de vérification pour le module de version
J'ai fait une méthode pour demander la prime vendredi
[Swift] API utilisée pour les applications qui ont réussi la sélection
J'ai créé un serveur et un client Restful au printemps.
J'ai créé une bibliothèque qui fonctionne comme un onglet dans Safari !!
J'ai créé une bibliothèque pour afficher des tutoriels sur Android.
J'ai créé un Wrapper qui appelle KNP depuis Java
À propos du problème que hidden_field peut être utilisé de manière insensée
Touches de raccourci pratiques pour Eclipse
J'ai essayé un puzzle qui ne peut être résolu que par les 10% de mauvais ingénieurs
J'ai créé une fonction de réponse pour l'extension Rails Tutorial (Partie 4): une fonction qui rend l'utilisateur unique
J'ai créé une fonction pour enregistrer des images avec l'API dans Spring Framework. Partie 2 (édition client)
J'ai créé un environnement de développement avec rails6 + docker + postgreSQL + Materialise.
Créez un fichier jar qui peut être exécuté sur Gradle
J'ai créé un plug-in qui exécute jextract avec des tâches Gradle
Problèmes facilement confondus avec Java et JavaScript
Rechercher une instruction Switch qui peut être convertie en une expression Switch
J'ai créé un MOD qui appelle instantanément un véhicule avec Minecraft
Je ne peux plus me connecter à une VM avec un conteneur Docker pouvant se connecter via SSH