Datum:
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
Datum:
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
Datum:
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.)"
Datum:
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
Datum:
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
Datum:
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
Datum:
Angehängte Dateien: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.
private Bitmap soebel(Bitmap input_image, int hoehe, int breite) { int new_x = 0, new_y = 0; int c; bmp.FastBitmap input = new bmp.FastBitmap(input_image); bmp.FastBitmap output = new bmp.FastBitmap(breite, hoehe); int[,] gx = new int[,] { { -1, 0, 1 }, { -2, 0, 2 }, { -1, 0, 1 } }; // The matrix Gx int[,] gy = new int[,] { { 1, 2, 1 }, { 0, 0, 0 }, { -1, -2, -1 } }; // The matrix Gy for (int i = 1; i < hoehe - 1; i++) // loop for the image pixels height { for (int j = 1; j < breite - 1; j++) // loop for image pixels width { new_x = 0; new_y = 0; for (int hw = -1; hw < 2; hw++) //loop for cov matrix { for (int wi = -1; wi < 2; wi++) { c = (input.GetPixel(j + wi, i + hw).B + input.GetPixel(j + wi, i + hw).R + input.GetPixel(j + wi, i + hw).G) / 3; new_x += gx[hw + 1, wi + 1] * c; new_y += gy[hw + 1, wi + 1] * c; } } if (new_x * new_x + new_y * new_y > 128 * 128) { output.SetPixel(j, i, Color.Black); } else { output.SetPixel(j, i, Color.White); } } } return output.ToBitmap(); } |
Datum:
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
Datum:
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.
Datum:
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;
Datum:
Hi Peter II, Ich kann als Quelle auch ein Array nehmen. Ich ändere dass mal kurz um. Gruß Marco
Datum:
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.
Datum:
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.
Datum:
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/... 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-... 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...
Datum:
Angehängte Dateien: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:
for (int hw = -1; hw < 2; hw++) //loop for cov matrix { for (int wi = -1; wi < 2; wi++) { c = (input[i + hw, j + wi] + input[i + hw, j + wi + 1] + input[i + hw, j + wi + 2]) / 3; new_x += gx[hw + 1, wi + 1] * c; new_y += gy[hw + 1, wi + 1] * c; } } |
Gruß Marco
Datum:
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.)
Datum:
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
for (int hw = -1; hw < 2; hw++) //loop for cov matrix { for (int wi = -1; wi < 2; wi++) { c = helligkeit[i + hw, j + wi]; new_x += gx[hw + 1, wi + 1] * c; new_y += gy[hw + 1, wi + 1] * c; } } |
schreiben und schon sparst du dir die mehrfach berechung bin dann morgen wieder da, da bekommt man bestimmt unter 20ms.
Datum:
so meinte ich es:
int helligkeit[x,y] = new int[x,y]; for( x ... ) for( y .. ) helligkeit[x,y] = (input[x, y] + input[x, y+1] + input[x, y+2]) / 3; |
Datum:
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
Datum:
Angehängte Dateien: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
Datum:
So mist da da ist noch ein Fehler drin. Und zwar folgende Zeillen
int HelligkeitBreite = breite / 3;
for (int i = 1; i < hoehe - 1; i++) // loop for the image pixels height
{
for (int j = 3; j < HelligkeitBreite - 3; j += 3) // loop for image pixels width
|
muss in folgendes abgeändert geändert werden
for (int i = 1; i < hoehe - 1; i++) // loop for the image pixels height
{
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
Datum:
irgendwie habe ich das gefühl das der code nicht stimmt:
for (int x = 0; x < hoehe; x++) for (int y = 0, y2 = 0; y < breite; y += 3, y2++) 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:
for (int j = 3; j < HelligkeitBreite - 3; j += 3) c = helligkeit[i + hw, j + wi]; |
Datum:
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
Datum:
kannst du mal den aktuell quellcode - am besten so damit ich selber mal testen kann bereitstellen?
Datum:
Angehängte Dateien:Hi Peter II, Sorry, denn hatte ich vergessen. Gruß Marco
Datum:
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.
c = helligkeit[i - 1, j]; new_x += -1 * c; new_y += 1 * c; c = helligkeit[i - 1, j + 1]; new_x += -1 * c; 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.
Datum:
Angehängte Dateien: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
Datum:
Da ist mir doch gerade wieder ein Rechenfehler passiert das i++ in der if-Abfrage muss i+=2 lauten.
Datum:
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.
Datum:
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
Datum:
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?
Datum:
Angehängte Dateien: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
Datum:
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.
Datum:
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
Datum:
Angehängte Dateien: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
Datum:
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
if (new_x * new_x + new_y * new_y > 128 * 128) { output[i, j/3] = true; } else { output[i, j/3] = false; } |
besser
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
Datum:
Angehängte Dateien: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
Datum:
Ich dachte für die Faltung der horizontalen und vertikalen Sobeloperation musst du noch die wurzel draus berechnen und in das mittlere feld schreiben
int new_xy = new_x * new_x + new_y * new_y; if (new_xy > 128*128) { output[i, j] = 128; } else { output[i, j] = Math.Sqrt(new_xy); // näherungsweise |new_x|+|new_y| // bei embedded anwendungen eingesetzt (ohne dsp) } |
was mir auch noch aufgefallen ist, die multiplikations 128*128 könntest du vor den schleifen berechnen und speichern
Datum:
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.
Datum:
Hsllo Marco, mir ist im folgenden Codeabschnitt noch folgendes aufgefallen:
for (short i = 1; i < hoehe - 1; i++)
{
j = 1;
//Variablen für den ersten Durchlauf mit Werten befühlen
c1_2 = helligkeit[i - 1, j];
c1_3 = helligkeit[i - 1, j + 1];
c2_2 = helligkeit[i, j];
c2_3 = helligkeit[i, j + 1];
c3_2 = helligkeit[i + 1, j];
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
output[i,j-1] = new_x * new_x + new_y * new_y > 128 * 128 ? 1 : 0; |
anbieten. Das ist etwas schneller. Gruß Markus
Datum:
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.
Datum:
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
Datum:
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
Datum:
@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/... 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
Datum:
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.
Datum:
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
Datum:
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.
Array[] helligkeit = new Array[hoehe][]; for (int i = 0; i < helligkeit.Length; i++) { helligkeit[i] = new byte[breite]; } int breite_array = breite * 3; bool tmp; for (int x = 0; x < hoehe; x++) for (int y = 0, y2 = 0; y < breite_array; y += 3, y2++) 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."
Datum:
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. ;-)
byte[][] helligkeit = new byte[hoehe][]; for ( int x = 0; x < helligkeit.Length; x++ ) { helligkeit[x] = new byte[breite]; for ( int y = 0,y2 = 0; y < helligkeit[x].Length*3; y += 3,y2++ ) helligkeit[x][y2] = (byte)input[x,y]+input[x,y+1]+input[x,y+2])/3); } |
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
Datum:
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)
byte[][] helligkeit = new byte[hoehe][]; for ( int x = 0; x < helligkeit.Length; x++ ) { byte[] tmp = new byte[breite]; helligkeit[x] = tmp; for ( int y = 0, y < tmp.Length; y++ ) { int y2 = y*3; tmp[y] = (byte)input[x,y2]+input[x,y2+1]+input[x,y2+2])/3); } } |
Datum:
Angehängte Dateien: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
Datum:
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:
[....]
helligkeit[0] = new byte[breite];
output[0] = new byte[breite];
helligkeit[1] = new byte[breite];
output[1] = new byte[breite];
helligkeit[0][0] = (byte)((input[0, 0] + input[0, 1] + input[0, 2]) / 3);
helligkeit[0][1] = (byte)((input[0, 3] + input[0, 4] + input[0, 5]) / 3);
helligkeit[1][0] = (byte)((input[1, 0] + input[1, 1] + input[1, 2]) / 3);
helligkeit[1][1] = (byte)((input[1, 3] + input[1, 4] + input[1, 5]) / 3);
byte c1_1, c1_2, c1_3, c2_1, c2_2, c2_3, c3_1, c3_2, c3_3;
int j;
for (short i = 1; i < hoehe - 1; i++) // loop for the image pixels height
{
//Variablen für den ersten Durchlauf mit Werten befühlen
helligkeit[i + 1] = new byte[breite];
output[i + 1] = new byte[breite];
helligkeit[i+1][1] = (byte)((input[i + 1, 0] + input[i + 1, 1] + input[i + 1, 2]) / 3);
helligkeit[i+1][2] = (byte)((input[i + 1, 3] + input[i + 1, 4] + input[i + 1, 5]) / 3);
c1_2 = helligkeit[i - 1][ 1];
c1_3 = helligkeit[i - 1][ 2];
c2_2 = helligkeit[i][ 1];
c2_3 = helligkeit[i][ 2];
c3_2 = helligkeit[i + 1][ 1];
c3_3 = helligkeit[i + 1][ 2];
for (j = 2; j < helligkeit[i].Length ; j++) // loop for image pixels width
{
helligkeit[i+1][j] = (byte)((input[i+1, j+2] + input[i+1, j+3] + input[i+1, j+4]) / 3);
c1_1 = c1_2;
c1_2 = c1_3;
c1_3 = helligkeit[i - 1][j];
[....]
|
Gruß Marco
Datum:
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
c1_3 = helligkeit[i - 1][ 2]; |
und dann
c1_2 = c1_3; 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
Datum:
Hi Sebastian L., Da gebe ich dir recht das der Sobel-Funktionieren muss. Habe es entsprechend abgeändert. Gruß Marco