Androidで手乗りPerfume

AndroidでARを使って3DのPerfumeを手のひらに乗せてみる.

AndAR Model Viewer

AndAR Model Viewerというオープンソースで無料のAndroidアプリを使う.AndAR Model Viewerを使うと,Androidのカメラで撮影している映像のARマーカの上にリアルタイムで3Dモデルを投影出来る.3Dモデルはアプリ内で数種類のものが選択可能だが,自分で用意したobj形式の3Dモデルを表示することもできる.

手乗りPerfume

PerfumeのGlobalサイトから,Perfumeメンバーの3Dスキャンデータがobj形式でダウンロードできるのでそれを使う.

ARマーカを印刷してAndAR Model Viewerを起動すると,以下の画像のようにAndroidの画面上でPerfumeが手のひらに乗る.(無料の3Dモデリングソフトblenderを使って3人で一つのobjファイルにしたり,大きさや色を変えている.)

f:id:whoopsidaisies:20131227143539j:plain

あとは,衣装ちゃんとしたいのと踊らせたい.

開発中のOpenCV 3.0でPerfumeの3Dデータを表示する

OpenCV 3.0.0-dev Vizモジュール

OpenCV 3.0の開発ブランチを見ていたら,Vizという3D表示用のモジュールがあったので使ってみる.

3D表示には,Point Cloud Libraryとかでも使われているVTKというオープンソースのライブラリが使われている.

環境

Windows 7
Visual Studio Professinal 2012
OpenCV ハッシュ値:59cab94fc7a43ad3a39414232fd34d23815b8547
VTKのバージョン:5.10.1(6.0.0だとうまく出来なかった.出来た人教えてください)

Perfumeの3Dデータ表示

Perfume official global websiteで公開されている,Perfumeの3次元スキャンデータを表示するプログラムを作った.ファイルフォーマットはOBJファイルのものを選択.

#include <opencv2/viz.hpp>
#include <opencv2/viz/widget_accessor.hpp>

#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkOBJReader.h>

using namespace cv;
using namespace std;

// Objファイルから3Dデータを読み込み可能なウィジェット
class WObj : public viz::Widget3D
{
public:
	WObj(const string& filename)
	{
		// OBJファイルを読み込むクラスの生成
		vtkSmartPointer<vtkOBJReader> reader = vtkSmartPointer<vtkOBJReader>::New();
		reader->SetFileName(filename.c_str());
		reader->Update();

		// mapperにOBJファイルからデータを取り込む
		vtkSmartPointer<vtkPolyDataMapper> mapper = vtkSmartPointer<vtkPolyDataMapper>::New();
		mapper->SetInputConnection(reader->GetOutputPort());

		// actorの設定
		vtkSmartPointer<vtkActor> actor = vtkSmartPointer<vtkActor>::New();
		actor->SetMapper(mapper);

		// ウィジェットにactorをセットする
		viz::WidgetAccessor::setProp(*this, actor);
	} 
};

int main()
{
	// 表示用ウィンドウの生成
	viz::Viz3d perfumeWindow("Creating Widgets");
	
	// Perfumeのウィジェットを生成
	string filename1 = "Perfume_Global_Site_Project_003\\model1.obj";
	string filename2 = "Perfume_Global_Site_Project_003\\model2.obj";
	string filename3 = "Perfume_Global_Site_Project_003\\model3.obj";
	WObj objAchan(filename1), objNocchi(filename2), objKashiyuka(filename3);

	// Perfumeのウィジェットをウィンドウに表示
	perfumeWindow.showWidget("Achan", objAchan);
	perfumeWindow.showWidget("Nocchi", objNocchi);
	perfumeWindow.showWidget("Kashiyuka", objKashiyuka);

	// 軸を表示
	Affine3dcloud_pose = Affine3f().translate(Vec3f(-500.0f,-400.0f,-60.0f));
	perfumeWindow.showWidget("Coordinate Widget", viz::WCoordinateSystem(200), cloud_pose);

	// 表示のスタート
	perfumeWindow.spin();
}

VTKの書き方をほぼそのまま使える.実行すると下の画像ような感じで,かしゆか,あーちゃん,のっちが表示される.

f:id:whoopsidaisies:20131220225614p:plain

視点の移動

視点を移動したり,ウィジェットを移動・回転したりできるので,適当に視点移動をさせてみた.上のコードの「perfumeWindow.spin();」を以下のコードに置き換える.

float angle = 0.0;
while(!perfumeWindow.wasStopped())
{
	angle += CV_PI * 0.01f;

	// カメラの姿勢を生成
	Point3f cam_pos(100+2000*cos(angle),500,3000*sin(angle));
	Point3f cam_focal_point(3.0f,3.0f,2.0f)
	Point3f cam_y_dir(0.0f,1.0f,0.0f);
	Affine3f cam_pose = viz::makeCameraPose(cam_pos, cam_focal_point, cam_y_dir);

	// 視点をセット
	perfumeWindow.setViewerPose(cam_pose);

	perfumeWindow.spinOnce(1, true);

}

実行すると,下の動画のようにウィンドウ内の表示が動く.


OpenCV 3.0.0 devでPerfumeの3次元データ表示 - YouTube

C# ComboBoxにenumの値を表示してコントロールのプロパティにバインドする

例として,PictureBoxSizeMode列挙体のメンバをコンボボックスに表示し,選択された値がピクチャボックスのSizeModeプロパティに設定されるようにする.

comboBox1.DataSource = Enum.GetValues(typeof(PictureBoxSizeMode));
comboBox1.DataBindings.Add(new Binding("SelectedItem", pictureBox1, "SizeMode"));

これで,コンボボックスの選択によってピクチャボックスの画像表示方法を変更できるようになる.

C# アンチエイリアシングしないPicturebox

画像がぼやける

下の画像みたいに,10x10画素の画像を拡大してPictureBoxコントロールに標示させたい.
f:id:whoopsidaisies:20131220063327p:plain
そのまま表示させようとすると,以下みたいにアンチエイリアシングされてぼやけてしまう.
f:id:whoopsidaisies:20131220063351p:plain

画像を縮小するとピクセルにグラデーション?がかかる」にあるように一度Graphicsを作成してからInterpolationModeとPixelOffsetModeを指定すればアンチエイリアシングしないで表示はできる.

が,わざわざGraphicsを作成するというのも面倒なので,そうしなくてもアンチエイリアシング処理しないPictureBoxコントロールを作る.

InterpolationModeを指定可能なPictureBox

Graphicsで画像の拡大・回転時の画素の補間方法はInterpolationModeで指定可能だが,これをPictureBoxでも指定できるようにする.

以下のように,PictureBoxを継承したコントロールを作ってやる.InterpolationModeプロパティをNearestNeighborに指定すれば,アンチエイリアシングなしで画像が表示される.

using System;
using System.Windows.Forms;
using System.Drawing.Drawing2D;
using System.ComponentModel;

namespace Whoopsidaisies
{
    class InterpolatedPictureBox : PictureBox
    {
        private InterpolationMode interpolation = InterpolationMode.Default;

        [DefaultValue(typeof(InterpolationMode), "NearestNeighbor"),
        Description("The interpolation used to render the image.")]
        public InterpolationMode Interpolation
        {
            get { return interpolation; }
            set
            {
                if (value == InterpolationMode.Invalid)
                    throw new ArgumentException("\"Invalid\" is not a valid value."); // (Duh!)

                interpolation = value;
                Invalidate(); // Image should be redrawn when a different interpolation is selected
            }
        }

        protected override void OnPaint(PaintEventArgs pe)
        {
            pe.Graphics.InterpolationMode = interpolation;
            pe.Graphics.PixelOffsetMode = PixelOffsetMode.Half;

            base.OnPaint(pe);
        }
    }
}

OpenCVで背景差分

※追記:OpenCV 3.4.1版の記事は下記リンク。使えるアルゴリズムが増えている。

whoopsidaisies.hatenablog.com


以降はOpenCV 2.4.7のサンプル。

背景差分

固定カメラで移動物体の検出をするのに有効な手法.OpenCV2.4.7では混合正規分布(Mixture of Gaussian Distribution, MoG)によるアルゴリズム,Godbehere,Matsukawa,Goldbergによるアルゴリズム(GMG)が用意されているので試してみる.

サンプルコード

BackgroundSubtractorMOGクラス,BackgroundSubtractorMOG2クラスおよびBackgroundSubtractorGMGクラスで背景差分の計算ができる.

アルゴリズムによってパラメータ等が違うが,基本的な計算は共通の書き方で使用できる.以下にサンプルコードを示す.

#include <opencv2/opencv.hpp>

void main()
{
	cv::VideoCapture cap = cv::VideoCapture("test.avi");

	// 背景差分計算用オブジェクトの生成
	cv::BackgroundSubtractorGMG backGroundSubtractor;
	//cv::BackgroundSubtractorMOG backGroundSubtractor;
	//cv::BackgroundSubtractorMOG2 backGroundSubtractor;

	while (cv::waitKey(1) == -1)
	{
		cv::Mat frame, foreGroundMask, output;

		// 画像取得
		cap >> frame;
		if (frame.empty())
			break;

		// マスク画像の取得
		backGroundSubtractor(frame, foreGroundMask);
	
		// 入力画像にマスク処理を行う
		cv::bitwise_and(frame, frame, output, foreGroundMask);

		cv::imshow("output", foreGroundMask);
	}
}

C#でExcelファイルを読み込む ClosedXmlを使用

ClosedXML

C#でExcelファイルを読み込む.以下の記事で紹介したClosedXMLを使用する.インストールはNuGetから可能.
C#でExcelファイルを作成する ClosedXmlを使用 - whoopsidaisies's diary

サンプルコード

ファイルを読み込んで,セルの値と数式を表示するプログラムを以下に載せる.すごく簡単.関数を使っているセルとかも計算後の値が表示される.

// ワークブックの読み込み
using (var wb = new ClosedXML.Excel.XLWorkbook(@"test.xlsx"))
{
    // すべてのシートについてループ
    foreach (var ws in wb.Worksheets)
    {
        Console.WriteLine(ws.Name);
        // 値が入っているセルについてループ
        foreach (var cell in ws.CellsUsed())
        {
            // セルのアドレス,値,数式を使ってれば数式を表示
            Console.WriteLine("{0} : {1} : {2}",
               cell.Address, cell.Value, cell.FormulaA1);
        }
    }
}

使用例

Sheet1
A1 : 1 :
B1 : 2 :
C1 : 3 :
D1 : 6 : SUM(A1:C1)
A2 : 4 :
B2 : 5 :
C2 : 6 :
D2 : 0.666666666666668 : VARP(A2:C2)
Sheet2
A1 : 山田 太郎 :
B1 : 山田 : LEFT(A1,FIND(" ",A1)-1)
Sheet3
A1 : 2013/12/16 20:01:39 : NOW()
A2 : 5 : Sheet1!A1+Sheet1!A2

C#で文字認識(tesseract-ocrのラッパを使う方法)

tesseract-ocr

tesseract-ocrはオープンソースのOCR(光学文字認識)エンジン.60以上の言語に対応しており,日本語の文字認識も可能.

A .Net wrapper for tesseract-ocr

tesseract-ocrはapiが用意されているが,そのC#ラッパー「A .Net wrapper for tesseract-ocr」を使う.

インストールはNuGetから行える.プロジェクトの右クリックメニューから「Nugetパッケージの管理」をクリックし,出てきたダイアログで「ocr」で検索すれば「A .Net wrapper for tesseract-ocr」が出てくるのでインストールする.

言語データのダウンロード

tesseract-ocrでは言語ごとに学習済みのデータファイルが用意されており,それを読み込む必要がある.以下のページからダウンロード可能である.

Downloads - tesseract-ocr - An OCR Engine that was developed at HP Labs between 1985 and 1995... and now at Google. - Google Project Hosting

英語ならtesseract-ocr-3.02.eng.tar.gz,日本語ならtesseract-ocr-3.02.jpn.tar.gzをダウンロードして解凍する.

解凍すると○○.traineddataというファイルが出てくる.各言語のファイルを同じフォルダに入れておくと便利.

サンプルコード

以下に使用例を示す.Bitmapクラスを使用しているため,コンソールでプロジェクトを作った場合はSystem.Drawing.dllへの参照を追加する必要があるので注意.

// OCRを行うオブジェクトの生成
//  言語データの場所と言語名を引数で指定する
var tesseract = new Tesseract.TesseractEngine(
    @"C:\tessdata", // 言語ファイルを「C:\tessdata」に置いた場合
    "jpn");         // 英語なら"eng" 「○○.traineddata」の○○の部分

// 画像ファイルの読み込み
var img = new System.Drawing.Bitmap(@"gazou.jpg");
// OCRの実行と表示
var page = tesseract.Process(img);
System.Console.Write(page.GetText());
英語の使用例

f:id:whoopsidaisies:20131216170917j:plain
入力画像

結果

Hamlet: If it be now, ‘tis not to come; if it be not to come, it will
be now; if it be not now, yet it will come: the readiness is all.
Since no man, of aught the leaves, Knows: what is’ t to leave
betimes? Let be.

きれいな画像とはいえ,完璧.

日本語の使用例

入力画像
f:id:whoopsidaisies:20131216170659j:plain

結果

来るぺきもの【ま`今来な〈ともし丶ずれ[ま来る。 今来れ【ま`後ーニ[ま来な
し丶。後江ニ来な【ナれ【ま`今来るナ三【ナのこと。肝腎なのーま覚悟ナ三。 L`つ死
んナ三らし丶し丶ヵ丶` そんなこと【ま考えてみナニところで` 言佳ーニも半ーーり【ましな
し`。 戸斤詮` ぁなナニイ壬せさ。 (ノ 丶ムレ`ント` 第5幕第2場)

日本語は結構厳しい.フォントにもよりそう.自分で学習データ作ったほうが良いのかも.

ホワイトリスト

ホワイトリストを指定すれば,文字の種類を限定でき読み取り精度が上がる可能性がある.以下のようにTesseract.SetVariableメソッドでホワイトリストを指定できる.

数字のみの場合
tesseract.SetVariable("tessedit_char_whitelist", "1234567890");
アルファベットのみの場合
tesseract.SetVariable("tessedit_char_whitelist", "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ");

学習

学習のインターフェースはついていないようなので,ラッパではなくてtesseract-ocr本体を使う必要がありそう.
日本語だと以下のページが参考になりそう.
日々是酩酊: TesseractOCR トレーニング その1
日々是酩酊: TesseractOCR トレーニング その2