2011年8月21日日曜日

たまには画像処理

久しぶりの更新です。
いろんな事情により画像処理の練習をしようと思ってSIFTを組んでみたのですが、
プログラムにマッチングさせるまでもなくいろいろとおかしいです。
いろいろと調べて間違ってるところを直さないと・・・





2011年7月22日金曜日

Kinect.Toolkit にジェスチャのテンプレートを追加する

Eternal CodingCode PlexでKinectでジェスチャの認識をするためのライブラリが公開されてます。

記事を読むのが一番早いと思いますが、ライブラリでは点の軌跡をそのまま解析する方法と、テンプレートを予め用意しておいてそれと比較する二種類の方法があるようです。

今回はそのテンプレートを自作してみようと思います。
どういうことかというと、公開されているサンプルの

この部分に表示されるものを自分で作ってみます。
(circleKB.saveに当たるものを作る。)

自作するジェスチャは次のような感じ。


今回はこのジェスチャの軌跡をテキストファイルに手打ちして、それを読み込んで.saveファイルを作成してみました。
大体の手順はTemplateGestureDetectorのAddメソッドに座標を追加していって、それをSaveStateメソッドでファイルに保存します。
以下が正確ではないですがコードにしたものです。

TemplateGestureDetector gd = new TemplateGestureDetector(null);
gd.StartRecordTemplate()
for (i = 0 ; i < n ; i++) {
gd.add(position , kinect.SkeletonEngine);
}
gd.EndRecordTemplate()
gd.SaveState(stream)


作成した結果がこちら


公開されているサンプルはほとんどいじっていないので、自作したテンプレートの認識結果を載せようとすると自分が写ってしまうので載せませんが、このとおりの動きをしたらきちんとジェスチャが認識されました。
最初に予定していたのに比べると傾いてしまいましたが、これは説明文をもっときちんと読むことで解消できるかと思います。

プロジェクトはこちらDownLoad

2011年7月9日土曜日

Kinect + OpenCVSharp フィルタ処理

Kinectから取ってきた深度画像とOpenCVを組み合わせて何かできないかと考えてます。
一応Kinectから取ってこれる画像は生の実画像に近いと思うのでOpenCVSharpに慣れるのもかねてフィルタ処理をしてみました。

画像中左上がKinectから取得してそのままの画像。
右上がガウシアンフィルタ。左下がブラー処理、右下がメディアンフィルタです。
これを見る限りだとフィルタ処理しても全然変わらないですね・・・
それでも若干ですがメディアンフィルタがエッジを保ちつつノイズを除去できているようには感じます。
そもそもフィルタ処理が必要かどうかという部分もありますが、使うならメディアンフィルタになるのかな・・・?

2011年7月2日土曜日

Kinect SDK + ARっぽいもの

前に、カメラからの距離の変化を利用してARっぽいことができないかと考えたので、その第一段階のようなものです。

Kinectから得られた最初のDepthFrameを基準とし、それと比べて距離が一定以上遠くなった領域の大体の重心に球体を描画しています。
もうちょっとちゃんと位置合わせしたりしないといけないですね。
まだまだ付け足さないといけない部分も多いですがこれをもとにいろいろいじろうと思います。

2011年6月26日日曜日

KinectSDK + Formアプリケーション ちょっとお遊び

現在本当は全身マウスっぽいものをやろうとしているのですが、XNA特有の問題を知らなかったりして時間を食って、現在Formアプリで作ってます。
大体は出来上がったのですが、精度面がまだかなり粗いのでその辺を改善したいと思ってます。
そんな中適当にやってたら残像?っぽいものが出てきて楽しかったので載せておきます。
適当にα値いじるだけですね。
画像は僕の腕ですw




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 Microsoft.Research.Kinect.Nui;

namespace Kinect_Residualimage
{
public partial class Form1 : Form
{
//画面表示用
Bitmap Texture;

//Kinectの変数
Runtime nui;

const int ALPHA_IDX = 3;
const int RED_IDX = 0;
const int GREEN_IDX = 1;
const int BLUE_IDX = 2;
byte[] colorFrame32 = new byte[640 * 480 * 4];

Graphics Graphi;

public Form1()
{
InitializeComponent();
}

private void Form1_Load(object sender, EventArgs e)
{
Texture = new Bitmap(640, 480, System.Drawing.Imaging.PixelFormat.Format32bppArgb);

//Kinectの初期化
nui = new Runtime();
try
{
nui.Initialize(RuntimeOptions.UseColor);
}
catch (InvalidOperationException)
{
Console.WriteLine("Runtime initialization failed. Please make sure Kinect device is plugged in.");
return;
}

//video streamのOpenをする
try
{
nui.VideoStream.Open(ImageStreamType.Video, 2, ImageResolution.Resolution640x480, ImageType.Color);
}
catch (InvalidOperationException)
{
Console.WriteLine("Failed to open stream. Please make sure to specify a supported image type and resolution.");
return;
}
nui.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_VideoFrameReady);

pictureBox1.Image = new Bitmap(pictureBox1.Width, pictureBox1.Height);
Graphi = Graphics.FromImage(pictureBox1.Image);
}

void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
{
PlanarImage Image = e.ImageFrame.Image;
byte[] converted = convertColorFrame(Image.Bits, (byte)50);
//画像の作成

unsafe {
fixed (byte* ptr = &converted[0]) {
Texture = new Bitmap(Image.Width , Image.Height , Image.Width * 4 , System.Drawing.Imaging.PixelFormat.Format32bppArgb , (IntPtr)ptr);
}
}

Graphi.DrawImage(Texture, 0, 0);

pictureBox1.Refresh();
}

//kinectから取り込んだColorFrameのフォーマットを直す
byte[] convertColorFrame(byte[] colorFrame , byte alpha)
{
for (int i = 0; i < colorFrame32.Length; i += 4)
{
colorFrame32[i + ALPHA_IDX] = alpha;
colorFrame32[i + RED_IDX] = colorFrame[i + 0];
colorFrame32[i + GREEN_IDX] = colorFrame[i + 1];
colorFrame32[i + BLUE_IDX] = colorFrame[i + 2];
}
return colorFrame32;
}

private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
nui.Uninitialize();
Environment.Exit(0);
}
}
}


2011年6月21日火曜日

Kinect SDK + XNA Jointを3次元座標で表示する


今までは2次元内での描画だったので今度は3次元座標を使って描画してみました。
もともと取得してこられるデータは3次元座標なので、XNAから使う分には3次元の方が楽でした。
ちゃんと3次元座標がとれたところで、マウスポインタを手で動かせるようにしてみようかと思います。そのうち。
今回のプロジェクトファイルですダウンロード

2011年6月20日月曜日

Kinect SDK + XNA スケルトンの表示

やっとKinectから取得してきたデータでスケルトンを描画できました。
早くXNAでもAPIとして2次元の直線とか基本的な図形をサポートしてもらいたいです。
さすがにソースコードは長いのでプロジェクトファイルを置いておきます。
ダウンロード

2011年6月17日金曜日

Kinectからの画像をXNAで表示する

公開されたばかりのKinectSDKのドキュメントを読みながらDirectXもWPFも使いたくない!
と思ったのでXNAでKinectから取り込んだ画像を表示できるようにしてみました。
ピクセルフォーマットとか直すところでつまづいたけど、VideoStreamとDepthStreamから得られる画像はきちんと表示できるようになりました。

土日のうちにボーンの描画までして何か作りたいですね。



using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;
using Microsoft.Research.Kinect.Nui;

namespace XNAFirst
{
/// <summary>
/// This is the main type for your game
/// </summary>
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;

//Kinectの変数
Runtime nui;
int totalFrames = 0;
int lastFrames = 0;
DateTime lastTime = DateTime.MaxValue;

//テクスチャ
private Texture2D texDepth = null;
private Texture2D texBorn = null;
private Texture2D texImage = null;

const int ALPHA_IDX = 3;
const int RED_IDX = 0;
const int GREEN_IDX = 1;
const int BLUE_IDX = 2;

byte[] depthFrame32 = new byte[320 * 240 * 4];
byte[] colorFrame32 = new byte[640 * 480 * 4];

public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
graphics.PreferredBackBufferWidth = 640;
graphics.PreferredBackBufferHeight = 480;
}

protected override void Initialize()
{
//kinectの初期化
nui = new Runtime();
try {
nui.Initialize(RuntimeOptions.UseDepthAndPlayerIndex | RuntimeOptions.UseSkeletalTracking | RuntimeOptions.UseColor);
} catch (InvalidOperationException) {
System.Windows.MessageBox.Show("Runtime initialization failed. Please make sure Kinect device is plugged in.");
return;
}
//video stream と depth stream のOpenをする
try {
nui.VideoStream.Open(ImageStreamType.Video , 2 , ImageResolution.Resolution640x480 , ImageType.Color);
nui.DepthStream.Open(ImageStreamType.Depth , 2 , ImageResolution.Resolution320x240 , ImageType.DepthAndPlayerIndex);
} catch (InvalidOperationException) {
System.Windows.MessageBox.Show("Failed to open stream. Please make sure to specify a supported image type and resolution.");
return;
}
//FPS制御のための時間取得
lastTime = DateTime.Now;

//イベント駆動以外でframeを取得したいのでここはちょっと保留
nui.DepthFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_DepthFrameReady);
nui.SkeletonFrameReady += new EventHandler<SkeletonFrameReadyEventArgs>(nui_SkeletonFrameReady);
nui.VideoFrameReady += new EventHandler<ImageFrameReadyEventArgs>(nui_VideoFrameReady);
base.Initialize();
}

void nui_VideoFrameReady(object sender, ImageFrameReadyEventArgs e)
{
//32bit per pixel RGBA image
PlanarImage Image = e.ImageFrame.Image;
texImage = new Texture2D(this.GraphicsDevice, Image.Width, Image.Height , false , SurfaceFormat.Color);
byte[] converted = convertColorFrame(Image.Bits);
texImage.SetData(converted);
}

void nui_SkeletonFrameReady(object sender, SkeletonFrameReadyEventArgs e)
{
//とりあえず保留
}

void nui_DepthFrameReady(object sender, ImageFrameReadyEventArgs e)
{
PlanarImage Image = e.ImageFrame.Image;
texDepth = new Texture2D(this.GraphicsDevice, Image.Width, Image.Height , false , SurfaceFormat.Color);
//byteデータで取得
//depthバッファを見やすくする
byte[] convertedDepthFrame = convertDepthFrame(Image.Bits);

//byteデータから画像orスプライトを作成する
texDepth.SetData(convertedDepthFrame);

totalFrames++;

//fps測定
DateTime cur = DateTime.Now;
if (cur.Subtract(lastTime) > TimeSpan.FromSeconds(1)) {
int frameDiff = totalFrames - lastFrames;
lastFrames = totalFrames;
lastTime = cur;
}
}

protected override void LoadContent()
{
// Create a new SpriteBatch, which can be used to draw textures.
spriteBatch = new SpriteBatch(GraphicsDevice);

// TODO: use this.Content to load your game content here
}

protected override void UnloadContent()
{
nui.Uninitialize();
}

protected override void Update(GameTime gameTime)
{
// Allows the game to exit
if (GamePad.GetState(PlayerIndex.One).Buttons.Back == ButtonState.Pressed)
this.Exit();

// TODO: Add your update logic here

base.Update(gameTime);
}

protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.White);

this.spriteBatch.Begin();

this.spriteBatch.Draw(texImage, new Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), Color.White);
//this.spriteBatch.Draw(texDepth, new Rectangle(0, 0, graphics.PreferredBackBufferWidth, graphics.PreferredBackBufferHeight), Color.White);

this.spriteBatch.End();

base.Draw(gameTime);
}

//kinectから取り込んだColorFrameをXNAのColorフォーマットに直す
byte[] convertColorFrame(byte[] colorFrame)
{
for (int i = 0 ; i < colorFrame32.Length ; i += 4) {
colorFrame32[i + ALPHA_IDX] = 255;
colorFrame32[i + RED_IDX] = colorFrame[i + 2];
colorFrame32[i + GREEN_IDX] = colorFrame[i + 1];
colorFrame32[i + BLUE_IDX] = colorFrame[i];
}
return colorFrame32;
}

// Converts a 16-bit grayscale depth frame which includes player indexes into a 32-bit frame
// that displays different players in different colors
//XNAのテクスチャーではColorフォーマットを使うことを想定している
byte[] convertDepthFrame(byte[] depthFrame16)
{
for (int i16 = 0, i32 = 0; i16 < depthFrame16.Length && i32 < depthFrame32.Length; i16 += 2, i32 += 4)
{
int player = depthFrame16[i16] & 0x07;
int realDepth = (depthFrame16[i16 + 1] << 5) | (depthFrame16[i16] >> 3);
// transform 13-bit depth information into an 8-bit intensity appropriate
// for display (we disregard information in most significant bit)
byte intensity = (byte)(255 - (255 * realDepth / 0x0fff));

depthFrame32[i32 + ALPHA_IDX] = 255;
depthFrame32[i32 + RED_IDX] = 0;
depthFrame32[i32 + GREEN_IDX] = 0;
depthFrame32[i32 + BLUE_IDX] = 0;

// choose different display colors based on player
switch (player)
{
case 0:
depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2);
depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2);
depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 2);
break;
case 1:
depthFrame32[i32 + RED_IDX] = intensity;
break;
case 2:
depthFrame32[i32 + GREEN_IDX] = intensity;
break;
case 3:
depthFrame32[i32 + RED_IDX] = (byte)(intensity / 4);
depthFrame32[i32 + GREEN_IDX] = (byte)(intensity);
depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
break;
case 4:
depthFrame32[i32 + RED_IDX] = (byte)(intensity);
depthFrame32[i32 + GREEN_IDX] = (byte)(intensity);
depthFrame32[i32 + BLUE_IDX] = (byte)(intensity / 4);
break;
case 5:
depthFrame32[i32 + RED_IDX] = (byte)(intensity);
depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 4);
depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
break;
case 6:
depthFrame32[i32 + RED_IDX] = (byte)(intensity / 2);
depthFrame32[i32 + GREEN_IDX] = (byte)(intensity / 2);
depthFrame32[i32 + BLUE_IDX] = (byte)(intensity);
break;
case 7:
depthFrame32[i32 + RED_IDX] = (byte)(255 - intensity);
depthFrame32[i32 + GREEN_IDX] = (byte)(255 - intensity);
depthFrame32[i32 + BLUE_IDX] = (byte)(255 - intensity);
break;
}
}
return depthFrame32;
}
}
}

2011年6月14日火曜日

AOJ 1136

いろいろあって何故か長時間解けなかったのが解けたので。
問題文は日本語なので説明は無し。
元となる図形の始点と終点を原点に移動した物をそれぞれ用意。
その後比較するデータと回転させて4パターンの比較を行う。


#include <iostream>
#include <vector>
#include <map>
#include <set>
#include <string.h>

using namespace std;

struct xy {
int first;
int second;
};

xy presets1[1000];
xy input[1000];

int main(void)
{
int n;
while (1) {
cin >> n;
if (n == 0) {
break;
}

memset(presets1 , 0 , sizeof(presets1));
memset(input , 0 , sizeof(input));
int m;
cin >> m;
for (int i = 0 ; i < m ; i++) {
cin >> presets1[i].first >> presets1[i].second;
}

for (int i = 0 ; i < n ; i++) {
int check;
cin >> check;
for (int j = 0 ; j < check ; j++) {
cin >> input[j].first >> input[j].second;
}
if (m != check) {
continue;
}
for (int k = 0 ; k < 4 ; k++) {
int ix0 = input[0].first;
int iy0 = input[0].second;
int px0 = presets1[0].first;
int py0 = presets1[0].second;
for (int j = 0 ; j < check ; j++) {
if (presets1[j].first - px0 != input[j].first - ix0 || presets1[j].second - py0 != input[j].second - iy0) {
goto NEXT;
}
}
cout << i + 1 << endl;
goto END;
NEXT:;
px0 = presets1[check - 1].first;
py0 = presets1[check - 1].second;
for (int j = 0 ; j < check ; j++) {
if (presets1[check - j - 1].first - px0 != input[j].first - ix0 || presets1[check - j - 1].second - py0 != input[j].second - iy0) {
goto NEXT2;
}
}
cout << i + 1 << endl;
goto END;
NEXT2:;
//回転作業
for (int j = 0 ; j < check ; j++) {
int temp = input[j].first;
input[j].first = input[j].second;
input[j].second = -temp;
}
}
END:;
}
cout << "+++++" << endl;
}
return 0;
}

2011年5月27日金曜日

セルオートマトン:雪の結晶

セルオートマトンの続き。
はじめに参考文献。
セルオートマトンを用いた雪の結晶の類似パターン生成
これに書いてあるのを実装して見ました。

適当にパターンいじってみたらこんな感じに。
やっぱりセルオートマトン楽しいな。
動物の模様とかもシミュレートできるようなのでそれも実装したいです。









2011年5月17日火曜日

AOJ 2205

国内予選までは解ける問題を一日2問解くようにしたい。

AOJ 2205。 難易度はB問。
宝くじでいくら当たってるかを解析する。
一枚のくじに対して複数のあたりが存在しないってことなので普通に書いたら終わり。

#include <iostream>
#include <vector>
#include <map>
#include <string>

using namespace std;

vector<pair<string , int> > treasure;

int main(void)
{
while (1) {
int n , m;
cin >> n >> m;
if (n == 0 && m == 0) {
break;
}
treasure.clear();
for (int i = 0 ; i < n ; i++) {
pair<string , int> temp;
cin >> temp.first >> temp.second;
treasure.push_back(temp);
}

int result = 0;
string have;
for (int i = 0 ; i < m ; i++) {
cin >> have;
for (int j = 0 ; j < n ; j++) {
for (int k = 0 ; k < treasure[j].first.size() ; k++) {
if (treasure[j].first[k] == '*') {
continue;
}
if (treasure[j].first[k] != have[k]) {
goto NEXT;
}
}
result += treasure[j].second;
break;
NEXT:;
}
}
cout << result << endl;
}
return 0;
}


AOJ 2149

AOJ 2149の問題。A問。
スロットマシーンであたりを引くまで最低何回やればいいか、みたいな内容だった気がする。


#include
#include

using namespace std;

vector real;

int main(void)
{
int N , A , B , C , X;
while (1) {
cin >> N >> A >> B >> C >> X;
if (N == 0 && A == 0 && B == 0 && C == 0 && X == 0) {
break;
}
real.clear();
for (int i = 0 ; i < N ; i++) {
int temp;
cin >> temp;
real.push_back(temp);
}
int count = 0;
for (int i = 0 ; i < real.size() ;) {
if (X == real[i]) {
i++;
if (i == real.size()) {
break;
}
}
X = (A * X + B) % C;
count++;
if (count > 10000) {
count = -1;
break;
}
}

cout << count << endl;
}

return 0;
}

2011年5月16日月曜日

AOJ 2019

記事も少ないので解いたものを載せていこうと思います。
結構前に解いたものなのでもう問題は覚えてないけど・・・

#include 
#include
#include
#include

using namespace std;

vector > data;

int main(void)
{
int n , m;
while (1) {
cin >> n >> m;
if (n == 0 && m == 0) {
break;
}

int maxd = 0;
data.clear();
pair temp;
for (int i = 0 ; i < n ; i++) {
cin >> temp.second >> temp.first;
maxd += temp.first * temp.second;
data.push_back(temp);
}
sort(data.begin() , data.end());
vector >::reverse_iterator begin = data.rbegin();
vector >::reverse_iterator end = data.rend();
for (; begin != end ; begin++) {
int length = min(begin->second , m);
maxd -= (length * begin->first);
m -= length;
if (m <= 0) {
break;
}
}

maxd = max(0 , maxd);
cout << maxd << endl;
}
return 0;
}


2011年5月1日日曜日

2次元セルオートマトン



引越し記事3つ目。

セルオートマトンのルールのうちいくつかをまとめたものを作りました。
Generationsルールとか面白いので大好きです。
ダウンロード

ラングトンの蟻



お引越し記事第二弾。

昔作ったラングトンの蟻です。
特に凝ったこともせずそのまま。
簡単なルールなのに規則的な動きが出てくるとやっぱり面白いですね。

動かない場合は、GLUTやらGLFWあたりを導入してもらえると大丈夫かと思います。
ダウンロード

2011年4月30日土曜日

1次元セルオートマトン



1次元セルオートマトンです。
GLUT、GLFW、AntTweakBarを使用しています。
使い方ですが、「000(1)」とか書いてある横で1になっている部分の括弧内の数字を足したものがルールxに相当します。
left以下の欄は初期状態で左端、真ん中、右端を1にするかどうかの設定です。

自己責任でお願いします。
ダウンロード

引越し、セッティング中

旧ブログから、作った物だけをお引越しさせています。
これから先につくるものもここで公開していきたいと思います。