Guten Abend.
Ich bräuchte mal ein wenig Input über die Erkennung von Flanken. Es geht
dabei im Speziellen darum:
1
Signal:
2
3
|----------------------|
4
| |
5
| |
6
-----------| |-------------
7
8
9
Erkannte Flanken:
10
11
|---| || | |-----|
12
| | || | | |
13
| | || | | |
14
-----------| |--||-|-----| |---------------
Wie mache ich jetzt daraus nach Möglichkeit wieder ein "Objekt"? Im
nächsten Zyklus kann es dann durchaus sein, dass das Signal wieder
komplett eingefangen wird.
Meine Idee war eine Art Integration zu machen. Jedes mal, wenn das
Signal erhalten bleibt, wird +1, jedes mal, wenn es verloren geht, -1
gerechnet. Die absoluten Minima wären dann meine Objektgrenzen aber das
funktioniert leider auch nicht zuverlässig zumal er ja dann das Ende des
Objekts erst viel viel später erkennt (wenn die Flanke 10 Tastungen High
war, dann braucht sie entsprechend auch wieder 10 Tastungen um low zu
werden).
Hat jemand einen guten Vorschlag für mich?
Vielen Dank, Grüße M.S.
deine Problemformulierung ist grandios unverständlich, in anbetracht der
Tatsache, dass das Problem kein Besonderes ist.
Wenn ich dich richtig verstehe, möchtest du entprellen und dann Flanken
erkennen?
Entprellen kannst du durch regelmäßiges Abtasten und zählen, wie lange
dein Signal in einem bestimmten Zustand ist. Das kostet natürlich Zeit,
und die Flanke kommt verzögert heraus.
Oder du rechnest nicht binär, sondern interpretierst 0 als 0 und 1 als
255. Dann könntest du einen Tiefpassfilter realisieren, der das
hochfrequenze "Geflacker" etwas dämpft. Das ist ähnlich deinem
Integrator und hat ähnliche Schwächen.
Wenn du aber nichts über den Unterschied zwischen "kurzer Flanke" und
"langer Störung" modellieren kannst, wird es schwierig, das Ganze
auseinander zu halten.
Hast du constraints, wie
- Flanken dauern mindestens x sec
- Flanken kommen höchstens alle x sec
- Störungen dauern höchstens x sec (Prellen)
...
?
rava schrieb:> deine Problemformulierung ist grandios unverständlich, in> anbetracht der> Tatsache, dass das Problem kein Besonderes ist.
Gut möglich :)
> Wenn ich dich richtig verstehe, möchtest du entprellen und dann Flanken> erkennen?
Nein, es geht um die Auswertung eines Einzeilen-Bildsensors. Nehmen wir
an, obiges Diagramm stellt einen schwarzen Karton dar, innerhalb der
steigenden und fallenden Flanke befindet sich ein weißes Objekt.
Das zweite Diagramm zeigt diesen einen Karton, der allerdings nicht
weiß, sondern als grau und nicht schwarz, sondern als dunkelgrau erkannt
wird. Dadurch, dass sich kein absoluter Schwellwert festlegen lässt (der
Karton muss nicht Schwarz/Weiß sein, sondern kann seinerseits auch
beispielsweise Grün/Lila sein), kann es zu einem Kippen kommen.
Was ist jetzt suche, ist eine Möglichkeit, das Geflackere möglichst
wieder zu einem Signal zusammenzusetzen damit das Objekt möglichst in
seiner Breite erkannt wird.
Martin Schwaikert schrieb:> Wie mache ich jetzt daraus nach Möglichkeit wieder ein "Objekt"?
Im Prinzip geht es hierbei um eine Tiefpaßfilterung im weitesten Sinne.
Und die erste Aufgabe bei sowas ist grundsätzlich immer, eine
Eckfrequenz festzulegen, die das Signal vom Rauschen trennt.
Wenn du Schwierigkeiten hast, so eine Eckfrequenz festzulegen, wird auch
jede denkbare Schaltung/Programm Schwierigkeiten haben, das Signal vom
Rauschen zu trennen.
> zumal er ja dann das Ende des> Objekts erst viel viel später erkennt
Sei froh, daß das so ist. Alles andere würde gegen bekannte Naturgesetze
verstoßen, insbesondere gegen das der Kausalität. Und das wäre erst ein
Chaos...
> Hat jemand einen guten Vorschlag für mich?
Versuche, die Störung und das Signal zu quantifizieren, also in Zahlen
auszudrücken. Wie lang ist die längste Störung, wie kurz ist das
kürzeste Signal? Dann kann man weitersehen.
Ohne diese Zahlenwerte ist das alles nur Stochern im Nebel.
c-hater schrieb:> Martin Schwaikert schrieb:>>> Wie mache ich jetzt daraus nach Möglichkeit wieder ein "Objekt"?>> Im Prinzip geht es hierbei um eine Tiefpaßfilterung im weitesten Sinne.
Ja, das kommt hin.
> Und die erste Aufgabe bei sowas ist grundsätzlich immer, eine> Eckfrequenz festzulegen, die das Signal vom Rauschen trennt.
Welche Eckfrequenz meinst Du damit genau? Ich habe mal den Code
angehägt, der die Bilddaten aussliest.
> Wenn du Schwierigkeiten hast, so eine Eckfrequenz festzulegen, wird auch> jede denkbare Schaltung/Programm Schwierigkeiten haben, das Signal vom> Rauschen zu trennen.
Also ich kann zumindest eine Grenzfrequenz angeben, und das düfte
ungefähr 5/480 sein. Alles was kleiner ist, kann man eigentlich
ignorieren.
>> zumal er ja dann das Ende des>> Objekts erst viel viel später erkennt>> Sei froh, daß das so ist. Alles andere würde gegen bekannte Naturgesetze> verstoßen, insbesondere gegen das der Kausalität. Und das wäre erst ein> Chaos...
Mein System muss nicht kausal sein, da ich zeitinvariant bin.
>> Hat jemand einen guten Vorschlag für mich?>> Versuche, die Störung und das Signal zu quantifizieren, also in Zahlen> auszudrücken. Wie lang ist die längste Störung, wie kurz ist das> kürzeste Signal? Dann kann man weitersehen.
Siehe den Code, der sollte diese Frage eigentlich weitgehend klären.
> Ohne diese Zahlenwerte ist das alles nur Stochern im Nebel.
Mir ging es ja auch primär um prinzipielle Ansätze.
Allerdings habe ich das Problem, dass bereits bei folgendem Code der
Cortex M3 - dank fehlender FPU - an seine Grenzen kommt. Ich brauche für
einen Schleifendurchlauf fast eine Sekunde. Faktor 10 schneller wäre mir
lieber...
Auch für mich als Mitttelstürmer ist das Thema "Flankenerkennung"
interessant. Ich habe allerdings das Gefühl, daß man hier Flachpässe
mit Tiefpässen verwechselt.
Mittelstürmer schrieb:> Auch für mich als Mitttelstürmer ist das Thema "Flankenerkennung"> interessant. Ich habe allerdings das Gefühl, daß man hier Flachpässe> mit Tiefpässen verwechselt.
Steckt in dieser Fußballanekdote ein Mehrwert, oder war das einfach nur
mal so zur allgemeinen Aufheiterung?
was deine Rechnerei angeht:
x / 3 ist ungefähr x * 85 / 256
da kann man also Geschwindigkeit rausholen, wenn die Genauigkeit so
genügt.
auch könntest du interval_1/interval_2 vor dem zweiten
Schleifendruchlauf vorberechnen, wenn das mit der Genauigkeit passt
(also wenn die Signalamplitude interval_2 deutlich kleiner als 256 ist)
grundsätzlich kannst du bei sowas oft "bitbreite" gegen "Division"
tauschen. Was schneller ist, kommt auf die genaue hardware an. ein
32Bit-µC stört sich an einer 16bit x 16bit multiplikation kaum.
zum Problem:
ist es immer ein Objekt? Ist das Objekt immer ganz sichtbar? Dann nimmt
die erste und letzte Flanke ;)
Ansonsten istes sehrwohl ein Problem, das man mit Entprellen lösen
könnte: ignoriere alle Signalflanken, die schneller kommen, als das
Objekt kurz sein kann.
Oder benutze ein Tiefpassfilter:
PixelBuffer[y] = alpha * PixelBuffer[y] + (1-alpha) * PixelBuffer[y-1]
alpha ist zwar kleiner 1, aber mit meinem Text von oben kannst du das
auch ohne FPU rechnen lassen. Den genauen Wert ermittle ich (beim
Entprellen und Entrauschen) immer durch ausprobieren ;)
Was bei dir fast 1 Sekunde dauern dürfte, wird das hier sein
1
if(PixelBuffer[y]>=middle){
2
DebugDrawLineYC(y,0xFFFF);
3
}
Die Ausgabe von Einzelpixel ist normalerweise notorisch langsam.
Floating Point Operationen seh ich erst mal in deinem Code so gut wie
überhaupt keine, und die eine, die da ist, die könnte man problemlos
auch durch eine Integer Operation ersetzen. Man muss ja nicht mit 0.9
multiplizieren. Man kann ja auch mit 10 mutliplizieren und durch 9
dividieren.
Im übrigen stimmt in deinem Programm irgendwas nicht. Ich seh quer
durchs ganze Programm lauter y (also Bildzeilen). Ich seh aber nirgends,
wo du diese Zeilen endlich mal in x-Richtung in Pixel auflösen würdest.
Du bist aber laufend mit Pixeloperationen zu gange. Irgendwas stimmt da
nicht.
ALs generelle Struktur würde ich irgendwas in der Richtung erwarten
1
for(y=.....480...)
2
{
3
getBildzeiley
4
5
for(x=.....320...)
6
{
7
bearbeitePixel[y][x]
8
}
9
}
10
11
for(y=0....480....)
12
{
13
for(x=0...320...)
14
{
15
rechnePixel[y][x]um
16
}
17
18
Pixelzeile[y]ausgeben
19
}
so in etwa würde ich die Struktur erwarten, wenn du mit Einzelpixel
operierst. Dein Code ist aber auffallend anders. Da sind nur lauter y
Schleifen, genauso wie da in die Pixel immer 1-dimensional zugegriffen
wird. Kommt mir komisch vor. Selbst wenn du nur eine Camerazeile hast.
Denn das bedeutet, dass du deinen Schwellwertbildung in jeder Zeile
anders festlegst. Und das ist eigenartig.
und gewöhn dir die ; nach den schliessenden Klammern ab! Die gehören da
nicht hin, können dir aber nocht jede Menge "Spass" bereiten, wenn du
sie mal an der falschen Stelle einsetzt.
Karl Heinz schrieb:> Was bei dir fast 1 Sekunde dauern dürfte, wird das hier sein> if (PixelBuffer[y] >= middle) {> DebugDrawLineYC(y, 0xFFFF);> }>> Die Ausgabe von Einzelpixel ist normalerweise notorisch langsam.
Das ist mir schon klar. Aber selbst ohne Ausgabe läuft er trotz 50MHz
takt recht lange durch, obwohl die emulierte FPU alles andere als
langsam ist.
> Man muss ja nicht mit 0.9> multiplizieren. Man kann ja auch mit 10 mutliplizieren und durch 9> dividieren.
Der Wert wird ja pro 960 Schleifendurchläufen nur einmal berechnet. Das
sollte nicht wirklich ausschlaggebend sein.
> Im übrigen stimmt in deinem Programm irgendwas nicht. Ich seh quer> durchs ganze Programm lauter y (also Bildzeilen). Ich seh aber nirgends,> wo du diese Zeilen endlich mal in x-Richtung in Pixel auflösen würdest.> Du bist aber laufend mit Pixeloperationen zu gange. Irgendwas stimmt da> nicht.
Ja, Du hast recht. Es geht auch nur um eine Zeile in vertikale Richtung.
Die Auswertung in X-Richtung beschränkt sich ebenfalls auf nur eine
Zeile. Mehr wird nicht gebraucht (und für mehr reicht die Rechenleistung
auch überhaupt nicht aus).
> und gewöhn dir die ; nach den schliessenden Klammern ab!
Ok.
Martin Schwaikert schrieb:> Ja, Du hast recht. Es geht auch nur um eine Zeile in vertikale Richtung.> Die Auswertung in X-Richtung beschränkt sich ebenfalls auf nur eine> Zeile. Mehr wird nicht gebraucht (und für mehr reicht die Rechenleistung> auch überhaupt nicht aus).
Mit Verlaub.
Aber das glaub ich nicht.
Ein 320*480 Pixelbild muss bei 50Mhz in wesentlich weniger als 1 Sekunde
durchrauschen. Du hast dich da irgendwo krass vertan. Das haben die
ersten Atari ST mit 10Mhz Takt und 68000 CPU schon um einiges besser
hinbekommen. Und das vor 30 Jahren. Und für das komplette Bild und nicht
nur 1 Bildzeile. Zwischendurch haben sie noch einen kompletten
Mandelbrot gerechnet.
Das muss dir doch spanisch vorkommen, wenn du in deinem ARM mehr
Rechenleistung hast als ein komplettes Rechenzentrum vor 30 Jahren und
dann schaffst du nicht mehr als 1 480 Pixel Bildzeile pro Sekunde?
Gib mal den ganzen Code der Funktion rüber, inklusive aller Variablen
und inklusive der Datentypen der Variablen.
> CameraImageDataGet(true, 320, y , 1, false, &PixelBuffer[y]);
gibts denn da keine Funktionen, die eine komplette Bildzeile holen? Oder
gleich das komplette Bild?
Martin Schwaikert schrieb:> Das ist mir schon klar. Aber selbst ohne Ausgabe läuft er trotz 50MHz> takt recht lange durch, obwohl die emulierte FPU alles andere als> langsam ist.
Du redest die ganze Zeit von FPU.
In deinem Code kommen keine Floating Point Operationen vor! Daher ist
die fehlende FPU komplett irrelevant! Du hast dir da eine nette Ausrede
zusammengebastelt - mehr nicht.
Karl Heinz schrieb:> Martin Schwaikert schrieb:>>> Ja, Du hast recht. Es geht auch nur um eine Zeile in vertikale Richtung.>> Die Auswertung in X-Richtung beschränkt sich ebenfalls auf nur eine>> Zeile. Mehr wird nicht gebraucht (und für mehr reicht die Rechenleistung>> auch überhaupt nicht aus).>> Mit Verlaub.> Aber das glaub ich nicht.
Nein, ist schon in Ordnung. Mich wunderte es auch. Aber das liegt an
vielen Faktoren:
1) Die Kamera ist über ein FPGA über den EPI Bus angeschlossen. Der
Memory Arbiter auf dem FPGA verhindert einen Zugriff, wenn die Kamera
gerade selbst in den ensprechenden Speicherbereich schreibt. Beide
Komponenten (Arbiter und Kamera) laufen aber mit zwei autonomen
Taktsystemen. Eine Synchronisation ist also hier schon gar nicht möglich
und kann schlimmstenfalls dazu führen, dass man eben genau dann die
Daten ausliest, wenn der Memory Arbiter gerade schreiben will.
2) Die erreichbare durchschnittliche Datenrate liegt bei 320kByte/s.
Jeder Bildpunkt hat mindestens 2 Byte. Das entspricht dann also guten
160.000 Bildpunkten pro Sekunde.
3) Das Auslesen in Y-Richtung ist ineffizient, da er zuvor jedes mal auf
Register zugreifen muss, die er beim Auslesen in x-Richtung nicht
braucht. Hier muss ich den Treiber erst noch umschreiben. In x-Richtung
ließt er n-Pixel aus, in y-Richtung ließt er jedoch n-mal 1 Pixel.
> Ein 320*480 Pixelbild muss bei 50Mhz in wesentlich weniger als 1 Sekunde> durchrauschen.
Wie gesagt. Das Bild hat 640x480x2Byte. Alleine die Übertragung braucht
1,92 Sekunden. Natürlich reicht der RAM von um die 100kByte nicht aus
dafür.
Ursprünglich angedacht war eigentlich eine Umsetzung eines anständigen
Objekterkennungsalgorithmus auf ein Vollbild. Davon mussten wir aber
schon längst abrücken, denn alleine die Berechnung der Momente würde
schon mehrere Sekunden pro Bild dauern. Darum wurde das ganze jetzt auf
eine Zeile zusammengestrichen. Unbefriedigend, aber nicht anders
machbar.
> Du hast dich da irgendwo krass vertan. Das haben die> ersten Atari ST mit 10Mhz Takt und 68000 CPU schon um einiges besser> hinbekommen. Und das vor 30 Jahren. Und für das komplette Bild und nicht> nur 1 Bildzeile. Zwischendurch haben sie noch einen kompletten> Mandelbrot gerechnet.
Genau das dachte ich ursprünglich auch. Ich wurde eines besseren
belehrt.
> Das muss dir doch spanisch vorkommen, wenn du in deinem ARM mehr> Rechenleistung hast als ein komplettes Rechenzentrum vor 30 Jahren und> dann schaffst du nicht mehr als 1 480 Pixel Bildzeile pro Sekunde?
Inklusive der Ausgabe natürlich.
> Gib mal den ganzen Code der Funktion rüber, inklusive aller Variablen> und inklusive der Datentypen der Variablen.
Dazu müsste ich jetzt die halbe Stellaris Graphics Library sowie das
halbe Driver Development Package posten. Das sprengt den Rahmen hier bei
weitem.
Karl Heinz schrieb:> Du hast dir da eine nette Ausrede> zusammengebastelt - mehr nicht.
Wieso Ausrede? Dass es zu langsam ist, war nie gegenstand der Frage. Es
ist schon klar, dass es viel zu langsam ist, wenn es noch kein bisschen
geschwindigkeitsoptimiert ist.
Am langsamsten ist, wie schon richtig angemerkt wurde, die
Graphikausgabe. Die brauche ich aber zum Debuggen und wird dann eh
gestrichen.
Ich habe ein ähnliches Problem zur Erkennung von Barcodes auf
unterschiedlichen Untergründen und bei verschiedener Helligkeit durch
Anpassung der Schaltschwellen im Vorfeld gelöst:
- Zunächst habe ich den Mittelwert Mm aller Helligkeitswerte gebildet
- dann habe ich den Mittelwert Mu aller Werte unterhalb von Mm ermittelt
- dann habe ich den Mittelwert Mo aller Werte oberhalb von Mm ermittelt
Alles was größer ist als Mo ist Weiss, Alles was kleiner ist als Mu ist
Schwarz. Funktioniert recht zuverlässig ...
Martin Schwaikert schrieb:> Allerdings habe ich das Problem, dass bereits bei folgendem Code der> Cortex M3 - dank fehlender FPU - an seine Grenzen kommt.
Das hat wohl nix mit einer fehlenden FPU zu schaffen...
> for (y = 0; y < 480; y++) {>> // eine Zeile auslesen> CameraImageDataGet(true, 320, y , 1, false, &PixelBuffer[y]);
Hier kopierst du erstmal das gesamte Bild in einen zweiten
Speicherbereich (zeilenweise). Da sehe ich schonmal einiges an
Sparpotential schlicht durch die Anwendung des guten alten
Double-Buffering.
Sprich: du läßt die Kamera Buffer A füllen, während du mit den Daten in
Buffer B arbeitest, bei jedem Frame-Sync läßt du Speicher A und B die
Rollen tauschen. Damit hast du diese sinnlose Umkopiererei schonmal
komplett eingespart und durch ein wenig Logik zu Bufferumschaltung und
ca. 300k zusätzliches RAM ersetzt. Geschätzte Einsparung an Rechenzeit:
99,9% (!).
> r = REDFROM565(PixelBuffer[y]);> g = GREENFROM565(PixelBuffer[y]);> b = BLUEFROM565(PixelBuffer[y]);
Und hier nutzt du von dem mühsam und teuer umkopierten Bilddaten dann
genau eine Spalte (ca. 0,3%) und brauchst dafür auch noch drei
indizierte Speicherzugriffe pro Pixel.
Wenn du nur eine Spalte brauchst, dann holst du natürlich auch bloß eine
Spalte und nicht alle 320! Nochmal eine Einsparung von ca. 99,7% beim
Umkopieren. Und der Bedarf an zusätzlichem RAM für's Double Buffering
reduziert sich nebenbei auch von 300k auf 1k...
Und die drei sinnlosen Indizierungen ersetzt du durch eine einzige zum
Einlesen in eine Hilfsvariable, auf die du dann deine Macros zur
Komponententrennung anwendest. Schatzungsweise Einsparung: irgendwas bei
50%.
> // In Graustufe umrechnen> PixelBuffer[y] = ((r + g + b) / 3);
So ein Blödsinn! Die ursprünglichen Daten haben in 16Bits gepaßt, da
wird eine Addition der daraus extrahierten Komponenten ganz sicher auch
in 16 Bit passen. Der springende Punkt ist: Die teuere Division durch
drei ist komplett überflüssig (und obendrein destruktiv bezüglich des
Informationsgehaltes der Daten).
Hier verläßt mich allerdings meine Schätzungskraft bezüglich der
Einsparung, da nicht weiß (und auch keine Lust habe, das
nachzuschlagen), wieviele Takte eine Integerdivision bei deiner CPU
kostet. Sicher ist nur eins: jeder einzelne davon wird hier vollkommen
nutzlos verschwendet!
> // Minimum und Maximum finden> if (PixelBuffer[y] < min) {>> min = PixelBuffer[y];>> } else if (PixelBuffer[y] > max) {>> max = PixelBuffer[y];>> };> };
Und schon wieder ein Haufen unnützer indizierter Speicherzugriffe. Jedes
dieser Datenwörter hast du schon einmal in einem Register gehabt,
nämlich nach der Addition der Farbkomponenten. Warum also diese zwei
popligen Vergleichsoperationen nicht dort mit erledigen?
Davon mal abgesehen: Was soll die Bestimmung von Minimum und Maximum
bezüglich einer Tiefpaßfilterung überhaupt bringen? Ich kenne keine
Variante eines sinnvollen und effizienten Tiefpaß, die das für irgendwas
brauchen würde.
Also, selbst wenn man irgendwie glaubt, daß Minimum und Maximum
irgendwie sinnvoll sein könnten, ist durch übliche und
logisch-grundsätzliche Optimierungen der Rechenzeitbedarf bis hierhin
schon auf weniger als ein Promille des von dir benötigten zu reduzieren.
> // Interval normieren>> int interval_1 = 0xFF;> int offset_1 = 0;> int interval_2 = max - min;> int offset_2 = min;> int middle = (int)((float)interval_2 * (float)0.9);>> for (y = 0; y < 480; y++) {> // Siehe Geradengleichung> PixelBuffer[y] = (((PixelBuffer[y] - offset_2) * interval_1)> / (interval_2)) + offset_1;>> if (PixelBuffer[y] >= middle) {> DebugDrawLineYC(y, 0xFFFF);> };> };
Und das ist dann wohl endgültig kompletter Schwachsinn. Ich kann
nichtmal erkennen, was das tuen soll, wie ein Tiefpaß sieht es
jedenfalls nicht aus. Wenn es auch diese Wirkung hat, wäre das wohl eher
als zufälliger Nebeneffekt zu betrachten.
Oder anders ausgedrückt: Nur dann, wenn das Designziel war, eine
möglichst komplexe und wirksame Implementierung für delay() zu schaffen,
dann ist es erreicht worden...
c-hater schrieb:> Martin Schwaikert schrieb:>>> Allerdings habe ich das Problem, dass bereits bei folgendem Code der>> Cortex M3 - dank fehlender FPU - an seine Grenzen kommt.>> Das hat wohl nix mit einer fehlenden FPU zu schaffen...
Nein, da hast Du vollkommen recht. Ich habe das Unterfangen heute
offiziell auf dieser Hardware eingestellt, weil es überhaupt keinen Wert
hat. Da Du Dir die Mühe gegeben hast, Kritik zu üben, möchte ich Dir
meinen Erkenntnisstand nicht vorenthalten:
Der M3 schafft satte 90.000 FLOPS. Auch wenn diese nur per Software
emuliert sind, wird das mehr als schnell genug. Es liegt also nicht
daran. Tatsächlich hat das Stellaris Framework - eher gesagt die
Widget_Paint()-Funktion gute 90% der Rechenzeit gekostet.
Aber das ist ein Opfer füs Debugging, dass hinterher nicht hätte
erbracht werden müssen.
>> for (y = 0; y < 480; y++) {>>>> // eine Zeile auslesen>> CameraImageDataGet(true, 320, y , 1, false, &PixelBuffer[y]);>> Hier kopierst du erstmal das gesamte Bild in einen zweiten> Speicherbereich (zeilenweise). Da sehe ich schonmal einiges an> Sparpotential
Theoretisch schon. Rein Praktisch ist die Kamera an einem FPGA
angebunden, der einen zum systemtakt asynchronen Refresh des
Bildspeichers durchführen muss (da die Kamera asynchron arbeitet). Um an
die Bilddaten zu kommen MUSS man über den EPI-Bus über das FPGA auf ein
SRAM zugreifen, dessen Zugriff durch einen Memory-Arbiter auf dem FPGA
kontrolliert wird, der auch nur dann einen Zugriff erlaubt, wenn nicht
gerade aus dem Kamera-Zeilenpuffer in das SRAM geschrieben wird.
Insgesamt ist das System so langsam, dass selbst bei optimalen
Bedingungen keine höhere Übertragungsrate als 1,3FPS erreicht werden
können (bei 640x480 RGB565).
Der EPI Bus selbst ist davon natürlich ziemlich gelangweilt.
> schlicht durch die Anwendung des guten alten> Double-Buffering.>> Sprich: du läßt die Kamera Buffer A füllen, während du mit den Daten in> Buffer B arbeitest, bei jedem Frame-Sync läßt du Speicher A und B die> Rollen tauschen. Damit hast du diese sinnlose Umkopiererei schonmal> komplett eingespart und durch ein wenig Logik zu Bufferumschaltung und> ca. 300k zusätzliches RAM ersetzt. Geschätzte Einsparung an Rechenzeit:> 99,9% (!).
Wie gesagt hättest Du damit vollkommen recht, wenn die Kamera und der
Prozessor keine zwei komplett autonomen Systeme wären, die nur über
einen Bus miteinander verbunden sind.
>>> r = REDFROM565(PixelBuffer[y]);>> g = GREENFROM565(PixelBuffer[y]);>> b = BLUEFROM565(PixelBuffer[y]);>> Und hier nutzt du von dem mühsam und teuer umkopierten Bilddaten dann> genau eine Spalte (ca. 0,3%) und brauchst dafür auch noch drei> indizierte Speicherzugriffe pro Pixel.
Weil aber auch nicht weniger geht. Das FPGA will eine Adresse und legt
dann die Daten auf den Datenbus. Ob ich sie abhole, oder nicht, wirkt
sich nicht auf die Zugriffszeit aus. Und ob ich jetzt 2 Byte oder 6 Byte
für ein Pixel nutze, macht bei einer Zeile keinen großen Unterschied.
>> Wenn du nur eine Spalte brauchst, dann holst du natürlich auch bloß eine> Spalte und nicht alle 320!
Ach jetzt; nein nein das ist nur eine Spalte, die am Offset 320 Pixel
anfängt, und 480 Pixel lang ist.
> Nochmal eine Einsparung von ca. 99,7% beim> Umkopieren. Und der Bedarf an zusätzlichem RAM für's Double Buffering> reduziert sich nebenbei auch von 300k auf 1k...>> Und die drei sinnlosen Indizierungen ersetzt du durch eine einzige zum> Einlesen in eine Hilfsvariable, auf die du dann deine Macros zur> Komponententrennung anwendest. Schatzungsweise Einsparung: irgendwas bei> 50%.>>> // In Graustufe umrechnen>> PixelBuffer[y] = ((r + g + b) / 3);>> So ein Blödsinn! Die ursprünglichen Daten haben in 16Bits gepaßt, da> wird eine Addition der daraus extrahierten Komponenten ganz sicher auch> in 16 Bit passen. Der springende Punkt ist: Die teuere Division durch> drei ist komplett überflüssig (und obendrein destruktiv bezüglich des> Informationsgehaltes der Daten).
Das klingt logisch. Als Windows-Programmierer nichtkritscher Anwendungen
lernt man leider nicht den nötigen Geiz bei der Nutzung von µC. Das gebe
ich zu.
> Hier verläßt mich allerdings meine Schätzungskraft bezüglich der> Einsparung, da nicht weiß (und auch keine Lust habe, das> nachzuschlagen), wieviele Takte eine Integerdivision bei deiner CPU> kostet. Sicher ist nur eins: jeder einzelne davon wird hier vollkommen> nutzlos verschwendet!>>> // Minimum und Maximum finden>> if (PixelBuffer[y] < min) {>>>> min = PixelBuffer[y];>>>> } else if (PixelBuffer[y] > max) {>>>> max = PixelBuffer[y];>>>> };>> };>> Und schon wieder ein Haufen unnützer indizierter Speicherzugriffe. Jedes> dieser Datenwörter hast du schon einmal in einem Register gehabt,> nämlich nach der Addition der Farbkomponenten. Warum also diese zwei> popligen Vergleichsoperationen nicht dort mit erledigen?>> Davon mal abgesehen: Was soll die Bestimmung von Minimum und Maximum> bezüglich einer Tiefpaßfilterung überhaupt bringen? Ich kenne keine> Variante eines sinnvollen und effizienten Tiefpaß, die das für irgendwas> brauchen würde.
Nein, hier geht es um eine Normierung um die mangelhafte AGC/AWB der
Kamera auszugleichen. Denn diese erzeugt gerne einen Kontrast, der jedes
einst farbige Pixel in ein Interval zwischen 0xFFFF und 0xFFF0 quetscht.
Durch die Normierung (und hierfür wird min und max benötigt), wird der
Farbraum wieder gedehnt.
> Also, selbst wenn man irgendwie glaubt, daß Minimum und Maximum> irgendwie sinnvoll sein könnten, ist durch übliche und> logisch-grundsätzliche Optimierungen der Rechenzeitbedarf bis hierhin> schon auf weniger als ein Promille des von dir benötigten zu reduzieren.>>> // Interval normieren>>>> int interval_1 = 0xFF;>> int offset_1 = 0;>> int interval_2 = max - min;>> int offset_2 = min;>> int middle = (int)((float)interval_2 * (float)0.9);>>>> for (y = 0; y < 480; y++) {>> // Siehe Geradengleichung>> PixelBuffer[y] = (((PixelBuffer[y] - offset_2) * interval_1)>> / (interval_2)) + offset_1;>>>> if (PixelBuffer[y] >= middle) {>> DebugDrawLineYC(y, 0xFFFF);>> };>> };>> Und das ist dann wohl endgültig kompletter Schwachsinn.
Das ist die Normierung.
> Ich kann> nichtmal erkennen, was das tuen soll, wie ein Tiefpaß sieht es> jedenfalls nicht aus. Wenn es auch diese Wirkung hat, wäre das wohl eher> als zufälliger Nebeneffekt zu betrachten.
Es ist kein Tiefpass.
> Oder anders ausgedrückt: Nur dann, wenn das Designziel war, eine> möglichst komplexe und wirksame Implementierung für delay() zu schaffen,> dann ist es erreicht worden...
Das Designziel war die Objektverfolgung zu optimieren. Das ist passee,
denn dafür reicht schlichtweg der Arbeitsspeicher nicht aus.
Es ist auch ein wenig dämlich, wenn ich für die Momentenberechnung die
RGB-Werte nach HSE/HSL umrechnen muss, obwohl das eigentlich schon die
Kamera kann aber das FPGA dahinter damit nicht klar kommt. Die ganze
Hardware, auf der das hier hätte laufen sollen ist eher gut gemeint als
gut gemacht und darüber hinaus inzwischen auch abgekündigt worden. Es
hat also einfach keinen Sinn hier länger daran festzuhalten, da es auch
keinen Kostendruck gibt, der einen besonders günstigen Prozessor
erwartet.