9

C#で画像のコントラストを調整する効率的な方法はありますか?

ピクセル単位の操作を推奨するこの記事を見たことがあります。速くない。

私はすでにいくつかの場所でカラー マトリックスを使用しており、それらが迅速であることがわかりました。それらを使用してコントラストを調整する方法はありますか? (注:この男はそれを誤解しています。)

私も EmguCV を使っています。OpenCV (Emgu がラップするもの)にはコントラスト機能があるように見えます。Emgu を介してこれにアクセスする方法はありますか? 現時点では、Emgu でできることは、ヒストグラムを正規化することだけです。これにより、コントラストが変更されますが、私の側である程度制御することはできません。

誰でもアイデアはありますか?

4

3 に答える 3

30

そのサンプルのコードが機能する場合は、ポインターを介してビットマップのピクセル データにアクセスできるオブジェクトBitmap.LockBitsを返すを使用して、コードを大幅に (桁違いに) 高速化できます。BitmapDataWeb および StackOverflow には、LockBits の使用方法を示す多数のサンプルがあります。

Bitmap.SetPixel()Bitmap.GetPixel()は人類に知られている最も遅い方法であり、どちらも人類に知られてColorいる最も遅いクラスであるクラスを利用しています。不注意な開発者への警告として名前を付ける必要がBitmap.GetPixelAndByGodYoullBeSorryYouDid()ありました。Bitmap.SetPixelWhileGettingCoffee

更新: そのサンプルのコードを変更する場合は、次のチャンクに注意してください。

System.Drawing.Bitmap TempBitmap = Image;
System.Drawing.Bitmap NewBitmap = new System.Drawing.Bitmap(TempBitmap.Width,
    TempBitmap.Height);
System.Drawing.Graphics NewGraphics = 
    System.Drawing.Graphics.FromImage(NewBitmap);
NewGraphics.DrawImage(TempBitmap, new System.Drawing.Rectangle(0, 0, 
    TempBitmap.Width, TempBitmap.Height), 
    new System.Drawing.Rectangle(0, 0, TempBitmap.Width, TempBitmap.Height),
    System.Drawing.GraphicsUnit.Pixel);
NewGraphics.Dispose();

これで置き換えることができます:

Bitmap NewBitmap = (Bitmap)Image.Clone();

更新 2: これは、AdjustContrast メソッドの LockBits バージョンです (その他のいくつかの速度改善を含む)。

public static Bitmap AdjustContrast(Bitmap Image, float Value)
{
    Value = (100.0f + Value) / 100.0f;
    Value *= Value;
    Bitmap NewBitmap = (Bitmap)Image.Clone();
    BitmapData data = NewBitmap.LockBits(
        new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), 
        ImageLockMode.ReadWrite,
        NewBitmap.PixelFormat);
    int Height = NewBitmap.Height;
    int Width = NewBitmap.Width;

    unsafe
    {
        for (int y = 0; y < Height; ++y)
        {
            byte* row = (byte*)data.Scan0 + (y * data.Stride);
            int columnOffset = 0;
            for (int x = 0; x < Width; ++x)
            {
                byte B = row[columnOffset];
                byte G = row[columnOffset + 1];
                byte R = row[columnOffset + 2];

                float Red = R / 255.0f;
                float Green = G / 255.0f;
                float Blue = B / 255.0f;
                Red = (((Red - 0.5f) * Value) + 0.5f) * 255.0f;
                Green = (((Green - 0.5f) * Value) + 0.5f) * 255.0f;
                Blue = (((Blue - 0.5f) * Value) + 0.5f) * 255.0f;

                int iR = (int)Red;
                iR = iR > 255 ? 255 : iR;
                iR = iR < 0 ? 0 : iR;
                int iG = (int)Green;
                iG = iG > 255 ? 255 : iG;
                iG = iG < 0 ? 0 : iG;
                int iB = (int)Blue;
                iB = iB > 255 ? 255 : iB;
                iB = iB < 0 ? 0 : iB;

                row[columnOffset] = (byte)iB;
                row[columnOffset + 1] = (byte)iG;
                row[columnOffset + 2] = (byte)iR;

                columnOffset += 4;
            }
        }
    }

    NewBitmap.UnlockBits(data);

    return NewBitmap;
}

注: このコードではusing System.Drawing.Imaging;、クラスの using ステートメントが必要であり、プロジェクトのallow unsafe codeオプションが (プロジェクトの [ビルド プロパティ] タブで) チェックされている必要があります。

GetPixel と SetPixel がピクセルごとの操作で非常に遅い理由の 1 つは、メソッド呼び出し自体のオーバーヘッドが大きな要因になり始めていることです。既存の BitmapData オブジェクトを使用する独自の SetPixel および GetPixel メソッドを記述できるため、通常、ここに示すコード サンプルはリファクタリングの候補と見なされますが、関数内の計算の処理時間はメソッドのオーバーヘッドに比べて非常に小さくなります。各コールの。Clampこれが、元のメソッドの呼び出しも削除した理由です。

これを高速化するもう 1 つの方法は、コピーを作成して変更されたコピーを返す代わりに、単に「破壊的な」関数にして、渡された Bitmap パラメータを変更することです。

于 2010-06-25T02:29:13.710 に答える
3

@MusiGenesis、

私が書いている画像エディターにこの方法を使用したことに注意してください。これはうまく機能しますが、このメソッドは次の行で AccessViolationException をトリガーすることがあります。

byte B = row[columnOffset];

BitDepth の標準化がなかったため、画像が 32 ビットカラーの場合、このエラーが発生していたことが原因であることに気付きました。だから私はこの行を変更しました:

BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height),  ImageLockMode.ReadWrite, NewBitmap.PixelFormat);

に:

BitmapData data = NewBitmap.LockBits(new Rectangle(0, 0, NewBitmap.Width, NewBitmap.Height), ImageLockMode.ReadWrite, PixelFormat.Format32bppRgb); 

私の問題を根絶したように見えるので、これが役立つことを願っています。

投稿ありがとうございます。

ジブ

于 2010-12-16T13:11:32.400 に答える
0

私は少し遅れていますが、これらはそのような変換用に最適化され、自分でピクセルを操作するよりもはるかに簡単であるため、カラー マトリックスの実装を使用します。 .html

于 2011-02-22T14:27:43.127 に答える