注意:画像の表示方法が分からないという方は、まず「コントロールやフォームに画像を表示する」をご覧ください。
画像のRGB(およびA)を補正するには、ColorMatrixクラスが便利です。ColorMatrixクラスは、5x5の行列を表します。色を表すRGBAW(赤、緑、青、アルファ、w(常に1))とColorMatrixの行列を掛けた結果が、変換後の新しい色になります。
┌ ┐ │ M00 M01 M02 M03 M04 │ ┌ ┐│ M10 M11 M12 M13 M14 │ ┌ ┐ │ R G B A 1 ││ M20 M21 M22 M23 M24 │=│ NewR NewG NewB NewA NewW │ └ ┘│ M30 M31 M32 M33 M34 │ └ ┘ │ M40 M41 M42 M43 M44 │ └ ┘
つまり新しい色(NewR、NewG、NewB)は、次のような式で計算できます。
NewR = M00*R + M10*G + M20*B + M30*A + M40 NewG = M01*R + M11*G + M21*B + M31*A + M41 NewB = M02*R + M12*G + M22*B + M32*A + M42
具体例を示しましょう。例えば下のような5x5の行列を用意すれば、赤を2倍、緑を1.5倍、青を0.5倍して0.1(255を1とした時)を足した色に変換することができます。
┌ ┐ │ 2.0 0.0 0.0 0.0 0.0 │ ┌ ┐│ 0.0 1.5 0.0 0.0 0.0 │ ┌ ┐ │ R G B A 1.0 ││ 0.0 0.0 0.5 0.0 0.0 │=│ 2.0*R 1.5*G 0.5*B+0.1 A 1.0 │ └ ┘│ 0.0 0.0 0.0 1.0 0.0 │ └ ┘ │ 0.0 0.0 0.1 0.0 1.0 │ └ ┘
このように、単位行列(n行n列が1で、それ以外が0)を基本として、赤の倍率を0行0列に、緑の倍率を1行1列に、青の倍率を2行2列に指定すればよいということが分かります。また、赤にプラスする値を4行0列に、緑にプラスする値を4行1列に、青にプラスする値を4行2列に指定すればよいということも分かります。
なお計算の結果、値が1(255を1とした時)を超えた時は1に、0に満たなかった時は0になるようです。
以下に、カラーバランスを調整するメソッドと、それを使用する例を示します。このメソッドは、指定した画像のRGBに、指定した倍率を掛けて画像を作成するものです。PictureBoxコントロール(PictureBox1)をクリックすると、画像(C:\test\1.png)の赤を2倍にした画像を作成して、PictureBox1に表示しています。
'Imports System.Drawing ''' <summary> ''' 指定した画像の色を補正した画像を取得する ''' </summary> ''' <param name="img">色の補正をする画像</param> ''' <param name="rScale">赤に掛ける倍率</param> ''' <param name="gScale">緑に掛ける倍率</param> ''' <param name="bScale">青に掛ける倍率</param> ''' <returns></returns> Public Shared Function CreateColorCorrectedImage( _ ByVal img As Image, ByVal rScale As Single, _ ByVal gScale As Single, ByVal bScale As Single) As Image '補正された画像の描画先となるImageオブジェクトを作成 Dim newImg As New Bitmap(img.Width, img.Height) 'newImgのGraphicsオブジェクトを取得 Dim g As Graphics = Graphics.FromImage(newImg) 'ColorMatrixオブジェクトの作成 '指定された倍率を掛けるための行列を指定する Dim cm As New System.Drawing.Imaging.ColorMatrix(New Single()() _ {New Single() {rScale, 0, 0, 0, 0}, _ New Single() {0, gScale, 0, 0, 0}, _ New Single() {0, 0, bScale, 0, 0}, _ New Single() {0, 0, 0, 1, 0}, _ New Single() {0, 0, 0, 0, 1}}) '次のようにしても同じ 'Dim cm As New System.Drawing.Imaging.ColorMatrix() 'cm.Matrix00 = rScale 'cm.Matrix11 = gScale 'cm.Matrix22 = bScale 'cm.Matrix33 = 1 'cm.Matrix44 = 1 'ImageAttributesオブジェクトの作成 Dim ia As New System.Drawing.Imaging.ImageAttributes() 'ColorMatrixを設定する ia.SetColorMatrix(cm) 'ImageAttributesを使用して描画 g.DrawImage(img, New Rectangle(0, 0, img.Width, img.Height), _ 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, ia) 'リソースを解放する g.Dispose() Return newImg End Function 'PictureBox1のClickイベントハンドラ Private Sub PictureBox1_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles PictureBox1.Click '色補正をする画像 Dim img As New Bitmap("C:\test\1.png") '赤を倍にする Dim newImg As Image = CreateColorCorrectedImage(img, 2, 1, 1) img.Dispose() 'PictureBox1に表示 If Not PictureBox1.Image Is Nothing Then PictureBox1.Image.Dispose() End If PictureBox1.Image = newImg End Sub
//using System.Drawing; /// <summary> /// 指定した画像の色を補正した画像を取得する /// </summary> /// <param name="img">色の補正をする画像</param> /// <param name="rScale">赤に掛ける倍率</param> /// <param name="gScale">緑に掛ける倍率</param> /// <param name="bScale">青に掛ける倍率</param> /// <returns></returns> public static Image CreateColorCorrectedImage(Image img, float rScale, float gScale, float bScale) { //補正された画像の描画先となるImageオブジェクトを作成 Bitmap newImg = new Bitmap(img.Width, img.Height); //newImgのGraphicsオブジェクトを取得 Graphics g = Graphics.FromImage(newImg); //ColorMatrixオブジェクトの作成 //指定された倍率を掛けるための行列を指定する System.Drawing.Imaging.ColorMatrix cm = new System.Drawing.Imaging.ColorMatrix( new float[][] { new float[] {rScale, 0, 0, 0, 0}, new float[] {0, gScale, 0, 0, 0}, new float[] {0, 0, bScale, 0, 0}, new float[] {0, 0, 0, 1, 0}, new float[] {0, 0, 0, 0, 1} }); //次のようにしても同じ //System.Drawing.Imaging.ColorMatrix cm = // new System.Drawing.Imaging.ColorMatrix(); //cm.Matrix00 = rScale; //cm.Matrix11 = gScale; //cm.Matrix22 = bScale; //cm.Matrix33 = 1; //cm.Matrix44 = 1; //ImageAttributesオブジェクトの作成 System.Drawing.Imaging.ImageAttributes ia = new System.Drawing.Imaging.ImageAttributes(); //ColorMatrixを設定する ia.SetColorMatrix(cm); //ImageAttributesを使用して描画 g.DrawImage(img, new Rectangle(0, 0, img.Width, img.Height), 0, 0, img.Width, img.Height, GraphicsUnit.Pixel, ia); //リソースを解放する g.Dispose(); return newImg; } //PictureBox1のClickイベントハンドラ private void PictureBox1_Click(object sender, EventArgs e) { //色補正をする画像 Bitmap img = new Bitmap(@"C:\test\1.png"); //赤を倍にする Image newImg = CreateColorCorrectedImage(img, 2, 1, 1); img.Dispose(); //PictureBox1に表示 if (PictureBox1.Image != null) { PictureBox1.Image.Dispose(); } PictureBox1.Image = newImg; }
上が元の画像で、下が赤を2倍にした画像です。
Bitmap.GetPixelメソッドで指定したピクセルの色を取得できます。また、Bitmap.SetPixelメソッドで指定したピクセルの色を変更することができます。この2つのメソッドを使えば、画像の色を変更することができます。
以下の例では、指定された値を赤、緑、青に加えるメソッドを作成しています。PictureBox1をクリックすると、赤に128を加えた画像を表示します。
'Imports System.Drawing ''' <summary> ''' 指定した画像の色を補正する ''' </summary> ''' <param name="img">補正する画像</param> ''' <param name="rValue">赤の増加値(-255~255)</param> ''' <param name="gValue">緑の増加値(-255~255)</param> ''' <param name="bValue">青の増加値(-255~255)</param> Public Shared Sub CorrectColorImage( _ ByVal img As Bitmap, ByVal rValue As Integer, _ ByVal gValue As Integer, ByVal bValue As Integer) For x As Integer = 0 To img.Width - 1 For y As Integer = 0 To img.Height - 1 '指定したピクセルの色を取得する Dim c As Color = img.GetPixel(x, y) '新しい色を計算する Dim newR As Integer = Math.Max(0, Math.Min(255, c.R + rValue)) Dim newG As Integer = Math.Max(0, Math.Min(255, c.G + gValue)) Dim newB As Integer = Math.Max(0, Math.Min(255, c.B + bValue)) '新しい色を設定する img.SetPixel(x, y, Color.FromArgb(c.A, newR, newG, newB)) Next Next End Sub 'PictureBox1のClickイベントハンドラ Private Sub PictureBox1_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles PictureBox1.Click '色補正をする画像 Dim img As New Bitmap("C:\test\1.png") '赤を128増加させる CorrectColorImage(img, 128, 0, 0) 'PictureBox1に表示 If Not PictureBox1.Image Is Nothing Then PictureBox1.Image.Dispose() End If PictureBox1.Image = img End Sub
//using System.Drawing; /// <summary> /// 指定した画像の色を補正する /// </summary> /// <param name="img">補正する画像</param> /// <param name="rValue">赤の増加値(-255~255)</param> /// <param name="gValue">緑の増加値(-255~255)</param> /// <param name="bValue">青の増加値(-255~255)</param> public static void CorrectColorImage(Bitmap img, int rValue, int gValue, int bValue) { for (int x = 0; x < img.Width; x++) { for (int y = 0; y < img.Height; y++) { //指定したピクセルの色を取得する Color c = img.GetPixel(x, y); //新しい色を計算する int newR = Math.Max(0, Math.Min(255, c.R + rValue)); int newG = Math.Max(0, Math.Min(255, c.G + gValue)); int newB = Math.Max(0, Math.Min(255, c.B + bValue)); //新しい色を設定する img.SetPixel(x, y, Color.FromArgb(c.A, newR, newG, newB)); } } } //PictureBox1のClickイベントハンドラ private void PictureBox1_Click(object sender, EventArgs e) { //色補正をする画像 Bitmap img = new Bitmap(@"C:\test\1.png"); //赤を128増加させる CorrectColorImage(img, 128, 0, 0); //PictureBox1に表示 if (PictureBox1.Image != null) { PictureBox1.Image.Dispose(); } PictureBox1.Image = img; }
結果は、以下のようになります。
GetPixelとSetPixelメソッドはあまりパフォーマンスがよくありませんので、画像のすべてのピクセルを書き換える場合は、Bitmap.LockBitsメソッドを使った方がよいでしょう。
以下に、Bitmap.LockBitsメソッドを使って上記のコードを書き直した例を示します。
'Imports System.Drawing 'Imports System.Drawing.Imaging ''' <summary> ''' 指定した画像の色を補正する ''' </summary> ''' <param name="img">補正する画像</param> ''' <param name="rValue">赤の増加値(-255~255)</param> ''' <param name="gValue">緑の増加値(-255~255)</param> ''' <param name="bValue">青の増加値(-255~255)</param> Public Shared Sub AdjustColorImage( _ ByVal img As Bitmap, ByVal rValue As Integer, _ ByVal gValue As Integer, ByVal bValue As Integer) '1ピクセルあたりのバイト数を取得する Dim pixelFormat As PixelFormat = img.PixelFormat Dim pixelSize As Integer = Image.GetPixelFormatSize(pixelFormat) / 8 If pixelSize < 3 OrElse 4 < pixelSize Then Throw New ArgumentException( _ "1ピクセルあたり24または32ビットの形式のイメージのみ有効です。", _ "img") End If 'または次のように元の画像とは異なるPixelFormatでLockBitsすることも可能 'この場合、UnlockBitsで元のPixelFormatに戻る 'ただし、元のPixelFormatとLockBits時のPixelFormatが異なる場合は、 '変更した色とは異なる色になる可能性がある 'pixelFormat = PixelFormat.Format32bppArgb 'pixelSize = 4 'Bitmapをロックする Dim bmpDate As BitmapData = _ img.LockBits(New Rectangle(0, 0, img.Width, img.Height), _ ImageLockMode.ReadWrite, img.PixelFormat) If bmpDate.Stride < 0 Then img.UnlockBits(bmpDate) Throw New ArgumentException( _ "ボトムアップ形式のイメージには対応していません。", _ "img") End If 'ピクセルデータをバイト型配列で取得する Dim ptr As IntPtr = bmpDate.Scan0 Dim pixels As Byte() = New Byte(bmpDate.Stride * img.Height - 1) {} System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, pixels.Length) 'すべてのピクセルの色を補正する For y As Integer = 0 To bmpDate.Height - 1 For x As Integer = 0 To bmpDate.Width - 1 'ピクセルデータでのピクセル(x,y)の開始位置を計算する Dim pos As Integer = y * bmpDate.Stride + x * pixelSize '新しい色を計算する Dim newR As Integer = _ Math.Max(0, Math.Min(255, pixels(pos + 2) + rValue)) Dim newG As Integer = _ Math.Max(0, Math.Min(255, pixels(pos + 1) + gValue)) Dim newB As Integer = _ Math.Max(0, Math.Min(255, pixels(pos) + bValue)) '色を変更する pixels(pos + 2) = CByte(newR) pixels(pos + 1) = CByte(newG) pixels(pos) = CByte(newB) Next Next 'ピクセルデータを元に戻す System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, pixels.Length) 'ロックを解除する img.UnlockBits(bmpDate) End Sub 'PictureBox1のClickイベントハンドラ Private Sub PictureBox1_Click(ByVal sender As Object, ByVal e As EventArgs) _ Handles PictureBox1.Click '色補正をする画像 Dim img As New Bitmap("C:\test\1.png") '赤を128増加させる AdjustColorImage(img, 128, 0, 0) 'PictureBox1に表示 If Not PictureBox1.Image Is Nothing Then PictureBox1.Image.Dispose() End If PictureBox1.Image = img End Sub
//using System.Drawing; //using System.Drawing.Imaging; /// <summary> /// 指定した画像の色を補正する /// </summary> /// <param name="img">補正する画像</param> /// <param name="rValue">赤の増加値(-255~255)</param> /// <param name="gValue">緑の増加値(-255~255)</param> /// <param name="bValue">青の増加値(-255~255)</param> public static void AdjustColorImage(Bitmap img, int rValue, int gValue, int bValue) { //1ピクセルあたりのバイト数を取得する PixelFormat pixelFormat = img.PixelFormat; int pixelSize = Image.GetPixelFormatSize(pixelFormat) / 8; if (pixelSize < 3 || 4 < pixelSize) { throw new ArgumentException( "1ピクセルあたり24または32ビットの形式のイメージのみ有効です。", "img"); } //または次のように元の画像とは異なるPixelFormatでLockBitsすることも可能 //この場合、UnlockBitsで元のPixelFormatに戻る //ただし、元のPixelFormatとLockBits時のPixelFormatが異なる場合は、 //変更した色とは異なる色になる可能性がある //pixelFormat = PixelFormat.Format32bppArgb; //pixelSize = 4; //Bitmapをロックする BitmapData bmpDate = img.LockBits( new Rectangle(0, 0, img.Width, img.Height), ImageLockMode.ReadWrite, img.PixelFormat); if (bmpDate.Stride < 0) { img.UnlockBits(bmpDate); throw new ArgumentException( "ボトムアップ形式のイメージには対応していません。", "img"); } //ピクセルデータをバイト型配列で取得する IntPtr ptr = bmpDate.Scan0; byte[] pixels = new byte[bmpDate.Stride * img.Height]; System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, pixels.Length); //すべてのピクセルの色を補正する for (int y = 0; y < bmpDate.Height; y++) { for (int x = 0; x < bmpDate.Width; x++) { //ピクセルデータでのピクセル(x,y)の開始位置を計算する int pos = y * bmpDate.Stride + x * pixelSize; //新しい色を計算する int newR = Math.Max(0, Math.Min(255, pixels[pos + 2] + rValue)); int newG = Math.Max(0, Math.Min(255, pixels[pos + 1] + gValue)); int newB = Math.Max(0, Math.Min(255, pixels[pos] + bValue)); //色を変更する pixels[pos + 2] = (byte)newR; pixels[pos + 1] = (byte)newG; pixels[pos] = (byte)newB; } } //ピクセルデータを元に戻す System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, pixels.Length); //アンセーフコードを使うと、以下のようにもできる //unsafe //{ // byte* pixelPtr = (byte*)bmpDate.Scan0; // for (int y = 0; y < bmpDate.Height; y++) // { // for (int x = 0; x < bmpDate.Width; x++) // { // //ピクセルデータでのピクセル(x,y)の開始位置を計算する // int pos = y * bmpDate.Stride + x * pixelSize; // //新しい色を計算する // int newR = Math.Max(0, Math.Min(255, pixels[pos + 2] + rValue)); // int newG = Math.Max(0, Math.Min(255, pixels[pos + 1] + gValue)); // int newB = Math.Max(0, Math.Min(255, pixels[pos] + bValue)); // //色を変更する // pixelPtr[pos + 2] = (byte)newR; // pixelPtr[pos + 1] = (byte)newG; // pixelPtr[pos] = (byte)newB; // } // } //} //ロックを解除する img.UnlockBits(bmpDate); } //PictureBox1のClickイベントハンドラ private void PictureBox1_Click(object sender, EventArgs e) { //色補正をする画像 Bitmap img = new Bitmap(@"C:\test\1.png"); //赤を128増加させる AdjustColorImage(img, 128, 0, 0); //PictureBox1に表示 if (PictureBox1.Image != null) { PictureBox1.Image.Dispose(); } PictureBox1.Image = img; }
注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。