Forum: PC-Programmierung C#:Bitmap an eine DLL-Funktion(C) übergeben


von maggo (Gast)


Lesenswert?

Hi,

Wie übergebe ich den Inhalt eines Bitmap an eine Funktion die in einer 
DLL ausgelegt ist. Die DLL werd ich in C geschrieben. Die Funktion soll 
das Bitmap auch verändert.

Oder wenn dass nicht geht das Bitmap direkt an die Funktion zu 
übergeben.
Dann könnte ich auch ein 2D-Array verwenden und übergeben. Da mir die 
Quellfunktion auch ein 2D-Array liefert kann.

Da habe ich aber noch immer das Problem, dass ich das nach das 
veränderte 2D-Array wieder in ein Bitmap bekommen muss. Damit ich es im 
Form angezeigt kann. Mit SetPixel will ich nicht Arbeiten da mir das zu 
langsam ist.
Das Bitmap hat eine Auflösung von 754x480Pixel.

Gruß maggo

von Bernd H. (geeky)


Lesenswert?


von maggo (Gast)


Lesenswert?

hallo Bernd H.

Danke für deine Antwort.
Ich habe mir dein Link angesehen und schon gefreut das ich eine Lösung 
habe.
Bis ich gemerkt habe das die Funktion auf dem .NET-Framwork aufbaut.
Die DLL ist in C geschrieben ohne .NET.
Als Compiler wird der DevC++ 4.9.9.2 verwendet.

Gruß Marco

von Peter II (Gast)


Lesenswert?

warum machst du es überhaupt mit der DLL?

ist eien reine .net lösung zu langsam?

es geht doch bestimmt um:
Beitrag "[C#] C-DLL-Funktion führt zum Fehler (PInvokeStackImbalance wurde erkannt.)"

von maggo (Gast)


Lesenswert?

Hi Peter II,

Ja, die rein .NET-Umsetzung ist mir zu langsam.

Ich brauche für ein Bild zum um Rechnen in .NET ca 700ms-750ms.
Des wegen will ich die rechenintensive Funktionen in die DLL auslagern. 
Da das reine C viel schneller ist. Da benötoige ich für die selbe 
Aufgabe > 10 ms.

Ja genau der bin ich.

Gruß Marco

von Peter II (Gast)


Lesenswert?

kannst du mal zeigen wie du es in .net gelöst hast, ich kann mir nicht 
vorstellen das es so extrem viel langsamer ist. Denn auch der transfer 
von .net zu C dauert ein wenig.
Dein C code war schon nicht optimal - evenutell kann man ja bei den .net 
code noch einiges verbessern

von Bernd H. (geeky)


Lesenswert?

maggo schrieb:
> Bis ich gemerkt habe das die Funktion auf dem .NET-Framwork aufbaut.

Ich bin davon ausgegangen, dass die Anwendung in C# geschrieben ist, die 
DLL in C geschrieben ist und dass mit Bitmap auf DLL-Seite die normale 
WinAPI Bitmap gemeint ist (welche über HBITMAPs angesprochen werden) ;D

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hier der quellcode der Funktion in .NET:
Die Klasse bmp habe ich mal angehängt.
Da durch war es mir erst mögkich die durchlaufzeit auf ca 700 ms zu 
reduzieren. Mit den normalen Bitmap-Funktionen hat er ca 15 Sekunden für 
ein Bild benötigt.
1
private Bitmap soebel(Bitmap input_image, int hoehe, int breite)
2
{
3
    int new_x = 0, new_y = 0;
4
    int c;
5
6
    bmp.FastBitmap input = new bmp.FastBitmap(input_image);
7
    bmp.FastBitmap output = new bmp.FastBitmap(breite, hoehe);
8
            
9
    int[,] gx = new int[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };   //  The matrix Gx
10
    int[,] gy = new int[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } };  //  The matrix Gy
11
12
    for (int i = 1; i < hoehe - 1; i++)   // loop for the image pixels height
13
    {
14
        for (int j = 1; j < breite - 1; j++) // loop for image pixels width    
15
        {
16
            new_x = 0;
17
            new_y = 0;
18
            for (int hw = -1; hw < 2; hw++)  //loop for cov matrix
19
            {
20
                for (int wi = -1; wi < 2; wi++)
21
                {
22
                    c = (input.GetPixel(j + wi, i + hw).B + input.GetPixel(j + wi, i + hw).R + input.GetPixel(j + wi, i + hw).G) / 3;
23
                    new_x += gx[hw + 1, wi + 1] * c;
24
                    new_y += gy[hw + 1, wi + 1] * c;
25
                }
26
            }
27
            if (new_x * new_x + new_y * new_y > 128 * 128)
28
            {
29
                output.SetPixel(j, i, Color.Black);
30
            }
31
            else
32
            {
33
                output.SetPixel(j, i, Color.White);
34
            }
35
        }
36
    }
37
    return output.ToBitmap();
38
}

von maggo (Gast)


Lesenswert?

Hi Bernd H.,

OK das wuste ich nicht. Das habe ich in dem Artikel auch nicht gesehen, 
dass es da eine WINAPI32-Version gibt. Werd mir mal auch anschauen.

Gruß Marco

von Peter II (Gast)


Lesenswert?

ist ja kein Wunder das der .net code viel langsamer ist.

in C verwendest du ein array aus bytes. ( char *bild_temp )

in .net ein array vom type color -> 32bit -> 4 Fache datenmenge!

Dann machst du für jeden Pixel einen Methodenaufruf ( output.SetPixel(j, 
i, Color.Black); )

in diese Methode prüfst du auch noch eine Variabel ab und setzt auch 
noch eine Variable:

 public void SetPixel(int x, int y, Color col)
        {
          while(!Ready){}
            color[x, y] = col;
            modified = true;
        }

wenn man soetwas vergleicht, dann sollte man auch die gleiche 
funktionaltiät vergleichen.

von Peter II (Gast)


Lesenswert?

auch das ist nicht optimal - hier muss (wenn es der optimier nicht merk) 
3mal ins Array gefasst werden:

c = (input.GetPixel(j + wi, i + hw).B +
     input.GetPixel(j + wi, i + hw).R +
     input.GetPixel(j + wi, i + hw).G) / 3;

besser ist schon mal

color tmp = input.GetPixel(j + wi, i + hw);
c = (tmp.R + tmp.R + tmp.G) / 3;

von maggo (Gast)


Lesenswert?

Hi Peter  II,

Ich kann als Quelle auch ein Array nehmen.
Ich ändere dass mal kurz um.

Gruß Marco

von Peter II (Gast)


Lesenswert?

noch etwas grundsätzliches:

eine for schleife über ein array ist in .net langsamer als ein foreach.

Bei einem foreach muss keine Bereichsprüfung gemacht werden, weil der 
iterator immer gültig ist. mit der for schleife und den index zugriff

x = a[i];

wird jedesmal ein bereichsprüfung des arrays gemacht.

Man müsste jetzt mal schaun ob es foreach auch auf mehrdimensionale 
arrays geht.

von Peter II (Gast)


Lesenswert?

hier ncoh ein vorschlag für eine optimierung (diese sollte richtig was 
bringen)


du berechnest in der untersten schleife die helligkeit eines pixels. 
Damit wird aber die helligkeit für jeden pixel insgesamt 9 mal 
berechnet.

Erstell bitte am anfang ein array[x,y] wo schon die helligkeit für jeden 
Pixel drin steht.

von Arc N. (arc)


Lesenswert?

Peter II schrieb:
> noch etwas grundsätzliches:
>
> eine for schleife über ein array ist in .net langsamer als ein foreach.
>
> Bei einem foreach muss keine Bereichsprüfung gemacht werden, weil der
> iterator immer gültig ist. mit der for schleife und den index zugriff
>
> x = a[i];
>
> wird jedesmal ein bereichsprüfung des arrays gemacht.

Nein
http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx


Was das ganze allerdings wesentlich beschleunigen würde ist, direkt mit 
einem eindimensionalen Array zu arbeiten und das Ergebnis in eine Bitmap 
umwandeln, anstatt mit Get/SetPixel zu arbeiten
http://stackoverflow.com/questions/6782489/create-bitmap-from-byte-array-of-pixel-data

Wenn eine Bitmap übergeben wird, kann man zum schnellen Zugriff auch 
Bitmap LockBits und BitmapData (BitmapData.Scan0) nutzen und zur Not mit 
unsafe und Zeigern arbeiten...

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hi Peter II,

Das der Thread nicht so lang wird habe ich mal die aktuelle Version als 
Datei angehängt.

Die Durchlaufzeit der Funktion hat sich auf 40ms reduziert.

Das mit der foreach versuche ich mal um zusetzten. Mal schauen was das 
noch alles Bringt. Gefühlt habe ich eigentlich keine größe Änderungen 
gemacht aber die Zeitersparnis ist schon enorm. Das DLL-Konzept ist 
zumindest schon mal vom Tisch.

Mit dem letzten Optimiervorschlag versteh ich gerade nicht genau was du 
meinst.
Du meine aber diesen Code-Bereich:
1
for (int hw = -1; hw < 2; hw++)  //loop for cov matrix
2
{
3
    for (int wi = -1; wi < 2; wi++)
4
    {
5
        c = (input[i + hw, j + wi] + input[i + hw, j + wi + 1] + input[i + hw, j + wi + 2]) / 3;
6
        new_x += gx[hw + 1, wi + 1] * c;
7
        new_y += gy[hw + 1, wi + 1] * c;
8
    }
9
}

Gruß Marco

von Peter II (Gast)


Lesenswert?

Arc Net schrieb:
>> wird jedesmal ein bereichsprüfung des arrays gemacht.
> Nein
> http://blogs.msdn.com/b/clrcodegeneration/archive/...

ich glaube doch, weil nicht mit einer Konstanten für das array und die 
forschleife und nicht mit length gearbeitet wird.

int[,] gx = new int[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } };

for (int hw = -1; hw < 2; hw++)


man müsste dann wenigsten schreiben:

for (int hw = 0; hw < gx.length; hw++)

(der syntax geht aber glaube ich nicht bei mehrdimensonalen arrays.)

von Peter II (Gast)


Lesenswert?

maggo schrieb:
> Mit dem letzten Optimiervorschlag versteh ich gerade nicht genau was du
> meinst.
> Du meine aber diesen Code-Bereich:

ja. dort wird jedes mal r+g+b/3 gerechnet und das ganze insgesamt 
3*3*x*y mal.

wenn du aber vorher ein array aufbaust

int helligkeit[x,y] = new int[x,y];
for( x ... )
  for( y .. )
     helligkeit[x,y] = (input[i + hw, j + wi] + input[i + hw, j + wi + 
1] + input[i + hw, j + wi + 2]) / 3;


dann kannst du später einfach
1
for (int hw = -1; hw < 2; hw++)  //loop for cov matrix
2
{
3
    for (int wi = -1; wi < 2; wi++)
4
    {
5
        c = helligkeit[i + hw, j + wi];
6
        new_x += gx[hw + 1, wi + 1] * c;
7
        new_y += gy[hw + 1, wi + 1] * c;
8
    }
9
}

schreiben und schon sparst du dir die mehrfach berechung

bin dann morgen wieder da, da bekommt man bestimmt unter 20ms.

von Peter II (Gast)


Lesenswert?

so meinte ich es:
1
int helligkeit[x,y] = new int[x,y];
2
for( x ... )
3
  for( y .. )
4
     helligkeit[x,y] = (input[x, y] + input[x, y+1] + input[x, y+2]) / 3;

von maggo (Gast)


Lesenswert?

Hi Peter II,

Ich habe mir das gerade mit gx.length der gibt leider nur den Wert 9 
aus.
Es gibt noch gx.GetLongLength(0) aber das ist ja ein Funktionsaufrufe 
und der kostet wieder Laufzeit.

Ja jetzt habe ich es verstanden.

Danke schon mal und noch einen schönen Abend.
Gruß Marco

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Guten Morgen,

Nach dem ich die Anderung durch geführt habe, sind es es jetzt ca 
13-15ms die die Funktion zum durchlaufen beötigt. Es sind regelmässig 
Peaks enthalten, da beträgt die Durchlaufzeit ca 24ms ich vermutte der 
Debugger unterbricht kurz den Prozess.

Ich habe zusätzlich noch das Rückgabearray auf bool geändert. Hat zwar 
messtechnisch hier nix gebracht aber später bei der Suche wo die Kannte 
ist wird warscheinlich von Vorteil sein.

Das mit der foreach-Schleife kann ich nicht nutzen da die Schleife mir 
nur ein Wert pro Durchgang liefert und ich zusätlich noch die 
Nachbarwerte benötige.

Gruß Marco

von maggo (Gast)


Lesenswert?

So mist da da ist noch ein Fehler drin.
Und zwar folgende Zeillen
1
    int HelligkeitBreite = breite / 3;
2
3
    for (int i = 1; i < hoehe - 1; i++)   // loop for the image pixels height
4
    {
5
        for (int j = 3; j < HelligkeitBreite - 3; j += 3) // loop for image pixels width
muss in folgendes abgeändert geändert werden
1
    for (int i = 1; i < hoehe - 1; i++)   // loop for the image pixels height
2
    {
3
        for (int j = 3; j < breite - 3; j += 3) // loop for image pixels width

Da mit bekomme ich eine Durchlaufzeit von 27-28 ms hin.

Gruß Marco

von Peter II (Gast)


Lesenswert?

irgendwie habe ich das gefühl das der code nicht stimmt:
1
 for (int x = 0; x < hoehe; x++)
2
        for (int y = 0, y2 = 0; y < breite; y += 3, y2++)
3
            helligkeit[x, y2] = (byte) ((input[x, y] + input[x, y + 1] + input[x, y + 2]) / 3);
hier füllst du das array helligkeit in 1er schritten (y2++)


aber hier verwendest du es in 3er schritten:
1
for (int j = 3; j < HelligkeitBreite - 3; j += 3)
2
     c = helligkeit[i + hw, j + wi];

von maggo (Gast)


Lesenswert?

Hi Peter II,

Das stimmt. Da war die ganze zeit ein Fehler drin.
Hatte bei der Optimierer die Ausgabe nie auf Richtigkeit kontrolliert.

Habe die Fehler beseitigt und jetzt kommt wieder eine sinnvolle Daten 
aus der Funktion.

Jetzt mit habe ich eine Durchlaufzeit von ca 90 ms.

Ich habe nach die zwei innersten Schleife gelöscht und den 
Schleifeninhalt als lineare Aufrufe umgeschrieben.
Zusätzlich alle int-Variablen durch short-Variablen ersetzt.

Jetzt habe ich eine Durchlauf zeit von ca 70 ms.

Meinst du ich könnte wenn ich statt mit einem Index besser mit einem 
Pointer auf das Array zu zugreiffen, die Laufzeit noch mal verkürzen?

Gruß Marco

von Peter II (Gast)


Lesenswert?

kannst du mal den aktuell quellcode - am besten so damit ich selber mal 
testen kann bereitstellen?

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hi Peter II,

Sorry, denn hatte ich vergessen.

Gruß Marco

von Peter II (Gast)


Lesenswert?

so ich habe mal ein wenig getestet - das Problem ist immer noch das er 
ein Rage-Check beim dem Array zugriff macht und das ganze viel zu oft. 
Habe leider keine sinnvolle möglichkeit gefunden ihm das auzureden.

Der code wird zwar nicht schöner dadurch aber wenn man die array  gx und 
ky gleich ausrechnet dann geht es noch ein ganzen stück schneller. Man 
kann dadurch sogar einige berechnunge komplett sparen.
1
 c = helligkeit[i - 1, j];
2
 new_x += -1 * c;
3
 new_y += 1 * c;
4
5
 c = helligkeit[i - 1, j + 1];
6
 new_x += -1 * c;
7
 new_y += 0 * c; //diese berechnen kann jetzt ganz weg

man müsste irgendwie das array gx und gy als const deklarieren damit er 
die werte selber in den code direkt einsetzt aber das habe ich nicht 
geschafft.

Die sache mit dem short würde ich nicht mache, bringt keine Vorteil und 
der code ist schlechter lesbar.

Wenn es dann immer noch zu langsam ist, dann hätte ich noch eine idee.

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hi Peter II,

Ich habe jetzt die beiden Array gx, gy raus genommen und alle Werte 
direkt an die entsprechende Stelle geschrieben. Die mit den Werten 0 
habe ich auskommentiert. Die Durchlaufzeit beträgt jetzt 36 ms.

Das einzigste was mir ein fällt um das ganze noch einmal zu 
beschleunigen  ist das alles mit Pointer zu realiesieren.
Das habe ich jetzt auch gemacht.
Jetzt habe ich eine Durchlaufzeit von 22-24ms.

Gruß Marco

von maggo (Gast)


Lesenswert?

Da ist mir doch gerade wieder ein Rechenfehler passiert das i++ in der 
if-Abfrage muss i+=2 lauten.

von Peter II (Gast)


Lesenswert?

also das mit dem pointer würde ich nicht machen - ist dann kein saubere 
c# mehr.

Hier noch meine Idee (weiss aber noch nicht wie man sie am besten 
umsetzen kann)

man müsste die 9 benötigen helligkeite in Variable speichern. Je Zeile 
und Spalte muss dann nur noch um 1 nachgerutscht werden.

h1 = h2
h2 = h3
h3 = (zugriff auf array)



damit müsste man viel weniger auf das array zugreifen.

von maggo (Gast)


Lesenswert?

Hi PeterII,

Ich werd mir mal die Idee heute Nacht mal durch den Kopf gehen lassen.
Ich habe glaub ich eine Lösung.

Was sind die Nebenwirkung von den Pointerzugriffe außer das die 
Kontrolle nicht mehr vorhanden ist, ob wir uns noch im Gültigen Array 
befinden?

Gruß
Marco

von Peter II (Gast)


Lesenswert?

maggo schrieb:
> Was sind die Nebenwirkung von den Pointerzugriffe außer das die
> Kontrolle nicht mehr vorhanden ist, ob wir uns noch im Gültigen Array
> befinden?

naja bin mir da nicht sicher ob das bei 32/64/arm immer geht. Außerdem 
bei einem fehler hat man gleich einen absturts und keine ordentliche 
exception und callstack.

Ist halt die Frage für was es ist. Brauchst du wirklich jede MS oder ist 
es schon schnell genug?

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hi PeterII,

Ich habe mal die Idee um gesetzt.
Die Umsetzung hat dann die Durchlaufzeit auf 23-24ms reduziert und liegt 
da mit auf Augenhöhe mit der Pointer-Variante.

Ich werd die Pointer-Variante nicht verwenden da es im Unsafe-Mode läuft 
und genau so schnell ist wie die Array-Variante. Von der Durchlaufzeit 
sollte es jetzt auch reichen, dass sind jetzt 41 Bilder/s. Die Kamera 
läuft mit 30 Bildern/s. Die Reserver brauch ich noch, da ich die 
Funktion in einen Backgroundworker auslagere.

Da ich es wissen wollte ob es auch bei der Pointer-Variante etwas bringt 
die Werte zwischen zuspeichern. Habe ich es auch mal auf die 
Pointer-Variante angewendet. Die Durchlaufzeit konnte etwas reduziert 
werden und zwar auf 22 ms.

Danke PeterII für deine Hilfe. Ohne dich hätte ich es nicht geschaft.

Gruß Marco

von Peter II (Gast)


Lesenswert?

so da kann man jetzt aber wieder noch mehr umbauen.

die braucht nach dieser änderung das array Helligkeit nicht mehr. Dann 
die greist nicht mehrfach auf ein Pixel zu.

Damit spart man sich die zeit zum erzeugen vom dem array.

von maggo (Gast)


Lesenswert?

Hi PeterII,

Ich glaub das bringt nicht sehr viel.

Da ich ohne das Helligkeit-Array insgesamt 9 Lesezugriffe auf das 
Bild-Array habe und mir das erzeugen des Array spare.

Mit dem Helligkeit-Array habe ich 3 Lesezugriffe auf des Bild-Array, ein 
schreiben Zugriff und 3 lesende Zugriffe auf das Helligkeit-Array.

Außer das erzeugen des Array-Objekt kostet sehr viel Zeit und dauert 
länger als 2 lesende Zugriffe auf ein Array bei einer Arraygröße von 
754x480.

Ich werd es mal umsetzten. Getreu nach dem Motto probieren geht übers 
studieren.

Gruß Marco

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hi PeterII,

Ich habe es mal um gesetzt.
Es hatte leider einen negative Effekt.
Die Durchlaufzeit beträgt jetzt 28-29ms.

Ich werd das Helligkeit-Array wieder verwenden.

Wenn es doch nicht langt dann wird  es 2 Backgroundworker-Funktion 
geben. Eine Funktion erzeugt das Helligkeit-Arrray und in der zweiten 
Funktion wird der Sobel-algorithmus auf das Array angewendet. Das sorgt 
dann für eine besser Auslastung der CPU.

Gruß Marco

von Markus V. (valvestino)


Lesenswert?

Hallo Marco,

rein interessehalber: Misst Du eigentlich Deine Zeiten mit der Debug- 
oder der Release-Einstellung im Projekt? Ich habe nämlich gerade mal 
einen kleinen Test mit einem Profiler gemacht und war dabei recht 
erstaunt, dass der Unterschied zwischen optimierter und Debug-Variante 
recht groß sein kann.

Und noch ein kleiner Hinweis, der in der Bearbeitungszeit auch noch ein 
paar Ticks einbringt (Release deutlich mehr als Debug): Verwende 
anstelle von
1
if (new_x * new_x + new_y * new_y > 128 * 128)
2
{
3
    output[i, j/3] = true;
4
}
5
else
6
{
7
    output[i, j/3] = false;
8
}

besser
1
output[i, j/3] = new_x * new_x + new_y * new_y > 128 * 128;

Bei meinem Testprogramm waren das immerhin ca. 0,5 ms pro Bild.

Gruß
Markus

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hi Markus,

Das ist eine gute Frage.
Hatte den Compiler schon die ganze Zeit auf Release stehen aber immer 
aus dem Visualstudio gestartet. Jetzt direkt die Anwendung gestartet und 
schon sind wir bei ca 17-18 ms Durchlaufzeit.

Den Tipp den die if-Abfrage wegzu lassen und das Ergebnis direkt in das 
Array zu schreiben kann ich leider nicht emhr machen. Da ich gemerkt 
habe ich kann keine bool-Werte addieren. Muste ich auf ein Byte-Array 
wechseln und da funktioniert der Trick leider nicht.

Habe den aktuellen Stand wieder Angehängt.

Gruß Marco

von Sebastian L. (Gast)


Lesenswert?

Ich dachte für die Faltung der horizontalen und vertikalen 
Sobeloperation musst du noch die wurzel draus berechnen und in das 
mittlere feld schreiben
1
int new_xy = new_x * new_x + new_y * new_y;
2
if (new_xy > 128*128)
3
{
4
    output[i, j] = 128;
5
}
6
else
7
{
8
    output[i, j] = Math.Sqrt(new_xy);
9
                // näherungsweise |new_x|+|new_y| 
10
                // bei embedded anwendungen eingesetzt (ohne dsp)
11
}

was mir auch noch aufgefallen ist,
die multiplikations 128*128 könntest du vor den schleifen berechnen und 
speichern

von Peter II (Gast)


Lesenswert?

Sebastian L. schrieb:
> die multiplikations 128*128 könntest du vor den schleifen berechnen und
das macht der compier schon - so dumm ist er nun auch wieder nicht.

von Markus V. (valvestino)


Lesenswert?

Hsllo Marco,

mir ist im folgenden Codeabschnitt noch folgendes aufgefallen:
1
    for (short i = 1; i < hoehe - 1; i++)
2
    {
3
        j = 1;
4
        //Variablen für den ersten Durchlauf mit Werten befühlen
5
        c1_2 = helligkeit[i - 1, j];
6
        c1_3 = helligkeit[i - 1, j + 1];
7
8
        c2_2 = helligkeit[i, j];
9
        c2_3 = helligkeit[i, j + 1];
10
11
        c3_2 = helligkeit[i + 1, j];
12
        c3_3 = helligkeit[i + 1, j + 1];

Du solltest j+1 durch die Konstante 2 ersetzen. Die Rechenergebnisse i-1 
und i+1 solltet Du zwischenspeichern und überall, vor allem in der 
inneren Schleife verwenden.

Wenn Du die innere Schleife von j=2 mit j<breite laufen lässt, sparst Du 
drei mal die Berechnung j+1, brauchst allerding bei der output-Zuweisung 
ein j-1.

Anstelle des if am Ende der inneren Schleife würde sich dann
1
output[i,j-1] = new_x * new_x + new_y * new_y > 128 * 128 ? 1 : 0;
anbieten. Das ist etwas schneller.

Gruß
Markus

von Peter II (Gast)


Lesenswert?

Markus Volz schrieb:
> Du solltest j+1 durch die Konstante 2 ersetzen. Die Rechenergebnisse i-1
> und i+1 solltet Du zwischenspeichern und überall, vor allem in der
> inneren Schleife verwenden.
>
> Wenn Du die innere Schleife von j=2 mit j<breite laufen lässt, sparst Du
> drei mal die Berechnung j+1, brauchst allerding bei der output-Zuweisung
> ein j-1.

schaut euch doch mal bitte vorher den asm code an - dann werden ihr 
feststellen das das schon der compiel sehr gut hinbekommt. Das 
Hauptproblem ist der zugriff auf ein mehrdimensonales Array - da macht 
er immer ein Rage-Check (das ganze mit einem call xxx ) Das kostet die 
meiste zeit.

Ob man nun

output[i,j-1] = new_x * new_x + new_y * new_y > 128 * 128 ? 1 : 0;

oder ein if schreibt, ändert maximal einen Takt aber das bringt am ende 
nichts.

von Markus V. (valvestino)


Lesenswert?

Peter II schrieb:
> Ob man nun
>
> output[i,j-1] = new_x * new_x + new_y * new_y > 128 * 128 ? 1 : 0;
>
> oder ein if schreibt, ändert maximal einen Takt aber das bringt am ende
> nichts.
dotTrace sagt was anderes. Das if ist messbar langsamer.

Gruß
Markus

von Peter II (Gast)


Lesenswert?

Markus Volz schrieb:
> dotTrace sagt was anderes. Das if ist messbar langsamer.

der ASM code im Relase ist bis auf den sprung gleich, und dieser macht 
nicht wirklich zeit aus. Es ist zwar mehr code aber diese wird ja nicht 
durchlaufen weil drüber gesprungen wird.

Ich kenn dotTrace nicht, aber ich zweifle mal das ergebniss an.

               if (new_x * new_x + new_y * new_y > 128 * 128) {
00000293  mov         eax,dword ptr [ebp-8]
00000296  imul        eax,dword ptr [ebp-8]
0000029a  mov         edx,dword ptr [ebp-0Ch]
0000029d  imul        edx,dword ptr [ebp-0Ch]
000002a1  add         eax,edx
000002a3  cmp         eax,4000h
000002a8  jle         000002BD
                  output[i, j] = true;
000002aa  push        dword ptr [ebp-28h]
000002ad  push        1
000002af  mov         edx,dword ptr [ebp-24h]
000002b2  mov         ecx,dword ptr [ebp-68h]
000002b5  call        5DA6BC00
000002ba  nop
000002bb  jmp         000002CD
               } else {
                  output[i, j] = false;
000002bd  push        dword ptr [ebp-28h]
000002c0  push        0
000002c2  mov         edx,dword ptr [ebp-24h]
000002c5  mov         ecx,dword ptr [ebp-68h]
000002c8  call        5DA6BC00
               }


      output[i, j] = new_x * new_x + new_y * new_y > 128 * 128;
000002cd  push        dword ptr [ebp-28h]
000002d0  mov         eax,dword ptr [ebp-8]
000002d3  imul        eax,dword ptr [ebp-8]
000002d7  mov         edx,dword ptr [ebp-0Ch]
000002da  imul        edx,dword ptr [ebp-0Ch]
000002de  add         eax,edx
000002e0  cmp         eax,4000h
000002e5  setg        al
000002e8  movzx       eax,al
000002eb  push        eax
000002ec  mov         edx,dword ptr [ebp-24h]
000002ef  mov         ecx,dword ptr [ebp-68h]
000002f2  call        5DA6BC00

von Markus V. (Gast)


Lesenswert?

@Peter II
Ok, jetzt habe ich ein klein wenig mehr Zeit, als heute früh.

Du hast natürlich recht, die Range-Checks bei den Array-Zugriffen wiegen 
sicherlich mehr als die paar Rechenoperationen und das if. Aber wenn man 
mit wenig Aufwand was besser machen kann, warum nicht?

@Marco
Zu den Range-Checks: C# (bzw. .Net) ist da recht pfiffig, siehe 
http://blogs.msdn.com/b/clrcodegeneration/archive/2009/08/13/array-bounds-check-elimination-in-the-clr.aspx

Eventuell wäre es günstiger, die Arrays helligkeit und output (oder auch 
nur helligkeit?) nicht mit [hoehe,breite] sondern mit [hoehe][breite] zu 
deklarieren. Dann kann in der Schleifenende-Bedingung mit 
helligkeit[i].length gearbeitet werden, was wiederum laut oben 
verlinktem Artikel den Range-Check beim Zugriff auf helligkeit 
ausschaltet.

Wenn ich dazu komme, werde ich mal dotTrace darauf loslassen.

Gruß
Markus

von Peter II (Gast)


Lesenswert?

Markus V. schrieb:
> Eventuell wäre es günstiger, die Arrays helligkeit und output (oder auch
> nur helligkeit?) nicht mit [hoehe,breite] sondern mit [hoehe][breite] zu
> deklarieren. Dann kann in der Schleifenende-Bedingung mit
> helligkeit[i].length gearbeitet werden, was wiederum laut oben
> verlinktem Artikel den Range-Check beim Zugriff auf helligkeit
> ausschaltet.

die Idee ist gut, aber wie legt man ein soetwas an? Dann muss man ja das 
anlegen schon in einer schleife machen oder nicht? Das kostet dann auch 
wieder zeit.

die meiste zeit wird beim Zugriff auf Helligeit drauf gehen (weil es 6 
mal gemacht wird), da aber dort auch eine Berechnung drin steckt (i-1, 
j+2 usw.) wird dann wieder ein Rage-check meines wissens immer gemacht.

von Markus V. (Gast)


Lesenswert?

Peter II schrieb:
> die Idee ist gut, aber wie legt man ein soetwas an? Dann muss man ja das
> anlegen schon in einer schleife machen oder nicht? Das kostet dann auch
> wieder zeit.

Ja, das Anlegen muss in einer Schleife erfolgen. Das passiert aber nur 
einmal, und nicht für jeden Bildpunkt. ;-) Da das Anlegen des [,]-Arrays 
auch Zeit kostet (die Array-Elemente werden hier auch initialisiert), 
dürfte der Aufwand dafür nicht wesentlich größer sein.

Peter II schrieb:
> die meiste zeit wird beim Zugriff auf Helligeit drauf gehen (weil es 6
> mal gemacht wird), da aber dort auch eine Berechnung drin steckt (i-1,
> j+2 usw.) wird dann wieder ein Rage-check meines wissens immer gemacht.

So wie ich den oben verlinkten Artikel zu den Range-Checks verstanden 
hatte, ist der Check nicht ganz so dramatisch, wie immer vermutet wird. 
Er besteht wohl aus einem Vergleich und einem Jump. Das mach in der 
Masse natürlich trotzdem Aufwand. Außerdem erkennt der Compiler wohl 
Zugriffsmuster und optimiert den Range-Check unter bestimmten Umständen 
weg.

Gruß
Markus

von maggo (Gast)


Lesenswert?

Hi,

Sorry das es solange gedauert hat bis ich mich wieder gemeldet habe.

Die ASM-Vergleiche hinken die nicht ein bischen, da doch bei jedem Start 
des Programm neu übersetzt wird? Da kann doch sich die .NET-Runtime bei 
der nächsten Version  doch irgend etwas anderes übersetzen  an 
lauffähigen Maschienecode?

So das mit dem geänderten Array habe ich nicht so ganz verstanden.
Ich habe mal das versucht ein zu pflegen. Aber irgend wie hat es nicht 
funktioniert.
1
Array[] helligkeit = new Array[hoehe][];
2
for (int i = 0; i < helligkeit.Length; i++)
3
{
4
    helligkeit[i] = new byte[breite];
5
}
6
7
int breite_array = breite * 3;
8
bool tmp;
9
10
for (int x = 0; x < hoehe; x++)
11
    for (int y = 0, y2 = 0; y < breite_array; y += 3, y2++)
12
        helligkeit[x][y2] = (byte)((input[x, y] + input[x, y + 1] + input[x, y + 2]) / 3);
Ermeldet mir bei allen Zugriffe auf das Array helligkeit folgendes:
"Indizierung mit [] kann nicht auf einen Ausdruck vom Typ "System.Array" 
angewendet werden."

von Markus V. (Gast)


Lesenswert?

maggo schrieb:
> Die ASM-Vergleiche hinken die nicht ein bischen, da doch bei jedem Start
> des Programm neu übersetzt wird? Da kann doch sich die .NET-Runtime bei
> der nächsten Version  doch irgend etwas anderes übersetzen  an
> lauffähigen Maschienecode?

Nein. Bei gleichem Input kommt immer der gleiche Output raus. ;-)

1
byte[][] helligkeit = new byte[hoehe][];
2
for ( int x = 0; x < helligkeit.Length; x++ )
3
{
4
    helligkeit[x] = new byte[breite];
5
    for ( int y = 0,y2 = 0; y < helligkeit[x].Length*3; y += 3,y2++ )
6
        helligkeit[x][y2] = (byte)input[x,y]+input[x,y+1]+input[x,y+2])/3);
7
}

Ich habe mal exemplarisch hingeschrieben, wie die Initialisierung des 
[][]-Arrays aussieht. Ich hoffe, ich habe Dein Beispiel korrekt 
übertragen. Wichtig ist, dass Du in der Scheifenbedingung das 
Array-Property .Length verwendest. Nur dadurch hast Du die Chance, dass 
der Optimierer die Range-checks entfernt.

Ich bin mal gespannt, ob das noch was bringt.

Gruß
Markus

von Peter II (Gast)


Lesenswert?

hier eine eventuelle verbesserung - nicht getestet

> y < helligkeit[x].Length*3
das wird nicht viel bringen, weil y überhaupt nicht für den zugriff auf 
helligkeit verwendet wird.

Auch finde ich diese 2 Variabeln nicht schön lesbar, auch wenn es 
evetuell etwas schneller ist (sollte aber vernachlässigbar sein)

1
byte[][] helligkeit = new byte[hoehe][];
2
for ( int x = 0; x < helligkeit.Length; x++ )
3
{
4
    byte[] tmp = new byte[breite];
5
    helligkeit[x] = tmp;
6
    for ( int y = 0, y < tmp.Length; y++ ) {
7
        int y2 = y*3;
8
        tmp[y] = (byte)input[x,y2]+input[x,y2+1]+input[x,y2+2])/3);
9
     }
10
}

von maggo (Gast)


Angehängte Dateien:

Lesenswert?

Hi,

Ich habe eure Änderungen eingepflegt.
Ich habe beide Varianen ausbrobiert und keinen Messbaren Unterschied 
festgestellt. Bei einer Messauflösung von 1 ms.
Da bei kam mir die Idee.
Wenn ich das Array-Output erzeuge haben doch alle Speicherzellen den 
Wert 0.
Da brauch ich nur noch die Werte zuspeichern dort wo ich die 1 
rausbekomme.

Jetzt bin ich bei einer DUrchlaufzeit von 20-18ms mit Debugger und ohne 
Debugger bei 9-10 ms.

Jetzt habe ich noch das Output-Array entsprechend geändert und es sind 
mit dem Debugger 18-19ms und ohne Debugger 7-8 ms Durchlaufzeit.

Gruß Marco

von Peter II (Gast)


Lesenswert?

maggo schrieb:
> Ich habe eure Änderungen eingepflegt.

warum meine nicht?

von maggo (Gast)


Lesenswert?

Hi Peter II,

Ich hatte beide varianten ausprobiert und mit meinen groben 
Messmitteln(Auflösung 1ms) keinen Unterschied feststellen gekonnt.
Da ich deine als erstes getestet hatte und dann Markus V. seine getestet 
hatte und ich es da nach nicht mehr umgebaut habe. Des wegen ist markus 
v. seine variante verwendet verwendet worden. Das war ohne wertung 
geschehen.

Hatte noch versucht die 2 For-Schleifen in eine zukombienieren aber das 
hat genaus lange gedauert. Wie 2 Forschleifen.

Der Anfangsbereich der Schleifen sah dann so aus:
1
[....]
2
            helligkeit[0] = new byte[breite];
3
            output[0] = new byte[breite];
4
            helligkeit[1] = new byte[breite];
5
            output[1] = new byte[breite];
6
            helligkeit[0][0] = (byte)((input[0, 0] + input[0, 1] + input[0, 2]) / 3);
7
            helligkeit[0][1] = (byte)((input[0, 3] + input[0, 4] + input[0, 5]) / 3);
8
            helligkeit[1][0] = (byte)((input[1, 0] + input[1, 1] + input[1, 2]) / 3);
9
            helligkeit[1][1] = (byte)((input[1, 3] + input[1, 4] + input[1, 5]) / 3);
10
            
11
            byte c1_1, c1_2, c1_3, c2_1, c2_2, c2_3, c3_1, c3_2, c3_3;
12
            int j;
13
            for (short i = 1; i < hoehe - 1; i++)   // loop for the image pixels height
14
            {
15
                
16
                //Variablen für den ersten Durchlauf mit Werten befühlen
17
                helligkeit[i + 1] = new byte[breite];
18
                output[i + 1] = new byte[breite];
19
                helligkeit[i+1][1] = (byte)((input[i + 1, 0] + input[i + 1, 1] + input[i + 1, 2]) / 3);
20
                helligkeit[i+1][2] = (byte)((input[i + 1, 3] + input[i + 1, 4] + input[i + 1, 5]) / 3);
21
                
22
                c1_2 = helligkeit[i - 1][ 1];
23
                c1_3 = helligkeit[i - 1][ 2];
24
25
                c2_2 = helligkeit[i][ 1];
26
                c2_3 = helligkeit[i][ 2];
27
28
                c3_2 = helligkeit[i + 1][ 1];
29
                c3_3 = helligkeit[i + 1][ 2];
30
31
                for (j = 2; j < helligkeit[i].Length ; j++) // loop for image pixels width    
32
                {
33
                    helligkeit[i+1][j] = (byte)((input[i+1, j+2] + input[i+1, j+3] + input[i+1, j+4]) / 3);
34
                    
35
                    c1_1 = c1_2;
36
                    c1_2 = c1_3;
37
                    c1_3 = helligkeit[i - 1][j];
38
39
[....]

Gruß Marco

von Sebastian L. (Gast)


Lesenswert?

Hi

Schau dir doch nochmal deine array indizes an, wenn der sobel richtig 
funktionieren soll.

bei aller optimierung sollte die funktion doch richtig bleiben.

wenn du
1
c1_3 = helligkeit[i - 1][ 2];

und dann
1
c1_2 = c1_3;
2
c1_3 = helligkeit[i - 1][j];  //j = 2

dann steht in c1_2 und c1_3 der selbe wert

du musst oben also die festen indizes 0 und 1 verwenden

Der Rand wird zwar nicht berechnet aber für die inneren werte schon 
einbezogen

von maggo (Gast)


Lesenswert?

Hi Sebastian L.,

Da gebe ich dir recht das der Sobel-Funktionieren muss.
Habe es entsprechend abgeändert.

Gruß
Marco

Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.