はじめに
今回は、HerokuとEinstein Visionを使って画像認識するLINE Botを作成してみたいと思います。
Einstein Visionはセールスフォース・ドットコム社が提供する画像認識APIです。
HerokuのAddonを利用することで簡単にアプリに組み込むことができます。
Einstein Visionはセールスフォース・ドットコム社が提供する画像認識APIです。
HerokuのAddonを利用することで簡単にアプリに組み込むことができます。
システム概要
Heroku上に構築したBotアプリで画像情報を受け取り、Einstein Visionで画像認識した結果をLINEに返信します。
今回、Botアプリの作成にはSpring Bootを使用します。
今回、Botアプリの作成にはSpring Bootを使用します。
必要なもの
- Heroku アカウント
- LINE Bot アカウント
- Java開発環境
- Git
- Heroku CLI
- LINE Bot アカウント
- Java開発環境
- Git
- Heroku CLI
Heroku設定
HerokuにAppを作成し、Einstein VisionをAddonに追加します。
すると、Config Varsに以下の項目が追加されます。
- EINSTEIN_VISION_ACCOUNT_ID
- EINSTEIN_VISION_PRIVATE_KEY
- EINSTEIN_VISION_URL
さらに、以下の環境変数をConfig Varsに追加します。
ここで定義しておけば環境毎に異なる変数をアプリで直接編集しなくて良いので便利です。
すると、Config Varsに以下の項目が追加されます。
- EINSTEIN_VISION_ACCOUNT_ID
- EINSTEIN_VISION_PRIVATE_KEY
- EINSTEIN_VISION_URL
さらに、以下の環境変数をConfig Varsに追加します。
ここで定義しておけば環境毎に異なる変数をアプリで直接編集しなくて良いので便利です。
Key | Value |
---|---|
EINSTEIN_VISION_MODEL_ID | GeneralImageClassifier |
LINE_CHANNEL_SECRET | LINE developers / Basic informationの「Channel Secret」 |
LINE_CHANNEL_TOKEN | LINE developers / Basic informationの「Channel Access Token」 |
Botアプリ作成
次にSpring BootでBotアプリを作成します。
LINEのイメージメッセージイベントは画像が直接送られてくる訳ではなく、イベントに含まれるmessageIdを元にLINEのContent APIをコールして取得する必要があります。
また、Einstein VisionはJWTを使ったOAuth2認証が必要になります。
これらを踏まえ以下のような処理を実装していきます。
① LINEのイメージイベントを受信する
② LINEから画像を取得する
③ Einstein VisionのJWTアサーションを作成する
④ JWTアサーションを使ってアクセストークンを取得する
⑤ Einstein Visionで画像を認識する
⑥ 認識結果をLINEに返信する
LINEのイメージメッセージイベントは画像が直接送られてくる訳ではなく、イベントに含まれるmessageIdを元にLINEのContent APIをコールして取得する必要があります。
また、Einstein VisionはJWTを使ったOAuth2認証が必要になります。
これらを踏まえ以下のような処理を実装していきます。
① LINEのイメージイベントを受信する
② LINEから画像を取得する
③ Einstein VisionのJWTアサーションを作成する
④ JWTアサーションを使ってアクセストークンを取得する
⑤ Einstein Visionで画像を認識する
⑥ 認識結果をLINEに返信する
1. Spring Bootプロジェクト作成
まずはプロジェクトを作成します。
プロジェクト作成には以下のSPRING INITIALIZRというサイトが便利です。
https://start.spring.io/
Maven Project with Javaを選択し、プロジェクトを作成します。
dependenciesは直接編集するのでここでは選択しなくて構いません。
Zipファイルが出来上がりますので、適当な場所に解凍します。
プロジェクト作成には以下のSPRING INITIALIZRというサイトが便利です。
https://start.spring.io/
Maven Project with Javaを選択し、プロジェクトを作成します。
dependenciesは直接編集するのでここでは選択しなくて構いません。
Zipファイルが出来上がりますので、適当な場所に解凍します。
2. pom.xmlの編集
pom.xmlを以下のように編集します。
LINE Bot SDKやJWTアサーションの作成に必要なライブラリなどを依存関係に追加します。
LINE Bot SDKやJWTアサーションの作成に必要なライブラリなどを依存関係に追加します。
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.example</groupId> <artifactId>line-img-prediction</artifactId> <version>0.0.1-SNAPSHOT</version> <packaging>jar</packaging> <name>line-img-prediction</name> <description>Demo project for Spring Boot</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.6.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <dependency> <groupId>com.linecorp.bot</groupId> <artifactId>line-bot-spring-boot</artifactId> <version>1.8.0</version> </dependency> <dependency> <groupId>commons-codec</groupId> <artifactId>commons-codec</artifactId> <version>1.9</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.8.5</version> </dependency> <dependency> <groupId>org.bitbucket.b_c</groupId> <artifactId>jose4j</artifactId> <version>0.6.0</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.18</version> <scope>provided</scope> </dependency> <dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.5</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
pom.xml
編集したら、STSやIntelliJなどのIDEでMavenプロジェクトとしてImportします。
3. application.yml作成
src/main/resources/config配下にapplication.ymlを作成し、アプリケーションの設定情報を定義します。
${Key}と記述することで、Heroku Config Varsで定義している外部設定値を読み込んでくれます。
${Key}と記述することで、Heroku Config Varsで定義している外部設定値を読み込んでくれます。
einsteinVision: accountId: ${EINSTEIN_VISION_ACCOUNT_ID} privateKey: ${EINSTEIN_VISION_PRIVATE_KEY} url: ${EINSTEIN_VISION_URL} expiryInSeconds: 720 tokenUrl: ${EINSTEIN_VISION_URL}v2/oauth2/token predictUrl: ${EINSTEIN_VISION_URL}v2/vision/predict modelId: ${EINSTEIN_VISION_MODEL_ID} line.bot: channel-token: ${LINE_CHANNEL_TOKEN} channel-secret: ${LINE_CHANNEL_SECRET} handler.path: /callback
application.yml
4. クラス作成
以下のクラスを作成します。
Class | Description |
---|---|
MyLineMessageHandler | LINEメッセージイベントのハンドラークラス |
EinsteinVisionTokenService | Einstein Visionのアクセストークンを取得するクラスのインタフェース |
EinsteinVisionTokenServiceImpl | Einstein Visionのアクセストークンを取得する実装クラス |
EinsteinVisionPredictionService | Einstein Visionで画像認識を行うクラスのインタフェース |
EinsteinVisionPredictionServiceImpl | Einstein Visionで画像認識を行う実装クラス |
EinsteinVisionProperties | Einstein Visionの設定情報を保持するクラス |
EinsteinVisionTokenResponseEntity | Einstein Visionのアクセストークン取得結果を保持するクラス |
EinsteinVisionPredictionResponseEntity | Einstein Visionの画像認識結果を保持するクラス |
EinsteinVisionProbabilityResponseEntity | Einstein Visionの画像認識結果の詳細を保持するクラス |
SDKがないためEinstein Visionに関連したクラスが多く見えますが、実際に処理が必要なクラスはEinsteinVisionTokenServiceImplとEinsteinVisionPredictionServiceImplだけです。
<MyLineMessageHandler>
LINEのメッセージイベントをハンドリングするクラスです。
通常であればRestControllerでエンドポイントを作成するところですが、今回はSpring Boot用のLINE Bot SDKの機能を利用します。
クラスに@LineMessageHandler、メソッドに@EventMappingをつけることで、エンドポイントとして機能するようになります。
メソッドには受け取りたいイベントに応じた型で引数を指定します。
署名検証やイベントの判別処理を書かなくて良いためかなり便利です。
<MyLineMessageHandler>
LINEのメッセージイベントをハンドリングするクラスです。
通常であればRestControllerでエンドポイントを作成するところですが、今回はSpring Boot用のLINE Bot SDKの機能を利用します。
クラスに@LineMessageHandler、メソッドに@EventMappingをつけることで、エンドポイントとして機能するようになります。
メソッドには受け取りたいイベントに応じた型で引数を指定します。
署名検証やイベントの判別処理を書かなくて良いためかなり便利です。
package com.example.lineimgprediction.handler; import com.example.lineimgprediction.entity.EinsteinVisionPredictionResponseEntity; import com.example.lineimgprediction.entity.EinsteinVisionProbabilityResponseEntity; import com.example.lineimgprediction.service.EinsteinVisionPredictionService; import com.example.lineimgprediction.service.EinsteinVisionTokenService; import com.linecorp.bot.client.LineMessagingClient; import com.linecorp.bot.client.MessageContentResponse; import com.linecorp.bot.model.ReplyMessage; import com.linecorp.bot.model.event.MessageEvent; import com.linecorp.bot.model.event.message.ImageMessageContent; import com.linecorp.bot.model.message.Message; import com.linecorp.bot.model.message.TextMessage; import com.linecorp.bot.spring.boot.annotation.EventMapping; import com.linecorp.bot.spring.boot.annotation.LineMessageHandler; import lombok.extern.slf4j.Slf4j; import org.apache.commons.codec.binary.Base64; import org.apache.commons.io.IOUtils; import org.springframework.beans.factory.annotation.Autowired; import java.io.IOException; import java.io.InputStream; import java.util.ArrayList; import java.util.List; import java.util.concurrent.ExecutionException; import java.util.function.Consumer; /** * LINEメッセージイベントのハンドラークラス. */ @Slf4j @LineMessageHandler public class MyLineMessageHandler { @Autowired private EinsteinVisionTokenService einsteinVisionTokenService; @Autowired private EinsteinVisionPredictionService einsteinVisionPredictionService; @Autowired private LineMessagingClient lineMessagingClient; /** * イメージメッセージイベントをハンドリングする. * @param event イメージメッセージイベント */ @EventMapping public void handleImageMessageEvent(MessageEvent<ImageMessageContent> event) { log.info("***** Image Message Event *****"); handleContent( event.getMessage().getId(), messageContentResponse -> replyMessage(messageContentResponse, event.getReplyToken())); } /** * メッセージコンテンツを取得する. * @param messageId メッセージID * @param messageConsumer メッセージコンシューマ */ private void handleContent(String messageId, Consumer<MessageContentResponse> messageConsumer) { final MessageContentResponse messageContentResponse; try { // LINEからメッセージコンテンツを取得する log.info("***** Get Message Content *****"); messageContentResponse = lineMessagingClient.getMessageContent(messageId).get(); } catch (InterruptedException | ExecutionException e) { throw new RuntimeException(e); } messageConsumer.accept(messageContentResponse); } /** * 画像を認識し、メッセージを返信する. * @param messageContentResponse メッセージコンテンツ取得結果 * @param replyToken リプライトークン */ private void replyMessage(MessageContentResponse messageContentResponse, String replyToken) { InputStream responseInputStream = messageContentResponse.getStream(); // Einstein Visionのアクセストークンを取得する log.info("***** Get Access Token *****"); final String accessToken = einsteinVisionTokenService.getAccessToken(); // Einstein Visionで画像認識を行う log.info("***** Prediction with Image *****"); EinsteinVisionPredictionResponseEntity einsteinVisionPredictionResponseEntity = null; try { einsteinVisionPredictionResponseEntity = einsteinVisionPredictionService.predictionWithImageBase64String( Base64.encodeBase64String(IOUtils.toByteArray(responseInputStream)), accessToken); } catch (IOException e) { new RuntimeException(e); } // 結果をパースする StringBuilder stringBuilder = new StringBuilder(); for (EinsteinVisionProbabilityResponseEntity probabilityResponseEntity : einsteinVisionPredictionResponseEntity.getProbabilities()) { if (stringBuilder.length() > 0) { stringBuilder.append("\n"); } stringBuilder.append(probabilityResponseEntity.getLabel() + "(" + probabilityResponseEntity.getProbability() * 100 + "%)"); } List<Message> messageList = new ArrayList<>(); messageList.add(new TextMessage(stringBuilder.toString())); // LINEに返信する log.info("***** Reply Message *****"); lineMessagingClient.replyMessage(new ReplyMessage(replyToken, messageList)); } }
MyLineMessageHandler.java
<EinsteinVisionProperties>
Einstein Visionの設定情報を保持するクラスです。
@DataはLombokが提供する機能です。
getter/setterなどのボイラーテンプレートを自動生成してくれます。
なお、Lombokを使用するにあたってIDE側で設定が必要になります。
IntelliJの場合は、File > Settings > Plugins > Browse repositoriesからLombok Pluginをインストールします。
@ConfigurationPropertiesはSpring Bootが提供する機能です。
DI対象のクラスに@ConfigurationPropertiesを付与することで、application.ymlの設定値を読み込んだ状態でインジェクションされます。
application.ymlはHeroku Config Varsの設定値を参照するようにしているので、このクラスからHeroku Config Varsの設定値を取得することができるようになります。
Einstein Visionの設定情報を保持するクラスです。
@DataはLombokが提供する機能です。
getter/setterなどのボイラーテンプレートを自動生成してくれます。
なお、Lombokを使用するにあたってIDE側で設定が必要になります。
IntelliJの場合は、File > Settings > Plugins > Browse repositoriesからLombok Pluginをインストールします。
@ConfigurationPropertiesはSpring Bootが提供する機能です。
DI対象のクラスに@ConfigurationPropertiesを付与することで、application.ymlの設定値を読み込んだ状態でインジェクションされます。
application.ymlはHeroku Config Varsの設定値を参照するようにしているので、このクラスからHeroku Config Varsの設定値を取得することができるようになります。
package com.example.lineimgprediction.properties; import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.stereotype.Component; @Component @Data @ConfigurationProperties(prefix = "einsteinVision") public class EinsteinVisionProperties { private String accountId; private String privateKey; private String url; private float expiryInSeconds; private String tokenUrl; private String predictUrl; private String modelId; }
EinsteinVisionProperties.java
<EinsteinVisionTokenService / EinsteinVisionTokenServiceImpl>
Einstein Visionのアクセストークンを取得するクラスです。
Private KeyからJWTアサーションを作成し、アクセストークンを要求します。
こちらの処理については以下のHeroku Dev Centerのサンプルを参考にしています。
https://devcenter.heroku.com/articles/einstein-vision#generate-a-token-in-java
Einstein Visionのアクセストークンを取得するクラスです。
Private KeyからJWTアサーションを作成し、アクセストークンを要求します。
こちらの処理については以下のHeroku Dev Centerのサンプルを参考にしています。
https://devcenter.heroku.com/articles/einstein-vision#generate-a-token-in-java
package com.example.lineimgprediction.service; /** * Einstein Visionのアクセストークンを取得するクラスのインタフェース. */ public interface EinsteinVisionTokenService { public String getAccessToken(); }
EinsteinVisionTokenService.java
package com.example.lineimgprediction.service; import com.example.lineimgprediction.entity.EinsteinVisionTokenResponseEntity; import com.example.lineimgprediction.properties.EinsteinVisionProperties; import org.apache.commons.codec.binary.Base64; import org.jose4j.jws.AlgorithmIdentifiers; import org.jose4j.jws.JsonWebSignature; import org.jose4j.jwt.JwtClaims; import org.jose4j.lang.JoseException; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; import sun.security.util.DerInputStream; import sun.security.util.DerValue; import java.io.IOException; import java.math.BigInteger; import java.security.KeyFactory; import java.security.NoSuchAlgorithmException; import java.security.PrivateKey; import java.security.spec.InvalidKeySpecException; import java.security.spec.RSAPrivateCrtKeySpec; import java.util.UUID; /** * EinsteinVisionのアクセストークンを取得する実装クラス. */ @Service public class EinsteinVisionTokenServiceImpl implements EinsteinVisionTokenService { @Autowired private EinsteinVisionProperties einsteinVisionProperties; /** * アクセストークンを取得する. * @return アクセストークン */ public String getAccessToken() { final HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.APPLICATION_FORM_URLENCODED); final MultiValueMap<String, String> bodyMap = new LinkedMultiValueMap<>(); bodyMap.add("assertion", createJwtAssertion()); bodyMap.add("grant_type", "urn:ietf:params:oauth:grant-type:jwt-bearer"); final HttpEntity<MultiValueMap<String, String>> httpEntity = new HttpEntity<>(bodyMap, httpHeaders); RestTemplate restTemplate = new RestTemplate(); final ResponseEntity<EinsteinVisionTokenResponseEntity> responseEntity = restTemplate.postForEntity( einsteinVisionProperties.getTokenUrl(), httpEntity, EinsteinVisionTokenResponseEntity.class); return responseEntity.getBody().getAccessToken(); } /** * JWTアサーションを作成する. * @return JWTアサーション */ private String createJwtAssertion() { final JwtClaims jwtClaims = new JwtClaims(); jwtClaims.setSubject(einsteinVisionProperties.getAccountId()); jwtClaims.setAudience(einsteinVisionProperties.getTokenUrl()); jwtClaims.setExpirationTimeMinutesInTheFuture(einsteinVisionProperties.getExpiryInSeconds() / 60); jwtClaims.setIssuedAtToNow(); // generate the payload final JsonWebSignature jwt = new JsonWebSignature(); jwt.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256); jwt.setPayload(jwtClaims.toJson()); jwt.setKeyIdHeaderValue(UUID.randomUUID().toString()); // sign using the private key jwt.setKey(createPrivateKey()); try { return jwt.getCompactSerialization(); } catch (JoseException e) { throw new IllegalStateException(e); } } /** * プライベートキーを作成する. * @return プライベートキー */ private PrivateKey createPrivateKey() { final String privateKeyBase64 = einsteinVisionProperties.getPrivateKey(); String privateKeyPEM = privateKeyBase64.replace("-----BEGIN RSA PRIVATE KEY-----\n", "") .replace("\n-----END RSA PRIVATE KEY-----", ""); // Base64 decode the data byte[] encoded = Base64.decodeBase64(privateKeyPEM); try { DerInputStream derReader = new DerInputStream(encoded); DerValue[] seq = derReader.getSequence(0); // skip version seq[0]; BigInteger modulus = seq[1].getBigInteger(); BigInteger publicExp = seq[2].getBigInteger(); BigInteger privateExp = seq[3].getBigInteger(); BigInteger primeP = seq[4].getBigInteger(); BigInteger primeQ = seq[5].getBigInteger(); BigInteger expP = seq[6].getBigInteger(); BigInteger expQ = seq[7].getBigInteger(); BigInteger crtCoeff = seq[8].getBigInteger(); RSAPrivateCrtKeySpec keySpec = new RSAPrivateCrtKeySpec( modulus, publicExp, privateExp, primeP, primeQ, expP, expQ, crtCoeff); KeyFactory keyFactory = KeyFactory.getInstance("RSA"); return keyFactory.generatePrivate(keySpec); } catch (IOException | NoSuchAlgorithmException | InvalidKeySpecException e) { throw new IllegalStateException(e); } } }
EinsteinVisionTokenServiceImpl.java
<EinsteinVisionTokenResponseEntity>
アクセストークン要求のレスポンスをパースした結果を保持するクラスです。
アクセストークン要求のレスポンスをパースした結果を保持するクラスです。
package com.example.lineimgprediction.entity; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @JsonIgnoreProperties(ignoreUnknown = true) @Data public class EinsteinVisionTokenResponseEntity { @JsonProperty("access_token") private String accessToken; @JsonProperty("token_type") private String tokenType; @JsonProperty("expires_in") private float expiresIn; }
EinsteinVisionTokenResponseEntity.java
<EinsteinVisionPredictionService / EinsteinVisionPredictionServiceImpl>
Einstein Visionに画像認識を要求するクラスです。
アクセストークンをヘッダにセットし、マルチパートで以下のAPIをコールします。
https://metamind.readme.io/docs/prediction-with-image-base64-string
レスポンスはEinsteinVisionPredictionResponseEntityにセットして返却します。
Einstein Visionに画像認識を要求するクラスです。
アクセストークンをヘッダにセットし、マルチパートで以下のAPIをコールします。
https://metamind.readme.io/docs/prediction-with-image-base64-string
レスポンスはEinsteinVisionPredictionResponseEntityにセットして返却します。
package com.example.lineimgprediction.service; import com.example.lineimgprediction.entity.EinsteinVisionPredictionResponseEntity; /** * Einstein Visionで画像認識を行うクラスのインタフェース. */ public interface EinsteinVisionPredictionService { public EinsteinVisionPredictionResponseEntity predictionWithImageBase64String(final String imageBase64String, final String accessToken); }
EinsteinVisionPredictionService.java
package com.example.lineimgprediction.service; import com.example.lineimgprediction.entity.EinsteinVisionPredictionResponseEntity; import com.example.lineimgprediction.properties.EinsteinVisionProperties; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.util.LinkedMultiValueMap; import org.springframework.util.MultiValueMap; import org.springframework.web.client.RestTemplate; /** * Einstein Visionで画像認識を行う実装クラス. */ @Service public class EinsteinVisionPredictionServiceImpl implements EinsteinVisionPredictionService { @Autowired private EinsteinVisionProperties einsteinVisionProperties; /** * Base64の画像情報をEinstein Visionで認識する. * @param imageBase64String Base64の画像情報 * @param accessToken アクセストークン * @return 認識結果 */ public EinsteinVisionPredictionResponseEntity predictionWithImageBase64String(final String imageBase64String, final String accessToken) { final HttpHeaders httpHeaders = new HttpHeaders(); httpHeaders.setContentType(MediaType.MULTIPART_FORM_DATA); httpHeaders.set("Cache-Control", "no-cache"); httpHeaders.set("Authorization", "Bearer " + accessToken); final MultiValueMap<String, Object> parts = new LinkedMultiValueMap<>(); parts.add("modelId", einsteinVisionProperties.getModelId()); parts.add("sampleBase64Content", imageBase64String); parts.add("numResults", 5); HttpEntity<MultiValueMap<String, Object>> httpEntity = new HttpEntity<>(parts, httpHeaders); RestTemplate restTemplate = new RestTemplate(); // Prediction with Image Base64 String ResponseEntity<EinsteinVisionPredictionResponseEntity> responseEntity = restTemplate.postForEntity( einsteinVisionProperties.getPredictUrl(), httpEntity, EinsteinVisionPredictionResponseEntity.class); return responseEntity.getBody(); } }
EinsteinVisionPredictionServiceImpl.java
<EinsteinVisionPredictionResponseEntity / EinsteinVisionProbabilityResponseEntity>
画像認識要求のレスポンスをパースした結果を保持するクラスです。
画像認識要求のレスポンスをパースした結果を保持するクラスです。
package com.example.lineimgprediction.entity; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; import java.util.List; @JsonIgnoreProperties(ignoreUnknown = true) @Data public class EinsteinVisionPredictionResponseEntity { @JsonProperty("message") private String message; @JsonProperty("object") private String object; @JsonProperty("probabilities") private List<EinsteinVisionProbabilityResponseEntity> probabilities; @JsonProperty("sampleId") private String sampleId; @JsonProperty("status") private String status; }
EinsteinVisionPredictionResponseEntity.java
package com.example.lineimgprediction.entity; import com.fasterxml.jackson.annotation.JsonIgnoreProperties; import com.fasterxml.jackson.annotation.JsonProperty; import lombok.Data; @JsonIgnoreProperties(ignoreUnknown = true) @Data public class EinsteinVisionProbabilityResponseEntity { @JsonProperty("label") private String label; @JsonProperty("probability") private float probability; }
EinsteinVisionProbabilityResponseEntity.java
5. Procfile作成
Herokuで起動するためにプロジェクト直下にProcfileを作成します。
web: java -Dserver.port=$PORT -jar target/*.jar --server.port=$PORT --spring.profiles.active=heroku
Procfile
Herokuデプロイ
Botアプリを作成したらHerokuへデプロイします。
デプロイ方法は割愛しますが、以下のHeroku Dev Centerのサイトが参考になります。
https://devcenter.heroku.com/articles/git
デプロイ方法は割愛しますが、以下のHeroku Dev Centerのサイトが参考になります。
https://devcenter.heroku.com/articles/git
LINE Webhook設定
BotアプリがLINEのメッセージイベントを受け取れるように、Webhookの設定を行います。
設定はLINE DevelopersのBasic Informationページで行います。
※Webhookが有効になっていない場合は有効化してください。
Webhook URLに以下のURLを設定します。
<Heroku App Domain>callback
設定はLINE DevelopersのBasic Informationページで行います。
※Webhookが有効になっていない場合は有効化してください。
Webhook URLに以下のURLを設定します。
<Heroku App Domain>callback
動作確認
設定が終わったら動作確認します。
なお、今回Einstein VisionのモデルはプリビルドされているGENERAL IMAGE MODELを使用しています。
試しに猫の画像を認識させてみます。
なお、今回Einstein VisionのモデルはプリビルドされているGENERAL IMAGE MODELを使用しています。
試しに猫の画像を認識させてみます。
種別はあってないですが、とりあえず猫のようなものだとは認識されていますね。
もう一つ試してみます。デジタル時計です。
もう一つ試してみます。デジタル時計です。
今度は高い精度で認識してくれました。
どれだけの精度で認識できるかはモデルに依存するところです。
他のモデルを使いたい場合は、Config Varsの「EINSTEIN_VISION_MODEL_ID」を変更してアプリを再起動すればOKです。
どれだけの精度で認識できるかはモデルに依存するところです。
他のモデルを使いたい場合は、Config Varsの「EINSTEIN_VISION_MODEL_ID」を変更してアプリを再起動すればOKです。
終わりに
今回はEinstein Visionを使った画像認識Botを作ってみました。
こういったことが手軽に試せるのもHerokuの良いところですね。
今後の機能拡張にも期待したいと思います。
また、Spring Bootは最近触り始めたのですが、JavaでWebアプリを作る際の面倒な設定が色々と省略できてとても良い印象を持ちました。
<今回作成したソースコード>
https://github.com/yusukenakamoto/line-img-prediction
こういったことが手軽に試せるのもHerokuの良いところですね。
今後の機能拡張にも期待したいと思います。
また、Spring Bootは最近触り始めたのですが、JavaでWebアプリを作る際の面倒な設定が色々と省略できてとても良い印象を持ちました。
<今回作成したソースコード>
https://github.com/yusukenakamoto/line-img-prediction
55 件