Swiftから画像認識(AlchemyAPIによる画像のタグ付け)サービスを使う


#画像認識(画像のタグ付け)って何？
画像をアップロードすると(または画像のURLを伝えると)、その画像を認識して、それが何かってことを返してくれるサービス。

例えば、public-domain-photosから拾ってきた下のヨセミテ公園の画像をアップロードすると、
![yosemite-meadows.png](https://qiita-image-store.s3.amazonaws.com/0/76526/3fe01a75-4e2c-e5b4-2dbd-8cbbd31dded3.png "yosemite-meadows.png")

下記のようなレスポンスで、それが"自然(nature)"であるとか、"ヨセミテ国立公園(yosemite)"であることを教えてくれる。scoreはその確からしさを表す。画像の自動分類なんかに使われる。

```
    "imageKeywords": [
        {
            "text": "nature",
            "score": "0.916827"
        },
        {
            "text": "yosemite",
            "score": "0.900249"
        }
```

#Alchemy APIって？
・機械学習系の技術で、言語認識や画像認識のサービスを提供している。日本語だとこちらの方の[ブログ](http://tech-sketch.jp/2014/06/alchemyvision.html)が詳しい。ただし、現在はIBMに買収されていて、IBMのPaaS(Bluemix)から使える(後述)。

・今回使うのは AlchemyVisionのImage TaggingというAPI。[デモサイト](http://vision.alchemy.ai/)があり、簡単に試せる。ちなみに日本では食べログが使っているらしい。

#Alchemy APIを利用可能にする
Free Planがあり、1日あたり1000回の呼び出しまで無料。以下、AlchemyAPIのapikeyとurlを取得するまでの手順。

##1.Bluemixへの登録
登録は[ここから](http://www.ibm.com/developerworks/jp/bluemix/registration.html)。30日無料で、30日以降も使用するのであれば要クレジットカード登録。クレジットカード登録ってのが若干敷居高いですが、フリープランだけ使ってれば課金されないはず。

##2.Bluemixにログイン
[BluemixのTOP](https://console.ng.bluemix.net)からログインする。

##3.Alchemy APIサービスを作成する
①上部のメニューからカタログを選ぶ
![Kobito.gIpoKg.png](https://qiita-image-store.s3.amazonaws.com/0/76526/0453dec7-58b3-8a3b-2ab7-4f5ba2ef8135.png "Kobito.gIpoKg.png")


②AlchemyAPIを見つけてクリック(Watsonカテゴリにある)
![Kobito.izX9dI.png](https://qiita-image-store.s3.amazonaws.com/0/76526/12ae65e9-8fa5-e18a-f576-5afe17bf305c.png "Kobito.izX9dI.png")

③「選択済みプラン」が「Free」であることを確認しつつ、デフォルトの状態で作成する
![Kobito.hrO2q6.png](https://qiita-image-store.s3.amazonaws.com/0/76526/8d57e7b1-3d09-1cc9-1929-f7facdd3ba49.png "Kobito.hrO2q6.png")

④サービスが作成されたら、左側のメニューから「サービス資格情報」をクリックして、"url"とapikeyを確認してメモる。これでAlchemyAPIを利用する準備は完了。
![Kobito.ksevoH.png](https://qiita-image-store.s3.amazonaws.com/0/76526/4c68b8ac-8870-bbe6-7e33-1319192a73c6.png "Kobito.ksevoH.png")


##4.Swiftから画像をアップロードして、AlchemyAPIのImage Taggingを利用する

ごにょごにょ苦労したんですが、下記で画像アップロード&呼出が成功しました(戻り値はJSON形式の例、XMLも可)。Image TagginのAPIドキュメントは[こちら](http://www.alchemyapi.com/api/image-tagging/image.html)。url-encodedで投げろというのがイマイチわかりにくかったのですが、"x-www-form-urlencoded"をヘッダーにつけて、bodyにバイナリを直接入れるリクエストとなるようです。

```
    let image = UIImage(named: "yosemite-meadows.png")
    getImageTag(image!)
    
    func getImageTag(image:UIImage){
        let apiKey = "xxx-xxx-xxx-xxx-xxx"//ここを先の手順で取得したapikeyに変更
        let url = "https://gateway-a.watsonplatform.net/calls/image/ImageGetRankedImageKeywords?imagePostMode=raw&outputMode=json&apikey=" + apiKey
        let myURL = NSURL(string: url)!
        let request = NSMutableURLRequest(URL: myURL)
        request.HTTPMethod = "POST"
        request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
        let imageData = UIImagePNGRepresentation(image)
        request.HTTPBody = imageData!
        let task = NSURLSession.sharedSession().dataTaskWithRequest(request) {
            data, response, error in
            
            // Your completion handler code here
            if let error = error {
                print("error: \(error)")
            }
            print(response)
            print(NSString(data: data!, encoding: NSUTF8StringEncoding))
        }
        task.resume()
    }
```

上記コードの戻り値はこんな感じ

```
//response
Optional(<NSHTTPURLResponse: 0x7fff32c0de00> { URL: https://gateway-a.watsonplatform.net/calls/image/ImageGetRankedImageKeywords?imagePostMode=raw&outputMode=json&apikey=d3a529b15ac9ebe550a51006815bf7a4xxxxxxxxx } { status code: 200, headers {
    "Access-Control-Allow-Origin" = "*";
    "Cache-Control" = "no-cache";
    Connection = "keep-alive";
    "Content-Length" = 473;
    "Content-Type" = "application/json";
    Date = "Sun, 29 Nov 2015 15:17:02 GMT";
    Server = nginx;
    "X-AlchemyAPI-CurrentVersion" = "12.22";
    "X-AlchemyAPI-Key" = d3a529b15ac9ebe550a51006815xxxxxxxx;
    "X-AlchemyAPI-Params" = "sentiment=0&knowledgeGraph=0&detectedLanguage=unknown&submitLanguage=detect";
    "X-AlchemyAPI-Status" = OK;
    "X-AlchemyAPI-Total-Transactions" = 4;
} })
//data
Optional({
    "status": "OK",
    "usage": "By accessing AlchemyAPI or using information generated by AlchemyAPI, you are agreeing to be bound by the AlchemyAPI Terms of Use: http://www.alchemyapi.com/company/terms.html",
    "url": "",
    "totalTransactions": "4",
    "imageKeywords": [
        {
            "text": "nature",
            "score": "0.916827"
        },
        {
            "text": "yosemite",
            "score": "0.900249"
        }
    ]
}
)
```

参考までにcurlやでリクエストするときは、下記コマンドで試せる。

```
//curl
curl --data-binary @YOUR_IMAGE.png "https://gateway-a.watsonplatform.net/calls/image/ImageGetRankedImageKeywords?imagePostMode=raw&apikey=d3a529b15ac9ebe550a5100xxxxxx"
//wget 未検証
wget -qO- --post-file YOUR_IMAGE.png 
```

##終わりに
リクエスト投稿しか試せていないけど、カメラで撮った写真をタグ付けるとか、アルバムから取得した画像を自動分類するとかできればちょっと便利なものができそう。本当はAlamofireで書きたかったのでそこは宿題。