2013年6月12日水曜日

決定木のサンプル

決定木のサンプルプログラム

数値パラメータ入力に対する決定木のサンプル.

#include <opencv2/opencv.hpp> //OpenCV関連ヘッダ全部インクルード
#ifdef _DEBUG  //Debugモードの場合
#pragma comment(lib, "opencv_core245d.lib")
#pragma comment(lib, "opencv_ml245d.lib")
#else  //Releaseモードの場合
#pragma comment(lib, "opencv_core245.lib")
#pragma comment(lib, "opencv_ml245.lib")
#endif

using namespace cv;
using std::cout;
using std::endl;

void showSplits(CvDTreeSplit *split)
{
    if(split==NULL) return;

    cout << "Split : vidx=" << split->var_idx;
    cout << ", quality=" << split->quality;
    cout << ", ord.c=" << split->ord.c << endl;
    showSplits(split->next);
}

void showTrees(const CvDTreeNode *root, string text)
{
    if(root==NULL) return;
    for(int d=0; d<root->depth; d++) cout << "-- ";
    cout << text << endl;
    cout << "sample count" << root->sample_count << endl;
    cout << "value:" << root->value << endl;
    showSplits(root->split);
    cout << endl;

    showTrees(root->left, "Left");
    showTrees(root->right, "Right");
}

int main()
{
    //http://www.sakurai.comp.ae.keio.ac.jp/classes/IntInfProc-class/2010/06DecisionTree.pdf より「破産の予測」
    // 1年あたりの支払い遅延回数,支出/収入
    Mat data = (Mat_<float>(14,2) <<
    3, 0.2,
    1, 0.3,
    4, 0.5,
    2, 0.7,
    0, 1.0,
    1, 1.2,
    1, 1.7,
    6, 0.2,
    7, 0.3,
    6, 0.7,
    3, 1.1,
    2, 1.5,
    4, 1.7,
    2, 1.9);
    Mat label = (Mat_<int>(14, 1) << 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1);

    CvDTreeParams param = CvDTreeParams( 
        3, // 最大の深さ
        2, // あるノードに対するサンプル数がこの値よりも少ない場合,分岐しない.
        0, // 別の終了条件 - 回帰木の場合のみ. 
        false, // trueの場合代理分岐が構築される.データの欠損や,変数の重要度の推定に必要
        2, // max_categories 
        0, // このパラメータが >1 の場合,木は cv_folds 分割交差検証法により刈り込まれる.
        false, // true の場合,木は刈り込み手続きによって切り捨てられる. 
        false, // true の場合,カットオフノードが,木から物理的に削除される.
        NULL // クラスラベル値によって保存されたクラス事前確率の配列.
    );

    DecisionTree dtree = DecisionTree();
    dtree.train(data, CV_ROW_SAMPLE, label, Mat(), Mat(), Mat(), Mat(), param);

    //学習データをテストに回してみる
    for( int i = 0; i < data.rows; i++ )
    {
        double r = (dtree.predict(data.row(i)))->value;
        cout << i << ":" << r << endl;
    }

    //ツリーの表示
    showTrees(dtree.get_root(), "Root");

    return 0;
}

2012年11月16日金曜日

DepthSense SDKで距離画像を取得,OpenCVで表示

SoftkineticのDepthSense SDKで,DS325からの距離画像をOpenCVのMat(CV_16SC1)に放り込み, 100mmで色相が一周するように擬似カラー化して表示する.
void onNewDepthSample(DepthNode node, DepthNode::NewSampleReceivedData data)
{
    int32_t w, h;
    FrameFormat_toResolution(data.captureConfiguration.frameFormat,&w,&h);

	if ( data.depthMap != nullptr ) {
        Mat depth( h, w, CV_16SC1, (void*)(const int16_t*)data.depthMap );
		Mat hsv( h, w, CV_8UC3);
		Mat psCol( h, w, CV_8UC3);

		namedWindow("color depth", 0);
		short level;
		for(int y=0; y<h; y++) {
			for(int x=0; x<w; x++) {
				level = depth.at<short>(y, x);
				if(level > 32000) { //invalid
			        hsv.data[y * hsv.step + x*3 + 0] = 0;
				    hsv.data[y * hsv.step + x*3 + 1] = 0;
				    hsv.data[y * hsv.step + x*3 + 2] = 0;
				} else {
					uchar p = level%100;
					hsv.data[y * depth8u.step + x*3 + 0] = (uchar)(p*2.56);
					hsv.data[y * depth8u.step + x*3 + 1] = 255;
					hsv.data[y * depth8u.step + x*3 + 2] = 255;
				}
			}
		}
		cvtColor(hsv, psCol, CV_HSV2BGR_FULL);
		imshow("color depth", psCol);
        int c = waitKey( 10 );
		if(c==27 || c=='q') g_context.quit();
		if(c=='c') imwrite("DS325.png", psCol);
    }
	g_dFrames++;
}

2012年7月26日木曜日

OpenNIでキャプチャしてOpenCVで処理してC#で表示する

ASUSのXtionPRO LiveからRGB画像とDepth画像を取得し,
OpenCVでちょこっと処理し,結果をC#で表示するプログラム.

今回はC++で基本的な作業を行なうDLLを作成し,C#でGUI部分を作成する.

  1. 基本DLL.OpenCVやOpenNIなどの外部ライブラリを使用する.
    • プロジェクト名:XCapBase
      • ソースファイル:XtionCaptureBase.cpp
      • ヘッダファイル:XtionCaptureBase.h
  2. ラッパークラスDLLその1.C++として,1のライブラリをくるみ,入出力のみ行う.
    • プロジェクト名:XCapWrapper
      • ソースファイル:XCaptureWrapper.cpp
      • ヘッダファイル:XCaptureWrapper.h
  3. ラッパークラスDLLその2.C++/CLIとして,2のライブラリをくるむ.C#から参照可能.
    • プロジェクト名:XCWrapCLI
      • ソースファイル:XCWrapCLI.cpp
      • ヘッダファイル:XCWrapCLI.h
  4. C#のWindows Formプログラム.
    • プロジェクト名:XtionCapture
1~3はDLLとして作成する.(Win32コンソールアプリケーションを選択し,ウィザードの最後でDLLにチェックを入れる)
いろいろと無駄があるかもしれないけれど,とりあえず動くのでOK.

XCapBase : 基本処理クラス

このクラスの中であれば,OpenCVやOpenNIの関数を自由に使える.
ただし,public 関数には,独自型を使わないようにしておく.

XtionCaptureBase.h
#ifdef XCAPBASE_EXPORTS
#define XCB_DECLSPEC __declspec(dllexport) // DLLにexport
#else
#define XCB_DECLSPEC __declspec(dllimport) // DLLをimport
#endif

#include <XnCppWrapper.h>
#include <opencv2/opencv.hpp>

class XCB_DECLSPEC CXtionCaptureBase
{
public:
 CXtionCaptureBase(void);
 ~CXtionCaptureBase(void);

 void capture(int *w, int *h, char **rgb, char **dmap);
private:
 xn::Context m_context;
 xn::ImageGenerator m_image;
 xn::DepthGenerator m_depth;
 cv::Mat m_RGBmat;
 cv::Mat m_Depthmat;
};
XtionCaptureBase.cpp
#include "XtionCaptureBase.h"

#pragma comment(lib,"openNI.lib")

#ifdef _DEBUG
#pragma comment (lib, "opencv_core242d.lib") 
#pragma comment (lib, "opencv_highgui242d.lib")
#pragma comment (lib, "opencv_imgproc242d.lib")
#else
#pragma comment (lib, "opencv_core242.lib") 
#pragma comment (lib, "opencv_highgui242.lib")
#pragma comment (lib, "opencv_imgproc242.lib")
#endif

using namespace xn;
using namespace cv;

CXtionCaptureBase::CXtionCaptureBase(void)
{
 m_context.Init(); 

 XnMapOutputMode mapMode; 
 mapMode.nXRes = 640; 
 mapMode.nYRes = 480; 
 mapMode.nFPS = 30; 
 
 m_image.Create(m_context); 
 m_image.SetMapOutputMode(mapMode); 

 m_depth.Create(m_context); 
 m_depth.SetMapOutputMode(mapMode); 

 m_context.StartGeneratingAll(); 

}

CXtionCaptureBase::~CXtionCaptureBase(void)
{
}

void CXtionCaptureBase::capture(int *w, int *h, char **rgb, char **dmap)
{
 ImageMetaData imageMD; 
 DepthMetaData depthMD; 

 m_context.WaitAnyUpdateAll();//wait and error processing

 m_image.GetMetaData(imageMD);
 m_depth.GetMetaData(depthMD);

 m_depth.GetAlternativeViewPointCap().SetViewPoint(m_image);  

 //Image
 Mat imageR(480, 640, CV_8UC3, (unsigned char*)imageMD.RGB24Data());
 cvtColor(imageR, m_RGBmat, CV_RGB2BGR);

 //Depth
 Mat depth16(480, 640, CV_16UC1, (unsigned short*)depthMD.Data());
 convertScaleAbs(depth16, m_Depthmat, 0.7, -300); //16bit -> 8bit

 *w = 640;
 *h = 480;
 *rgb = (char *)m_RGBmat.data;
 *dmap = (char *)m_Depthmat.data;
}

XCapWrapper : C++のラッパークラス

プロジェクトの「参照」に,XCapBaseを追加する.

XCapWrapper.h
#ifdef XCAPWRAPPER_EXPORTS
#define XCW_DECLSPEC __declspec(dllexport) // DLLにexport
#else
#define XCW_DECLSPEC __declspec(dllimport) // DLLをimport
#endif

class XCW_DECLSPEC CXCaptureWrapper
{
public:
 CXCaptureWrapper(void);
 ~CXCaptureWrapper(void);

 void capture(int *w, int *h, char **rgb, char **dmap);
};
XCapWrapper.cpp
#include "XCaptureWrapper.h"
#include "../XCapBase/XtionCaptureBase.h"

CXtionCaptureBase *cxcb;

CXCaptureWrapper::CXCaptureWrapper(void)
{
 cxcb = new CXtionCaptureBase();
}

CXCaptureWrapper::~CXCaptureWrapper(void)
{
 delete cxcb;
}

void CXCaptureWrapper::capture(int *w, int *h, char **rgb, char **dmap)
{
 cxcb->capture(w, h, rgb, dmap);
}

XCWrapCLI : C++/CLIのラッパークラス

「構成プロパティ」の「全般」で共通言語ランタイムサポート(/clr)を選択する.
プロジェクトの共通プロパティ「参照」に「System.Drawing」と「XCapWrapper」を追加.
キャプチャした画像をBitmapにして返す.

参考サイト:C++/CLIで動かしているOpenCVのCvImageをC#.NETのBitmapオブジェクトにして読み込む

ここで取り出そうとするビットマップ画像の幅は,4の倍数じゃないとエラーが起こる.

XCWrapCLI.h
#pragma once

using namespace System::Drawing;

namespace XCWrapper {
 public ref class XCWrapCLI
 {
 public:
  XCWrapCLI(void);
  void capture(void);
  System::Drawing::Bitmap^ getBitmap(void);
  System::Drawing::Bitmap^ getDepthmap(void);
 private:
  System::Drawing::Bitmap^ bitmap;
  System::Drawing::Bitmap^ depthmap;
 };
}
XCWrapCLI.cpp
#include "XCWrapCLI.h"
#include "../XCapWrapper/XCaptureWrapper.h"

namespace XCWrapper {
 CXCaptureWrapper *xcap;
 XCWrapCLI::XCWrapCLI(void)
 {
  xcap = new CXCaptureWrapper();
 }

 void XCWrapCLI::capture()
 {
  int w, h;
  char *prgb, *pdmap;
  xcap->capture(&w, &h, &prgb, &pdmap);

  //RGB Image
  this->bitmap = dynamic_cast<System::Drawing::Bitmap^>(
   gcnew System::Drawing::Bitmap(w, h, w*3,
   System::Drawing::Imaging::PixelFormat::Format24bppRgb,
   static_cast<System::IntPtr>(prgb)));

  //DepthMap
  this->depthmap = dynamic_cast<System::Drawing::Bitmap^>(
   gcnew System::Drawing::Bitmap(w, h, w,
   System::Drawing::Imaging::PixelFormat::Format8bppIndexed,
   static_cast<System::IntPtr>(pdmap)));
  System::Drawing::Imaging::ColorPalette^ pal = this->depthmap->Palette;
  for(int i=0; i<256; i++) pal->Entries[i] = Color::FromArgb(255,i,i,i);
  this->depthmap->Palette = pal;
 }

 System::Drawing::Bitmap^ XCWrapCLI::getBitmap(void)
 {
  return this->bitmap;
 }

 System::Drawing::Bitmap^ XCWrapCLI::getDepthmap(void)
 {
  return this->depthmap;
 }
}


XtionCapture : C#のプログラム

スタートボタンを押すと,キャプチャ画像を(スレッドを使って)表示する.

mainForm.cs
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;

using System.Threading;
using XCWrapper;

namespace XtionCapture
{
    public partial class mainForm : Form
    {
        private Thread trd;
        private bool moveFlag;
        XCWrapCLI xcap;

        public mainForm()
        {
            InitializeComponent();
            moveFlag = false;
            xcap = new XCWrapCLI();
        }

        private void mainForm_Load(object sender, EventArgs e)
        {
            trd = new Thread(new ThreadStart(this.ThreadTask));
            trd.IsBackground = true;
            trd.Start();
        }

        private void ThreadTask()
        {
            while (true)
            {
                if (moveFlag)
                {
                    xcap.capture();
                    pictureBox1.Image = xcap.getBitmap();
                    pictureBox2.Image = xcap.getDepthmap();
                }

                Thread.Sleep(30);
            }
        }

        private void StartButton_Click(object sender, EventArgs e)
        {
            moveFlag = true;

        }

        private void StopButton_Click(object sender, EventArgs e)
        {
            moveFlag = false;
        }
    }
}

2011年8月5日金曜日

OpenCVの関数を使って連立方程式を解く

未知数が3つの3元連立方程式

2a + 2b + 3c = 15
3a + 5b + 2c = 19
5a + 3b + 3c = 20

OpenCVのMatの逆行列を計算する関数を使って,

#include <opencv2/opencv.hpp>

using namespace cv;
using std::cout;
using std::endl;

int main()
{
    Mat matA = (Mat_<float>(3,3) << 2, 2, 3, 3, 5, 2, 5, 3, 3);
    Mat matB = (Mat_<float>(3,1) << 15, 19, 20);
    Mat matX(3, 1, CV_32FC1);

    matX = matA.inv()*matB;

    cout << "a=" << matX.at<float>(0) << endl;
    cout << "b=" << matX.at<float>(1) << endl;
    cout << "c=" << matX.at<float>(2) << endl;
}

ちゃんと,a=1, b=2, c=3の解が出る.

2011年7月28日木曜日

OpenCV C++ ヒストグラムクラス

OpenCV2.X向けのヒストグラムクラス。

1Dとカラーのやつは「OpenCV 2 Computer Vision Application Programming Cookbook」に載っているやつ。これを基に、2次元ヒストグラム画像作成など自分なりに手を入れているけれど、まだ中途半端。

#include <opencv2/opencv.hpp>

class Histogram1D {
private:
 int histSize[1];
 float hranges[2];
 const float* ranges[1];
 int channels[1];
public:
 Histogram1D(int hsize = 256) {
  histSize[0] = hsize; //number of bins
  hranges[0] = 0.0;  //min pixel value
  hranges[1] = 255.0;  //max pixel value
  ranges[0] = hranges;
  channels[0] = 0;  //by default, we lookup channel 0
 }

 void setHistSize(int hsize) {
  histSize[0] = hsize;
 }

 void setRanges(float min, float max) {
  hranges[0] = min;
  hranges[1] = max;
  ranges[0] = hranges;
  if(max-min<histSize[0]) {
   histSize[0] = (int)(max-min);
  }
 }

 cv::MatND getHistogram(const cv::Mat &image, int ch = 0) {
  cv::MatND hist;
  channels[0] = ch;
  cv::calcHist(&image, 1, channels, cv::Mat(), hist, 1, histSize, ranges);
  return hist;
 }

 cv::Mat getHistogramImage(const cv::Mat &image, int channel = 0) {
  cv::MatND hist= getHistogram(image, channel);

  double maxVal = 0;
  double minVal = 0;
  cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);

  cv::Mat histImg(histSize[0], histSize[0], CV_8U, cv::Scalar(255));

  int hpt = static_cast<int>(0.9*histSize[0]);

  for(int h=0; h<histSize[0]; h++) {
   float binVal = hist.at<float>(h);
   int intensity = static_cast<int>(binVal*hpt/maxVal);

   cv::line(histImg, cv::Point(h,histSize[0]), cv::Point(h,histSize[0]-intensity), cv::Scalar::all(0));
  }
  return histImg;
 }
};

class Histogram2D {
private:
 int histSize[2];
 float hranges[2];
 const float* ranges[2];
 int channels[2];
public:
 Histogram2D(int h1size=256, int h2size = 256) {
  histSize[0] = h1size;
  histSize[1] = h2size;
  hranges[0] = 0.0;
  hranges[1] = 255.0;
  ranges[0] = hranges;
  ranges[1] = hranges;
  channels[0] = 0;
  channels[1] = 1;
 }

 cv::MatND getHistogram(const cv::Mat &img1, const cv::Mat &img2) {
  cv::Mat planes[2];
  img1.copyTo(planes[0]);
  img2.copyTo(planes[1]);
  cv::MatND hist;
  cv::calcHist(planes, 2, channels, cv::Mat(), hist, 2, histSize, ranges);
  return hist;
 }

 cv::Mat getHistogramImage(const cv::Mat &img1, const cv::Mat &img2) {
  cv::MatND hist= getHistogram(img1, img2);

  double maxVal = 0;
  double minVal = 0;
  cv::minMaxLoc(hist, &minVal, &maxVal, 0, 0);

  cv::Mat histImg(histSize[0], histSize[1], CV_8U, cv::Scalar(0));

  for(int h=0; h<histSize[0]; h++) {
   for(int i=0; i<histSize[1]; i++) {
    float binVal = hist.at<float>(h,i);
    uchar intensity = static_cast<uchar>(binVal*255/maxVal);
    histImg.at<uchar>(h,i) = intensity;
   }
  }
  return histImg;
 }
};

class ColorHistogram {
private:
 int histSize[3];
 float hranges[2];
 const float* ranges[3];
 int channels[3];
public:
 ColorHistogram(int hSize = 256) {
  histSize[0] = histSize[1] = histSize[2] = hSize;
  hranges[0] = 0.0;
  hranges[1] = 255.0;
  ranges[0] = hranges;
  ranges[1] = hranges;
  ranges[2] = hranges;
  channels[0] = 0;
  channels[1] = 1;
  channels[2] = 2;
 }

 cv::MatND getHistogram(const cv::Mat &image) {
  cv::MatND hist;
  cv::calcHist(&image, 1, channels, cv::Mat(), hist, 3, histSize, ranges);
  return hist;
 }

 cv::SparseMat getSparseHistogram(const cv::Mat &image) {
  cv::SparseMat hist(3, histSize, CV_32F);
  cv::calcHist(&image, 1, channels, cv::Mat(), hist, 3, histSize, ranges);
  return hist;
 }
};

呼び出す側のソース例。
#include <opencv2/opencv.hpp>
#include "histogram.hpp"

#ifdef _DEBUG
#pragma comment(lib,"opencv_core230d.lib") 
#pragma comment(lib,"opencv_highgui230d.lib")
#pragma comment(lib,"opencv_imgproc230d.lib")
#else
#pragma comment(lib,"opencv_core230.lib") 
#pragma comment(lib,"opencv_highgui230d.lib")
#pragma comment(lib,"opencv_imgproc230d.lib")
#endif

using namespace cv;

using std::cout;
using std::endl;

int main()
{
 //モノクロ画像のヒストグラム表示
 Mat gray_img = imread("d:/test1.bmp", 0);
 imshow("gray", gray_img);

 Histogram1D h1(256);
 imshow("Histogram", h1.getHistogramImage(gray_img));

 //カラー画像のチャネル別ヒストグラム画像表示j
 Mat cimg = imread("d:/test2.bmp");
 imshow("color", cimg);

 imshow("B", h1.getHistogramImage(cimg));
 imshow("G", h1.getHistogramImage(cimg, 1));
 imshow("R", h1.getHistogramImage(cimg, 2));

 Mat hsvImg;
 cvtColor(cimg, hsvImg, CV_BGR2HSV_FULL);
 imshow("H", h1.getHistogramImage(hsvImg, 0));
 imshow("S", h1.getHistogramImage(hsvImg, 1));
 imshow("V", h1.getHistogramImage(hsvImg, 2));
 //h1.setRanges(0.0, 180.0);
 //imshow("H", h1.getHistogramImage(hsvImg));

 //2チャンネル2次元ヒストグラム画像表示
 std::vector<cv::Mat> planes;
 cv::split(cimg, planes);
 Histogram2D h2(128,128);
 imshow("2Dhist", h2.getHistogramImage(planes[0], planes[1]));

 //各ヒストグラムを得る。
 MatND hist1 = h1.getHistogram(gray_img);
 MatND hist2 = h2.getHistogram(planes[0], planes[1]);

 //cout << hist2 << endl;
 waitKey();
}

2011年7月15日金曜日

cv::MatのXMLファイルへの入出力

Matの内容をXML形式でファイルに出力 or ファイルから入力する方法のメモ.

2種類のMatを出力するプログラム
#include <opencv2/opencv.hpp>

using namespace cv;

int main()
{
 Mat matA = (cv::Mat_<double>(3,3) << 1, 0, 0, 0, 1, 0, 0, 0, 1);
 Mat matB = cv::Mat::ones(5, 5, CV_8U)*3;

 FileStorage cvfs("D:/test.xml", CV_STORAGE_WRITE);
 write(cvfs,"matA", matA);
 write(cvfs,"matB", matB);
}

出力されたxmlファイルの中身.
<?xml version="1.0"?>
<opencv_storage>
<matA type_id="opencv-matrix">
  <rows>3</rows>
  <cols>3</cols>
  <dt>d</dt>
  <data>
    1. 0. 0. 0. 1. 0. 0. 0. 1.</data></matA>
<matB type_id="opencv-matrix">
  <rows>5</rows>
  <cols>5</cols>
  <dt>u</dt>
  <data>
    3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3 3</data></matB>
</opencv_storage>

読み込む方のプログラム
int main()
{
    Mat ma, mb;

    FileStorage cvfs("D:/test.xml", CV_STORAGE_READ);
    FileNode node(cvfs.fs, NULL);
    read(node["matB"], mb);
    read(node["matA"], ma);
    std::cout << ma << std::endl;
    std::cout << mb << std::endl;
}

2011年7月14日木曜日

OpenCV2.3向けのVisualStudio2010プロパティシート

VisualStudio2010のプロパティシートで,OpenCV2.3向けの設定.
  • 解凍したwindows用バイナリ(OpenCV-2.3.0-win-superpack.exe)を「C:\OpenCV2.3」に配置
  • プラットフォームが「Win32(x86)」の場合,C:\Users\(ユーザー名)\AppData\Local\Microsoft\MSBuild\v4.0 のMicrosoft.Cpp.Win32.user.props を下記の通り編集.
<?xml version="1.0" encoding="utf-8"?>
<Project DefaultTargets="Build" ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
 <PropertyGroup>
  <ExecutablePath>C:\OpenCV2.3\build\bin;C:\OpenCV2.3\build\x86\vc10\bin;$(ExecutablePath)</ExecutablePath>
  <IncludePath>C:\OpenCV2.3\build\include;$(IncludePath)</IncludePath>
  <LibraryPath>C:\OpenCV2.3\build\x86\vc10\lib;$(LibraryPath)</LibraryPath>
 </PropertyGroup>
</Project>
対象プラットフォームがx64の場合には,Microsoft.Cpp.x64.user.propsを編集し,上記x86をx64に置き換えれば良い.