Google App Engine(Java)+Spring Boot+Vuetifyから、Google Cloud Vision APIを呼び出しOCRを実現
ファイルのアップロードができるようになったので、Google Cloud Vision APIを呼び出し、アップロードした画像をOCR解析してみる。
1.Google Cloud APIクライアントライブラリを使用
以下のURLの手順にしたがって、チュートリアルをこなし、疎通を確認。
https://cloud.google.com/vision/docs/libraries?hl=ja#client-libraries-install-java
さて、上記チュートリアルでは、サービスアカウントKEYをローカルで管理するのだが、GAEに上げるときにはどうするのだろう?
調べるのが面倒なので、いったん、デプロイ、API呼び出しを実行してみて、エラーログを確認してみる。
URLにアクセスして、APIを有効化しなさいと。
[b~favophrase/1.417820383360958412].<stderr>: com.google.api.gax.rpc.PermissionDeniedException: io.grpc.StatusRuntimeException:
PERMISSION_DENIED: Cloud Vision API has not been used in project 512734593206 before or it is disabled.
Enable it by visiting https://console.developers.google.com/apis/api/vision.googleapis.com/overview?project=99999999999 then retry.
If you enabled this API recently, wait a few minutes for the action to propagate to our systems and retry.
アクセスすると、あぁ、この画面ね。確かに。
ということで有効化する。
2.GAE Java(Spring Boot)側の実装
2.1 CloudVIsionService
Google Cloud VIsion API を呼び出すユーティリティサービスを、Spring サービスとして作成。
上記チュートリアルのサンプルコードに、OCRのドキュメントを見ながら、少し手を加える。
テキストが、画像上に表示されている位置情報などもあるのだが、とりあえず、解析結果”description” だけ取得するようにする。
動かしてい見ると、最初に解析結果全体が与えられて、以降、ブロックごとに取得できるようなので、とりあえず先頭のみ抜き出すようにコードを書いておく。
package info.typea.google.api.cloudvision;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.logging.Logger;
import org.springframework.stereotype.Service;
import com.google.cloud.vision.v1.AnnotateImageRequest;
import com.google.cloud.vision.v1.AnnotateImageResponse;
import com.google.cloud.vision.v1.BatchAnnotateImagesResponse;
import com.google.cloud.vision.v1.EntityAnnotation;
import com.google.cloud.vision.v1.Feature;
import com.google.cloud.vision.v1.Feature.Type;
import com.google.cloud.vision.v1.Image;
import com.google.cloud.vision.v1.ImageAnnotatorClient;
import com.google.cloud.vision.v1.WebDetection;
import com.google.protobuf.ByteString;
import com.google.protobuf.Descriptors.FieldDescriptor;
@Service
public class CloudVisionService {
private static final Logger logger = Logger.getLogger(CloudVisionService.class.getName());
public List<String> getOcrDocumentText(InputStream in) {
Map<String, Object> annotateMap = annotateImage(in, Type.DOCUMENT_TEXT_DETECTION);
@SuppressWarnings("unchecked")
List<String> result = (List<String>)annotateMap.get("description");
return result;
}
public Map<String, Object> annotateImage(InputStream in, Type type) {
Map<String, Object> result = new HashMap<>();
try(
DataInputStream reader = new DataInputStream(in);
ByteArrayOutputStream writer = new ByteArrayOutputStream();
ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
byte[] buf = new byte[1024];
while(reader.read(buf) >= 0) {
writer.write(buf);
}
byte[] data = writer.toByteArray();
ByteString imgBytes = ByteString.copyFrom(data);
List<AnnotateImageRequest> requests = new ArrayList<>();
Image img = Image.newBuilder().setContent(imgBytes).build();
Feature feat = Feature.newBuilder().setType(type).build();
AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
.addFeatures(feat)
.setImage(img)
.build();
requests.add(request);
BatchAnnotateImagesResponse response = vision.batchAnnotateImages(requests);
List<AnnotateImageResponse> responses = response.getResponsesList();
for(AnnotateImageResponse res : responses) {
if (res.hasError()) {
System.out.printf("Error:%s\n", res.getError().getMessage());
}
switch(type) {
case TEXT_DETECTION:
case DOCUMENT_TEXT_DETECTION:
for (EntityAnnotation annotation : res.getTextAnnotationsList()) {
annotation.getAllFields().forEach((k,v) -> {
// TODO とりあえず、OCRの解析結果のみ保持。先頭にすべて、以降にブロック?ごとの解析結果が格納されている。
if ("description".equals(k.getName())) {
List<String> discriptions = (List<String>)result.get("description");
if (discriptions == null) {
discriptions = new ArrayList<>();
}
discriptions.add(v.toString());
result.put(k.getName(), discriptions);
}
});
}
break;
// TODO 他のタイプ処理
}
}
} catch(Exception e) {
e.printStackTrace();
}
return result;
}
}
2.2 アプリケーションAPI
ファイルのアップロードを受け付けるアプリケーションのAPIから、上記のCloudVisionService を呼び出す。@Autowired アノテーション付与したメンバーとして宣言しておく。
前回JSONで返すためのMapに”text”エントリーを追加する。
@PostMapping("/api/upload-file")
public ResponseEntity<Map<String,String>> uploadFile(
@RequestHeader(name="Origin", required=false) String origin,
@RequestParam("file") MultipartFile file,
@RequestParam("fileName") String fileName) {
HttpHeaders headers = new HttpHeaders();
headers.add("Access-Control-Allow-Origin", origin);
List<String> discriptionList = null;
try {
discriptionList = cloudVisionService.getOcrDocumentText(file.getInputStream());
} catch(Exception e) {
}
Map<String,String> result = new HashMap<>();
result.put("message", "file uploaded success.");
result.put("fileName", fileName);
result.put("text", ((discriptionList == null || discriptionList.isEmpty())?"":discriptionList.get(0)));
return new ResponseEntity<>(result, headers, HttpStatus.OK);
}
3.Vuetify側の実装
3.1 OCR結果出力領域
UI側で、{{ text }} に画像ファイルをアップロードしOCRをかました結果のテキストを表示させる、エリアを用意。
<pre>{{text}}</pre>
3.2 AxiosによるAjax通信
- UIとバインドする、”text” を、data() に定義
- axios.post(…).then((response) => { …} ) にて、this.text に結果オブジェクトのtext要素をセットする
<script>
import axios from 'axios';
export default {
name: 'FavoPhrase',
data() {
return {
file: '',
fileName: '',
text: 'initial annotate.',
}
},
methods: {
sampleAjax : function() {
axios.get(process.env.VUE_APP_API_BASE_URL + "/api/hello")
.then((res) => {
alert("status=" + res.status + ":" + res.data);
});
},
handleFileUpload: function(e) {
this.file = this.$refs.file.files[0];
if (this.file) {
this.fileName = this.file.name;
}
},
submitFile: function() {
let formData = new FormData();
formData.append('file', this.file);
formData.append('fileName', this.fileName);
console.log(formData);
axios.post(process.env.VUE_APP_API_BASE_URL + "/api/upload-file",
formData,
{
headers: {
'Content-Type': 'multipart/formdata'
}
}
).then((response) => {
this.text = response.data.text;
console.log(response.data);
}).catch((response) => {
alert("Faiure!\n" + response);
});
}
}
}
</script>
3.動作確認
このブログの先頭部分をキャプチャした、以下の画像を読み込ませてみる。
text.png
おぉ、結果が表示された!
かなり使えそうな結果!
