Azure Computer Vision APIを用いたアダルトスコアを元に、8万冊以上のマンガの表紙をソート出来るようにしてみた話

# はじめまして
皆さん、こんにちは。Qitta2回目の投稿、松井 敏と申します（以前は@moririringという名前で活動してました）。現在僕は、読んだマンガを登録するサイト「[マンガ読んだ！！](https://manga-yonda.com
)」を作っています。[マンガ読んだ！！](https://manga-yonda.com)は、「読んだボタン」を押すだけで簡単に読んだマンガを記録できるサービスで、最近β版にしました。

# マンガ読んだ！！
以前に本を登録するサービスを使ったことがありますが、自分で1冊ずつ登録していく必要がありました。それはとても面倒で、1000冊読んでいれば、1000回登録が必要でした。[マンガ読んだ！！](https://manga-yonda.com
)は ボタンを押すだけで登録できます。しかも作者やシリーズでまとめて登録も簡単です！マンガの数は現在、集英社、講談社、小学館などから8万冊以上。毎日増え続けています。

# Azure Computer Vision API
さて、Azure Computer Vision API（以下Vision API）というサービスがあります。ある時、このキーワードを知って、[サイト](https://azure.microsoft.com/ja-jp/services/cognitive-services/computer-vision/)を見てみました。すると以下のような説明がありました。

> タグ付け、説明、ドメイン固有モデルを使用してコンテンツを特定し、確実にラベル付けします。成人向け/わいせつな描写に対する設定を適用すれば、アダルト コンテンツの自動制限を有効にできます。画像の種類や写真内の配色を特定します。

これだ！と思いました。実はマンガ読んだ！！でやりたいことありました。それは、表紙画像のチェックです。

# マンガの表紙チェック
マンガ読んだ！！ではマンガを[表紙](https://manga-yonda.com/Comics)で表示しています。表紙は、書店で並べられているぐらいなので、基本全年齢OKです。（適切じゃない[表紙](https://www.amazon.co.jp/dp/B009LHBTXK/ref=dp-kindle-redirect?_encoding=UTF8&btkr=1)で回収騒ぎになった作品もありますけどね！）しかし、これ本当に青少年に見せて良いんだろうか？という表紙は結構あります。そこにVision APIです！サイト上で画像のURLを送信すればチェック出来るのでいくつか試してみました。

# Vision APIの表紙チェック
例えばラブコメの王道はもちろん、成人向けコンテンツfalseです。
<a href="https://manga-yonda.com/Comics/Details?isbn=9784091240460">
<img src="https://comics.shogakukan.co.jp/book-images/w400/books/9784091240460.jpg" width=20%>
</a>

もう少し際どいラブコメの王道も、falseです。
<a href="https://manga-yonda.com/Comics/Details?isbn=9784091266873">
<img src="https://comics.shogakukan.co.jp/book-images/w400/books/9784091266873.jpg" width=20%>
</a>

水着は結構いけるのか、これもfalse。
<a href="https://manga-yonda.com/Comics/Details?isbn=9784065110966">
<img src="http://bkmkn.s3-website-ap-northeast-1.amazonaws.com/9784065110966/9784065110966_w.jpg" width=20%>
</a>

でも、艶々先生はやっぱり成人向けコンテンツtrue。
<a href="https://manga-yonda.com/Comics/Details?isbn=9784253140713">
<img src="https://www.cmoa.jp/data/image/title/title_0000101496/VOLUME/100001014960001.jpg" width=20%>
</a>

表紙チェックとしての精度、結構悪くなさげです！

# プログラムからVision APIで表紙チェック 
とはいえ、手作業で一つずつ確認するのは面倒です。面倒なことはもちろんプログラムです。[サイト](https://docs.microsoft.com/ja-jp/azure/cognitive-services/computer-vision/quickstarts/csharp)にAPIの使い方が書いてあります。下記のプログラムを実行すればapiResultに結果が返ってきます。

```c#
const string uriBase = "https://westcentralus.api.cognitive.microsoft.com/vision/v1.0/analyze";
const string dataUrl = "http://www.s-manga.net/common/thumb/512/4-08-872571-9.jpg";
const string subscriptionKey = "13hc77781f7e4b19b5fcdd72a8df7156";
using (var httpClient = new HttpClient())
{
    var content = (await httpClient.GetAsync(dataUrl, HttpCompletionOption.ResponseHeadersRead)).Content;
    httpClient.DefaultRequestHeaders.Add("Ocp-Apim-Subscription-Key", subscriptionKey);
    var apiResult = await (await httpClient.PostAsync(uriBase, content)).Content.ReadAsStringAsync();
}
```
APIは5000トランザクションまでは無料で使えます。というわけでマンガ読んだで使っている表紙画像にAPIをかけてみました。（※表紙画像を使うことは集英社などに問題ない旨確認済みです）。

# isAdultContent と adultScore 
実際にAPIを叩くと、以下のようなJSONが返ってきます、

```json
 {"adult":{"isAdultContent":false,"adultScore":0.038640901446342468,"isRacyContent":false,"racyScore":0.0293082557618618},"requestId":"ec898276-690d-42d3-a9f0-dfd14132f66b","metadata":{"height":1000,"width":636,"format":"Png"}}
```
なので以下のクラスを定義して受けます。

```c#
public class Adult
{
    public bool isAdultContent { get; set; }
    public float adultScore { get; set; }
    public bool isRacyContent { get; set; }
    public float racyScore { get; set; }
}
```
isAdultContent でアダルト判定できるだけでなくて、adultScore でアダルトスコア（※以下アダルトは大人と記述）も取得出来ます。集まってきた数千件のデータをみていて、思いついちゃいました。スコアは数値です。数値という事はソートが出来ます！そう、表紙機能の大人スコアソートです。実際に大人ソートしてみて色々面白いことが分かりました。

# 大人ソート 
確かに大人ソートの上位を見てみると、いわゆる大人スコアが高いそれっぽい表紙が並びます。ところが、ノイズが結構あります。

例えば、この表紙はトップレベルで高い大人スコアを誇っていますが、それ程でもありません。
<a href="https://manga-yonda.com/Comics/Details?isbn=9784063955743">
<img src="http://bkmkn.s3-website-ap-northeast-1.amazonaws.com/9784063955743/9784063955743_w.jpg" width=20%>
</a>

また、この表紙も相当な大人スコアです。やっぱり猫は大人ですよね。
<a href="https://manga-yonda.com/Comics/Details?isbn=9784063828702">
<img src="http://bkmkn.s3-website-ap-northeast-1.amazonaws.com/9784063828702/9784063828702_w.jpg" width=20%>
</a>

特にインパクトが強かったのはこれでしょうか。やっぱり裸の表紙は大人スコア高めですね。
<a href="https://manga-yonda.com/Comics/Details?isbn=9784088707433">
<img src="http://www.s-manga.net/common/thumb/512/978-4-08-870743-3.jpg" width=20%>
</a>

成人向け判定はfalseだけど、大人スコアが高かったのが例えばこれです。確かにすれすれな感じですよね！
<a href="https://manga-yonda.com/Comics/Details?isbn=9784063747102">
<img src="http://bkmkn.s3-website-ap-northeast-1.amazonaws.com/9784063747102/9784063747102_w.jpg" width=20%>
</a>

まあ、ぶっちゃけ、大人スコアが高いのはイコール肌色率が大きな要因のようです。

# Vision API時間制限
Vision APIで躓いたポイントも少し書きます。プログラムで大量のチェックを走らせる時、ローカルでテストしていた時は問題なかったのですが、Azure WebJobsから叩いたら必ず一定数やるとエラーが返ってきました。ログを見ると時間制限があるようで、短期間に連続で呼ぶと駄目なようです。1秒程度間隔を空ければ大体問題ないと思います。数が多いので非同期使いたかったんですけどね。。

# Vision APIトランザクションの扱い
もう一つドはまりしました。Vision APIは¥112/1,000トランザクションです。8万件やろうとすると大体1万円ぐらいです。無料枠は当然使い切って、有料枠で使い始めたのですが、トランザクションの扱いを完全に勘違いしていました。URLで1回問い合わせることを1トランザクションかと思ったら1パラメータ1トランザクションでした。2パラメータ指定して取得していたので単純に倍使ってました。これは課金的に本当に痛かった。。

# Vision APIのエラー処理
あとは、APIの基本だと思いますが、やっぱりちょくちょくエラーも返ってきます。最初はエラーがあったら止める、安定運用になったらスルーが良いと思います。最初の頃はエラーなら止めていたので、Bad Request、Rate limit is exceeded、InvalidImageFormatなどが来ました。日々更新はadultScoreがなかったらスルーにしています。

#まとめ
そんなこんなで[マンガ読んだ！！](https://manga-yonda.com
)の8万件を超す表紙データにすべてに大人スコアがつけました。これで一覧での大人ソートは勿論、出版社や作者でも大人ソートできます。[手塚治虫](https://manga-yonda.com/Comics?SearchKey=author&SearchValue=%E6%89%8B%E5%A1%9A%E6%B2%BB%E8%99%AB)のマンガを大人順で並べたり、[キン肉マン](https://manga-yonda.com/Comics?SearchKey=series&SearchValue=%E3%82%AD%E3%83%B3%E8%82%89%E3%83%9E%E3%83%B3)を大人順で並べたりするのも面白いと思います！ちなみにこの機能は**登録ユーザー限定**です。是非[登録](https://manga-yonda.com/Account/Register)して、大人順で[マンガ一覧](https://manga-yonda.com/Comics)を見てください！