7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

GASとGoogle Cloud Vision APIで認識精度を検証してみた

Posted at

最近、あまりコードが書けていないのですが、仕事の方があまりにも非効率極まりなくなってきたので、ToDo管理システムを構築してやろうと思っているのですが、既存のツールだけだとどうしても「手書きメモ」のリマインドがもれるので、どうにかしてやろうと思ったのです。

作ろうと思っているシステム

GoodNotesでメモを書き出す→Google Driveに保存→Google Apps Scriptで定期的にGoogle Vision APIを叩いて、メモを取得する→ToDoを登録
という野望を持っているわけです。

参考にした資料

Google Vision APIはGoogle Apps Scriptには標準で用意されていないので、APIを直接叩きます。既に同様のことが記事になっていました。
(参考記事:Google Apps ScriptでCloud Vision APIを使ってみる

本日のコード.gs

ほぼ、参考サイトのコピペです。なんかすみません…。特定のフォルダにある画像ファイルを順番にVision APIに通しています。

コード.gs
function doGet(e) {  
  var url = "https://accounts.google.com/o/oauth2/auth";
  var client_id = PropertiesService.getScriptProperties().getProperty("client_id");
  
  var accessToken = PropertiesService.getScriptProperties().getProperty("access_token");
  if(accessToken == null) {    
    //必要なパラメータ郡
    var param = {
      "response_type" : "code",
      "client_id" : client_id,
      "redirect_uri" : getCallbackURL_(),  //←は↓に書いてあります
      "state" : ScriptApp.newStateToken().withMethod("callback").withArgument("name", "value").withTimeout(2000).createToken(),  //←の指定で/usercallbackが呼び出された後、callback関数を呼び出し、その際nameというパラメータでvalueという値を渡し 2000秒でタイムアウトになるという設定が可能になります。
      "scope" : "https://www.googleapis.com/auth/cloud-vision",
      "access_type" : "offline",
      "approval_prompt": 'force'
    };
    var params = [];
    for(var name in param){ 
      params.push(name + "=" + encodeURIComponent(param[name]));
    }
    
    url = url + "?" + params.join("&");
    
    Logger.log(url);
    return HtmlService.createHtmlOutput('<a href="' + url + '" target="_blank">認証</a>') ;
  } else {
    return HtmlService.createHtmlOutput('<p>設定済みです。</p>');
  }
}

function getCallbackURL_(){
  var url = ScriptApp.getService().getUrl() ;
  if ( url.indexOf('/exec') >= 0 ) return url.slice(0, -4) + 'usercallback' ;
  return url.slice(0, -3) + 'usercallback' ;
}

function callback(e) {
  var credentials = fetchAccessToken_(e.parameter.code) ;
  var scriptProperties = PropertiesService.getScriptProperties() ;
  Logger.log(credentials);
  scriptProperties.setProperty('access_token', credentials.access_token) ;
  scriptProperties.setProperty('refresh_token', credentials.refresh_token) ;
  
  return HtmlService.createHtmlOutput('<p>設定しました。</p>');
}

function fetchAccessToken_(code) {
  var prop = PropertiesService.getScriptProperties();
  var res = UrlFetchApp.fetch("https://accounts.google.com/o/oauth2/token", {
    "method" : "POST",
    payload : {
      "code" : code,
      "client_id" : prop.getProperty("client_id"),
      "client_secret" : prop.getProperty("client_secret"),
      "redirect_uri" : getCallbackURL_(),
      "grant_type" : "authorization_code"
    },
    muteHttpExceptions : true
  });

  return JSON.parse(res.getContentText());
}

function todoDetection() {
  var targetFolder = PropertiesService.getScriptProperties().getProperty("target_folder");
  var folder = Drive.Children.list(targetFolder, {q: "trashed=false"});
  for(var i = 0; i < folder.items.length; i++) {
    var target = Drive.Files.get(folder.items[i].id);
    var file = DriveApp.getFileById(target.id);
    
    var result = imageAnnotate(file);
    if(!result) {
      Logger.log("target file name = " + target.title);
    }
  }
}

function imageAnnotate(file){
  var accessToken = PropertiesService.getScriptProperties().getProperty("access_token");
  
  var payload = JSON.stringify({
    "requests":[
      {
        "image": {
          "content": Utilities.base64Encode(file.getBlob().getBytes())
        }, 
        "features": [
          {
            "type": "TEXT_DETECTION",
            "maxResults": 100
          }
        ],
      }
    ]
  });
  
  try {
    var res = UrlFetchApp.fetch("https://vision.googleapis.com/v1/images:annotate", 
                                {
                                  method : 'post',
                                  headers: {
                                    authorization: 'Bearer ' + accessToken
                                  }, 
                                  contentType: 'application/json', 
                                  payload : payload
                                });
  } catch(e) {
    // 失敗
    Logger.log(e);
    return false;
  }
  
  var obj = JSON.parse(res.getContentText());
  var res = obj.responses;
    
  for(var i = 0; i < res.length; i++) {
//    Logger.log(res[i].textAnnotations);
    var textAnnotations = res[i].textAnnotations;
    for(var j = 0; j < textAnnotations.length; j++) {
      Logger.log(textAnnotations[j].description);
    }
  }
  
  return true;
}

結果

iPadのGoodNotesで出力した画像では、正しく認識しませんでした。文章の一部が文字に起こせたものもありましたが、実用には不十分でした。紙に書いたものをスキャナで取れば、もう少し精度があがるんでしょうか。そもそも、字が汚いという話もありそうですが…。今後の課題です。

参考資料

http://qiita.com/soundTricker/items/b4df3072088fcbd0e36d
http://qiita.com/wezardnet/items/ebd0c98f3fc04bcc53a0

7
10
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
7
10

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?