関連記事
- 今週もひたすらモノ書き #JAVA 祭りがまだまだ続く そろそろC#を書かせろー 【2024年12月04日(水)】
- 【#CSHARP】ソリューション内でサービス,Winformアプリを混在で作成させ,バイナリ出力先を同じにするとサービスが起動しない 【2024年02月08日(木)】
- 【#PHP】謎な #Laravel 案件 引き渡したじ 【2024年01月11日(木)】
- 【C#】謎な改造アプリ,SIMD命令を導入して高速化しようか,思案中 【2023年11月14日(火)】
- Javaの”やらかし”でC#と人気逆転か 激変プログラミング言語人気ランキング 【2023年10月18日(水)】
画像のポインタ、普通は、バイト配列で処理するもんですが
ちょいと裏技で、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バイトも4バイトも、実メモリ消費は4バイトになってたりするかもしれません^^;;;;
>>康ちゃん@まったり~ さま
読み込み即書き込みなんで
データキャッシュよりは,アライメントでドカンと引き上げてるほうが,可能性ありそうですねぇ
最近のCPU工学は,とんでもない実装になっているみたいじゃし
まぁ,オレラが小細工するよりは
素直に見やすいコードを書いたほうが良さそうですのぉ(笑