1

私のアプリケーション

RGB をグレースケール イメージに変換する必要があるアプリケーションを作成しています。変換は機能しますが、3648 * 2736 ピクセルの画像の変換には約 7 秒かかります。set と getpixel には時間がかかることはわかっています。しかし、画像が小さくなくても、Lockbits を使用している場合はそれほど時間はかからないと思います。(それが間違っている場合は修正してください)。たぶん、コード内で致命的な間違いを犯しただけです。

コード

public static long ConvertToGrayScaleV2(Bitmap imageColor, bool useHDTVConversion)
        {
            Stopwatch stpw = new Stopwatch();
            stpw.Start();
            System.Drawing.Imaging.BitmapData imageColorData = imageColor.LockBits(new Rectangle(new Point(0, 0), imageColor.Size),
                System.Drawing.Imaging.ImageLockMode.ReadWrite, imageColor.PixelFormat);

            IntPtr PtrColor = imageColorData.Scan0;
            int strideColor = imageColorData.Stride;
            byte[] byteImageColor = new byte[Math.Abs(strideColor) * imageColor.Height];
            System.Runtime.InteropServices.Marshal.Copy(PtrColor, byteImageColor, 0, Math.Abs(strideColor) * imageColor.Height);

            int bytesPerPixel = getBytesPerPixel(imageColor);
            byte value;
            if (bytesPerPixel == -1)
                throw new Exception("Can't get bytes per pixel because it is not defined for this image format.");
            for (int x = 0, position; x < imageColor.Width * imageColor.Height; x++)
            {
                position = x * bytesPerPixel;
                if (useHDTVConversion)
                {
                    value = (byte)(byteImageColor[position] * 0.0722 + byteImageColor[position + 1] * 0.7152 + byteImageColor[position + 2] * 0.2126);                    
                }
                else
                {
                    value = (byte)(byteImageColor[position] * 0.114 + byteImageColor[position + 1] * 0.587 + byteImageColor[position + 2] * 0.299);
                }
                byteImageColor[position] = value;
                byteImageColor[position+1] = value;
                byteImageColor[position+2] = value;

            }

            System.Runtime.InteropServices.Marshal.Copy(byteImageColor, 0, PtrColor, Math.Abs(strideColor) * imageColor.Height);
            imageColor.UnlockBits(imageColorData);
            stpw.Stop();
            return stpw.ElapsedMilliseconds;
        }

        public static int getBytesPerPixel(Image img)
        {
            switch (img.PixelFormat)
            {
                case System.Drawing.Imaging.PixelFormat.Format16bppArgb1555:    return 2;
                case System.Drawing.Imaging.PixelFormat.Format16bppGrayScale:   return 2;
                case System.Drawing.Imaging.PixelFormat.Format16bppRgb555: return 2;
                case System.Drawing.Imaging.PixelFormat.Format16bppRgb565: return 2;
                case System.Drawing.Imaging.PixelFormat.Format1bppIndexed: return 1;
                case System.Drawing.Imaging.PixelFormat.Format24bppRgb: return 3;
                case System.Drawing.Imaging.PixelFormat.Format32bppArgb: return 4;
                case System.Drawing.Imaging.PixelFormat.Format32bppPArgb: return 4;
                case System.Drawing.Imaging.PixelFormat.Format32bppRgb: return 4;
                case System.Drawing.Imaging.PixelFormat.Format48bppRgb: return 6;
                case System.Drawing.Imaging.PixelFormat.Format4bppIndexed: return 1;
                case System.Drawing.Imaging.PixelFormat.Format64bppArgb: return 8;
                case System.Drawing.Imaging.PixelFormat.Format64bppPArgb: return 8;
                case System.Drawing.Imaging.PixelFormat.Format8bppIndexed: return 1;
                default: return -1;
            }

        }
4

4 に答える 4

4

私はこれが古いことを知っていますが、いくつかの重要な点があります:

これimageColor.Width * imageColor.Heightは、必要以上に 1000 万回 (3648 * 2736) 近く実行している高価な操作です。

  • forループは、反復ごとに再計算しています
  • それだけでなく、CLR は Bitmap オブジェクトWidthHeightプロパティにそれぞれ1,000 万回も移動する必要があります。これは、ビットマップでこれを実行しようとするたびに必要な操作よりも 3000 万多い操作です。

変化する:

for (int x = 0, position; x < imageColor.Width * imageColor.Height; x++)
{
    ...
}

に:

var heightWidth = imageColor.Width * imageColor.Height;
for (int x = 0, position; x < heightWidth; x++)
{
    ...
}

3 つのさまざまな操作 (R、G、B、255 の可能な値) のすべての潜在的な結果をキャッシュし、新しい値を 1000 万回計算する代わりに新しい値へのルックアップを使用すると、パフォーマンスが大幅に向上することもわかります。 .

完全な非常に高速なコードを次に示します (ColorMatrix よりもはるかに高速です)。事前に計算された可能性のあるすべての値をローカル変数に移動したことに注意してください。ループ内では、最小限の作業しか必要ありません。

                var lookupR = new byte[256];
                var lookupG = new byte[256];
                var lookupB = new byte[256];
                var rVal = hdtv ? 0.114 : 0.0722;
                var gVal = hdtv ? 0.587 : 0.7152;
                var bVal = hdtv ? 0.299 : 0.2126;
                for (var originalValue = 0; originalValue < 256; originalValue++)
                {
                    var r = (byte)(originalValue * rVal);
                    var g = (byte)(originalValue * gVal);
                    var b = (byte)(originalValue * bVal);
                    // Just in case...
                    if (r > 255) r = 255;
                    if (g > 255) g = 255;
                    if (b > 255) b = 255;
                    lookupR[originalValue] = r;
                    lookupG[originalValue] = g;
                    lookupB[originalValue] = b;
                }
                unsafe
                {
                    var pointer = (byte*)(void*)bitmap.LockBits(new Rectangle(0, 0, bitmap.Width, bitmap.Height), ImageLockMode.ReadWrite, bitmap.PixelFormat);
                    var bytesPerPixel = getBytesPerPixel(bitmap);
                    var heightWidth = bitmap.Width * bitmap.Height;
                    for (var y = 0; y < heightWidth; ++y)
                    {
                        var value = (byte) (lookupR[pointer[0]] + lookupG[pointer[1]] + lookupB[pointer[2]]);
                        pointer[0] = value;
                        pointer[1] = value;
                        pointer[2] = value;
                        pointer += bytesPerPixel;
                    }
                    bitmap.UnlockBits();
                }
                break;
于 2014-04-20T02:49:47.703 に答える
3

ビットマップ データの配列を反復処理する必要がある、同様のパフォーマンスの問題に遭遇しました。imagecolor.widthと.heightで行っているように、ループ内またはループの境界としてビットマップの幅または高さのプロパティを参照すると、パフォーマンスが大幅に低下することがわかりました。ループの外側で整数を宣言し、ビットマップの高さと幅を事前にキャッシュするだけで、ループ時間が半分に短縮されました。

于 2012-10-27T04:19:05.403 に答える
2

グレースケールに変換する場合は、代わりに ColorMatrix 変換を使用してみてください。から: https://web.archive.org/web/20141230145627/http://bobpowell.net/grayscale.aspx

   Image img = Image.FromFile(dlg.FileName);
        Bitmap bm = new Bitmap(img.Width,img.Height);
        Graphics g = Graphics.FromImage(bm); 

  

        
        ColorMatrix cm = new ColorMatrix(new float[][]{   new float[]{0.5f,0.5f,0.5f,0,0},
                                  new float[]{0.5f,0.5f,0.5f,0,0},
                                  new float[]{0.5f,0.5f,0.5f,0,0},
                                  new float[]{0,0,0,1,0,0},
                                  new float[]{0,0,0,0,1,0},
                                  new float[]{0,0,0,0,0,1}}); 

        

        /* 

        //Gilles Khouzams colour corrected grayscale shear
        ColorMatrix cm = new ColorMatrix(new float[][]{   new float[]{0.3f,0.3f,0.3f,0,0},
                                  new float[]{0.59f,0.59f,0.59f,0,0},
                                  new float[]{0.11f,0.11f,0.11f,0,0},
                                  new float[]{0,0,0,1,0,0},
                                  new float[]{0,0,0,0,1,0},
                                  new float[]{0,0,0,0,0,1}}); 

        */ 


        ImageAttributes ia = new ImageAttributes();
        ia.SetColorMatrix(cm);
        g.DrawImage(img,new Rectangle(0,0,img.Width,img.Height),0,0,img.Width,img.Height,GraphicsUnit.Pixel,ia);
        g.Dispose();
于 2012-10-09T18:54:28.830 に答える
0

マーシャリングとコピーにはかなりの時間がかかっていると思います。このリンクでは、画像をグレースケーリングするための3つの方法について説明します。

于 2012-10-09T21:06:55.720 に答える