Tanpohp

Tag: c#

PLinq – und wann man es nicht nutzen sollte

by on Nov.03, 2011, under Snippets

Zur Zeit beschäftige ich mich mit Bildverarbeitung auf der GPU. Dabei muss ich auf eine Reihe von Bildern Faltungen und dergleichen anwenden. Viele Bilder – das schreit nach PLinq, so dachte ich. Aus organisatorischen Gründen habe ich 3 Bildpackte welche jeweils eine List sind. Diese enthalten dann wiederum das Graustufenbild als byte[].

List<List<byte[]>> allImagePackages;

PLinq bietet nun eine ausgesprochen einfache Möglichkeit die Bilder parallel zu bearbeiten. Der Code sieht dann etwa wie folgt aus:

allImagePackages.AsParallel().ForAll(
	imagePackage => imagePackage.AsParallel().ForAll(
		singleImage =>DoSomethingComplex(singleImage)));

Da es sich um verschachtelte Listen handelt kann, kann man auf jeder Ebene AsParallel nutzen um diese parallel zu bearbeiten. Nach einigem experimentiern mit meinen Bildern kam ich dabei auf ein interessantes Phänomen. Mein Bilder sind zu 3 Pakete gebündelt welche jeweils 50 oder 255 Bilder enthalten. Dabei ergaben sich folgende Bearbeitungszeiten:

Modus 50 Bilder 255 Bilder
Ohne AsParallel 30,4sec 140,8sec
AsParallel auf beiden Ebenen 7,1sec 25,4sec
AsParallel auf der untersten Ebene 5,7sec 22,8sec

Es scheint, als bringe das parallele abarbeiten von parallel abzuarbeiten Aufgaben keinen Geschwindigkeitsvorteil, im Gegenzug sogar ein Verwaltungsoverhead. Ich stecke zu wenig in der Materie drin um ein fundiert Erkärung für diese Phänomen zu liefern, aber ich habe eine Theorie: Das äußere AsParallel (im Folgenden AP1) hat lediglich die Aufgabe die Verarbeitung der einzelnen Pakete zu starten.  Das innere AsParallel (im Folgenden AP”) hingegen hat schwer zu tun. Ich meine mal gelesen zu haben, dass PLinq einzelne Threads gleichermaßen zum Zuge kommen lässt.  Das heißt sowohl AP1 als auch AP2 werden vielleicht 100 mal pro Sekunden an die CPU gelassen. Währen AP2 diese Zeit nutzt um die Bildverarbeitung voran zu treiben, stellt AP1 lediglich fest, das AP2 noch nicht fertig ist und gibt die CPU wieder frei. Dieser Overhead bremst dann das ganze Programm, wodurch dann die oben genannten Zahlen zustanden kommen.

Comments Off on PLinq – und wann man es nicht nutzen sollte :, , , more...

LogfileSplitter – a small tool for splitting logfiles

by on May.16, 2011, under ByTheWay, Tools

Kürzlich wurde ich von einem Freund gebeten ein kleines Programm zu schreiben, welches in der Lage ist, große Logdatein eines Programms zu splitten um diese besser auswerten zu können.   Getest habe ich das Programm an einer 7MiB großen Logdatei mit knapp 125000 Zeilen. Das Programm splittet die Datei in 169 Dateien innerhalb von knapp 25 Sekunden, was natürlich start von dem Suchstring abhängt.

Die Logdateien werden in ein vorher gewählten Ordner geschrieben und mit ein Präfix versehen. Die Analyse findet den Suchstring mittels der Methoden: Is, Contains, BeginsWith, EndWith.

logfilesplitter gui screenshot

Scrrenshot of the LogfileSplitter V0.51

Download: LogfileSplitter

Comments Off on LogfileSplitter – a small tool for splitting logfiles :, , , , , 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...

HowTo: Embedded Resource aus XAML und C# nutzen

by on Apr.18, 2010, under HowTo, MicroMite

Bilder und weiteren Inhalt in eine EXE einzubinden und zu nutzen hat einen besonderen Reiz: man muss keine Fehlerbehandlung einbauen die zur Laufzeit nicht auffindbare Dateien behandeln, niemand (oder zumindest die meisten) können diese Inhalte werde auslesen noch verändern.

Damit die Ressource eingebettet werden kann, muss diese in Visual Studio erst einmal zum Projekt hinzugefügt werden. An einem Beispiel gehe ich das mal durch:Embedded PNG-File Wie im Bild rechts zu sehen ist, habe ich ein Bild Delete.png zum Projekt hinzugefügt. Um dieses aus XAML anzusprechen bedarf es einer speziellen Einstellung: Im Eigenschaftenfenster muss unter “Build Action” Resource ausgewählt sein. Die allgemeine Syntax würde lauten:

   Icon="/{AssemblyName};component/directory/Image.ico"

Wobei {AssemblyName} durch den Namen des Assembly ersetzt werden muss (meist der Name des WPF-Projektes). Der anschließenden Teil (“;component/”) muss ebenfalls vorhanden sein und ist immer gleich. Den AssemblyName kann auch zur Laufzeit mit folgendem Befehl abgefragt werden:

   this.getType().Assembly.GetName().Name;

Und das ganze am Beispiel des Icons eines Window:

<Window 
   x:Class="MicroMite.Gui.MainWindow"
   Title="MicroMite" Height="400" Width="280"
   xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   Icon="/MicroMite;component/content/icon/miscellaneous/MiteWindowIcon.ico"/>

Um aus C# mittels Programmcode auf die Ressource zugreifen zu können, muss diese in den Eigenschaften als “Embedded Ressource” unter “Build Action” aufgeführt werden. Mir ist es bisher nicht gelungen eine “Resource” aus C# anzusprechen oder eine “Embedded Ressource” unter XAML – sehr schade eigentlich. Ansonsten geht das wie folgt:

   String uri = @"content.icon.miscellaneous.Delete.png";
   Assembly assembly = this.getType().Assembly;
   String assemblyName = ssembly.GetName().Name;
   System.IO.Stream stream = assembly.GetManifestResourceStream(assemblyName+"." + uri);
   Icon.Resource = new PngBitmapDecoder(stream,
                                         BitmapCreateOptions.PreservePixelFormat,
                                         BitmapCacheOption.Default).Frames[0];

Da die Inhalte roh in die Exe eingebettet werden, behalten diese ihr Format bei und müssen erst mit einem passenden Decoder zu einer ImageSource konvertiert werden. Im Namespace  System.Windows.Media.Imaging stehe passenden Decoder für die Bildformate PNG, JPEG, BMP, GIF, TIFF und WMB  bereit. Ich werde demnächst eine DLL hier einstellen, welche all diese Funktionalitäten abstrahiert.

1 Comment :, , , more...

HowTo: Anaglyph Shader in XNA

by on Feb.27, 2010, under 3D, Computer Graphics, HowTo, shader

Im Rahmen meiner Bachelorarbeit brauchte ich eine Möglichkeit, in XNA Bilder anaglyph zu kodieren. Ausgangsmaterial waren 2 Bilder, eines für das linke, eines für das rechte Auge. Diese müssen Mithilfe einer SpriteBatch gemalt und durch Verwendung eines Pixelshaders entsprechend kodiert werden. Der verwendete Shader kodiert die Bilder so, das diese mit einer rot-cyan-Brille korrekt betrachtet werden können. Die Kodierung selbst erfolgt durch den Dubois-Algorithmus von Frederic V. Hessmann, welchen ich aus seinem Java-Programm zu Erstellung von Anaglyphbildern adaptiert habe.
Auf der Seite von XNA sieht das Ganze dann wie folgt aus:

Texture2D left = Texture2D.FromFile(graphicsDevice, FilepathLeft);
Texture2D right = Texture2D.FromFile(graphicsDevice, FilepathRight);

spriteBatch.Begin(
   SpriteBlendMode.None,
   SpriteSortMode.Immediate,
   SaveStateMode.SaveState);
{
   effect.Begin();
   {
      effect.CurrentTechnique.Passes[0].Begin();
      effect.Parameters["left"].SetValue(left);
      effect.CommitChanges();
      spriteBatch.Draw(right, new Microsoft.Xna.Framework.Rectangle(
         0, 0, Width, Height), Color.White);
      effect.CurrentTechnique.Passes[0].End();
   }
   effect.End();
}
spriteBatch.End();

Das linke Teilbild wird als Parameter an den Shader übergeben, das rechte Teilbild wird direkt mit der SpriteBatch gemalt.

//Auszug aus dem Shader

float4 PixelShader(float2 uv: TEXCOORD0) : COLOR
{
   float4 l = tex2D(leftSampler, uv);
   float4 r = tex2D(ColorMapSampler, uv);

   float red = 0.456100*l.r + 0.500484*l.g + 0.176381*l.b
             - 0.0434706*r.r - 0.0879388*r.g - 0.00155529*r.b;
   if (red > 1) { red = 1; }   if (red < 0) { red = 0; }

   float green = -0.0400822*l.r - 0.0378246*l.g
                 -0.0157589*l.b + 0.378476*r.r
                 +0.73364*r.g - 0.0184503*r.b;

   if (green > 1) { green = 1; }   if (green < 0) { green = 0; }

   float blue = -0.0152161*l.r - 0.0205971*l.g
                -0.00546856*l.b - 0.0721527*r.r
                -0.112961*r.g + 1.2264*r.b;

   if (blue > 1) { blue = 1; }   if (blue < 0) { blue = 0; }

   return float4(red, green, blue, 1);
}

Als erstes wird der aktuelle Pixel aus de Bildern gesampelt, wobei der linke Sampler ein gewöhnlicher Texturesampler ist und der rechte Sample auf Register s0 (sampler ColorMapSampler : register(s0)) zugreift. Im Anschluss werden die 3 Farbkanäle des Zielpixels nach dem Dubois-Algorithmus berechnet und als zu zeichnender Pixel zurück geliefert.

Der im Folgenden verlinkte AnaglypShader.fx und JPSLoader.cs bauen im Wesentlichen auf diesem Beispiel auf und erweitern dieses noch. Der JPS-Loader ist in der Lage ein JPS-Bild (JPEG-Stereo) zu laden, welches im Grunde eine JPEG-Bild ist, bei dem das linke und rechte Bild nebeneinander in der gleichen Datei liegen. Der JPS-Loader zerschneidet das Bild und liefert so das Ausgangsmaterial für den oben beschriebenen
AnaglypShader.

Comments Off on HowTo: Anaglyph Shader in XNA :, , , , 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...