DOBON.NET プログラミング道: .NET Framework, VB.NET, C#, Visual Basic, Visual Studio, インストーラ, ...

色を反転させた画像(ネガティブイメージ)を表示する

注意:画像の表示方法が分からないという方は、まず「コントロールやフォームに画像を表示する」をご覧ください。

ColorMatrixを使用した方法

ネガティブイメージ(色を反転させた画像)を作成するには、画像のすべてのピクセルで、255からRGBの各成分を引きます。この処理は、ColorMatrixクラスを使用すれば簡単にできます。ColorMatrixに関して詳しくは「画像のカラーバランスを補正して表示する」をご覧ください。

以下の例では、指定された画像からネガティブイメージを作成するメソッドを作成しています。また、PictureBoxコントロール(PictureBox1)をクリックすると、画像ファイル(C:\test\1.bmp)のネガティブイメージが表示されるようにしています。

VB.NET
コードを隠すコードを選択
'Imports System.Drawing

''' <summary>
''' 指定された画像からネガティブイメージを作成する
''' </summary>
''' <param name="img">基の画像</param>
''' <returns>作成されたネガティブイメージ</returns>
Public Shared Function CreateNegativeImage(ByVal img As Image) As Image
    'ネガティブイメージの描画先となるImageオブジェクトを作成
    Dim negaImg As New Bitmap(img.Width, img.Height)
    'negaImgのGraphicsオブジェクトを取得
    Dim g As Graphics = Graphics.FromImage(negaImg)

    'ColorMatrixオブジェクトの作成
    Dim cm As New System.Drawing.Imaging.ColorMatrix()
    'ColorMatrixの行列の値を変更して、色が反転されるようにする
    cm.Matrix00 = -1
    cm.Matrix11 = -1
    cm.Matrix22 = -1
    cm.Matrix33 = 1
    cm.Matrix40 = 1
    cm.Matrix41 = 1
    cm.Matrix42 = 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 negaImg
End Function

'PictureBox1のClickイベントハンドラ
Private Sub PictureBox1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles PictureBox1.Click
    'ネガティブイメージにする画像
    Dim img As Image = Image.FromFile("C:\test\1.bmp")
    'ネガティブイメージを作成
    Dim negaImg As Image = CreateNegativeImage(img)
    img.Dispose()
    'PictureBox1に表示
    If PictureBox1.Image IsNot Nothing Then
        PictureBox1.Image.Dispose()
    End If
    PictureBox1.Image = negaImg
End Sub
C#
コードを隠すコードを選択
//using System.Drawing;

/// <summary>
/// 指定された画像からネガティブイメージを作成する
/// </summary>
/// <param name="img">基の画像</param>
/// <returns>作成されたネガティブイメージ</returns>
public static Image CreateNegativeImage(Image img)
{
    //ネガティブイメージの描画先となるImageオブジェクトを作成
    Bitmap negaImg = new Bitmap(img.Width, img.Height);
    //negaImgのGraphicsオブジェクトを取得
    Graphics g = Graphics.FromImage(negaImg);

    //ColorMatrixオブジェクトの作成
    System.Drawing.Imaging.ColorMatrix cm =
        new System.Drawing.Imaging.ColorMatrix();
    //ColorMatrixの行列の値を変更して、色が反転されるようにする
    cm.Matrix00 = -1;
    cm.Matrix11 = -1;
    cm.Matrix22 = -1;
    cm.Matrix33 = 1;
    cm.Matrix40 = cm.Matrix41 = cm.Matrix42 = 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 negaImg;
}

//PictureBox1のClickイベントハンドラ
private void PictureBox1_Click(object sender, EventArgs e)
{
    //ネガティブイメージにする画像
    Image img = Image.FromFile(@"C:\test\1.bmp");
    //ネガティブイメージを作成
    Image negaImg = CreateNegativeImage(img);
    img.Dispose();
    //PictureBox1に表示
    if (PictureBox1.Image != null)
    {
        PictureBox1.Image.Dispose();
    }
    PictureBox1.Image = negaImg;
}

Bitmap.LockBitsメソッドを使用した方法

次にすべてのピクセルデータの色を計算して設定する方法を紹介します。それには、Bitmap.GetPixelメソッドで色を取得して、Bitmap.SetPixelメソッドで色を設定するというやり方が考えられます。しかしこの方法はパフォーマンスに劣るため、大量のピクセルの色を変更する場合は、Bitmap.LockBitsメソッドを使った方がいいです。

この方法によって、指定した画像をネガティブイメージに変更するメソッドの例を示します。

VB.NET
コードを隠すコードを選択
'Imports System.Drawing
'Imports System.Drawing.Imaging

''' <summary>
''' 指定された画像をネガティブイメージに変換する
''' </summary>
''' <param name="img">変換する画像</param>
Public Shared Sub ChangeToNegativeImage(ByVal img As Bitmap)
    '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 bmpData As BitmapData = _
        img.LockBits(New Rectangle(0, 0, img.Width, img.Height), _
                     ImageLockMode.ReadWrite, pixelFormat)

    If bmpData.Stride < 0 Then
        img.UnlockBits(bmpData)
        Throw New ArgumentException( _
            "ボトムアップ形式のイメージには対応していません。", _
            "img")
    End If

    'ピクセルデータをバイト型配列で取得する
    Dim ptr As IntPtr = bmpData.Scan0
    Dim pixels As Byte() = New Byte(bmpData.Stride * img.Height - 1) {}
    System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, pixels.Length)

    'すべてのピクセルの色を反転させる
    For y As Integer = 0 To bmpData.Height - 1
        For x As Integer = 0 To bmpData.Width - 1
            'ピクセルデータでのピクセル(x,y)の開始位置を計算する
            Dim pos As Integer = y * bmpData.Stride + x * pixelSize
            '青、緑、赤の色を変更する
            pixels(pos) = CByte(255 - pixels(pos))
            pixels(pos + 1) = CByte(255 - pixels(pos + 1))
            pixels(pos + 2) = CByte(255 - pixels(pos + 2))
        Next
    Next

    'ピクセルデータを元に戻す
    System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, pixels.Length)

    'ロックを解除する
    img.UnlockBits(bmpData)
End Sub

'PictureBox1のClickイベントハンドラ
Private Sub PictureBox1_Click(ByVal sender As Object, ByVal e As EventArgs) _
        Handles PictureBox1.Click
    'ネガティブイメージにする画像
    Dim img As Image = Image.FromFile("C:\test\1.bmp")
    'ネガティブイメージに変換
    ChangeToNegativeImage(img)
    'PictureBox1に表示
    If Not (PictureBox1.Image Is Nothing) Then
        PictureBox1.Image.Dispose()
    End If
    PictureBox1.Image = img
End Sub
C#
コードを隠すコードを選択
//using System.Drawing;
//using System.Drawing.Imaging;

/// <summary>
/// 指定された画像をネガティブイメージに変換する
/// </summary>
/// <param name="img">変換する画像</param>
public static void ChangeToNegativeImage(Bitmap img)
{
    //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 bmpData = img.LockBits(
        new Rectangle(0, 0, img.Width, img.Height),
        ImageLockMode.ReadWrite,
        pixelFormat);

    if (bmpData.Stride < 0)
    {
        img.UnlockBits(bmpData);
        throw new ArgumentException(
            "ボトムアップ形式のイメージには対応していません。",
            "img");
    }

    //ピクセルデータをバイト型配列で取得する
    IntPtr ptr = bmpData.Scan0;
    byte[] pixels = new byte[bmpData.Stride * img.Height];
    System.Runtime.InteropServices.Marshal.Copy(ptr, pixels, 0, pixels.Length);

    //すべてのピクセルの色を反転させる
    for (int y = 0; y < bmpData.Height; y++)
    {
        for (int x = 0; x < bmpData.Width; x++)
        {
            //ピクセルデータでのピクセル(x,y)の開始位置を計算する
            int pos = y * bmpData.Stride + x * pixelSize;
            //青、緑、赤の色を変更する
            pixels[pos] = (byte)(255 - pixels[pos]);
            pixels[pos + 1] = (byte)(255 - pixels[pos + 1]);
            pixels[pos + 2] = (byte)(255 - pixels[pos + 2]);
        }
    }

    //ピクセルデータを元に戻す
    System.Runtime.InteropServices.Marshal.Copy(pixels, 0, ptr, pixels.Length);

    //アンセーフコードを使うと、以下のようにもできる
    //unsafe
    //{
    //    byte* pixelPtr = (byte*)bmpData.Scan0;
    //    for (int y = 0; y < bmpData.Height; y++)
    //    {
    //        for (int x = 0; x < bmpData.Width; x++)
    //        {
    //            //ピクセルデータでのピクセル(x,y)の開始位置を計算する
    //            int pos = y * bmpData.Stride + x * pixelSize;
    //            //青、緑、赤の色を変更する
    //            pixelPtr[pos] = (byte)(255 - pixelPtr[pos]);
    //            pixelPtr[pos + 1] = (byte)(255 - pixelPtr[pos + 1]);
    //            pixelPtr[pos + 2] = (byte)(255 - pixelPtr[pos + 2]);
    //        }
    //    }
    //}

    //ロックを解除する
    img.UnlockBits(bmpData);
}

//PictureBox1のClickイベントハンドラ
private void PictureBox1_Click(object sender, EventArgs e)
{
    //ネガティブイメージにする画像
    Bitmap img = new Bitmap(@"C:\test\1.bmp");
    //ネガティブイメージに変換
    ChangeToNegativeImage(img);
    //PictureBox1に表示
    if (PictureBox1.Image != null)
    {
        PictureBox1.Image.Dispose();
    }
    PictureBox1.Image = img;
}
  • 履歴:
  • 2012/8/2 サンプルをメソッドにした。「Bitmap.LockBitsメソッドを使用した方法」を追加。
  • 2015/6/30 「LockBitsメソッドを使用する方法」のサンプルで、元のPixelFormatとは異なるPixelFormatでLockBitsできる説明を追加。ボトムアップ形式のチェックを追加など。
  • 2016/9/4 bmpDateという変数名をbmpDataに変更。

注意:この記事では、基本的な事柄の説明が省略されているかもしれません。初心者の方は、特に以下の点にご注意ください。

  • このサイトで紹介されているコードの多くは、例外処理が省略されています。例外処理については、こちらをご覧ください。
  • イベントハンドラの意味が分からない、C#のコードをそのまま書いても動かないという方は、こちらをご覧ください。
  • コードの先頭に記述されている「Imports ??? がソースファイルの一番上に書かれているものとする」(C#では、「using ???; がソースファイルの一番上に書かれているものとする」)の意味が分からないという方は、こちらをご覧ください。
  • Windows Vista以降でUACが有効になっていると、ファイルへの書き込みに失敗する可能性があります。詳しくは、こちらをご覧ください。
  • .NET Tipsをご利用いただく際は、注意事項をお守りください。