実画像取得の処理をシェーダーを交えて行います。
(作成日:2012/04/02)
以前行った実画像取得のプログラムでは、イベントハンドラ内で以下のような操作を行っていました。
for (int y = 0; y < image.Height; y++)
{
for (int x = 0; x < image.Width; x++, no += 4)
{
// BGRAからRGBAに変換する
temp = imageData[no];
imageData[no] = imageData[no + 2];
imageData[no + 2] = temp;
// Alphaを255にする
imageData[no + 3] = 255;
}
}
この処理で何を行いたかったかというと、byte配列の情報をRGBAの順にする・A(アルファ)値を255に指定することです。
KinectではBGRAの順で色情報が格納されています。またA値は0です。
XNA上でそのまま描画すると下図のようになります。
このようにならないために処理を行っていましたが、XNAを用いているのでシェーダーで処理してみましょう。
1.プロジェクトを作る
2.参照設定を追加
XNAによる実画像取得のようにしてください。
3.エフェクトファイルを作る
XNAではシェーダーの処理をエフェクトファイルに記述します。
エフェクトファイルはソリューションエクスプローラ内の[Content]を右クリックし、[追加]→[新しい項目]を選択します。
エフェクトファイルを選択、ファイル名を指定し追加してください。
追加するとエフェクトファイルが生成され、[Content]内に表示されます。
imageeffect.fxには下記のように記述してください。
imageEffect.fx
// テクスチャー
texture imageTexture;
struct VertexShaderInput
{
float4 texcoord : TEXCOORD0;
};
struct VertexShaderOutput
{
float4 texcoord : TEXCOORD0;
};
sampler textureSampler = sampler_state
{
Texture = imageTexture;
};
float4 PixelShaderFunction(VertexShaderOutput input) : COLOR0
{
// 画像の色情報の取得
float4 tex = tex2D(textureSampler, input.texcoord);
// BとRの値を交換
float tmp = tex.r;
tex.r = tex.b;
tex.b = tmp;
// アルファ値を255に設定
tex.a = 255;
return tex;
}
technique Technique1
{
pass Pass1
{
PixelShader = compile ps_2_0 PixelShaderFunction();
}
}
4.プログラミング
以下のコードのように入力してください。
Game1.cs
using System;
using System.Collections.Generic;
using System.Linq;
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;
// kinectのセンサクラス
// Microsoft.Kineを参照に追加
using Microsoft.Kinect;
namespace xnaimageshader
{
public class Game1 : Microsoft.Xna.Framework.Game
{
GraphicsDeviceManager graphics;
SpriteBatch spriteBatch;
/// <summary>
/// 実画像テクスチャ
/// 640x480
/// </summary>
private Texture2D imageTexture;
/// <summary>
/// kinectから取得したRGBデータ
/// (byte型配列)
/// </summary>
private byte[] imageData;
/// <summary>
/// KINECTのセンサクラス
/// </summary>
private KinectSensor kinect;
/// <summary>
/// 画像処理用のエフェクトファイル
/// </summary>
private Effect imageEffect;
public Game1()
{
graphics = new GraphicsDeviceManager(this);
Content.RootDirectory = "Content";
// ウィンドウサイズの指定
graphics.PreferredBackBufferHeight = 480;
graphics.PreferredBackBufferWidth = 640;
}
protected override void Initialize()
{
// kinectの初期化
kinect = KinectSensor.KinectSensors[0];
// カラー画像の取得を開始する
kinect.ColorFrameReady += new EventHandler<ColorImageFrameReadyEventArgs>(ColorImageReady);
kinect.ColorStream.Enable(ColorImageFormat.RgbResolution640x480Fps30);
// Kinectを起動する
kinect.Start();
base.Initialize();
}
protected override void LoadContent()
{
spriteBatch = new SpriteBatch(GraphicsDevice);
imageEffect = Content.Load<Effect>("imageEffect");
}
protected override void UnloadContent(){}
protected override void Update(GameTime gameTime)
{
base.Update(gameTime);
}
protected override void Draw(GameTime gameTime)
{
GraphicsDevice.Clear(Color.CornflowerBlue);
// 通常の描画
spriteBatch.Begin();
// 実画像Textureの描写(描画サイズを半分に指定)
if (imageTexture != null)
spriteBatch.Draw(imageTexture, Vector2.Zero, null, Color.White, 0.0f, Vector2.Zero, 0.5f, SpriteEffects.None, 0.0f);
spriteBatch.End();
// エフェクトファイルを使った描画
spriteBatch.Begin(0, null, null, null, null, imageEffect);
if (imageTexture != null)
spriteBatch.Draw(imageTexture, new Vector2(320,0), null, Color.White, 0.0f, Vector2.Zero, 0.5f, SpriteEffects.None, 0.0f);
spriteBatch.End();
base.Draw(gameTime);
}
/////////// イベントハンドラ ///////////
/// <summary>
/// カラー画像の取得
/// </summary>
void ColorImageReady(object sender, ColorImageFrameReadyEventArgs e)
{
// kinectからカラーイメージを取得
ColorImageFrame image = e.OpenColorImageFrame();
if (image != null)
{
// imageData配列の初期化
imageData = new byte[image.PixelDataLength];
// imageのピクセルデータをpixelDataへコピーする
image.CopyPixelDataTo(imageData);
// imageTextureの初期化
imageTexture = new Texture2D(GraphicsDevice, image.Width, image.Height);
// imageTextureにimageDataを反映する
imageTexture.SetData(imageData);
}
}
}
}
実行結果
このような感じで表示されるはずです。
(画像は半分の大きさで表示しています)