C#で地球を表示する(VTKのEarthSourceによる方法)

C#でVTKのvtkEarthSourceクラスによって地球を表示させる.

ActiViz

ActiVizは,3Dコンピュータグラフィックスの可視化ライブラリVTK(The Visualization ToolKit)のC#ラッパである.NuGetからインストールできる.簡単な解説は以下のページを参照.
C#で3Dモデル表示(VTKのC#ラッパActiVizによる方法) - whoopsidaisies's diary

RenderWindowControl

NuGetからActiVizをインストールし,RenderWindowControlをフォームに追加する.

vtkEarthSource

VTKにはvtkEarthSourceという,地球が表示されるオブジェクトがあるので使う.ソースコードは以下のようになる.

using (var earth = new Kitware.VTK.vtkEarthSource())
using (var mapper = new Kitware.VTK.vtkCompositePolyDataMapper())
using (var actor = new Kitware.VTK.vtkActor())
{
    mapper.SetInputConnection(earth.GetOutputPort());
    actor.SetMapper(mapper);
    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddActor(actor);
}

実行すると以下のように地球が表示される.ドラッグで視点変更が出来る.
f:id:whoopsidaisies:20140209185406p:plain

C++でLINQライクな処理(LINQ for C++による方法)

LINQ for C++

LINQは.NET Framework3.5から組み込まれた機能で,C#等の言語でSQLのデータベース操作のようなことを可能とする.「C# is LINQ」と言う方までいほどに便利な機能.

しかし,C++/CLIはLINQに対応する予定はないとのこと.そこで,LINQに似た機能をC++に提供してくれるのがLINQ for C++である.というわけでLINQ for C++を使ってみる.

導入

LINQ for C++はヘッダファイルひとつで実装されているので,以下のページからダウンロードしてきてインクルードするだけで使用できる.NuGetからのインストールも取得可能である.

LINQ for C++ - Home

使用例1

以下に使用例を示す.LINQ for C++を使って,テストの成績のうち国語と英語がともに50点以上の人のリストを取得している.

普通の配列に対して使うことが出来る.コード補完も微妙だし,型明記しなくちゃいけないし,メソッド形式ないしでC#のLINQほど軽快には書けないものの,便利ではある.

#include <cpplinq.hpp>

void main()
{
	using namespace cpplinq;

	struct Seiseki
	{
		char Name[256];
		int Kokugo;
		int Sansuu;
		int Eigo;
		int Rika;
		int Shakai;
	};

	Seiseki seisekiList[] = 
	{
		{"Yamada",98,78,61,83,70},
		{"Tanaka",20,33,16,39,48},
		{"Yoshida",72,65,92,13,26},
		{"Suzuki",49,82,55,11,96},
		{"Sasaki",83,47,61,23,60}
	};
	
	auto result = from_array(seisekiList)
		>> where([](Seiseki s) {return s.Kokugo > 50 && s.Eigo > 50;})
		>> to_vector();

	for (int i = 0; i < result.size();  ++ i)
		printf("%s : 国語%d 英語%d\n", result[i].Name, result[i].Kokugo, result[i].Eigo);
}

結果は以下のように表示される.

Yamada : 国語98 英語61
Yoshida : 国語72 英語92
Sasaki : 国語83 英語61

使用例2

無理やりOpenCVで使ってみる.BRISKで特徴点抽出し,オリエンテーションの向きによって表示する際の色を変えている.

#include <opencv2/opencv.hpp>
#include <cpplinq.hpp>

void main()
{
	using namespace cpplinq;

	auto image = cv::imread("Penguins.jpg");

	auto detector = cv::FeatureDetector::create("BRISK");
	std::vector<cv::KeyPoint> keyPoints;
	detector->detect(image, keyPoints);
	auto key1 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 0 <= k.angle && k.angle < 90;})
		>> to_vector();
	auto key2 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 90 <= k.angle && k.angle < 180;})
		>> to_vector();
	auto key3 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 180 <= k.angle && k.angle < 270;})
		>> to_vector();
	auto key4 = from(keyPoints)
		>> where([](cv::KeyPoint k) { return 270 <= k.angle && k.angle < 360;})
		>> to_vector();

	cv::drawKeypoints(image, key1, image, cv::Scalar(255,0,0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::drawKeypoints(image, key2, image, cv::Scalar(0,255,0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::drawKeypoints(image, key3, image, cv::Scalar(0,0,255), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::drawKeypoints(image, key4, image, cv::Scalar(255,255,0), cv::DrawMatchesFlags::DRAW_RICH_KEYPOINTS);
	cv::imshow("Key Point", image);
	while(cv::waitKey(1) == -1);
}

f:id:whoopsidaisies:20140117010914p:plain

C# 名前付きの色一覧を配列に取得してRGB値をDataGridViewに表示する

Color構造体

.NET FrameworkのColor構造体はRGB値で色の指定もできるが,あらかじめRGB値が指定されていて名前を指定して使える色がある.「Color.Red」とか「Color.Green」といった感じで使うことが出来る.

本記事では,名前が付いた色の一覧を取得してColor構造体の配列に取得,さらにRGB値と色をDataGridViewに表示する方法を紹介する.

名前付きの色の取得

名前付きの色は,Color構造体の静的プロパティとして定義されている.色の一覧をColor構造体の配列に格納するコードは以下のようになる.

var colors = typeof(Color).GetProperties(BindingFlags.Static | BindingFlags.Public)
    .Select(p => Color.FromName(p.Name))
    .ToList();

1行目では,GetPropertiesメソッドを使って,Color構造体のパブリックな静的プロパティの型情報の一覧を取得している.BindingFlags構造体を使って取得するプロパティの検索方法を指定できるが,StaticとPublicを指定することでパブリックな静的プロパティを取得している.

2行目では,LINQSelectメソッドとhttp://msdn.microsoft.com/ja-jp/library/system.drawing.color.fromname(v=vs.110).aspx:Color.FromaNameメソッドを使って色の名前から,その色のColorオブジェクトを生成している.

3行目で,結果をListに変換している.(ListとかArrayとかに変換したほうが後の処理が楽なので)

DataGridViewで表示

取得した色の一覧をDataGridViewコントロールに表示する.

dataGridView1.DataSource = colors;

上記の一行を追加すると以下のように色の名前とRGB値が表示される.
f:id:whoopsidaisies:20140115195242p:plain

表示するプロパティの選択

上の画像だと「IsKnownColor」とかの余計なプロパティも表示されているので,表示しないようにする.

foreach (DataGridViewColumn col in dataGridView1.Columns)
    if (col.Name != "R" && col.Name != "G" && col.Name != "B" && col.Name != "Name")
        col.Visible = false;

DataGridViewの各列はVisibleプロパティによって表示・非表示を切り替えられるので,RGBと名前以外の列を非表示に設定している.

f:id:whoopsidaisies:20140115200155p:plain

セルの色付け

名前とRGB値だけだとどんな色か想像がつきにくいので,セルに色を付ける.ついでに名前が表示されている列を先頭にする.

for (int i = 0; i < colors.Count; ++i)
    dataGridView1["Name", i].Style.BackColor = colors[i];
dataGridView1.Columns["Name"].DisplayIndex = 0;

DataGridViewの各セルStyleプロパティによって色やフォント等の書式を指定出来る.ここではBackColorプロパティによって背景色を指定している.

また,DisplayIndexプロパティによって名前の表示列を先頭にしている.

実行すると以下のように名前のセルに色がついて表示される.(Transparentのところは後ろのウィンドウが表示されている.)

f:id:whoopsidaisies:20140115201854p:plain

ソースコード

上記コードをまとめると以下のようになる.

var colors = typeof(Color).GetProperties(System.Reflection.BindingFlags.Static | System.Reflection.BindingFlags.Public)
    .Select(p => Color.FromName(p.Name))
    .ToList();

dataGridView1.DataSource = colors;

foreach (DataGridViewColumn col in dataGridView1.Columns)
    if (col.Name != "R" && col.Name != "G" && col.Name != "B" && col.Name != "Name")
        col.Visible = false;

for (int i = 0; i < colors.Count; ++i)
    dataGridView1["Name", i].Style.BackColor = colors[i];
dataGridView1.Columns["Name"].DisplayIndex = 0;

C#でOpenCVを使う(C++/CLIによる方法)

OpenCVラッパクラス

C#でOpenCVを手軽に使うには

OpenCvSharpをつかう その17(NuGetで導入) - schima.hatenablog.com
C#でOpenCVを動かす~NuGetでOpenCV.Netをインストール - whoopsidaisies's diary

のページのように,NuGetからC#ラッパをインストールして使う方法があるが,必要な機能がラッピングされていないことがある.

このページでは,C++/CLIでOpenCVの必要な機能だけ持つラッパクラスを自分で作り,C#から呼び出す方法を紹介する.

C++/CLI

通常のC++のライブラリをC#から呼び出そうとするとDllImport属性で宣言が必要だったりして面倒であるが,C++/CLIで作ったDLLであれば参照に追加するだけで使用できる.

手順

手順は以下の通りである.

C#プロジェクト作成

実行されるC#のプロジェクト

C++/CLIのDLL用プロジェクト作成

OpenCVをラップするクラス用のプロジェクト

プロジェクトの参照の設定

作成したクラスをC#から読み込むための設定

OpenCVのインストール

OpenCVのインストール

C++/CLIでラッパクラスの作成

OpenCVによる処理部分の実装

C#のプロジェクトの出力パスの変更

OpenCVのDLLの場所とかの関係上の設定

C#から呼び出す

C#からの呼び出し部分とGUIの実装


以下に各手順の詳細を記す.

C#プロジェクト作成

Visual Studioで新しいプロジェクトを作成する.
f:id:whoopsidaisies:20140112101313p:plain

C++/CLIのDLL用プロジェクト作成

ソリューションを右クリックして,「追加」の「新しいプロジェクト(N)...」でC++/CLIのDLL作成用のプロジェクトを作成する.「Visual C++」の「CLR」の「クラス ライブラリ」を選択.
f:id:whoopsidaisies:20140112101334p:plain

プロジェクトの参照の設定

C++/CLIで作成したDLLをC#から呼び出すための参照設定を行う.C#のプロジェクト内の「参照設定」を右クリックし「参照の追加(R)...」を選択する.開いたダイアログで「ソリューション」の「プロジェクト」を選択し,C++/CLIのプロジェクトの名前のところにチェックを入れる.
f:id:whoopsidaisies:20140112101620p:plain
f:id:whoopsidaisies:20140112102324p:plain

OpenCVのインストール

C++/CLIのプロジェクトを右クリックし「NuGet パッケージの管理」を選択.ダイアログで「オンライン」を選択し,検索窓で「OpenCV」を検索,インストールする.
f:id:whoopsidaisies:20140112101806p:plain

C#のプロジェクトの出力パスの変更

NuGetでインストールしたOpenCVのDLLが,C#プロジェクトで作成した実行ファイルから参照できる位置に配置されないため,実行ファイルの出力パスを変えてやる.C#のプロジェクトを右クリックし「プロパティ(R)」を選択.「ビルド」の「出力パス」を「..\Debug\」に変える(Releaseモードなら..\Release\).
f:id:whoopsidaisies:20140112110351p:plain
この手順を忘れると,実行時に以下のようなエラーメッセージが表示されてOpenCVの呼び出しに失敗する.
f:id:whoopsidaisies:20140112110400p:plain

C++/CLIでラッパクラスの作成

C++/CLIでOpenCVの処理のコードを書く.C++/CLIのプロジェクトを作成したときに色々とファイルが生成されているので,そのうちの「OpenCvClrLibrary.h」(プロジェクト名.h)を以下のようにする.画像をファイルから読み込んで,BRISK特徴点を抽出して表示するサンプルである.

// OpenCvClrLibrary.h

#pragma once
#include <opencv2/opencv.hpp>

using namespace System;

namespace OpenCvClrLibrary {

	public ref class TestClass
	{
	public:
		static void TestMethod()
		{
			auto image = cv::imread("Penguins.jpg");

			auto brisk = cv::BRISK();
			std::vector<cv::KeyPoint> keyPoint;
			brisk.detect(image, keyPoint);
			cv::drawKeypoints(image, keyPoint, image);

			cv::imshow("image", image);
			while (cv::waitKey(1) == -1);
			cv::destroyAllWindows();
		}
	};
}

「ref class」はC++/CLI用のクラス.詳細は省略するが,こういう書き方をすることでC#でそのまま使えるクラスを作れる.
「auto」はc++11から追加された機能で,変数の初期値から型推論を行ってくれる.Visual Studio 2012以降では使えるみたい.横に長くなりがちなOpenCV(に限らないけど)のコードではこれを使えるのは嬉しい.

C#から呼び出す

C#から上記の特徴点抽出処理を呼び出すコードを以下のように書く.

using System;
using System.Windows.Forms;

namespace OpenCvFromCSharp
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void button1_Click(object sender, EventArgs e)
        {
            OpenCvClrLibrary.TestClass.TestMethod();
        }
    }
}

Buttonコントロールを配置して,クリックすると特徴点が書き込まれた画像が以下のように表示される.

f:id:whoopsidaisies:20140112104341p:plain

C#でAI,PSD,EPS,SVG,PDF等の画像ファイルを開く(ImageMagickのC#ラッパMagick.NETによる方法)

Magick.NET

100種類以上の画像を操作したり表示したりできるライブラリImageMagickのC#ラッパMagick.NETを使って,C#でAI,PSD,EPS,SVG,PDF等の画像ファイルを開く.

インストール

NuGet経由でインストール可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから「Magick.NET」を検索してインストール.

f:id:whoopsidaisies:20140108210712p:plain

プラットフォームを変える

プロジェクトのプラットフォームをインストールしたパッケージ(「x64」か「x86」)に変える.「Any CPU」だと動かない場合があるので注意.

サンプルコード

以下にepsファイルを開いて,PictureBoxに表示するサンプルを示す.ファイル名の部分を変えれば,eps以外のファイルも読み込むことが出来る.

var magickImage = new ImageMagick.MagickImage(
    @"blossom tree.eps",
    new ImageMagick.MagickReadSettings() { Density = new ImageMagick.MagickGeometry(300, 300) });
pictureBox1.Image = magickImage.ToBitmap();

ImageMagick.MagickGeometry(300, 300)の数字の部分を大きくすると,AI,EPS,PDF等のベクター画像の読み込み後の解像度を高くすることが出来る.

以下のように,ベクター画像を高解像度にすると拡大してもきれいに表示出来る.
f:id:whoopsidaisies:20140108211811j:plain
f:id:whoopsidaisies:20140108211819p:plain

C#でDICOMファイルからボリュームレンダリング ActiViz(VTK)による方法

C#でActiVizを使って,DICOM形式画像の連番ファイルを読み込みボリュームレンダリングにより3D画像を生成・表示する.

ActiViz

ActiVizは,3Dコンピュータグラフィックスの可視化ライブラリVTK(The Visualization ToolKit)のC#ラッパである.NuGetからインストールできる.簡単な解説は以下のページを参照.
C#で3Dモデル表示(VTKのC#ラッパActiVizによる方法) - whoopsidaisies's diary

ボリュームレンダリング

ActiVizには,DICOM画像を読み込むクラスもボリュームレンダリングを行うクラスも用意されているので,簡単にDICOM画像からの3D画像の表示を行える.以下にサンプルコードを示す.

using (var dicomReader = new vtkDICOMImageReader())
using (var mapper = new vtkFixedPointVolumeRayCastMapper())
using (var opacity = new vtkPiecewiseFunction())
using (var property = new vtkVolumeProperty())
using (var volume = new vtkVolume())
{
    // DICOM画像が入っているフォルダを指定
    dicomReader.SetDirectoryName(@"DICOM");
    dicomReader.Update();
    // MapperにDICOM画像を読み込んで3Dボリュームへ登録
    mapper.SetInputConnection(dicomReader.GetOutputPort());
    volume.SetMapper(mapper);
    // 不透明度と補間アルゴリズムの設定
    opacity.AddSegment(0, 0, 3900, 1);
    property.SetScalarOpacity(opacity);
    property.SetInterpolationTypeToLinear();
    volume.SetProperty(property);
    // 表示用ウィンドウへの3Dボリュームの登録
    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddVolume(volume);
}

VTKのWIKIページにDICOMのサンプルデータがあったので表示させてみた結果を以下に示す.プログラム上では,マウスドラッグにより視点の変更が可能である.

f:id:whoopsidaisies:20131230234937p:plain

C#で3Dモデル表示(VTKのC#ラッパActiVizによる方法)

ActiViz

C#で3Dモデル表示する方法はたくさんあると思うが,今回は,3Dコンピュータグラフィックスの可視化ライブラリであるVTK(The Visualization ToolKit)の,C#ラッパ「ActiViz」を使った方法について書く.

ActiVizおよびVTKの良さそうな点を以下にざっとあげる.

  • オープンソース
  • NuGet経由で簡単インストール
  • C#のWindows フォーム アプリケーション用のコントロールが用意されている
  • 表示するだけなら結構短いコードで書ける
  • VTKは次期OpenCVとかPCL(Point Cloud Library)とかでも使われているので,書き方を覚えておくと便利?

という感じで,手軽かつそれなりに汎用性がありそう.

インストール

NuGet経由でインストール可能.プロジェクトを右クリックして「NuGetパッケージの管理」を選択する.オンラインから「ActiViz」を検索.x86とx64があるが,x64だとコントロール追加できなかったりするので,x86が無難かも(自分の環境だけ?他の環境では確認していないので誰か詳しい方教えてくれると助かります).

f:id:whoopsidaisies:20131228121622p:plain

RenderWindowControlの追加

3D表示用のコントロールが用意されているので追加する.

ツールボックスの適当なところで右クリックして「アイテムの選択(I)...」を選択.「.NET Framework コンポーネント」タブの「参照(B)...」ボタンを押して,「(ソリューションフォルダ)\packages\Activiz.NET.x86.5.8.0\lib\net20\Kitware.VTK.dll」を選択したらOK.

 

すると,ツールボックスに「RenderWindowControl」ができるので,Formに配置する.

f:id:whoopsidaisies:20131228121729p:plain

サンプルコード

3Dモデルファイルの読み込み

以下にPLYファイルを読み込んで表示するサンプルを示す.MapperとかActorとか出てくるが,そこら辺の説明はこことかを参照.

using (var reader = new Kitware.VTK.vtkPLYReader())
using (var mapper = new Kitware.VTK.vtkCompositePolyDataMapper())
using (var actor = new Kitware.VTK.vtkActor())
{
    // 表示させたい3Dモデルファイル
    reader.SetFileName(@"bun_zipper.ply");
    // Mapperにオブジェクトを写像する
    mapper.SetInputConnection(reader.GetOutputPort());
    // ActorにMapperをセットする
    actor.SetMapper(mapper);
    // 描画ウィンドウにActorを追加する
    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddActor(actor);
}

上記コードを,フォームロードイベントとかに書いておくと,以下のように3Dモデルが表示される.(plyファイルはStanford Bunnyを使用)
描画ウィンドウ上で左クリックや右クリックしてドラッグすると,視点の変化を行える.

f:id:whoopsidaisies:20131228123539p:plain

ply以外のファイル

上記コードの

using (var reader = new Kitware.VTK.vtkPLYReader())

のvtkPLYReaderの部分を変えてやると,ply以外の3Dモデルファイルも読み込める.以下に,その一例を示す.

  • vtkOBJReader … wavefrontのobjファイル
  • vtkSimplePointsReader … スペース区切りの3D点群データ
  • vtkParticleReader … カンマ区切りの3D点群データ

他にも様々な形式に対応している.

複数モデルの表示

複数のモデルファイルを読み込んで表示するサンプルを示す.以下のようにモデルの数だけActorを作ってやるとよい.

private void ShowMultipleModels()
{
    // objファイルからモデルを読み込んでActorを生成する
    var actor1 = ActorFromObjFile("model1.obj");
    var actor2 = ActorFromObjFile("model2.obj");
    var actor3 = ActorFromObjFile("model3.obj");

    // 描画ウィンドウにActorを追加する
    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddActor(actor1);
    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddActor(actor2);
    renderWindowControl1.RenderWindow.GetRenderers().GetFirstRenderer().AddActor(actor3);
}

private static Kitware.VTK.vtkActor ActorFromObjFile(string filename)
{
    var actor = new Kitware.VTK.vtkActor();
    using (var reader = new Kitware.VTK.vtkOBJReader())
    using (var mapper = new Kitware.VTK.vtkCompositePolyDataMapper())
    {
        reader.SetFileName(filename);
        mapper.SetInputConnection(reader.GetOutputPort());
        actor.SetMapper(mapper);
    }
    return actor;
}

ShowMultipleModelsを呼び出すと,以下のように3Dモデルが3つ表示される.(objファイルはPerfumeのグローバルサイトのデータを使用)

f:id:whoopsidaisies:20131228130248p:plain

その他

VTK/Examples - KitwarePublic
にサンプルコードが豊富に用意されている.