Forum: Mikrocontroller und Digitale Elektronik Inspiration zur Flankenerkennung


von Martin S. (sirnails)


Lesenswert?

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.

von rava (Gast)


Lesenswert?

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)
...
?

von Martin S. (sirnails)


Lesenswert?

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.

: Bearbeitet durch User
von c-hater (Gast)


Lesenswert?

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.

von Martin S. (sirnails)


Lesenswert?

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...
1
         int y;
2
         int min = 0xFFFF, max = 0;
3
         int r = 0, g = 0, b = 0;
4
5
         for (y = 0; y < 480; y++) {
6
7
           // eine Zeile auslesen
8
           CameraImageDataGet(true, 320, y , 1, false, &PixelBuffer[y]);
9
10
           r = REDFROM565(PixelBuffer[y]);
11
           g = GREENFROM565(PixelBuffer[y]);
12
           b = BLUEFROM565(PixelBuffer[y]);
13
14
           // In Graustufe umrechnen
15
           PixelBuffer[y] = ((r + g + b) / 3);
16
17
18
           // Minimum und Maximum finden
19
           if (PixelBuffer[y] < min) {
20
21
             min = PixelBuffer[y];
22
23
           } else if (PixelBuffer[y] > max) {
24
25
             max = PixelBuffer[y];
26
27
           };
28
         };
29
30
         // Interval normieren
31
32
         int interval_1 = 0xFF;
33
          int offset_1 = 0;
34
          int interval_2 = max - min;
35
          int offset_2 = min;
36
          int middle = (int)((float)interval_2 * (float)0.9);
37
38
          for (y = 0; y < 480; y++) {
39
           // Siehe Geradengleichung
40
           PixelBuffer[y] = (((PixelBuffer[y] - offset_2) * interval_1) / (interval_2)) + offset_1;
41
42
           if (PixelBuffer[y] >= middle) {
43
             DebugDrawLineYC(y, 0xFFFF);
44
           };
45
         };

: Bearbeitet durch User
von Mittelstürmer (Gast)


Lesenswert?

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.

von Martin S. (sirnails)


Lesenswert?

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?

von Mittelstürmer (Gast)


Lesenswert?

Martin fragte:
>...oder war das einfach nur mal so zur allgemeinen Aufheiterung?

Ja, war es.

von rava (Gast)


Lesenswert?

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 ;)

von Karl H. (kbuchegg)


Lesenswert?

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
    get Bildzeile y
4
5
    for( x = ..... 320 ... )
6
    {
7
      bearbeite Pixel[y][x]
8
    }
9
  }
10
11
  for( y = 0 .... 480 .... )
12
  {
13
    for( x = 0 ... 320 ... )
14
    {
15
      rechne Pixel[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.

: Bearbeitet durch User
von TM F. (p_richner)


Lesenswert?

Frage: Könnte man das Problem auch analog lösen? Einige Lösungen wären 
hier:
http://www.mikrocontroller.net/articles/Entprellung

von Martin S. (sirnails)


Lesenswert?

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.

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Karl H. (kbuchegg)


Lesenswert?

> CameraImageDataGet(true, 320, y , 1, false, &PixelBuffer[y]);

gibts denn da keine Funktionen, die eine komplette Bildzeile holen? Oder 
gleich das komplette Bild?

von Karl H. (kbuchegg)


Lesenswert?

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.

: Bearbeitet durch User
von Martin S. (sirnails)


Lesenswert?

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.

von Martin S. (sirnails)


Lesenswert?

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.

von Frank (Gast)


Lesenswert?

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 ...

von c-hater (Gast)


Lesenswert?

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...

von Martin S. (sirnails)


Lesenswert?

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.

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.