まだプログラマーですが何か?

プログラマーネタ中心。たまに作成したウェブサービス関連の話も https://twitter.com/dotnsf

2017/05

(追記 2019/02/27 現在このいらすとや検索サービスは停止しています)


先日、IBM Watson Summit 2017 (内のユーザーグループイベント)の開催に併せてリリースした「いらすとや検索」のチューンナップに挑戦しました。



まず、このサービスの内容を簡単に解説します。自分自身も使っていますが、質の高いフリー素材イラストを数多く公開いただいている「いらすとや」様のイラストを、なんとなく覚えている手描きイラストから IBM Watson の類似画像検索機能を使って検索できるようにする、というものです。

この検索サービスでは、「いらすとや」様のイラスト画像を、そのイラストが紹介されているページの URL 情報と併せて IBM Watson の Visual Recognition インスタンスに学習させ、類似画像検索を可能にしています。 そして「いらすとや検索」サイトで描いた絵を画像化し、似たイラスト画像を探した上でイラスト紹介ページへのリンクを作る、という比較的シンプルなロジックで作成しています。ユーザー会の中で紹介し、そこそこの反響もいただきました:
2017050201
ユーザー会での説明資料より)


ただ自分で使っているうちに、作った当初は気付かなかった部分も見えてきました。その中の1つが「カラフルなイラスト画像が検索候補にあまり出てこない」というものでした。理由も明白で、検索時に描くイラストがモノクロなのだから、「類似した画像」を検索するとモノクロの(あるいはモノクロに近い)画像が大量に候補として出て来るのです。形が似ているかどうかというよりも、色の類似性で選ばれてしまっている感じでした。


この問題を解決しようとすると、方法は大きく2つ考えることができます。1つは「検索時に描くイラストに色を付けられるようにする」方法、もう1つは「学習時にモノクロ変換した画像を学習して、そのモノクロ変換した画像の中での類似画像を探す」方法です。要はカラー画像として類似画像を探すのか、モノクロ画像として類似画像を探すのか、という2つの方法です。

実装そのものはどちらでもできると思いますが、このうちの前者はあまりユーザーフレンドリーではないと思っています(手軽に描いたイラストで検索できることがサービスの価値であり、彩色の手間をかけてまでイラストを検索したいと思えない)。というわけで、後者の解決策を実装しました。 ただ画像のグレースケール化は(ロジックが確立されているという意味で)まだ簡単なのですが、モノクロ(白か黒かの2値)化となると、中間くらいの色を白と黒のどちらと判断すべきか、というイラストの特性によってもロジックが左右される問題があるので結構難しかったりします。そのあたりはいずれ詳しく紹介できればと思っています。


で、このチューンナップのビフォー&アフターがこちら:

【ビフォー】
2017050201


【アフター】
2017050202



チューンナップ前の類似画像検索の結果は全体的に白っぽかったと思いますが、チューンナップ後の結果がかなりカラフルになったと思います。つまりモノクロ画像で類似画像を検索した結果、現在はカラフルな画像もシンプルなイラストからの検索候補として得られるようになった、ということです。

Java で byte 型配列に格納した画像データの情報に対して、幅や高さを調べたり、少し手を加えたりする目的でいったん BufferedImage 型に変換したり、その逆の変換を行う機会があったので、その時の作業メモです。

byte 型配列から BufferedImage 型への変換
byte[] img = new byte[];
    :
  (img にデータを格納する処理(省略))
    :
try{
  BufferedImage image = ImageIO.read( new ByteArrayInputStream( img ) );
    :
}catch( Exception e ){
}

BufferedImage 型から byte 型配列への変換
byte[] img = new byte[];
BufferedImage image = null;
    :
  (image にデータを格納する処理(省略))
    :
try{
  ByteArrayOutputStream bos = new ByteArrayOutputStream();
  BufferedOutputStream os = new BufferedOutputStream( bos );
  image.flush();
  ImageIO.write( image, "png", os ); //. png 型
  img = bos.toByteArray();
    :
}catch( Exception e ){
}

byte 型配列から BufferedImage 型への変換は ImageIO クラスを使って一発でできますが、逆の BufferedImage 型から byte 型配列へはフォーマット(上記の場合だと PNG)を指定する必要がある、という点に注意です。


このページのトップヘ