Tanpohp

Archive for October, 2010

Festval of Lights in Berlin

by on Oct.25, 2010, under Holiday&Life, Night Shot

Einige Fotos welche ich beim diesjährigen Festival of Lights gemacht habe:

Comments Off on Festval of Lights in Berlin more...

Optimierung in C# mittels Pointer

by on Oct.23, 2010, under Snippets

Ich habe mich in den letzten Tagen viel mit Farbraumkonvertierung, sowie Pixelextrahierung und dessen Beschleunigungsmöglichkeiten und C# beschäftigt. Mich hat besonders interessiert, ob das Pointerkonzept in C# das Potenzial hat, Anwendungen derart zu beschleunigen, dass es sich lohnt sich damit zu beschäftigen. Dabei habe ich einige sehr interessante Entdeckungen gemacht.

Der folgende Code wandelt ein byte[] im RGB Format in YCbCr um.

public static void Rgb2YCbCr24Bit(ref byte[] rgb)
{
    int y = 0, cr = 0, cb = 0, cg = 0;
    for (int pos = 0; pos < rgb.Length-2; pos += 3)
    {
        r = rgb[pos + 1];
        g = rgb[pos + 2];
        b = rgb[pos + 3];
        lum = ((r + g + b) / 3);
        rgb[pos + 1] = (byte)lum;
        rgb[pos + 2] = (byte)(lum - b);
        rgb[pos + 3] = (byte)(lum - r);
    }
}

Ich habe diesen Code mehrfach mit einem 36MPixel Bild getestet damit eventuelle Störungen durch andere Programme nicht so stark ins Gewicht fallen. Ergebnis: 2023ms.
Darauf hin habe ich das gleich mit Pointern umsetzt:

public static void Rgb2YCbCr24Bit(ref byte[] rgb)
{
    int r = 0, g = 0, b = 0, lum = 0;
    unsafe
    {
        fixed (byte* p1 = rgb)
        {
            byte* p = p1;
            for (int pos = 0; pos < rgb.Length - 2; pos += 3)
            {
                r = *(p);
                g = *(p + 1);
                b = *(p + 2);
                lum = ((r + g + b) / 3);
                *(p) = (byte)lum;
                *(++p) = (byte)(lum - b);
                *(++p) = (byte)(lum - r);
                ++p;
            }
        }
    }
}

Mit den selben Bild: 1572ms. Das heißt eine Beschleunigung um 23%. An dem Code ist zu sehen, das der Grünkanal nur einmal benötigt wird und die Zwischenspeicherung damit redundant ist. Darauf hin änderte ich den Code wie folgt ab.

public static void Rgb2YCbCr24Bit(ref byte[] rgb)
{
    int r = 0, b = 0, lum = 0;
    unsafe
    {
        fixed (byte* p1 = rgb)
        {
            byte* p = p1;
            for (int pos = 0; pos < rgb.Length - 2; pos += 3)
            {
                r = *(p);
                b = *(p + 2);
                lum = ((r + *(p + 1) + b) / 3);
                *(p) = (byte)lum;
                *(++p) = (byte)(lum - b);
                *(++p) = (byte)(lum - r);
                ++p;
            }
        }
    }
}

Interessanter Weise wurde der Code damit nicht schneller, sondern langsamer. Gegenüber der Implementierung mit Arrays betrug die Beschleunigung nur noch 12%. Würde mich sehr stark interessieren, warum das so ist.

An einer anderen Stelle extrahierte ich RGB-Werte aus einer Bitmap. Als erstes ist zu sagen, dass obgleich eines der Formate PixelFormat.Format32bppArgb heißt, entspricht dies nicht der Ordnung der Pixel um Speicher. Diese liegen genau anderes herum im Speicher des Bitmap: Gbra.
Mit folgende Code laß ich die Rgb-Werte aus einer Bitmap aus.

public static byte[] GetPixelsRgb(Bitmap source)
{
    int width = source.Width, height = source.Height;
    byte[] result = new byte[width * height * 3];
    BitmapData bd = source.LockBits(
                            new Rectangle(0, 0, width, height),
                            System.Drawing.Imaging.ImageLockMode.ReadOnly,
                            source.PixelFormat);
    unsafe
    {
        byte* bp = (byte*)bd.Scan0;
        fixed (byte* rPtr = result)
        {
            byte* rp = rPtr;
            int count = result.Length;
            for (int i = 0; i < count; i += 3)
            {
                *rp = *(bp + 2);
                *(rp + 1) = *(bp + 1);
                *(rp + 2) = *(bp);
                bp += 3;
                rp += 3;
            }
        }
    }
    source.UnlockBits(bd);
    return result;
}

Zu beachten ist, das der Code nur wie zu erwarten funktioniert, wenn (source.width*3)%4==0, da ein Stride (siehe MSDN) mit 0 aufgefüllt wird. Mit dem 36MPixel Bild bedarf es 799ms zum Auslesen des Bildes.

Beim betrachten dieses Codes viel mit folgendes auf: für jeden Pixel wird in der for-Schleife eine Addition +=3 ausgeführt und ein Vergleich ausgeführt, ob der Code am Ende des Bitmap angekommen ist. Die meisten Bilder haben eine Breiten die zumindest durch 4 teilbar ist, viele haben Breiten die durch 8 oder 16 teilbar sind. Damit könnten unnötige Überprüfungen unterbunden werden. Daraus resultiert folgender Code:

public static byte[] GetPixelsRgbFast(Bitmap source)
{
    int width = source.Width, height = source.Height;
    byte[] result = new byte[width * height * 3];

    BitmapData bd = source.LockBits(
                            new Rectangle(0, 0, width, height),
                            System.Drawing.Imaging.ImageLockMode.ReadOnly,
                            source.PixelFormat);
    unsafe
    {
        byte* bp = (byte*)bd.Scan0;
        fixed (byte* rPtr = result)
        {
            byte* rp = rPtr;

            for (int i = 0; i < result.Length; i += 24)
            {
                //pixel 0
                *rp = *(bp + 2);
                *(rp + 1) = *(bp + 1);
                *(rp + 2) = *(bp);
                //pixel 1
                *(rp + 3) = *(bp + 5);
                *(rp + 4) = *(bp + 4);
                *(rp + 5) = *(bp + 3);
                //pixel 2
                *(rp + 6) = *(bp + 8);
                *(rp + 7) = *(bp + 7);
                *(rp + 8) = *(bp + 6);
                //pixel 3
                *(rp + 9) = *(bp + 11);
                *(rp + 10) = *(bp + 10);
                *(rp + 11) = *(bp + 9);
                //pixel 4
                *(rp + 12) = *(bp + 14);
                *(rp + 13) = *(bp + 13);
                *(rp + 14) = *(bp + 12);
                //pixel 5
                *(rp + 15) = *(bp + 17);
                *(rp + 16) = *(bp + 16);
                *(rp + 17) = *(bp + 15);
                //pixel 6
                *(rp + 18) = *(bp + 20);
                *(rp + 19) = *(bp + 19);
                *(rp + 20) = *(bp + 18);
                //pixel 7
                *(rp + 21) = *(bp + 23);
                *(rp + 22) = *(bp + 22);
                *(rp + 23) = *(bp + 21);
            }
        }
    }
    source.UnlockBits(bd);
    return result;
}

Der Code behandelt 8 Pixel auf einmal und erspart sich damit 7 Additionen und 7 unnötige Vergleiche. Zeit zum auslesen des 36MPixel Bildes: 410ms. Das ist eine satte Beschleunigung um 49%.

Es zeigte sich, dass das Pointer-Konzept in C# einige Leistungsreserven anzapfen kann und es sich in zeitkritischen Anwendungen lohnt diese Umzusetzten. Viel überraschender fand ich die Ergebnisse des zweiten Experimentes, die relativ unanhängig von Pointern oder C# sind. Hier zeigt sich was mit ein wenig Mehraufwand in der Implementierung geschafft werden kann.

Comments Off on Optimierung in C# mittels Pointer :, more...

Urlaub an der Ostsee

by on Oct.18, 2010, under Holiday&Life, Photography

Vorheriges Wochenende war ich an der Ostsee ein paar Tage ausspannen. Diese Gelegenheit habe ich genutzt um einige Fotos zu machen. Leider ist es mir nicht gelungen, eine langzeitaufnahme von der Brandung zu machen, da es trotz Bewölkung einfach zu hell war. Dennoch sind einige schöne Fotos zustande gekommen.

Comments Off on Urlaub an der Ostsee more...

Looking for something?

Use the form below to search the site:

Still not finding what you're looking for? Drop a comment on a post or contact us so we can take care of it!

Visit our friends!

A few highly recommended friends...