IntPtrで引っ張り出した画像へのポインタ、Int*で処理したほうが速い?

画像のポインタ、普通は、バイト配列で処理するもんですが
ちょいと裏技で、int*にキャストしてやって、intの配列で処理してやったほうが
メモリ~CPU間の通信量が減って、速度が上がる

と、C言語の頃は、王道だったんですが…

C#の世界でも、通用するのかと、ちょいとサンプル書いて、実行してみました

条件は以下のとおり

CPU : AMD Athlon64x2 3800+(2.0GHz)
Memory : 1GB
OS : Windows XP Pro
言語: Visual Studio 2005(C#2.0) + .Net FrameWork2.0

画像データ1000回の
グレースケール処理を実行
リリースコンパイル
640 * 480
24ビットピクセル
byte配列処理
10,437ミリ秒
640 * 480
32ビットピクセル
int配列処理
10,093ミリ秒

あいたっ!
さほど、差が無い(滝汗

JITが、スットコドッコイなコード、吐き出しているのかなぁ。。。

3バイト画像を無理して4バイトに変換してInt配列処理させるよりは
byte配列でそのまんま、処理させたほうが、かえっていいかも

4バイトにしちゃうと、容量25%増しだし…

でも、C言語じゃ、当たり前だった、小技、使っても意味無いとは、ねぇ。。。


頭きたんで、このグレースケール処理、4スレッドでぶん回してみる!
次回をお楽しみにぃ~~

    public partial class Form1 : Form
    {
        private Bitmap m_ImageBackup;   // 一時データ
        private Bitmap m_bmapIn;	    // 入力データ
        private Bitmap m_bmapOut;      // 出力データ
        private int m_Width;            // Image サイズ
        private int m_Height;           // Image サイズ
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {

            pictureBox1.Image = null;
        }

        private void button1_Click(object sender, EventArgs e)
        {
            string FileName;
            if (openFileDialog1.ShowDialog() != DialogResult.OK)
            {
                return;
            }
            FileName = openFileDialog1.FileName;
            m_ImageBackup = new Bitmap(FileName);
            m_Width = m_ImageBackup.Width;
            m_Height = m_ImageBackup.Height;
            m_bmapOut = new Bitmap(m_Width, m_Height);
            if (radioButton1.Checked == true)
            {
                m_bmapIn = m_ImageBackup;
            }
            else
            {
                Rectangle dstRect = new Rectangle(0, 0, m_Width, m_Height);
                m_bmapIn = m_ImageBackup.Clone(dstRect, PixelFormat.Format32bppArgb);
            }

            if (pictureBox1.Image != null)
            {
                pictureBox1.Image.Dispose();
            }

            pictureBox1.Image = m_bmapIn;
        }

        private void button2_Click(object sender, EventArgs e)
        {
            DateTime dt = DateTime.Now;  
            int min  = dt.Minute;
            int sec  = dt.Second;
            int mili = dt.Millisecond;  

            lblStart.Text = "開始時間:" + min.ToString() + "_" + sec.ToString() + "_" + mili.ToString();
            Bitmap bd = pictureBox1.Image as Bitmap;
            Bitmap bs; 
            BitmapData bDataInS; 
            BitmapData bDataInD; 
            IntPtr Scan0_InS; 		// ピクセルデータの開始アドレス
            IntPtr Scan0_InD; 		// ピクセルデータの開始アドレス
            bs = new Bitmap(m_bmapIn);
            if (radioButton1.Checked == true)
            {
                bDataInS = bs.LockBits(new Rectangle(0, 0, bs.Width, bs.Height),
                                    ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                Scan0_InS = bDataInS.Scan0;		// ピクセルデータの開始アドレス

                bDataInD = bd.LockBits(new Rectangle(0, 0, bd.Width, bd.Height),
                                    ImageLockMode.ReadWrite, PixelFormat.Format24bppRgb);
                Scan0_InD = bDataInD.Scan0;		// ピクセルデータの開始アドレス

                for(int i = 0; i < 1000; i++)
                {
                    GrayScaleB(Scan0_InS, Scan0_InD, bd.Width, bd.Height);
                }

                bd.UnlockBits(bDataInD);
                bs.UnlockBits(bDataInS);

            }
            else
            {
                bDataInS = bs.LockBits(new Rectangle(0, 0, bd.Width, bd.Height),
                                    ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                Scan0_InS = bDataInS.Scan0;		// ピクセルデータの開始アドレス

                bDataInD = bd.LockBits(new Rectangle(0, 0, bd.Width, bd.Height),
                                    ImageLockMode.ReadWrite, PixelFormat.Format32bppArgb);
                Scan0_InD = bDataInD.Scan0;		// ピクセルデータの開始アドレス

                for(int i = 0; i < 1000; i++)
                {
                    GrayScaleI(Scan0_InS, Scan0_InD, bd.Width, bd.Height);
                }
                bd.UnlockBits(bDataInD);
                bs.UnlockBits(bDataInS);
            }



            DateTime dt2 = DateTime.Now;  
            min  = dt2.Minute;
            sec  = dt2.Second;
            mili = dt2.Millisecond;  
            lblEnd.Text = "終了時間:" + min.ToString() + "_" + sec.ToString() + "_" + mili.ToString();;
            TimeSpan tm = dt2.Subtract(dt);
            lblJob.Text = "経過時間:" + tm.TotalSeconds.ToString();
            //pictureBox1.Refresh();
            Refresh();
        }

        public void GrayScaleI(IntPtr ptS, IntPtr ptD, int tmpWidth, int tmpHeight)
        {
            int x, y;
            int index;
            byte PixelData;
            byte red, green, blue;
            uint dat;

            unsafe
            {
                uint* ps = (uint*)ptS;
                uint* pd = (uint*)ptD;
                for (y = 0; y < tmpHeight; y++)
                {
                    for (x = 0; x < tmpWidth; x++)
                    {
                        index = (y * tmpWidth) + x;
                        dat = ps[index];
                        red   = (byte) ((dat & 0x00ff0000) >> 16);
                        green = (byte) ((dat & 0x0000ff00) >> 8);
                        blue  = (byte) ((dat & 0x000000ff));
                        PixelData = (byte)(0.299f * ((float)red) + 0.587f * ((float)green) + 0.114f * ((float)blue));
                        pd[index]  = (uint)(PixelData * 0x010101) | 0xff000000;
                    }
                }
            }
        }

        public void GrayScaleB(IntPtr ptS, IntPtr ptD, int tmpWidth, int tmpHeight)
        {
            int x, y;
            int index;
            byte PixelData;
            byte red, green, blue;

            int scanl = tmpWidth * 3;
            unsafe
            {
                byte* ps = (byte*)ptS;
                byte* pd = (byte*)ptD;
                for (y = 0; y < tmpHeight; y++)
                {
                    for (x = 0; x < scanl; x += 3)
                    {
                        index = (y * scanl) + x;

                        blue  = ps[index ];
                        green = ps[index + 1];
                        red   = ps[index + 2];
                        PixelData = (byte)(0.299f * ((float)red) + 0.587f * ((float)green) + 0.114f * ((float)blue));

                        pd[index]     = PixelData;
                        pd[index + 1] = PixelData;
                        pd[index + 2] = PixelData;
                    }
                }
            }
        }
    }

3件のフィードバック

  1. 康ちゃん@まったり~ より:

    データキャッシュが効いてるんじゃないですか?

  2. 康ちゃん@まったり~ より:

    あ、それとメモリもコンパイラがデータをワードアラインに揃えるように処理しちゃって、
    3バイトも4バイトも、実メモリ消費は4バイトになってたりするかもしれません^^;;;;

  3. くまさん より:

    >>康ちゃん@まったり~ さま

    読み込み即書き込みなんで
    データキャッシュよりは,アライメントでドカンと引き上げてるほうが,可能性ありそうですねぇ

    最近のCPU工学は,とんでもない実装になっているみたいじゃし

    まぁ,オレラが小細工するよりは
    素直に見やすいコードを書いたほうが良さそうですのぉ(笑

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

このサイトはスパムを低減するために Akismet を使っています。コメントデータの処理方法の詳細はこちらをご覧ください