画像処理プログラムの基本は画像の輝度値(画素値)を取得して、様々な処理をすることとなりますが、C#では輝度値を取得するメソッドにSetPixel/GetPixelのメソッドが用意されていますが、これは処理が遅いことで有名。
そこで、OpenCVのIplImage構造体やMatクラスのエッセンスを取り入れつつ、.NETのBitmapクラスっぽく、さらに画像データを簡単に扱えるようにすることを目指したImageDataクラスなるライブラリを作成してみました。
基本コンセプトは
●画像の輝度値の取得/設定が簡単
●SetPixel/GetPixelよりは高速
●シンプルなプログラムが書けること
といったところです。
画像処理をこれから学ぼうとする人、とりあえず簡単にアルゴリズムの評価プログラムを作って見たい人向けを想定しています。パフォーマンス重視の方はポインタ(Scan0)を参照してください。
というスタンスです。
今のところフリーですので、ご興味があれば使ってみてください。
ただし、本ライブラリを使用した事による不具合等の責任を負えませんので、ご了承願います。
ファイル:ImagingSolution.Imaging.ImageData.dll
.NET Frameworkバージョン:4.5.2
Visual Studio 2015 C#を使用
ダウンロード
公開日 | バージョン | サンプルプログラム | |
2016.3.19 | Ver.0.1.1 | ImageDataSample011.zip | Bitmapファイル読込部分のバグ修正
Reaginプロパティに影響 |
2016.3.17 | Ver.0.1.0 | ImageDataSample010.zip | 初版
バグなどが含まれる可能性があります。 今後、仕様が変更される可能性もあります。 |
コンストラクター
名前 | 説明 |
ImageData(int, int, int, int) | 画像の幅、高さ、画像のビット数、メモリのビット数を指定して、ImageDataクラスの新しいインスタンスを初期化します。 |
ImageData(int, int, int, System.Drawing.Imaging.PixelFormat, System.IntPtr) | 画像の幅、高さ、メモリの幅のバイト数、ピクセルフォーマット、外部メモリのポインタを指定して、ImageDataクラスの新しいインスタンスを初期化します。 |
ImageData(int, int, System.Drawing.Imaging.PixelFormat) | 画像の幅、高さ、ピクセルフォーマットを指定して、ImageDataクラスの新しいインスタンスを初期化します。 |
ImageData(string) | 画像ファイル名を指定して、ImageDataクラスの新しいインスタンスを初期化します。 |
プロパティ
名前 | 説明 |
BiCubicVal | biCubiによる補間の計算時に用いる変数を取得/設定します。
初期値:-0.75 |
BorderType | 画像の外側の輝度値の参照方法をBorderTypeEnumにより取得/設定します。
初期値:BorderTypeEnum.Mirror |
BufferBit | 画像を格納するメモリのビット数を取得します。
8、16、24、32のいづれを指定します。 |
ByteAlignment | 画像を格納するメモリのアライメントのバイト数を取得します。 |
Channel | 画像のチャンネル数(色数)を取得します。
1, 3, 4のいづれか |
Height | 画像の高さ(画素数)を取得します。 |
ImageBit | 画像のビット数を取得します。
8~16、24、30、32のいづれを指定します。 |
InterpolationMode | 画素間の輝度値の取得時の補間モードをInterpolationModeEnumにより取得/設定します。
初期値:InterpolationModeEnum.Bilinear |
MinusValueMode | 輝度値(画素値)に負の値を設定時の処理方法をMinusValueModeEnumにより取得/設定します。
初期値:MinusValueModeEnum.Zero |
PixelFormat | 画像のピクセルフォーマットをSystem.Drawing.Imaging.PixelFormatにより取得します。 |
Region | 画像処理処理範囲(参照範囲)をSystem.Drawing.Rectangleにより取得/設定します。 |
Sacn0 | 画像データを格納するメモリのポインタをSystem.IntPtrにより取得します。 |
Stride | 画像データを格納するメモリの幅をバイト数で取得します。 |
Width | 画像の幅(画素数)を取得します。 |
インデクサ
名前 | 説明 |
this[float, float, int] | 画像の行位置(Y座標)、列位置(X座標)、チャンネル番号(B[0]、G[1]、R[2])を指定して輝度値を取得します。 |
this[float, float] | 画像データの行位置(Y座標)、列位置(X座標)を指定して輝度値を取得します。 |
this[int, int, int] | 画像の行位置(Y座標)、列位置(X座標)、チャンネル番号(B[0]、G[1]、R[2])を指定して輝度値を取得/設定します。 |
this[int, int] | 画像データの行位置(Y座標)、列位置(X座標)を指定して輝度値を取得/設定します。 |
列挙型
名前 | 説明 |
BorderTypeEnum | BorderTypeプロパティを指定するのに用います。
Clamp:最外周の輝度値を用いる Mirror:(初期値)画像の外側から折り返した輝度値を用いる ToZero:画像の外側は輝度値0にする |
InterpolationModeEnum | InterpolationModeプロパティで画素間の輝度値を取得する際の補間方法を指定するのに用います。
Bicubic:バイキュービック Bilinear:(初期値)バイリニア NearestNeighbor:ニアレストネイバー |
MinusValueModeEnum | インデクサで負の値を指定した時の処理方法を指定します。
Absolute:負の場合は絶対値をとります。 Zero:(初期値)負の場合は0にします。 |
メソッド
名前 | 説明 |
Clone() | ImageDataクラスオブジェクトのクローンを作成します。画像データもコピーされます。 |
Clone(bool) | 画像データをコピーする(true)/しない(false)を指定してImageDataクラスオブジェクトのクローンを作成します。 |
CopyMemory(System.IntPtr, System.IntPtr, int) | コピー先のポインタ、コピー元のポインタ、コピーサイズを指定して画像データをコピーします。 |
CopyTo(ImagingSolution.Imaging.ImageData) | 画像データを指定したImageDataクラスオブジェクトへコピーします。 |
Dispose() | ImageDataクラスで使用されているリソースを解放します。 |
ImageData.IsEqualImageSize(
ImagingSolution.Imaging.ImageData, ImagingSolution.Imaging.ImageData) |
2つのImageDataクラスの画像サイズが等しい(true)か等しくない(false)かを確認します。 |
ResetRegion() | 指定した領域(Regionプロパティ)を解除します。 |
Save(string) | ImageDataクラスの画像データをファイルに保存します。 |
ToBitmap() | ImageDataクラスオブジェクトをSystem.Drawing.Bitmapクラスへ変換します。 |
ZeroMemory(System.IntPtr, int) | ポインタで示されているメモリから指定バイト数分、0に設定します。 |
サンプルプログラム
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows.Forms;
namespace ImageDataSample
{
public partial class Form1 : Form
{
///
/// 表示用ImageDataクラスオブジェクト
///
ImagingSolution.Imaging.ImageData _img;
public Form1()
{
InitializeComponent();
}
///
/// 画像ファイルを開く
///
///
private void mnuFileOpen_Click(object sender, EventArgs e)
{
//ファイルを開くダイアログボックスの作成
var ofd = new OpenFileDialog();
//ファイルフィルタ
ofd.Filter = "Image File(*.bmp,*.jpg,*.png,*.tif)|*.bmp;*.jpg;*.png;*.tif|Bitmap(*.bmp)|*.bmp|Jpeg(*.jpg)|*.jpg|PNG(*.png)|*.png";
//ダイアログの表示 (Cancelボタンがクリックされた場合は何もしない)
if (ofd.ShowDialog() == DialogResult.Cancel) return;
if (_img != null) _img.Dispose();
_img = new ImagingSolution.Imaging.ImageData(ofd.FileName);
picImage.Image = _img.ToBitmap();
}
///
/// 名前を付けて画像を保存
///
///
private void mnuFileSave_Click(object sender, EventArgs e)
{
// 画像データが設定されていない場合は何もしない
if (_img == null) return;
//名前を付けて保存ダイアログボックスの作成
var sfd = new SaveFileDialog();
//ファイルフィルタ
sfd.Filter = "Bitmap(*.bmp)|*.bmp|Jpeg(*.jpg)|*.jpg|PNG(*.png)|*.png|CSV(*.csv)|*.csv";
//ダイアログの表示 (Cancelボタンがクリックされた場合は何もしない)
if (sfd.ShowDialog() == DialogResult.Cancel) return;
// 名前を付けて画像データを保存
_img.Save(sfd.FileName);
}
///
/// 終了
///
///
/// /param>
private void mnuFileExit_Click(object sender, EventArgs e)
{
this.Close();
}
///
/// 輝度値の表示
///
///
private void picImage_MouseMove(object sender, MouseEventArgs e)
{
if (_img == null) return;
// X座標
int px = e.X;
// Y座標
int py = e.Y;
if (_img.Channel == 1)
{
// モノクロの場合
lblBright.Text = "(" + px.ToString() + ", " + py.ToString() + ") = "
+ _img[py, px].ToString();
}
else if (_img.Channel == 3)
{
// カラーの場合
lblBright.Text = "(" + px.ToString() + ", " + py.ToString() + ") = ("
+ _img[py, px, 2].ToString() + ", " // R
+ _img[py, px, 1].ToString() + ", " // G
+ _img[py, px, 0].ToString() + ")"; // B
}
}
private void mnuFilter_Click(object sender, EventArgs e)
{
// フィルタ処理
if (_img == null) return;
var sw = new System.Diagnostics.Stopwatch();
// 時間計測開始
sw.Start();
// 処理後のImageDataクラス
ImagingSolution.Imaging.ImageData dst = null;
// クリックされたメニュー
var mnu = sender as System.Windows.Forms.ToolStripMenuItem;
switch (mnu.Text){
case "GrayScale":
// グレースケール変換
ConvertGrayScale(_img, ref dst);
break;
case "Gaussian":
// ガウシアンフィルタ
GaussianBlur(_img, ref dst);
break;
case "Sobel":
// ソーベルフィルタ
Sobel(_img, ref dst);
break;
}
// 時間計測停止
sw.Stop();
// 処理時間の表示
lblTime.Text = sw.ElapsedMilliseconds.ToString() + "mSec";
// 元の画像データを解放
_img.Dispose();
// 表示用画像データを設定
_img = dst;
// 画像の表示
picImage.Image = _img.ToBitmap();
}
///
/// グレースケールへ変換
///
private void ConvertGrayScale(ImagingSolution.Imaging.ImageData src, ref ImagingSolution.Imaging.ImageData dst)
{
int i, j;
int width = src.Width;
int height = src.Height;
// dstが無効の場合、モノクロのImageDataクラスをインスタンス
if (dst == null) dst = new ImagingSolution.Imaging.ImageData(width, height, System.Drawing.Imaging.PixelFormat.Format8bppIndexed);
for (j = 0; j < height; j++)
{
for (i = 0; i < width; i++)
{
dst[j, i] = (int)(
src[j, i, 2] * 0.299 // R
+ src[j, i, 1] * 0.587 // G
+ src[j, i, 0] * 0.114); // B
}
}
}
///
/// ガウシアンフィルタ
///
/// ;
///
///
private void Sobel(ImagingSolution.Imaging.ImageData src, ref ImagingSolution.Imaging.ImageData dst)
{
int i, j;
int width = src.Width;
int height = src.Height;
int channel = src.Channel;
// 処理後画像データを確保(メモリのコピーはしない)
var XSobel = src.Clone(false);
var YSobel = src.Clone(false);
// 負の値は絶対値にする
XSobel.MinusValueMode = ImagingSolution.Imaging.ImageData.MinusValueModeEnum.Absolute;
YSobel.MinusValueMode = ImagingSolution.Imaging.ImageData.MinusValueModeEnum.Absolute;
for (j = 0; j < height; j++)
{
for (i = 0; i < width * channel; i++)
{
XSobel[j, i] = (
-src[j - 1, i - channel] + src[j - 1, i + channel]
- src[j, i - channel] * 2 + src[j, i + channel] * 2
- src[j + 1, i - channel] + src[j + 1, i + channel]
);
YSobel[j, i] = (
-src[j - 1, i - channel] - src[j - 1, i] * 2 - src[j - 1, i + channel]
+ src[j + 1, i - channel] + src[j + 1, i] * 2 + src[j + 1, i + channel]
);
}
}
// X方向とY方向を足し合わせる
dst = XSobel + YSobel;
// 解放
XSobel.Dispose();
YSobel.Dispose();
}
}
}
コメント
[…] 3/17 【C#】ImageDataクラスライブラリ公開 […]
[…] 3/17 【C#】ImageDataクラスライブラリ公開 […]
[…] 3/17 【C#】ImageDataクラスライブラリ公開 […]
[…] 3/17 【C#】ImageDataクラスライブラリ公開 […]