イメージバッファにアクセスする
概要
ビット深度8bitのモノクロ、ビット深度16bitのモノクロ、ビット深度24bit(3byte)のカラー、ビット深度32bit(4byte)のカラーそれぞれの画像を保存とイメージバッファーにアクセスし輝度値を操作する方法について説明します。
サンプルプログラム
Software | IC Imaging Control 3.5, Visual Studio™ 2019 |
---|---|
サンプル(C#) | pixelformat_cs_3.5.zip |
実行結果
Y800(Y800.bmp),Y16(y16.tiff)で保存した画像を拡大すると、左上のピクセルが黒、グレー、白の順で配列されているのが分かります。
RGB24(RGB24.bmp)、RGB32(RGB32.bmp)で保存した画像を拡大すると、左上のピクセルが黒、グレー、白の順で配列されているのが分かります。
Visual Studioのプログラム上ではすでにボタンの設置や関数は定義済ですので、IC Imaging Contorl3.5をインストールされていれば、実行ボタンだけですぐにデバッグで動作確認することができます。
画面にあるそれぞれのボタンなどのコントローラの機能は下記の通りです。
[Y800]ボタン | ビット深度8bitのモノクロのフォーマットでbmp形式の画像を保存します。 |
---|---|
[Y16]ボタン | ビット深度16bitのモノクロのフォーマットでbmp形式の画像を保存します。カメラ自体は10bitか12bitまでしか対応していませんので、残り4bit分の精度の保証はしておりません。 |
[RGB24]ボタン | ビット深度24bit(3byte)のカラーのフォーマットでbmp形式の画像を保存します。(RGB(赤・青・緑)のそれぞれの色情報を1byte分として保持しています。) |
[RGB32]ボタン | ビット深度32bit(4byte)のカラーのフォーマットでbmp形式の画像を保存します。(RGB(赤・青・緑)のそれぞれの色情報を1byte分と、アルファ値(透明度)の情報を1byte分保持しています。なお、アルファ値はIC Imaging Controlでは使用されておりません。) |
初期設定
private void Form1_Load(object sender, EventArgs e)
{
if( !icImagingControl1.LoadShowSaveDeviceState("lastSelectedDeviceState.xml") )
{
MessageBox.Show("No device was selected.", this.Text, MessageBoxButtons.OK, MessageBoxIcon.Information);
this.Close();
return;
}
icImagingControl1.LiveDisplayDefault = false;
icImagingControl1.LiveDisplaySize = icImagingControl1.Size;
//設定したカラーフォーマットがY16だった場合にはY16ボタンが使えるように許可
cmdY16.Enabled = icImagingControl1.VideoFormat.StartsWith("Y16");
//OverlayBitmapオブジェクトを使用しない
icImagingControl1.OverlayBitmapPosition = TIS.Imaging.PathPositions.None;
icImagingControl1.LiveStart();
}
BufferAccessHelperクラスを使ってIFrame.Ptrをサポートするデータへの配列を制御するため、Form1がロードされたタイミングでOverlayBitmapPositionを無効化します。
Y800ボタンクリック時の処理
private void cmdY800_Click(object sender, EventArgs e)
{
TIS.Imaging.IFrameQueueBuffer frame = GrabImage(TIS.Imaging.MediaSubtypes.Y800);
if ( frame == null) return;
BufferAccessHelper buf = new BufferAccessHelper( frame );
// Y800は 上の行から読み込む
int y = 0;
txtOutput.Text = "Image buffer pixel format is Y800\r\n";
txtOutput.Text += "Pixel 1: " + buf[0, y] + "\r\n";
txtOutput.Text += "Pixel 2: " + buf[1, y];
// (0,0)ピクセルの座標位置の輝度値を0 (黒)にする
buf[0, y] = 0;
// (1,0)ピクセルの座標位置の輝度値を0 (グレー)にする
buf[1, y] = 128;
// (2,0)ピクセルの座標位置の輝度値を255 (白)にする
buf[2, y] = 255;
//Bitmap形式で画像を保存する
TIS.Imaging.FrameExtensions.SaveAsBitmap(frame, "Y800.bmp");
}
GrabImageメソッドを使って新しいフレームを取得します。
BufferAccessHelperクラスをインスタンス化することでポインタにアクセスできるようになります。
Y800のイメージバッファのピクセルは左から右へ、上から下へと割り当てられているので、取得したフレームの左上の座標は(0,0)となります。
buf変数の配列に輝度値の数値を入れることでピクセル単位で輝度値を変更することができます。
RGB24ボタンクリック時の処理
private void cmdRGB24_Click(object sender, EventArgs e)
{
TIS.Imaging.IFrameQueueBuffer frame = GrabImage(TIS.Imaging.MediaSubtypes.RGB24);
if ( frame == null) return;
BufferAccessHelper buf = new BufferAccessHelper(frame);
// RGB24は 下の行から読み込む
int y = frame.FrameType.Height - 1;
txtOutput.Text = "Image buffer pixel format is RGB24\r\n";
txtOutput.Text += "Pixel 1: ";
txtOutput.Text += "R=" + buf[0 * 3 + 2, y] + ", ";
txtOutput.Text += "G=" + buf[0 * 3 + 1, y] + ", ";
txtOutput.Text += "B=" + buf[0 * 3 + 0, y] + "\r\n";
txtOutput.Text += "Pixel 2: ";
txtOutput.Text += "R=" + buf[1 * 3 + 2, y] + ", ";
txtOutput.Text += "G=" + buf[1 * 3 + 1, y] + ", ";
txtOutput.Text += "B=" + buf[1 * 3 + 0, y];
// (0,y)ピクセルの座標位置の輝度値を赤にする
buf[0 * 3 + 2, y] = 255;
buf[0 * 3 + 1, y] = 0;
buf[0 * 3 + 0, y] = 0;
// (1,y)ピクセルの座標位置の輝度値を緑にする
buf[1 * 3 + 2, y] = 0;
buf[1 * 3 + 1, y] = 255;
buf[1 * 3 + 0, y] = 0;
// (2,y)ピクセルの座標位置の輝度値をblueにする
buf[2 * 3 + 2, y] = 0;
buf[2 * 3 + 1, y] = 0;
buf[2 * 3 + 0, y] = 255;
//Bitmap形式で画像を保存する
TIS.Imaging.FrameExtensions.SaveAsBitmap(frame, "RGB24.bmp");
}
GrabImageメソッドを使って新しいフレームを取得します。
BufferAccessHelperクラスをインスタンス化することでポインタにアクセスできるようになります。
RGB24のイメージバッファのピクセルは左から右へ、下から上へと割り当てられているので、取得したフレームの左上の座標は(0,フレームの高さ分-1)となります。
buf変数の配列に輝度値の数値を入れることでピクセル単位で輝度値を変更することができます。
RGB32ボタンクリック時の処理
private void cmdRGB32_Click(object sender, EventArgs e)
{
TIS.Imaging.IFrameQueueBuffer frame = GrabImage(TIS.Imaging.MediaSubtypes.RGB32);
if( frame == null ) return;
BufferAccessHelper buf = new BufferAccessHelper( frame );
// RGB32は 下の行から読み込む
int y = frame.FrameType.Height - 1;
txtOutput.Text = "Image buffer pixel format is RGB32\r\n";
txtOutput.Text += "Pixel 1: ";
txtOutput.Text += "R=" + buf[0 * 4 + 2, y] + ", ";
txtOutput.Text += "G=" + buf[0 * 4 + 1, y] + ", ";
txtOutput.Text += "B=" + buf[0 * 4 + 0, y] + "\r\n";
txtOutput.Text += "Pixel 2: ";
txtOutput.Text += "R=" + buf[1 * 4 + 2, y] + ", ";
txtOutput.Text += "G=" + buf[1 * 4 + 1, y] + ", ";
txtOutput.Text += "B=" + buf[1 * 4 + 0, y];
// (0,y)ピクセルの座標位置の輝度値をredにする
buf[0 * 4 + 2, y] = 255;
buf[0 * 4 + 1, y] = 0;
buf[0 * 4 + 0, y] = 0;
// (1,y)ピクセルの座標位置の輝度値をgreenにする
buf[1 * 4 + 2, y] = 0;
buf[1 * 4 + 1, y] = 255;
buf[1 * 4 + 0, y] = 0;
// (2,y)ピクセルの座標位置の輝度値をblueにする
buf[2 * 4 + 2, y] = 0;
buf[2 * 4 + 1, y] = 0;
buf[2 * 4 + 0, y] = 255;
//Bitmap形式で画像を保存する
TIS.Imaging.FrameExtensions.SaveAsBitmap(frame, "RGB32.bmp");
}
GrabImageメソッドを使って新しいフレームを取得します。
BufferAccessHelperクラスをインスタンス化することでポインタにアクセスできるようになります。
RGB32のイメージバッファのピクセルは左から右へ、下から上へと割り当てられているので、取得したフレームの左上の座標は(0,フレームの高さ分-1)となります。
buf変数の配列に輝度値の数値を入れることでピクセル単位で輝度値を変更することができます。
Y16ボタンクリック時の処理
private void cmdY16_Click(object sender, EventArgs e)
{
TIS.Imaging.IFrameQueueBuffer buf = GrabImage(TIS.Imaging.MediaSubtypes.Y16);
if (buf == null) return;
//2ピクセルを取得
UInt32 val0 = ReadY16(buf, 0, 0);
UInt32 val1 = ReadY16(buf, 0, 1);
txtOutput.Text = "Image buffer pixel format is Y16\r\n";
txtOutput.Text += "Pixel 1: " + val0 + "\r\n";
txtOutput.Text += "Pixel 2: " + val1;
// (0,0)ピクセルの座標位置の輝度値をBlack)にする
WriteY16(buf, 0, 0, 0x0000);
// (0,1)ピクセルの座標位置の輝度値をGrayにする
WriteY16(buf, 0, 1, 0x8000);
// (0,2)ピクセルの座標位置の輝度値をWhiteにする
WriteY16(buf, 0, 2, 0xFFFF);
//Tiff形式で画像を保存する
TIS.Imaging.FrameExtensions.SaveAsTiff( buf, "y16.tiff");
}
//イメージデータの輝度値の書き込み
private unsafe UInt16 ReadY16(TIS.Imaging.IFrameQueueBuffer buf, int row, int col)
{
// Y16は 上の行から読み込む (画像幅に依存した、ライン当たりのBytes量を取得)
int offset = row * buf.FrameType.BytesPerLine + col * 2;
//メモリから 16 ビット符号付き整数を読み取り
return (UInt16)System.Runtime.InteropServices.Marshal.ReadInt16(new IntPtr(buf.Ptr), offset);
}
private unsafe void WriteY16(TIS.Imaging.IFrameQueueBuffer buf, int row, int col, UInt16 value)
{
int offset = row * buf.FrameType.BytesPerLine + col * 2;
//メモリに 16 ビット符号付き整数を書き込み
System.Runtime.InteropServices.Marshal.WriteInt16( new IntPtr( buf.Ptr ), offset, (short)value);
}
GrabImageメソッドを使って新しいフレームを取得します。
イメージバッファのデータにアクセスするために、BufferAccessHelperクラスではなく、新たに16bitの符号なし整数データとして取得する必要があります。System.Runtime.InteropServices.Marshal.ReadInt16を 使用することによりヘルパー関数のReadY16が使えます。
RGB32のイメージバッファのピクセルは左から右へ、下から上へと割り当てられているので、取得したフレームの左上の座標は(0,フレームの高さ分-1)となります。
buf変数の配列に16進数の輝度値の数値を入れることでピクセル単位で輝度値を変更することができます。
イメージバッファへのアクセス方法
private TIS.Imaging.IFrameQueueBuffer GrabImage(Guid colorFormat)
{
bool wasLive = icImagingControl1.LiveVideoRunning;
//ライブストリーミング停止
icImagingControl1.LiveStop();
//現在設定中のシンクオブジェクトを保存する
TIS.Imaging.BaseSink oldSink = icImagingControl1.Sink;
//ビデオストリームから不定期に、1枚、または複数の画像を取得する際に使用します。
TIS.Imaging.FrameSnapSink sink = new TIS.Imaging.FrameSnapSink( new TIS.Imaging.FrameType( colorFormat ) );
//シンク設定
icImagingControl1.Sink = sink;
try
{
icImagingControl1.LiveStart();
}
catch (TIS.Imaging.ICException ex)
{
MessageBox.Show(ex.Message);
icImagingControl1.Sink = oldSink;
return null;
}
//FrameSnapSinkで取得したイメージバッファを参照する
TIS.Imaging.IFrameQueueBuffer rval = null;
try
{
//フレームを取得する
rval = sink.SnapSingle( TimeSpan.FromSeconds( 1 ) );
}
catch (TIS.Imaging.ICException ex)
{
MessageBox.Show(ex.Message);
}
icImagingControl1.LiveStop();
//古いシンクを復元する
icImagingControl1.Sink = oldSink;
if (wasLive)
icImagingControl1.LiveStart();
return rval;
}
イメージバッファへのアクセスはIFrameQueueBufferインターフェースを使います。
イメージバッファ内には何もありませんので、画像をキャプチャするためにFrameSnapSink.SnapSingleメソッドを呼び出しフレームを取得します。イメージバッファにアクセスしたのちにライブストリーミングをディスプレイに表示するために保存しておいたシンクオブジェクトを復元しています。
class BufferAccessHelper
{
TIS.Imaging.IFrameQueueBuffer buf;
public BufferAccessHelper( TIS.Imaging.IFrameQueueBuffer buf )
{
this.buf = buf;
}
public unsafe byte this[int x,int y]
{
get
{
byte* ptr = buf.Ptr + y * buf.FrameType.BytesPerLine + x;
return *ptr;
}
set
{
byte* ptr = buf.Ptr + y * buf.FrameType.BytesPerLine + x;
*ptr = value;
}
}
}
イメージバッファのデータにアクセスするために、BufferAccessHelperのクラスを定義しています。
IFrame.Ptrプロパティで画像データへのポインタにアクセスすることができます。そうすることで、イメージバッファー内にbuf[column,line]として個別のデータにアクセスできます。
下記のAPIリファレンスマニュアルにもその他関数などの説明があります。
プログラマーズガイド:イメージバッファにアクセスする
IC Imaging Control_Ver3.5(C#/VB.NET) APIリファレンスマニュアル