2010/04/13

ピクセル単位での操作

前回までは、画像全体に対して同一の操作を行ってきました。
今度は、1ピクセルごとにデータをいじりたいと思います。

というわけで今回のお題は色相関。
↓こんなの









横幅360ピクセルの画像を生成して、
1ピクセルごとに青、緑、赤の値を変化させていっています。

関数を使ったものと、ポインタで直接値を変更したものを作ってみました。


//色相関みたいなものを作る
#include "cv.h"
#include "highgui.h"
 double g_cvSet2DTime;
 double g_PointerTime;

void  SetScalarToPixcel(IplImage* img,int x, int setblue, int setgreen, int setred);
void  SetScalarToPixcel_PointVer(IplImage* img,int x, int setblue, int setgreen, int setred);



int main( int argc, char** argv )
{
 //表示用のウィンドウを作る
 cvNamedWindow("Gradation",CV_WINDOW_AUTOSIZE);
 cvNamedWindow("Gradation2",CV_WINDOW_AUTOSIZE);

 g_cvSet2DTime=0.0;
 g_PointerTime=0.0;
 //空っぽのイメージを作成
 IplImage * colorMap=cvCreateImage(cvSize(360,40),IPL_DEPTH_8U,3); //IPL_DEPTH_8U 符号なし8ビット整数
 IplImage * colorMapPointer=cvCreateImage(cvSize(360,40),IPL_DEPTH_8U,3);

 //初期化(真っ黒になる)
 cvZero(colorMap);
 cvZero(colorMapPointer);
 

 //WikiPediaに色相関を作る方法が載っていたのでそれを参考に作成します。
 //                青     緑     赤
 //  0°- 60°   0固定       0から255へ  255固定
    // 60°-120°   0固定       255固定     255から0へ
 //120°-180°   0から255へ  255固定     0固定
 //180°-240°   255固定     255から0へ  0固定
 //240°-300°   255固定     0固定       0から255へ
 //300°-360°   255から0へ  0固定       255固定
 //
 int modular=0;
 int addvalue=0;
 for(int degree=1;degree<=360;degree++)
 { 
  modular=(degree)%60;
  
  if(degree==60)
  {
   SetScalarToPixcel(colorMap,degree,0,255,255);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,0,255,255);
  }
  if(degree==120)
  {
   SetScalarToPixcel(colorMap,degree,0,255,0);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,0,255,0);
  }
  if(degree==180)
  {
   SetScalarToPixcel(colorMap,degree,255,255,0);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,255,255,0);
  }
  if(degree==240)
  {
   SetScalarToPixcel(colorMap,degree,255,0,0);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,255,0,0);
  }
  if(degree==300)
  {
   SetScalarToPixcel(colorMap,degree,255,0,255);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,255,0,255);
  }
  if(degree==360)
  {
   SetScalarToPixcel(colorMap,degree,0,0,255);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,0,0,255);
  }

  if(  0<=degree && degree< 60)
  { 
   addvalue=modular*4;
   SetScalarToPixcel(colorMap,degree,0,addvalue,255);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,0,addvalue,255);
  }
  if( 60<degree && degree<120)
  { 
   addvalue=255-modular*4;
   SetScalarToPixcel(colorMap,degree,0,255,addvalue);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,0,255,addvalue);
  }
  if(120<degree && degree<180)
  { 
   addvalue=modular*4;
   SetScalarToPixcel(colorMap,degree,addvalue,255,0);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,addvalue,255,0);
  }

  if(180<degree && degree<240)
  { 
   addvalue=255-modular*4;
   SetScalarToPixcel(colorMap,degree,255,addvalue,0);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,255,addvalue,0);
  }
  if(240<degree && degree<300)
  { 
   addvalue=modular*4;
   SetScalarToPixcel(colorMap,degree,255,0,addvalue);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,255,0,addvalue);
  }
  if(300<degree && degree<360)
  { 
   addvalue=255-modular*4;
   SetScalarToPixcel(colorMap,degree,addvalue,0,255);
   SetScalarToPixcel_PointVer(colorMapPointer,degree,addvalue,0,255);
  }

  
 }
 cvShowImage("Gradation",colorMap);
 cvShowImage("Gradation2",colorMapPointer);

 printf( "execution time(cvSet2D) = %g ms\n", g_cvSet2DTime/((double)cvGetTickFrequency()*1000.) );
 printf( "execution time(Pointer) = %g ms\n", g_PointerTime/((double)cvGetTickFrequency()*1000.) );
 //5秒間表示
 cvWaitKey(5000);

 //リソースの開放
 cvReleaseImage(&colorMap);
 cvReleaseImage(&colorMapPointer);
 cvDestroyWindow("Gradation");
 cvDestroyWindow("Gradation2");
}

void  SetScalarToPixcel(IplImage* img,int x, int setblue, int setgreen, int setred)
{
 double t = 0;
    t = (double)cvGetTickCount();
   
  for(int y=0;y<img->height;y++)
  {
   //インデックスがheight,widthの順で並んでるので注意
   cvSet2D(
    img,
    y,
    x-1,
    cvScalar(setblue,setgreen,setred)
    );
  }
 t = (double)cvGetTickCount() - t;
    g_cvSet2DTime=g_cvSet2DTime+t; 
}
void  SetScalarToPixcel_PointVer(IplImage* img,int x, int setblue, int setgreen, int setred)
{
 double t = 0;
    t = (double)cvGetTickCount();
   
  for(int y=0;y<img->height;y++)
  {
   uchar* ptr=(uchar*)(img->imageData + y *img->widthStep);

    ptr[3*x+0]=setblue;
    ptr[3*x+1]=setgreen;
    ptr[3*x+2]=setred;
   
  }
 t = (double)cvGetTickCount() - t;
    
 g_PointerTime=g_PointerTime+t;
}


関数を使った場合と、ポインタで直接いじった場合の差は以下のようになりました。
execution time(cvSet2D) = 3.85831 ms
execution time(Pointer) = 0.969397 ms

関数を使うと、オーバーヘッドはどうしても出てしまいますね。。。

2010/04/10

Syntax Highlighter でコードを見やすく!

ソースコードをページに貼ってるのですが、いまいち見づらい。
なにかいいものは無いものかと、色々と巡回していると、
Syntax Highlighter というものがあるようです。
これはBlogger用のウィジェットで、コードが下記のように表示できるものです。

/*
*  カメラからキャプチャしたデータを半分のサイズにし、
* グレースケールに変換したものを出力する。
*/

#include "cv.h"
#include "highgui.h"

int main(int argc, char** argv)
{
 //表示用のウィンドウを作成する 
 cvNamedWindow("OriginalWindow",CV_WINDOW_AUTOSIZE);  //カメラで取得したデータの表示用
 cvNamedWindow("SmallSizeWindow",CV_WINDOW_AUTOSIZE); //半分のサイズでグレイスケールのデータの表示用

 //カメラから取り込む
 //cvCreateCameraCaptureに0を指定すると、最初に見つかるカメラデバイスが選ばれます。
 CvCapture *capture = cvCreateCameraCapture(0);
 
 //カメラが無い時用
 //CvCapture *capture = cvCrateFileCapture(動画ファイル名);


 printf("Please press ESC key to exit.");

 //カメラのサイズを取り込みます。 cvGetCapturePropertyの戻り値はdoubuleなので、intにキャスト
 CvSize size=cvSize
     (
        (int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH ),
        (int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT )
     );
 
 //縦横を半分に
 CvSize halfsize=cvSize((int)size.width/2,(int)size.height/2);


 //カメラから取得したデータ用
 IplImage * CapturedFrame;
 
 //半分のサイズのデータを格納する
 //カラーのままなので、チャネル数は3
 IplImage * SmallFrame=cvCreateImage(halfsize,IPL_DEPTH_8U,3);
    //グレースケール用
 //グレースケールなので、チャネル数は1
 IplImage * GrayFrame=cvCreateImage(halfsize,IPL_DEPTH_8U,1);


 while(1)
 {
  //1フレームキャプチャ
  CapturedFrame=cvQueryFrame(capture);
  if(!CapturedFrame) break;

  //小さいサイズに変更
  cvResize(CapturedFrame,SmallFrame);

  //小さくしたものをグレースケールに変更
  //cvCvtColorを使用する際は、変換元と変換先のサイズが同じである必要があります。
  cvCvtColor(SmallFrame,GrayFrame,CV_RGB2GRAY);
    
     //ウィンドウに表示
     cvShowImage("OriginalWindow",CapturedFrame);
     cvShowImage("SmallSizeWindow",GrayFrame);
 
  //33ミリ秒 キーの入力を待つ(escキーが押されるとループを抜ける)
  //33ミリ秒ごとにキャプチャするので、表示は30FPS程度になります
  char c=cvWaitKey(33);
  if(c==27) break;
  
 }

 //リソースを開放
 cvReleaseCapture(&capture);
 cvReleaseImage(&GrayFrame);
 cvReleaseImage(&SmallFrame); 

 cvDestroyWindow("OriginalWindow");
 cvDestroyWindow("OriginalWindow");

}   

導入の仕方ですが、
1、http://fazibear.blogspot.com/ にアクセスします。
2、右上のSearch box に、syntax Highlighter と入力します。
3、該当するエントリが表示されるので、”To add it check out this page.”と書かれているところをクリックします。
4、そのまま追加します。

使うときは、編集画面で
<pre name="code" class="c-sharp">
... some code here ...
</pre>
と入力すればOKです。

これでかなりコードが見やすくなりました!

2010/04/09

カメラからのキャプチャ

前回は画像を表示したので、今度は動画を表示したいと思います。
また、サイズの変更とグレースケール化も一緒に行います。

出来上がりは以下のような感じになります。


以下、ソースコードになります。

/*
* カメラからキャプチャしたデータを半分のサイズにし、

* グレースケールに変換したものを出力する。
*/

#include "cv.h"
#include "highgui.h"

int main(int argc, char** argv)
{
//表示用のウィンドウを作成する 
cvNamedWindow("OriginalWindow",CV_WINDOW_AUTOSIZE); //カメラで取得したデータの表示用
cvNamedWindow("SmallSizeWindow",CV_WINDOW_AUTOSIZE); //半分のサイズでグレイスケールのデータの表示用

//カメラから取り込む
//cvCreateCameraCaptureに0を指定すると、最初に見つかるカメラデバイスが選ばれます。
CvCapture *capture = cvCreateCameraCapture(0);

//カメラが無い時用
//CvCapture *capture = cvCrateFileCapture(動画ファイル名);


printf("Please press ESC key to exit.");

//カメラのサイズを取り込みます。 cvGetCapturePropertyの戻り値はdoubuleなので、intにキャスト
CvSize size=cvSize
(
(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_WIDTH ),
(int)cvGetCaptureProperty(capture,CV_CAP_PROP_FRAME_HEIGHT )
);

//縦横を半分に
CvSize halfsize=cvSize((int)size.width/2,(int)size.height/2);


//カメラから取得したデータ用
IplImage * CapturedFrame;

//半分のサイズのデータを格納する
//カラーのままなので、チャネル数は3
IplImage * SmallFrame=cvCreateImage(halfsize,IPL_DEPTH_8U,3);
//グレースケール用
//グレースケールなので、チャネル数は1
IplImage * GrayFrame=cvCreateImage(halfsize,IPL_DEPTH_8U,1);


while(1)
{
//1フレームキャプチャ
CapturedFrame=cvQueryFrame(capture);
if(!CapturedFrame) break;

//小さいサイズに変更
cvResize(CapturedFrame,SmallFrame);

//小さくしたものをグレースケールに変更
//cvCvtColorを使用する際は、変換元と変換先のサイズが同じである必要があります。
cvCvtColor(SmallFrame,GrayFrame,CV_RGB2GRAY);

//ウィンドウに表示
cvShowImage("OriginalWindow",CapturedFrame);
cvShowImage("SmallSizeWindow",GrayFrame);

//33ミリ秒 キーの入力を待つ(escキーが押されるとループを抜ける)
//33ミリ秒ごとにキャプチャするので、表示は30FPS程度になります
char c=cvWaitKey(33);
if(c==27) break;

}

//リソースを開放
cvReleaseCapture(&capture);
cvReleaseImage(&GrayFrame);
cvReleaseImage(&SmallFrame); 

cvDestroyWindow("OriginalWindow");
cvDestroyWindow("OriginalWindow");

}

2010/04/06

ブログ新設

画像処理ライブラリ OpenCVの情報を発信するブログを作りました。
OpenCVをはじめて間もないですが、ハマッたところなども含めて
少しずつ更新していきたいと思います。

OpenCV 2.1が出ました

画像認識ソフト OpenCVの新しいバージョンが出たので、早速ダウンロードしました。

ダウンロードとインストールの手順は下記のとおりとなります。

1、ここにアクセス
http://sourceforge.net/projects/opencvlibrary/files/

2、私はVC++ 2008 Express Editionを使用しているので、
OpenCV-2.1.0-win32-vs2008.exe
を選びました。

3、exeを実行し、インストールを行います。
 ・ライセンスに同意します
 ・OpenCVのディレクトリを環境変数のPATHに登録します
 ・デスクトップにOpenCVのアイコンを追加します
 ・インストール先はデフォルトのままにします。
 ・インストールタイプを「Full」に設定します。

4、サンプルを実行してみます。
サンプルはC:\OpenCV2.1\samples\c に入っています。
USBカメラなどがないとだめなものもありますが、edge.exeなどはカメラがなくても実行できます。


5、VC++でコードを書いてみます。
 ・VC++を開きます。
 ・ファイル->新規作成->プロジェクト->Win32->コンソールアプリケーション を選びます。
 ・プロジェクトウィザードが出ますが、とりあえず「完了」を押します。
 ・ファイルがいくつか自動的に作成されます。
 ・コードを下記のように変更します。

//サンプル画像を表示するだけのアプリ

#include "stdafx.h"
#include "C:\\OpenCV2.1\\include\\opencv\\highgui.h"

int main(int argc, char** argv)
{
//ファイルから画像を読み込みます
IplImage *img=cvLoadImage("C:\\OpenCV2.1\\samples\\c\\fruits.jpg");

cvNamedWindow("OpenCVTest",CV_WINDOW_AUTOSIZE); //画像出力用のウィンドウを作成します
cvShowImage("OpenCVTest",img); //ウィンドウに画像を表示します
cvWaitKey(0); //何かキーが押されるまで待ちます

cvReleaseImage(&img); //リソースを開放します。
cvDestroyWindow("OpenCVTest");
}


保存してビルドすると、下記のようなエラーがわらわら出てきます。

error C3861: 'clLoadImage': 識別子が見つかりませんでした

ライブラリファイルへの参照の設定がされていないと、上記のようなエラーが出るようです。
(VC++、OpenCVともに触り始めたばかりなので詳しいことはわかりません)

設定は下記のとおりとなります。
・プロジェクトのプロパティを開きます
・構成プロパティ->リンカ->全般->追加のライブラリディレクトリに、
C:\OpenCV2.1\lib と設定します(OpenCVをインストールした場所のlibディレクトリです)
・プロパティ->リンカ->入力->追加の依存ファイルに、
 cv210d.lib
 cvaux210d.lib
 cxcore210d.lib
 cxts210.lib
 highgui210d.lib
 ml210d.lib
 opencv_ffmpeg210d.lib
の6つを加えます。
Releaseの時はdを除いたファイル名に設定します。

リビルドを行ってデバッグ開始ボタンを押すと、下記のようなウィンドウが表示されると思います。



とりあえず初めてのアプリはこんな感じです。

OpenCVにはステレオグラムの作成や顔検出など、色々と機能がありますが
まだ使い始めたばかりなのであまり詳しくはわかりません。

現在はオライリーから出版されている「詳解 OpenCV」を読みながら遊んでいます。

最終的には、ラジコン戦車にカメラを搭載して障害物をよけるものを作りたいなーと考えてます。