2011年7月17日日曜日

face.comのAPIを使って、類似の顔を検索するサービスを作ってみる。

このへんで使った技術を使って、実際にサービスを作ってみる。
face.comのAPIを使って、似た顔を判別させる方法
face.comのAPIをrubyのライブラリを使って遊んでみる

今回はface.comにAKB48の顔写真DBを作り、twitterのアイコンがどれだけAK48のメンバーに似てるか、表示させます。
DBは予め登録してある前提で。

コントローラー
class IndexController < ApplicationController

def index
end

def show
  require 'face'
  require 'open-uri'
  require 'uri'
  require 'net/http'
  @entries = Array.new
  http = Net::HTTP.new( 'api.twitter.com', 80 )
  req = Net::HTTP::Get.new( "http://api.twitter.com/1/users/profile_image/#{params[:user]}.json?size=original" )
  res = http.request( req )
  img_url = URI.parse( res['location'] ).to_s

  client = Face.get_client(:api_key => '' , :api_secret => '')

  data = client.faces_recognize(:urls => img_url , :uids => 'all@akbtter')
  photos = data['photos']
  photos.each do |photo|
    photo['tags'].each do |tags|
     unless tags['uids'] == []
      confidence = tags['uids'][0]['confidence']
      uid = tags['uids'][0]['uid']
      member = Member.find_by_uid( uid )

      hash = {"name" => member.name, "confidence" => confidence }
      @entries << hash
    end
   end
  end
 end
end

ビュー
#index/index.html.erb

AKBったー


twitterのIDを入力すれば、そのユーザーのアイコンがAKBの誰に似ているか判定してくれます。

※現在は神7にのみ対応
<% form_tag :action => 'show' do %>
 <%= text_field_tag 'user' %>
 <%= submit_tag 'チェック' %>
<% end %>


#index/show.html.erb
<% unless @entries.count == 0 %>
<% @entries.each do |f| %>
<%= f["name"] %>に似ています。

似ている度:<%= f["confidence"] %>%
<% end %>
<% else %>
似ているメンバーはいません。
<% end %>

マイグレーション
class CreateMembers < ActiveRecord::Migration
 def self.up
  create_table :members do |t|
   t.string :name, :uid
   t.timestamps
  end
 end

 def self.down
  drop_table :members
 end
end

で、デザインとか整えたりすると、こんな感じのサービスになります。
http://akb.heroku.com

2011年7月16日土曜日

ralisでPC用とiPhone用のデザインを振り分ける

iPhoneからアクセスされると専用のビューを表示させるように設定する。

config/initalizes/mime_types.rbの以下の文のコメントアウトをはずす。
Mime::Type.register_alias "text/html", :iphone

動作させたいコントローラーに以下を追記

before_filter :adjust_format_for_iphone

private

def adjust_format_for_iphone
 request.format = :iphone if request_iphone?
end

public

def request_iphone?
  request.user_agent && request.user_agent[/iPhone|Android/]
end


で、専用のビューを作成する。(ここではindexというコントローラーとindexというアクション名で説明)

layouts/index.iphone.erb #iPhone用のレイアウトファイル
index/index.iphone.erb #index用のレイアウトファイル


あとはそれぞれデザインすればOK。

iPhone用のデザインはUiUIKitというのがあって、それを使うと比較的簡単に作れる。


参考
http://yusukezzz.net/blog/archives/1484
http://d.hatena.ne.jp/t-1732/20100828/1282970904

追記
androidにも対応してみました。
iPhoneと同じデザインですけど。。。
もし個別でやるなら、

private

def adjust_format_for_iphone
 request.format = :iphone if request_iphone?
 request.format = :android if request_android?
end

public

def request_iphone?
  request.user_agent && request.user_agent[/iPhone/]
end

def request_android?
   request.user_agent && request.user_agent[/Android/]
end

にして、index.android.erbですかね?
試してないのでわかりません><

2011年7月15日金曜日

face.comのAPIを使って、似た顔を判別させる方法

face.comのAPIを使うと顔を判断してくれるのですが、このときface.comのDBに似た顔があれば、それを判別して返してくれます。
具体的にどのようなことをすればいいのか日本語で説明しているサイトがなかったので、ちょっと紹介してみます。
なお書いているコードはRubyですが、基本的な流れは他の言語でも一緒です。

face.comの登録はこちらからしてください。
http://developers.face.com/signup/?g

登録したら「API Key」、「API Sercret」、「namespace」をメモっといてください。
都度使用します。
また途中「uid」が出てきますが、これは「任意のID@namespace」です。
任意のIDはどこかで定義することなく、いきなりAPIに飛ばして平気です。
またこの任意のID部分で、なんの写真か判断させますので、人物ごとにユニークなIDをふってください。

【流れ】
1:faces.detectもしくはfaces.recognizeでAPIを叩き、tags.addで使用するデータを取得。
2:1で取得した情報をもとにtags.addでタグ付け。
3:faces.trainで、2で設定したタグをface.comのDBに登録する。
4:faces.recognizeを使って写真を投げると、DBに似た顔の写真があればそのuidを返してくれます。

具体的なコードはこちらです。
登録用と似た写真を判断するようにメソッドを分けてます。

と、その前に
# sudo gem install face
でface.com用のライブラリをインストールしといてください。

require 'rubygems'
require 'face'

#APIkeyの設定
api_key = ''
api_secret = ''

#使用する画像の設定
@img_url = "http://a2.twimg.com/profile_images/1049472282/image_bigger.jpg"
@label = "篠田麻里子"

#shinoda@namespaceが帰ってきたら篠田麻里子だと判定するようDBなどに登録しておくといいんじゃない?
#uidで誰か判定させるので、人物ごとのユニークにしてください。
@uid = "shinoda@namespace"

@client = Face.get_client(:api_key => api_key , :api_secret => api_secret)

#画像の登録
def regist
 data = @client.faces_recognize(:urls => @img_url , :uids => @uid) #face.comのAPIになげる
 photos = data['photos']
 photos.each do |photo|
    photo['tags'].each do |tags|
     result = @client.tags_add(:tid => tags['tid'],
          :uid => @uid,
          :label => @label,
          :url => photo['url'],
          :x => tags['center']['x'],
          :y => tags['center']['y'],
          :width => tags['width'],
          :height => tags['height'],
          :tagger_id => tags['tagger_id']
) #帰ってきたデータをもとにタグを登録。
p result
@client.faces_train(:uids => @uid, :namespace => 'namespace') #タグ付けした情報をface.comのDBに登録。
  end
 end
end

#画像の判定時
def tag_show
 data = @client.faces_recognize(:urls => @img_url , :uids => 'all@namespace') #all@namespaceに対して投げると、namespaceに入っているデータ全部で認定してくれる。
 photos = data['photos']
 photos.each do |photo|
  photo['tags'].each do |tags|
   unless tags['uids'] == []
    puts "#{tags['uids'][0]['uid']}に#{tags['uids'][0]['confidence']}%似ています。"
   end
  end
 end
end


regist
#tag_show


all@namespaceに対してfaces.recognizeさせると、namespaceに登録してあるIDのうち、似ている画像があれば、すべて返してくれる。
投げた写真が、篠田麻里子と前田敦子に似ていた場合、shinoda@namespace,maeda@namespaceみたいな感じで返してくれます。

なお個別に似ているかどうか判断したい場合は個別のuidを指定してください。
(投げた画像が篠田麻里子にどの程度似ているか判定させたい場合、shinoda@namespace宛にfaces.recognizeさせる。
 このとき前田敦子にどれだけ似ていてもその判定はされない。)

似ているどはconfidenceの中に入ってます。

参考:http://journal.mycom.co.jp/column/tool/022/index.html

2011年6月29日水曜日

anemoneの使用時のメモリ問題解決法

anemoneを使ってページ全体をクロールしようとするとメモリをやたら食います。
どうやらこれはデータをメモリにキャッシュしているかららしい。

というわけで、キャッシュをmongodbに保存するようにして、解決する。

まずはyumリポジトリを追加
# sudo vim /etc/yum.repos.d/10gen-mongodb.repo

name=10gen Repository
baseurl=http://downloads.mongodb.org/distros/centos/5.4/os/x86_64/
gpgcheck=0
enabled=0
これを書きこんで保存。

で、必要なものをインストール。
# sudo yum install mongo-10gen mongo-10gen-server
# sudo gem install bson_ext SystemTimer

で、起動する。
# sudo /etc/init.d/mongod start

mongodbを使うように書きなおす。

require 'rubygems'
require 'anemone'

# mongodbを使うのとquery_stringsは取得しないように設定。またアクセス間隔を2秒にする。
  opts = {
   :storage => Anemone::Storage.MongoDB,
   :skip_query_strings => true,
   :delay => 2,
  }

Anemone.crawl("http://example.com/", opts) do |anemone|
 anemone.on_every_page do |page|
   puts page.url
  end
 end
end

参考;
http://work.blog.eggplant.org.uk/2011/03/web-anemone.html

2011年6月28日火曜日

anemoneを使って、サイト内のURLをすべて取得する。

anemoneさん便利すぎでしょ・・・

#sudo gem install anemone


require 'rubygems'
require 'anemone'

Anemone.crawl("http://example.com/") do |anemone|
 anemone.on_every_page do |page|
   puts page.url
  end
 end
end

2011年6月27日月曜日

face.comのAPIをrubyのライブラリを使って遊んでみる

face.comのAPIを使って、flickrのinterestingに掲載された画像のうち、顔の画像だけ選択し、顔の部分には笑い男の画像をつけてみます。
なお笑い男の画像は自分で用意してください。
あとAPIはhttp://developers.face.com/で簡単に登録できます。
画像の修正にはrmagickを使ってますので、昨日の記事を参考にインストールしといてください。

rubyにface.comを扱えるライブラリがあるので、インストール
#sudo gem install face

以下のコードに、api部分を入力する、

require 'rubygems'
require 'face'
require 'open-uri'
require 'nokogiri'
require 'RMagick'
include Magick


client = Face.get_client(:api_key => 'api_key', :api_secret => 'api_secret')
url = "http://www.flickr.com/explore/interesting/2011/06/"
laughingman_image = Magick::Image.read('laughingman.gif').first
url_list = Array.new
img_url_list = Array.new

#flickrからURLを取得
data = open( url )
doc = Nokogiri::HTML( data )

doc.css('a').each do |f|
 begin
  if /\/explore\/interesting\/(\d+)\/(\d+)\/(\d+)\// =~ f.attribute('href').value
   url_list << f.attribute('href').value
  end
 rescue
  next
 end 
end

puts 'flickrの日付URL取得完了'

url_list.each do |f|
 img_url = URI.join(url, f)
 img_data = open( img_url )
 img_data = Nokogiri::HTML( img_data )
 img_data.css('img.pc_img').each do |img|
  img_url_list << img.attribute('src').value
 end
end

puts 'imgの個別URL取得完了'

#face.comのAPIにURLをわたして画像を認識させ、顔があれば、顔の部分に笑い男を貼り付ける。
img_url_list.each do |img_url|
 data = client.faces_detect(:urls => img_url )
 filename = File.basename( img_url )

 photo = data['photos']
 photo.each do |photo|
  if photo['tags'].first #tagsは配列形式で帰ってくるので、firstを付けてます。
   image = Magick::Image.read( img_url ).first #RMagickで読み込んだファイルも配列なので、firstを付ける。
   begin
    photo['tags'].each do |tag|
     width = photo['width'] * (tag['width'] / 100)
     height = photo['height'] * (tag['height'] / 100)
     x = photo['width'] * (tag['center']['x'] / 100)
     y = photo['height'] * (tag['center']['y'] / 100)
     overlay_image = laughingman_image.resize_to_fit(width, height)
     image.composite!(overlay_image, x-width/2, y-height/2, OverCompositeOp)
    end
    image.write("./img/#{filename}")
    puts filename
   rescue
    puts '処理に失敗しました。'
    next
   end
  end
 end
end

参考;http://labs.appshelf.info/category/face-com/

2011年6月26日日曜日

flickrから人気の画像を取得し、rmagickを使って、サムネイルとしてリサイズする

flickrにはその日人気になった写真みたいなのをだしているので、その画像を取得し、150×150の画像にリサイズするプログラムです。
写真のサイズはそれぞれバラバラなので、大きい方の辺を150にし、足りない部分は背景を黒にしています。

一応サーバ上で動かすこと前提としています。

まずはサーバにImagemagickとRmagick、必要なライブラリをインストール

#画像編集に必要なライブラリをインストール(先にいれといたほうがいいらしいです)
# sudo yum install libjpeg-devel libpng-devel


#Image magickのインストール
# wget ftp://ftp.kddlabs.co.jp/graphics/ImageMagick/ImageMagick-6.7.0-8.tar.gz
# tar xvzf ImageMagick-6.7.0-8.tar.gz
# cd ImageMagick-6.7.0-8
# ./configure
# sudo make & make install

#Rmagickのインストール
# sudo gem install rmagick

このままだとなぜかlibMagickCore.so.2を読めないとか出てくるので、bash_profileに以下を追加
# vim .bash_profile
export LD_LIBRARY_PATH=/lib/
↑これを追加する。

これでインストールは完了。

次にこんな感じのファイルを作って、保存して、実行。

require 'rubygems'
require "RMagick"
require 'open-uri'
require 'nokogiri'
require 'uri'
include Magick #多分必要。


def flickr_img_download
 url = "http://www.flickr.com/explore/interesting/2011/06/"
 url_list = Array.new
 img_url_list = Array.new

 data = open( url )
 doc = Nokogiri::HTML( data )

#URLのリストを取得してます。
 doc.css('a').each do |f|
  begin
   if /\/explore\/interesting\/(\d+)\/(\d+)\/(\d+)\// =~ f.attribute('href').value
    url_list << f.attribute('href').value
   end
  rescue
   next
  end 
 end

 url_list.each do |f|
  img_url = URI.join(url, f)
  img_data = open( img_url )
  img_data = Nokogiri::HTML( img_data )
  img_data.css('img.pc_img').each do |img|
   img_url_list << img.attribute('src').value
  end
 end

 #画像を保存してます。
 img_url_list.each do |f|
  begin
   filename = File.basename( f )
   open("img/#{filename}", 'wb') do |file|
    open( f ) do |doc|
     file.write(doc.read)
     puts filename
    end
   end
  rescue
   next
  end
 end
end

def resize
 #背景の画像を作成
 whitefil = Magick::Image.new(150,150){self.background_color = 'none'}

 #フォルダ内の画像リストを作る。
 dir = Dir.glob(["./img/*.jpg", "./img/*.png"])

 dir.each do |filename|
  begin
   image = Magick::Image.read( filename ).first
   if image.columns < 150 or image.rows < 150 #サイズが150以下ならリサイズせずに余白だけ付ける。
     image = whitefil.composite(image, Magick::CenterGravity, Magick::OverCompositeOp)
   else image.columns > 150 or image.rows > 150 #サイズが150以上なら最大の幅を150になるよう、ファイルの比率は変えずに変換。
     image = image.resize_to_fit!(150,150)
     image = whitefil.composite(image, Magick::CenterGravity, Magick::OverCompositeOp)
   end
     image.write( filename )
  rescue Magick::ImageMagickError
   File.delete( filename )
   puts filename
  end
 end
end

flickr_img_download
resize

メソッド二回に分けてリサイズしているけど、本当は一回のメソッドで、かつ、リサイズ前のバックアップ用ファイルを別フォルダに保存しておきたい。
なぜか一回のメソッド中にやろうとすると、保存に失敗するので、こんな感じにしてます。
やり方知っている人がいたら助けてください。。。