RGB24bit→Gray8bit変換高速化

C# 4.0でRGB24bitの画像をGrayscale 8bitに変換する必要ができたのですが、調べ方が悪いのか.NET Frameworkに該当する機能は見当たらず、自力変換しているケースがちらほら。

というわけで、以下のように画像データをbyte[]配列に放り込み実験。
JpegBitmapDecoder jpegDecoder = new JpegBitmapDecoder(
    new Uri("4288x2848.jpg"),
    BitmapCreateOptions.PreservePixelFormat,
    BitmapCacheOption.OnLoad);

BitmapSource InputBitmap = jpegDecoder.Frames[0];
int InputStride = InputBitmap.PixelWidth * 3;
if ((InputBitmap.PixelWidth * 3) % 4 > 0) InputStride += 4 - (InputBitmap.PixelWidth * 3) % 4;
byte[] InputData = new byte[InputStride * InputBitmap.PixelHeight];
InputBitmap.CopyPixels(InputData, InputStride, 0);

int OutputStride = InputBitmap.PixelWidth;
if( InputBitmap.PixelWidth % 4 > 0) OutputStride += 4 - InputBitmap.PixelWidth % 4;
byte[] OutputData = new byte[OutputStride * InputBitmap.PixelHeight];

ここではWPF系のBitmapSourceを使ってますが本質は画像データをbyte[]配列に入れることなので、System.Drawing.Bitmapを使う場合は、Bitmap.LockBitsとMarshal.Copyを駆使して下さいませ。

で、安直に以下の二重ループで変換すると、4288x2848ドットの画像を変換するのに約960msec (@Core i7 920)。今回の要件でこの速度はちと不味い。
for (int y = 0; y < InputBitmap.PixelHeight; y++)
{
    for (int x = 0; x < InputBitmap.PixelWidth; x++)
    {
        double intensity =
            0.29891 * InputData[y * InputStride + x * 3] +
            0.58661 * InputData[y * InputStride + x * 3 + 1] +
            0.11448 * InputData[y * InputStride + x * 3 + 2];
        OutputData[y * OutputStride + x] = (byte)intensity;
    }
}
で、半日こねくり回してたどり着いたのが以下のコード。同条件で約50msec。
Parallel.For(0, InputImage.PixelHeight, y =>
{
    for (int InputOffset = y * InputStride, OutputOffset = y * OutputStride; OutputOffset < (y + 1) * OutputStride; )
    {
        int intensity =
            313430 * InputData[InputOffset++] +
            615105 * InputData[InputOffset++] +
            120041 * InputData[InputOffset++];
        OutputData[OutputOffset++] = (byte)(intensity >> 20);
     }
});
  • byte[]配列のインデックスを和積(x+y*stride)でなくint値を増加させると、倍速くらいに
  • intensityをdoubleじゃなくintで計算するのは10%程度の効果。8bitの結果を得るのに20bitで計算するのは明らかに過剰ですが、別にintに収まっているうちは何bitにしても速度変わらないし…
  • 最も効いたのがParallel.For。Core i7 920が4コア+HTなので、4~5倍速はあるかなと思ったのですが、それをはるかに超える上がり方。これがCore2 Duo 2.4GHz上だと20倍速以上7~8倍速程度になるようなのですが、理由がわからない(^^;)

もちろん上記コードが最速とは思っておらず、unsafeやC++を使えばもっと速くできそうですが、Parallel.For使った結果で目的は達してしまったのでやめた次第。

今回はParallel.Forがうれしい誤算だったわけですが、逆にシングルスレッド処理は何をやっているんだという疑問が…

[参考] [.NET]いまさら?Parallel.Forを使ってみた(その1) (GDD Blog 2010/9/25)Link

[2012/4/26 追記] 同じプログラムを改めてCore2 Duoで走らせると両者の速度差は約7.4倍。それでも2コアじゃ説明付かない速度差ですが。

[2012/4/27 追記] 以下のUnsafeコードだとさらに10%速い(44~45msec@Core i7 920、Parallel.For部分のみの計測)。ここまでする必要があるかどうかはケースバイケースかな。

IntPtr srcData = Marshal.AllocHGlobal(InputStride * InputImage.PixelHeight);
IntPtr dstData = Marshal.AllocHGlobal(OutputStride * InputImage.PixelHeight);
try
{
    InputImage.CopyPixels(System.Windows.Int32Rect.Empty, srcData, InputStride * InputImage.PixelHeight, InputStride);

    Parallel.For(0, InputImage.PixelHeight, y =>
    {
        unsafe
        {
            for (byte* pInput = (byte*)srcData + y * InputStride, pOutput = (byte*)dstData + y * OutputStride;
                pOutput < (byte*)dstData + (y + 1) * OutputStride; )
            {
                int intensity =
                    313430 * *pInput++ +
                    615105 * *pInput++ +
                    120041 * *pInput++;
                *pOutput++ = (byte)(intensity >> 20);
            }
        }
    });
}
finally
{
    Marshal.FreeHGlobal(srcData);
    Marshal.FreeHGlobal(dstData);
}

[2012/4/28 追記] Unsafe版のコードに余計なものが入っていたので整理

[2012/5/1 追記] Strideの計算を訂正

— posted by mu at 11:20 pm   commentComment [0]  pingTrackBack [0]

親不知抜歯 - 抜糸完了

blog20120424-ToothRemoved

抜いた歯の跡にできた巨大な陥没。痛みはすっかりないのですが、ここに食べ物がしっかりと詰まります(;´Д`) 抜糸の時、医師からは強めにうがいして詰まったものを取って下さいと言われましたが、

全然取れないし

職場の先輩の話だと2ヶ月くらいで歯茎が埋まって詰まらなくなるらしい。憂鬱だな。

— posted by mu at 10:28 pm   commentComment [0]  pingTrackBack [0]

本日も接続不良なり

blog20120422-PSO2ClosedBeta

PSO2Link Closed β参加者にはすっかりおなじみになっただろう、サーバへの接続不良。前作PSULink では正式サービス開始直後に押し寄せたユーザー数を捌ききれず接続不良が大きな問題となりましたが、あれを知っている人にはその再来に見えちゃう。

やっぱりねぇ2月のα2であの完成度だったのに、わずか2ヶ月でβってなんか無理があるなぁとは思ってたんです。しかもかなり機能追加(曰く正式版で予定しているものは全部入れた)してきている。α3ですと言っておけばまだプレーヤーの期待を損ねる度合いも少なかったと思うのですが、開発発表から既に一年、2011年中リリース予定が2012年にずれ込み、営業方面の顔色見るとそうは言えなかったのだろうな。

参加者の中には期間延長を主張する人もいるようですが、この状態でGWに突入すれば結果は見えてるような。開発側が大量負荷のデータを欲していたなら最初からGW挟んだ日程を取ったと思うので、一旦撤収してClosed β2というのが個人的予想。

でもこういう混沌、嫌いじゃない(笑)

— posted by mu at 10:33 pm   commentComment [0]  pingTrackBack [0]

PSO2 Closed β開始

PSO2 Closedβ 理想と現実
下は特にひどい場面を選んだのですが(^^;)
昨日よりサービス開始。混んでいるわけじゃないのに、めちゃくちゃ重い。 さてPSO2Link の売りの機能の一つにキャラクターデザインの多様性があります。顔の輪郭から体型、胸の形まであったりして凝りだすときりがないのですが、作り終わってプレイを始めるとデザイン時のものとかなり差が感じられ、愕然とすることが多々。特に顔。 写真はキャラクタークリエイト時(上)とゲーム開始直後のチュートリアル中(下)のもの。自キャラを最大身長で作ったので、背の低いキャラに目を向けると見下ろす形になるのですが、なんか怖い。勘弁して下さい…

[2012/04/21 追記] お約束の表記追加。そう、正式版とは異なるのです。
「画像は『PHANTASY STAR ONLINE 2』クローズドβテストにおいて撮影された実機画像です。 開発中のため、正式版とは異なる内容であり、今後改良される可能性があります。」 『PHANTASY STAR ONLINE 2』公式サイト http://pso2.jp/Link

blog20120420-PSO2ClosedBeta-2

[2012/04/21 追記] PSO2の名誉のために、これくらいはプレイ中でも表示できますよってのを。他のプレイヤーも含めたくさん表示しないといけないので、 キャラクリより画質が落ちてしまうのはしょうがないですけどね。高性能なグラボ買えってことか(笑)

— posted by mu at 12:49 am   commentComment [0]  pingTrackBack [0]

Wagnaria 春の大大大感謝祭

blog20120415-Wagnaria

ライブビュー(映画館生中継)に参加。開演前の楽屋中継やアンコールを含めると笑いあり涙なしの約3時間、Blu-rayLink に入りきるかと心配されるわけだ。

結構女性が見に来ていたのは意外だったけど、よく考えれば男性キャラ多いか > WORKING!

— posted by mu at 10:26 pm   commentComment [0]  pingTrackBack [0]

T: Y: ALL: Online:
ThemeSwitch
  • Basic
Created in 0.0142 sec.
prev
2012.4
next
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30