システム開発・Webサイト構築 プラスラス

2008/2/11 月曜日

【.NET】カラー行列(ColorMatrix)で画像のRGBの値を調整する

このエントリーを含むはてなブックマーク Yahoo!ブックマークに登録 Google ブックマーク del.icio.us

VB.NETやC#で画像のRGB値を操作するには、System.Drawing.Bitmap.GetPixel()System.Drawing.Bitmap.SetPixel()を使用して、各ピクセルの値を変更することで実現可能です。

しかし、大きな画像全体の色情報を変更するには、GetPixel()やSetPixel()は重いため、処理に時間がかかりすぎます。

ColorMatrixで色の平行移動をした例

やりたいことが各ピクセルに対する一律に行うような操作であれば、画像を描画する際にImageAttributesの各設定を行うことで、工夫次第で簡単なコードで高速に動作させることができる可能性があります。検討してみてください。

(ImageAttributesが用途に合わなければ、時間がかかってもGetPixcel()、SetPixcel()を使って処理するか、System.Drawing.Bitmap.LockBits() System.Drawing.Bitmap.UnlockBits() を使用して内部データにアクセスする方法を考えます。)

今回は、ImageAttributesのSetColorMatrix()を使い、カラー行列で各ピクセルのRGB値に一律的に加算・減算を行う方法を紹介します。

カラー行列について

具体的に図で説明します。

元画像のすべてのピクセルに対して下記の計算が行われると考えれば良いです。

下図が元のあるピクセルのRGBAの値だとします。左からR(赤)、G(緑)、B(青)、A(不透明度)です。
元のRGBA

これに、下図のカラー行列との計算を行うとします。下の各セルに任意の数値が入るイメージです。
カラー行列

カラー行列にセットするのは、5 × 5の配列です。

実際は、左上の4 × 4の部分が元のRGBAとの行列の積になり、5行目の部分は元のRGBAに対する平行移動(加算)が行われます。

計算結果のRGBAは、下図のようになります。左から結果のR、G、B、Aの値です。

計算後のRGBA

RGBAの各値の範囲は、0から1に置き換えて考えます。たとえば各チャンネル8ビットであらわされる場合の255の値は0~255の最大なので、ここでの値は1です。

計算は、まず元のRGBAと青に塗ってある部分とで行列のかけ算を行います。

その結果に対して赤で塗ってあるところをそれぞれ足し算します。

緑に塗ってある部分は単純に5 × 5の行列とみなすためのダミーで、元のRGBAに全く影響しません。1~4行目は常に0で、5行目は常に1にします。

また、行列の積の部分(上表の青に塗ってあるところ)は、結果の値が0から1の範囲外になった場合は小数部だけ保持されます。たとえば計算結果が1.5の場合は0.5になります。

平行移動の部分(赤に塗ってある)については、0以下の場合は0に、1以上の場合は1に切り詰められます。

青以外の値を一律ゼロにするサンプル

カラー行列を使用した描画を行うには、ImageAttributesオブジェクトのSetColorMatrix()でColorMatrixオブジェクトを設定し、Graphics.DrawImage()の引数にそのImageAttributesオブジェクトを指定します。

以下のコードは全てのピクセルのRとGの値を0にしてしまうサンプルです。

'VB.NETのサンプル
'ファイルを読み込む
Dim sourceImage As Bitmap = _
    DirectCast(Bitmap.FromFile("ファイル名.bmp", True), Bitmap)

'RGB値に加算する値を計算
'RGB値に対する変更幅(加算後の値は最大 1 最小 0)
Dim r As Single = -1.0F    '赤:なくしてしまう
Dim g As Single = -1.0F    '緑:なくしてしまう
Dim b As Single =  0.0F    '青:変更しないものは0

'ColorMatrixにセットする行列を 5 * 5 の配列で用意
'(平行移動(加減算)だけ記述)
Dim matrixElement As Single()() = _
                    {New Single() {1, 0, 0, 0, 0}, _
                     New Single() {0, 1, 0, 0, 0}, _
                     New Single() {0, 0, 1, 0, 0}, _
                     New Single() {0, 0, 0, 1, 0}, _
                     New Single() {r, g, b, 0, 1}}

'ColorMatrixオブジェクトの作成
Dim matrix As ColorMatrix = New ColorMatrix(matrixElement)

'ImageAttributesにセット
Dim attr As ImageAttributes = New ImageAttributes()
attr.SetColorMatrix(matrix)

'元のイメージの大きさ
Dim imageWidth As Integer = sourceImage.Width
Dim imageHeight As Integer = sourceImage.Height

'同じ大きさで新しいビットマップを用意
Dim changedImage As Bitmap = New Bitmap(imageWidth, imageHeight)

'新しいビットマップにImageAttributesを指定して
'元のビットマップを描画

Dim graph As Graphics = Graphics.FromImage(changedImage)

graph.DrawImage(sourceImage, _
                New Rectangle(0, 0, imageWidth, imageHeight), _
                0, 0, imageWidth, imageHeight, _
                GraphicsUnit.Pixel, _
                attr)

graph.Dispose()

'--表示や保存などの処理をここに記述--

'使い終わったら後始末
'sourceImage.Dispose()
'changedImage.Dispose()
//C#のサンプル

//元のイメージを読み込む
Bitmap sourceImage =
    (Bitmap)Bitmap.FromFile(@"ファイル名.bmp", true)

//RGB値に対する変更幅(加算後の値は最大 1 最小 0)
float r = -1.0F;    //赤:なくしてしまう
float g = -1.0F;    //緑:なくしてしまう
float b =  0.0F;    //青:変更しないものは0

//ColorMatrixにセットする行列を 5 * 5 の配列で用意
//(平行移動(加減算)だけ記述)
float[][] matrixElement =
                      {new float[]{1, 0, 0, 0, 0},
                       new float[]{0, 1, 0, 0, 0},
                       new float[]{0, 0, 1, 0, 0},
                       new float[]{0, 0, 0, 1, 0},
                       new float[]{r, g, b, 0, 1}};

//ColorMatrixオブジェクトの作成
ColorMatrix matrix = new ColorMatrix(matrixElement);

//ImageAttributesにセット
ImageAttributes attr = new ImageAttributes();
attr.SetColorMatrix(matrix);

//元のイメージの大きさ
int imageWidth  = sourceImage.Width;
int imageHeight = sourceImage.Height;

//同じ大きさで新しいビットマップを用意
Bitmap changedImage = new Bitmap(imageWidth, imageHeight);

//新しいビットマップにImageAttributesを指定して
//元のビットマップを描画
Graphics graph = Graphics.FromImage(changedImage);

graph.DrawImage(sourceImage,
                new Rectangle(0, 0, imageWidth, imageHeight),
                0, 0, imageWidth, imageHeight,
                GraphicsUnit.Pixel,
                attr);

graph.Dispose();

//--表示や保存などの処理をここに記述--

//使い終わったら後始末
//sourceImage.Dispose();
//changedImage.Dispose();

このプログラムを実行すると以下の画像のような変換が行われます。
青以外の値を一律ゼロ

平行移動の値は、-1を加算した結果0より小さくはなる値は0であるため、RGBのRとGは0になり、Bの値は元のまま残ります。
赤い文字:(1, 0, 0) → (0, 0, 0)
緑の文字:(0, 1, 0) → (0, 0, 0)
青い文字:(0, 0, 1) → (0, 0, 1)
白の背景:(1, 1, 1) → (0, 0, 1)

色の平行移動のサンプルプログラム

実際に動作するプログラムを作成したので、興味のある方はご覧ください。

RGBEdit 実行中スクリーンショット

実行ファイルのダウンロード

RGBEdit VB.NETのソースファイル

RGBEdit C#のソースファイル

*このサンプルプログラムは、トラックバーでリアルタイムに制御していますが、大きな解像度の画像だと動きが鈍いと思います。

実際にリアルタイムのプレビューが必要であれば、確認に必要な程度の小さな解像度の画像に変換してからプレビューさせて、最終的に元の画像に適用させるなどのアプローチが良いと思われます。

関連図書

ディジタル画像処理の基礎と応用 改訂版―Visual C#.NET&Visual Basic.NETによる 基本概念から
ディジタル画像処理の基礎と応用 改訂版―Visual C#.NET&Visual Basic.NETによる 基本概念から

関連記事

タグ: ,
Filed under: C#,Programming,VB.NET — Nakai @ 19:24:09

コメントはまだありません »

コメントはまだありません。

この投稿へのコメントの RSS フィード。 TrackBack URL

コメントする

HTML convert time: 0.866 sec. Powered by WordPress