Miyabiarts.net

一年に一度の更新頻度

月別アーカイブ: 1月 2011

Japan Computer Vision Day ―全日本CV勉強会―

名古屋CV・PRML勉強会をやらせていただいている関係ですが、東京・大阪・広島の勉強会と合同、かつMIRU2011実行委員会の協力を得て、全国合同のCV勉強会「Japan Computer Vision Day ―全日本CV勉強会―」を開催する運びとなりました(パチパチ)。

開催情報は、現段階で以下のとおりです。

名称: Japan Computer Vision Day ―全日本CV勉強会―
日時:2011/7/19(MIRU2011前日)
時刻:午後(予定) 詳細は内容により、後日決定
場所:金沢市文化ホール(MIRU2011会場)
主催:全日本CV勉強会(以下、構成勉強会)
「コンピュータビジョン最先端ガイド」勉強会@関東
名古屋CV・ PRML勉強会
関西CV・PRML勉強会
後援:
画像の認識・理解シンポジウムMIRU2011実行委員会 (予定)
電子情報通信学会PRMU研究会
広島画像情報学セミナー
若手プログラム2010実行委員会

少し前から、各勉強会の幹事の方々と相談していたのですが、ようやく最低限の情報が確定し、オープンなものとすることができました。

各勉強会が発足した時と同様に参加者がやりたいことをやることをコンセプトとしていますため、何をやるかは現段階では何も決まっていません。
反対に言うと、参加者がやりたいことがそのまま開催内容となります。

Twitterやイベントサイトなどで、開催内容に対する意見を随時募集しています。
普段やってみたいけどなかなかできない企画があれば、良い機会ですので、是非活用してください。
参加者全員で楽しいイベントとしましょう!

広告

ぽぷかる

愛知・ポップカルチャー聖地化計画と称して愛・地球博記念公園(愛称:モリコロパーク)で、愛知ポップカルチャー”ぽぷかる”というイベントが2/13(日)に開催されるようです。

イベントそのものも面白そうで、時間が空いていそうなので遊びに行こうかと思っている所ですが、最近面白いのがイメージキャラクタの”ぽぷかる”ちゃん。

イベントの広報として、Twitter上に現れた@popcul_aichiちゃん(中の人などいない)。
特にリプライを送ったわけでもないのに、ぽぷかる関連のつぶやきをすると、凄い勢いで返事を返してくれます。
広報の鏡です。
(皮肉とかではなく、単純にいい仕事しているな。という意味で:))
イベントが成功に終わることを願っています。

明日は藤が丘駅で、ぽぷかる限定リニモカードを朝10時から発売予定みたいなので、起きることができたら買いに行ってみようかと思います。

ちなみになぜかDSTokaiの勉強会の1つとして予定に組み込まれていますw

入門自然言語処理読書会

第1回 入門自然言語処理読書会に参加登録しました。

画像処理の方は、一応専門なのでそれなりには分かるのですが、自然言語処理の方が基本的にさっぱりです。
アプリケーションとしては、MeCabで形態素解析して、文章とかTwitterを解析してカテゴリ分類したり、簡単な会話が出来るBOT作ったりしたことはあるのですが、理論的なことはほとんど分かっていません。
ちょっとカバーできる分野を増やすために勉強に行ってきます。

[amazon asin=’4873114705′ type=’book’]

OpenCV祭コワイ

3/5に第10回コンピュータビジョン勉強会@関東(番外編)、通称「OpenCV祭」が開催されます。
昨年、OpenCV2.2が公開されて、色々と機能が追加されたりと何かと話題だったので、良い機会です。

開催される企画が立ち始めた頃から眺めていたのですが、Twitter上でOpenCVに関することを割と呟いていたおかげか、

@dandelion1124
dandelion
@miyabiarts CV祭りin東京が3/5になりそうですが,名古屋から刺客として参戦されますか?
http://twitter.com/#!/dandelion1124/status/27330319749222401

とまぁ、お誘いを受けたわけで、はじめは参加だけしようかと思って、お茶を濁していたのですが、色々と会話しているうちに調子に乗って、

@miyabiarts
miyabiarts
@dandelion1124 もう、「C++ or pythonインタフェース」でも「OpenCVにある機能をひたすらGUIで見せる」でも「ポエム」でもなんでも良いので、時間が空いたら発表に突っ込んどいてくださいw
http://twitter.com/#!/miyabiarts/status/27385404852277249

と発言したおかげで、いつの間にか発表者側に回っていましたとさ。

まぁ最近良く触っているだけあって、発表するネタには困らないだろうと思っていました。

そして、昨日OpenCV祭のイベントページができて、その日のうちに参加者が定員に達したのですが、参加者の顔ぶれが怖すぎるんですがw
勉強会や研究でよく見かけるC++やARや機械学習詳しい人ばかりですよ((((;゜Д゜)))ガクガクブルブル

特に、私はOpenCV関係者というわけではなく、最近TwitterでOpenCVに呟いているだけの人なんですがw
ただまぁ、発表するからにはなるべく楽しんでもらおうとネタ作りをしてきます。
今のところ、話の流れで「Python+OpenCV」が発表予定になっていますが、内容が変わる可能性は高いです。
というか、まだ深く考えていません(・ω・)b

そこに深淵を見た

最近、OpenCVをひたすら調査しており、分かったことは随時Twitterに垂れ流している状態です。
調べれば調べるほど色々用意されていることに驚くことばかりです。

先日実装した特徴点検出・特徴量抽出プログラムも、ほぼそのまんまのものサンプルに用意されていました。
また、最近実装して、今度公開するつもりだったBag of Featureを用いた一般物体認識のコードなんかもサンプルに存在しました。
分かったことは、サンプルはしっかりと見ましょう。
ということです。
深い。深すぎる。

しばらくはサンプルを調査して、新たに加わったインタフェースなどの使い方を覚えていこうかと思います。
分かったものから順に解説記事でも書いていく予定です。
というか、ここらへんはOpenCV.jpに書いたほうが良いのかな?

Panoramioからの写真収集

写真共有サイトPanoramio(http://www.panoramio.com/)は、ユーザがアップロードした写真を地図(Google Maps)上に重畳して表示するサービスです。
APIが用意されており、プログラムからアクセスすることが出来るため、今回はPythonを用いて写真を収集するコードを掲載します。
なお、基本的にLinuxを想定しており、画像のダウンロードにはwgetが必要となります。

Panoramioでは、APIに任意の範囲(経度・緯度)を与えることにより、その範囲内の画像を収集することができます。
ただし、全世界を含むような範囲を指定したら、全ての写真を収集できるというわけでなく、指定した範囲の大きさに応じてある程度収集する写真が選別されます。

下のコードでは、output_jsonに写真のメタデータをJSON形式で保存します。
メタデータには、写真のタイトルや写真が撮影された経度・緯度情報が保存されています。
また、output_image_dirに写真そのものをダウンロードすることができます。
各写真のファイル名は、写真のIDとなり、メタデータと対応付いています。
これらの保存先の変数と、経度・緯度範囲を示すminx、maxx、miny、maxyを指定してやることにより写真を収集します。
今回のコードでは、名古屋周辺をデフォルトの値として指定しています。


#!/usr/bin/python
# -*- coding: utf-8 -*-

import urllib
import json
import os

# 出力パラメータ
# 写真データ(JSON形式)
output_json = 'output.json'
# 写真(画像)を収集するかのフラグ
isCollectImage = True
# 写真(画像)の出力先ディレクトリ
output_image_dir = './images/'


# 収集パラメータの設定
# 収集する写真の種類(public/full/ユーザID)
type = 'public'
# 収集する範囲を設定
# 経度
minx = 136.56143
maxx = 137.38540

# 緯度
miny = 34.85550
maxy = 35.52887


# 写真のサイズ(small/medium/large)
size = 'medium'
# 収集するループ1回の写真の収集枚数(写真を分割収集するため)
span = 100


# 緯度・経度の大小関係を合わせる
if minx > maxx:
    minx, maxx = maxx, minx

if miny > maxy:
    miny, maxy = maxy, miny

# panoramioのURL
url = 'http://www.panoramio.com/map/get_panoramas.php'

# 収集結果
photos = []

i = 0
while True:
    # パラメータを設定
    params = urllib.urlencode( { 'set' : type,
                                 'minx' : minx,
                                 'miny' : miny,
                                 'maxx' : maxx,
                                 'maxy' : maxy,
                                 'size' : size,
                                 'from' : span * i,
                                 'to' : span * ( i + 1 ) } )

    # panoramioにリクエスト
    f = urllib.urlopen( url + '?' +  params )
    buf = f.read()
    j = json.loads( buf )

    photos += j[ 'photos' ]

    if j[ 'has_more' ] == False:
        break

    i += 1

# JSON形式でファイル出力
fp = open( output_json, 'w' )
fp.write( str( photos ) )
fp.close()

# 画像を収集
if isCollectImage == True:
    for photo in photos:
        print photo[ 'photo_url' ]
        cmd = 'wget ' + photo['photo_file_url' ] + ' -O ' + output_image_dir + '/' + str( photo['photo_id'] ) + '.jpg'
        os.system( cmd )

おまけ:写真のテキストタグの収集

以上のコードで、写真、およびメタデータを収集することができるのですが、写真に付随しているテキストタグは収集することができません。
そのため、無理やりですが、写真のIDを用いて、写真が掲載されているWebページのHTMLを解析することでテキストタグを取得するコードを示します。
下のコードでは、関数宣言のみなので、実際に使うときには呼び出すようにしてください。
また、HTML解析にはBeautifulSoupを用いているため、必要に応じてインストールしてください。

#!/usr/bin/python
# -*- coding: utf-8 -*-

import urllib
from BeautifulSoup import BeautifulSoup

# idの写真のテキストタグを取得
def get_tag( id ):
    # panoramioの写真ページアクセス
    # HTMLを取得
    url = 'http://www.panoramio.com/photo/' + str( id )
    f = urllib.urlopen( url )

    # HTML解析
    soup = BeautifulSoup( f.read() )

    # タグを取得
    taglst = []
    tags = soup.findAll( 'ul', {'id':'tags'} )
    for tag in tags:
        for t in tag.findAll( 'a' ):
            if t[ 'href' ] != '#':
                taglst.append(  t.contents[ 0 ].strip() )
    return taglst

以上がPanoramioからの写真収集となります。
気をつけることとして、一度に大量の写真を取得しようとして、Paranomioに負荷をかけ過ぎにようにしてください。

OpenCVを用いた特徴点対応付け

OpenCV2.2になって、C++インタフェースが随分整理されましたので、色々と触っていました。
主に特徴点周りのクラスを見ていたのですが、画像から特徴点を検出し、画像間で対応付けるコードを簡単に書けるようになりました。
以下に2画像間の特徴点の対応付けのコードを掲載します。

#include <opencv2/opencv.hpp>
//
int main()
{
	// 画像1の読み込み
	cv::Mat_<cv::Vec3b> img1 = cv::imread( "in1.jpg" );

	// 画像2の読み込み
	cv::Mat_<cv::Vec3b> img2 = cv::imread( "in2.jpg" );

	// SIFT用のデフォルトパラメータを設定
	double threshold = cv::SIFT::DetectorParams::GET_DEFAULT_THRESHOLD();
	double edge_threshold = cv::SIFT::DetectorParams::GET_DEFAULT_EDGE_THRESHOLD();
	double magnification = cv::SIFT::DescriptorParams::GET_DEFAULT_MAGNIFICATION();

	// SIFTの特徴点検出・特徴量抽出器を用意
	cv::SiftFeatureDetector detector( threshold, edge_threshold );
	cv::SiftDescriptorExtractor extractor( magnification );
	/*
	// SURFの場合
	cv::SurfFeatureDetector detector();
	cv::SurfDescriptorExtractor extractor();
	*/

	// 画像1から特徴点を検出
	std::vector<cv::KeyPoint> keypoints1;
	detector.detect( img1, keypoints1 );

	// 画像2から特徴点を検出
	std::vector<cv::KeyPoint> keypoints2;
	detector.detect( img2, keypoints2 );

	// 画像1の特徴点における特徴量を抽出
	cv::Mat descriptor1;
	extractor.compute( img1, keypoints1, descriptor1 );

	// 画像2の特徴点における特徴量を抽出
	cv::Mat descriptor2;
	extractor.compute( img2, keypoints2, descriptor2 );

	// 対応点を求める
	std::vector<cv::DMatch> matches;

	// L2距離を用いた全探索対応付け
	cv::BruteForceMatcher< cv::L2 > matcher;
	// Flannベースの対応付け
//	cv::FlannBasedMatcher matcher;

	// 特徴量比較による特徴点の対応付け
	matcher.match( descriptor1, descriptor2, matches );

	// 結果を描画
	cv::Mat_<cv::Vec3b> result;
	cv::drawMatches( img1, keypoints1, img2, keypoints2, matches, result );

	// 結果を出力
	cv::imwrite( "result.jpg", result );

	return 0;
}

といった感じで、簡単なコードだと思いませんか?

簡単にやっていることを説明します。
各画像から、SIFTにより特徴点を検出し、その特徴量を抽出します。
特徴点の検出は、cv::FeatureDetectorを継承したcv::SiftFeatureDectetorを、特徴量の抽出は、cv::DescriptorExtractorを継承したcv::SiftDescriptorExtractorを用いることでそれぞれ行うことが出来ます。
コード中のコメントを入れ替えれば、SIFTからSURFに簡単に切り替えることもできます。
その他の特徴点検出・特徴量抽出手法についても同じです。
そして、特徴量間の対応付けを行います。
こちらも対応付けに用いる手法(cv::DescriptorMatcher)を、定義するクラスで簡単に切り替えることができます。
今回は、L2距離基準を用いた全探索対応付けを行うcv::BruteForceMatcherを用いています。
最後に、特徴点とその対応をcv::drawMatchesにより描画することで、結果を得ることができます。

テスト画像

テスト画像は、以下の2種類を用いています。
左側が原画像で、右側は原画像を45度回転しています。
これらの画像の特徴点対応付けを行います。



特徴点対応付け結果

下の画像がそれぞれの対応付け結果です。
各画像中の円で表されるものが特徴点で、特徴点間に引かれている線分が特徴点の対応を示しています。
1つ目の画像は、特徴点自体があまり出ておらず、対応付けもそれほど精度良くないようです。
2つ目の画像は、特徴点が多く検出され、誤対応もありますが、大半は精度良く対応付いています。

特徴点対応付けの確認

特徴点の対応付けが正しく行われているか、同じ画像同士を対応付けることにより確認しております。
下の画像を見れば、同じ特徴点が対応付いている(対応を表す線が平行である)ことが分かります。

というわけで、随分簡単に特徴点対応付けができるようになりました。
もちろん精度などの問題も残されているのでパラメータなどの調整が必要となってきます。

分からないことや不明なことがあれば、コメントください。

OpenCVでの画素へのアクセス方法

tsunotterで、

「OpenCVで任意のピクセルの画素値に直接どうやってアクセスする? #opencv」

という質問があったので、私のOpenCVでの画素へのアクセス方法を書いておきます。

私は、基本的にOpenCVはC++インタフェースを使っているので、画像はcv::Mat_で扱っています。
画素へのアクセスは、以下の2つの方法を使い分けており、普段は1つ目で、処理を速くしたい場合は2つ目の方法を用いています。
サンプルコードでは、400×400のRGB画像の全画素を赤くするもので、やっていることはcv::Fillと同じです。

任意の画素へのアクセス

単純にcv::Mat_::operator()( y, x )を用いて、画素にアクセスする手法です。
xとyが逆にならないように気をつけてください。
また、cols=width、rows=heightとなっています。

#include <opencv2/opencv.hpp>

int main()
{
  // 400x400 RGB画像を作成
  cv::Mat_<cv::Vec3b> img( cvCreateImage( cvSize( 400, 400 ), IPL_DEPTH_8U, 3 ) );

  // 全画素を赤くする
  for( int y = 0 ; y < img.rows ; ++y )
    {
      for( int x = 0 ; x < img.cols ; ++x )
	{
          // 画素へアクセス
	  img( y, x )[ 0 ] = 0;
	  img( y, x )[ 1 ] = 0;
	  img( y, x )[ 2 ] = 0xff;
	}
    }
  // 画像出力
  cv::imwrite( "result.bmp", img );
  return 0;
}

当然ですが、cv::Vec3bをunsigned charに変えることで、グレースケール画像も扱えます。

任意の画素へのアクセス(高速版)

1つ目の方法だと、1つの画素にアクセスするたびにcv::Mat_::operator ( y, x )を呼び出すオーバヘッドがかかるため、ポインタ操作でなるべくオーバヘッドを減らす方法です。
スキャンラインで画像を走査する際に、スキャンラインの先頭画素のポインタを取得した上で、後はポインタのインクリメンタルによりアクセスする画素を進めます。
気をつけることは、ポインタを進めすぎるとバッファ・オーバランが起こるので、アクセス範囲には十分気をつけてください。

#include <opencv2/opencv.hpp>;

int main()
{
  // 400x400 RGB画像を作成
  cv::Mat_<cv::Vec3> img( cvCreateImage( cvSize( 400, 400 ), IPL_DEPTH_8U, 3 ) );

  // 全画素を赤くする
  for( int y = 0 ; y < img.rows ; ++y )
    {
      // 先頭の画素のポインタを取得
      cv::Vec3b *p = &img( y, 0 );
      for( int x = 0 ; x < img.cols ; ++x )
	{
           // 画素へアクセス
	  ( *p )[ 0 ] = 0;
	  ( *p )[ 1 ] = 0;
	  ( *p )[ 2 ] = 0xff;
     // 次の画素へ進める
	  p++;
	}
    }
  // 画像出力
  cv::imwrite( "result.bmp", img );
  return 0;
}

cv::Vec3bをunsigned charに変えることで、グレースケール画像も扱えます。
このとき、画素へのアクセスに用いるポインタは型情報も持っているため、ポインタのインクリメンタルで画素単位で進めることができ、1画素が何バイトで構成されているかを意識しないで良いので楽ですし、間違いが少なくなります。

他にもやり方は色々ありますが、私は以上の方法で画素にアクセスすることが多いです。

本日、未熟者

一日文章を見ている思い出しか残っていません。

焼肉を食べに行ったことが唯一の思い出です。

しばらくこんな日が続くと思います。

OpenCV関連で中途半端な記事は書いているので、近日中に公開できたらと思います。

ゲームしたいのですけどね

「朝8時くらいに研究室に来てくれると嬉しい。」と言われたので、7時くらいに起きて、家を出る準備をしていたのですが、出ようとしたら電話がかかってきて、「9時30分くらいで良いよ。」と言われたので、少しだけサガ3が進みました。本当に少しだけなんだけど。

昨日、サガ3を買いにゲームショップに行ったときに最近発売されたゲームをいくつか見ましたけど、やりたいゲームばかりで困りますね。最近発売された・されるゲームのなかでやりたいものをリストアップすると10本くらいあります。

ただ、やりたいのですが、やる時間がなかなか取れないのが難しいところ。忙しいというわけではなく、時間が空いたらゲーム以上にやりたいことがあるため、なかなかやれないというのが実情です。今は時間が空いたら、画像処理とかパターン認識とかの本読んだり、コード書いたりするのが楽かったりします。知識が増えるというのもあるのですが、最近、このあたりの活動のおかげで面白い人たちと話す機会が増えたので、楽しくて仕方がないのです。

それでもまぁサガ3みたいにRPGを始めたら、一気に終わらせたい欲求に駆られるので、やはりゲームはしたいですね。出来ることといえば、時間を効率的に使って、やる時間を捻出することですが、その場合、時間が空いたらまた新しいことをし始めるため、結局どこまで行っても時間は足りないのですけどね。