Google App Engine(Java)+Spring Boot+Vuetifyから、Google Cloud Vision APIを呼び出しOCRを実現

  1. Google App Engine Java Standard + Spring Boot 環境構築
  2. Spring Bootのテンプレートエンジン Thymeleafを適用

  3. Spring BootにVue.jsを利用する

  4. Spring BootにVuetify導入からクロスドメインAjax通信

  5. Spring Boot+Vuetify GAEへデプロイ、動作確認

  6. Xamarin.FormsアプリをiOS用にビルド

  7. Xamarin.Forms ポップアップの表示と Http通信

  8. Spring Boot+Vuetify ファイルのアップロードとJSONで結果を返す

ファイルのアップロードができるようになったので、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.

アクセスすると、あぁ、この画面ね。確かに。

ということで有効化する。

enable_cloudvision_api

2.GAE Java(Spring Boot)側の実装

2.1 CloudVIsionService

Google Cloud VIsion API を呼び出すユーティリティサービスを、Spring サービスとして作成。

上記チュートリアルのサンプルコードに、OCRのドキュメントを見ながら、少し手を加える。

テキストが、画像上に表示されている位置情報などもあるのだが、とりあえず、解析結果”description” だけ取得するようにする。

動かしてい見ると、最初に解析結果全体が与えられて、以降、ブロックごとに取得できるようなので、とりあえず先頭のみ抜き出すようにコードを書いておく。

  1. package info.typea.google.api.cloudvision;
  2.  
  3. import java.io.ByteArrayOutputStream;
  4. import java.io.DataInputStream;
  5. import java.io.InputStream;
  6. import java.util.ArrayList;
  7. import java.util.HashMap;
  8. import java.util.List;
  9. import java.util.Map;
  10. import java.util.logging.Logger;
  11.  
  12. import org.springframework.stereotype.Service;
  13.  
  14. import com.google.cloud.vision.v1.AnnotateImageRequest;
  15. import com.google.cloud.vision.v1.AnnotateImageResponse;
  16. import com.google.cloud.vision.v1.BatchAnnotateImagesResponse;
  17. import com.google.cloud.vision.v1.EntityAnnotation;
  18. import com.google.cloud.vision.v1.Feature;
  19. import com.google.cloud.vision.v1.Feature.Type;
  20. import com.google.cloud.vision.v1.Image;
  21. import com.google.cloud.vision.v1.ImageAnnotatorClient;
  22. import com.google.cloud.vision.v1.WebDetection;
  23. import com.google.protobuf.ByteString;
  24. import com.google.protobuf.Descriptors.FieldDescriptor;
  25.  
  26. @Service
  27. public class CloudVisionService {
  28. private static final Logger logger = Logger.getLogger(CloudVisionService.class.getName());
  29.  
  30. public List<String> getOcrDocumentText(InputStream in) {
  31. Map<String, Object> annotateMap = annotateImage(in, Type.DOCUMENT_TEXT_DETECTION);
  32. @SuppressWarnings("unchecked")
  33. List<String> result = (List<String>)annotateMap.get("description");
  34. return result;
  35. }
  36. public Map<String, Object> annotateImage(InputStream in, Type type) {
  37. Map<String, Object> result = new HashMap<>();
  38. try(
  39. DataInputStream reader = new DataInputStream(in);
  40. ByteArrayOutputStream writer = new ByteArrayOutputStream();
  41. ImageAnnotatorClient vision = ImageAnnotatorClient.create()) {
  42. byte[] buf = new byte[1024];
  43. while(reader.read(buf) >= 0) {
  44. writer.write(buf);
  45. }
  46. byte[] data = writer.toByteArray();
  47. ByteString imgBytes = ByteString.copyFrom(data);
  48. List<AnnotateImageRequest> requests = new ArrayList<>();
  49. Image img = Image.newBuilder().setContent(imgBytes).build();
  50. Feature feat = Feature.newBuilder().setType(type).build();
  51. AnnotateImageRequest request = AnnotateImageRequest.newBuilder()
  52. .addFeatures(feat)
  53. .setImage(img)
  54. .build();
  55. requests.add(request);
  56. BatchAnnotateImagesResponse response = vision.batchAnnotateImages(requests);
  57. List<AnnotateImageResponse> responses = response.getResponsesList();
  58. for(AnnotateImageResponse res : responses) {
  59. if (res.hasError()) {
  60. System.out.printf("Error:%s\n", res.getError().getMessage());
  61. }
  62. switch(type) {
  63. case TEXT_DETECTION:
  64. case DOCUMENT_TEXT_DETECTION:
  65. for (EntityAnnotation annotation : res.getTextAnnotationsList()) {
  66. annotation.getAllFields().forEach((k,v) -> {
  67. // TODO とりあえず、OCRの解析結果のみ保持。先頭にすべて、以降にブロック?ごとの解析結果が格納されている。
  68. if ("description".equals(k.getName())) {
  69. List<String> discriptions = (List<String>)result.get("description");
  70. if (discriptions == null) {
  71. discriptions = new ArrayList<>();
  72. }
  73. discriptions.add(v.toString());
  74. result.put(k.getName(), discriptions);
  75. }
  76. });
  77. }
  78. break;
  79. // TODO 他のタイプ処理
  80. }
  81. }
  82. } catch(Exception e) {
  83. e.printStackTrace();
  84. }
  85. return result;
  86. }
  87. }

2.2 アプリケーションAPI

ファイルのアップロードを受け付けるアプリケーションのAPIから、上記のCloudVisionService を呼び出す。@Autowired アノテーション付与したメンバーとして宣言しておく。

前回JSONで返すためのMapに”text”エントリーを追加する。

  1. @PostMapping("/api/upload-file")
  2. public ResponseEntity<Map<String,String>> uploadFile(
  3. @RequestHeader(name="Origin", required=false) String origin,
  4. @RequestParam("file") MultipartFile file,
  5. @RequestParam("fileName") String fileName) {
  6. HttpHeaders headers = new HttpHeaders();
  7. headers.add("Access-Control-Allow-Origin", origin);
  8.  
  9. List<String> discriptionList = null;
  10. try {
  11. discriptionList = cloudVisionService.getOcrDocumentText(file.getInputStream());
  12. } catch(Exception e) {
  13. }
  14. Map<String,String> result = new HashMap<>();
  15. result.put("message", "file uploaded success.");
  16. result.put("fileName", fileName);
  17. result.put("text", ((discriptionList == null || discriptionList.isEmpty())?"":discriptionList.get(0)));
  18.  
  19. return new ResponseEntity<>(result, headers, HttpStatus.OK);
  20. }

3.Vuetify側の実装

3.1 OCR結果出力領域

UI側で、{{ text }} に画像ファイルをアップロードしOCRをかました結果のテキストを表示させる、エリアを用意。

  1. <pre>{{text}}</pre>

3.2 AxiosによるAjax通信

  • UIとバインドする、”text” を、data() に定義
  • axios.post(…).then((response) => { …} ) にて、this.text に結果オブジェクトのtext要素をセットする

  1. <script>
  2. import axios from 'axios';
  3.  
  4. export default {
  5. name: 'FavoPhrase',
  6. data() {
  7. return {
  8. file: '',
  9. fileName: '',
  10. text: 'initial annotate.',
  11. }
  12. },
  13. methods: {
  14. sampleAjax : function() {
  15. axios.get(process.env.VUE_APP_API_BASE_URL + "/api/hello")
  16. .then((res) => {
  17. alert("status=" + res.status + ":" + res.data);
  18. });
  19. },
  20. handleFileUpload: function(e) {
  21. this.file = this.$refs.file.files[0];
  22. if (this.file) {
  23. this.fileName = this.file.name;
  24. }
  25. },
  26. submitFile: function() {
  27. let formData = new FormData();
  28. formData.append('file', this.file);
  29. formData.append('fileName', this.fileName);
  30. console.log(formData);
  31. axios.post(process.env.VUE_APP_API_BASE_URL + "/api/upload-file",
  32. formData,
  33. {
  34. headers: {
  35. 'Content-Type': 'multipart/formdata'
  36. }
  37. }
  38. ).then((response) => {
  39. this.text = response.data.text;
  40. console.log(response.data);
  41. }).catch((response) => {
  42. alert("Faiure!\n" + response);
  43. });
  44. }
  45. }
  46. }
  47. </script>

3.動作確認

このブログの先頭部分をキャプチャした、以下の画像を読み込ませてみる。

text.png

text

ocr_local

おぉ、結果が表示された!

かなり使えそうな結果!

Follow me!

コメントを残す

メールアドレスが公開されることはありません。 * が付いている欄は必須項目です