Forum: Mikrocontroller und Digitale Elektronik Camera OV7670: Register Settings für Farbbilder gesucht


von Andreas S. (igel1)


Lesenswert?

Hi Leute,

ich breche mir gerade die Ohren auf der Suche nach einer geeigneten 
Konfiguration, damit meine OV7670-Kamera (ohne FIFO) halbwegs farbechte 
Bilder auswirft (QVGA, YUV422).

Hat jemand von Euch das schon einmal geschafft?
Wenn ja: mit welchen Register Settings?

Viele Grüße

Igel1

von macload1 (Gast)


Lesenswert?

Wie weit bist du denn vom Resultat entfernt?

Kannst du schon erkennbare Bilder samplen?

Das mit den Echtfarben hängt leider auch von der genutzten Linse ab, 
also Feinabstimmungen gegenüber anderen funktionnierenden Projekten 
wirst du in jedem Fall machen müssen.

Ich hab's gerade nicht bei mir, kann dir aber morgen Abend meinen Code 
schicken. (wenn ich's nicht vergesse)

macload1

von macload1 (Gast)


Lesenswert?

In einem meiner Projekte hatte ich mich an diesen Register-Werten 
orientiert:
https://github.com/dinuxbg/pru-gcc-examples/tree/master/ov7670-cam/host-uio

Gab ein ziemlich blasses Bild, aber gut erkennbar und die Farben gingen 
schon in die richtige Richtung!

Ich hoffe, dass dir das weiterhilft.

von Andreas S. (igel1)


Lesenswert?

@Macload1:  Vielen Dank schon einmal für Deine Hinweise!

Ja, ich kann bereits QVGA-Bilder von der Kamera auf dem kleinen Display 
meines STM32F429-Discovery-Boards anzeigen.

Der Weg ist: OV7670 -> DCMI -> DMA -> internes RAM meines STM32F429 
Mikrocontrollers -> SPI -> ILI9341

Die Bilder scheinen mir korrekt und halbwegs scharf, aber ich habe 
bislang entweder nur sehr blasse Schwarzweiss-Bilder oder schwache 
Buntbilder mit Fehlfarben in Dunkelbereichen erhalten. Kontrast ist 
ebenfalls eher schwach und die automatische Belichtung neigt zur 
Überbelichtung.

Zur Berechnung der RGB-Werte aus den YUV-Werten habe ich Jorge 
Aparicio's Formeln aus seinem Blog verwendet.

Formeln: 
https://upload.wikimedia.org/wikipedia/en/math/4/e/b/4ebf7992636ec7100e2f0f68a4f2c2ca.png

Blog: 
http://embeddedprogrammer.blogspot.de/2012/07/hacking-ov7670-camera-module-sccb-cheat.html

Den von Dir verlinkten Codee werde ich einmal ausprobieren. Zusätzlich 
würde mich mich sehr freuen, wenn Du Deinen Code zur Verfügung stellen 
könntest.

Viele Grüße

Igel1


----------------------------------------------------------------------


PS:


Mit dieser Initialisierung erhalte ich eher Schwarz-Weiss-Bilder, die 
bereits bei leicht sich ändernder Beleuchtung in der Belichtung stark 
schwanken:
1
// QVGA 320x240
2
static unsigned char OV9655_QVGA[][2]=
3
{
4
  0x12, 0x80,
5
6
  0x11, 0x01,
7
  0x12, 0x00,
8
  0x0C, 0x04,
9
  0x3E, 0x19,
10
  0x70, 0x3A,
11
  0x71, 0x35,
12
  0x72, 0x11,
13
  0x73, 0xF1,
14
  0xA2, 0x02
15
}


Und so verwandle ich den YUV422 - Code in RGB-Farben, die ich über eine 
Bibliotheksfunktion von Tilen Majerle an mein ILI9341 sende. Der Code 
ist weder schön noch effizient - es geht mir erst einmal nur darum, 
saubere Bilder zu sehen - Geschwindigkeit kommt später dran:
1
// Bitte beachten: im Code wird zwar überall QQVGA als Bezeichner verwendet,
2
//                 in Wirklichkeit sind's aber QVGA-Werte, die ich nutze
3
4
    for(y_0=0; y_0<QQVGA_HEIGHT; y_0++) {
5
        for(x_0=0; x_0<QQVGA_WIDTH; x_0+=2){
6
            if((x_0%4)==0){
7
                mycolorY  = (int32_t)qqvgaframe1[x_0*2 + 1 + y_0*QQVGA_WIDTH*2];
8
                mycolorCb = (int32_t)qqvgaframe1[x_0*2 + 0 + y_0*QQVGA_WIDTH*2];
9
                mycolorCr = (int32_t)qqvgaframe1[x_0*2 + 2 + y_0*QQVGA_WIDTH*2];
10
            } else {
11
                mycolorY  = (int32_t)qqvgaframe1[x_0*2 + 1 + y_0*QQVGA_WIDTH*2];
12
                mycolorCb = (int32_t)qqvgaframe1[x_0*2 + 2 + y_0*QQVGA_WIDTH*2];
13
                mycolorCr = (int32_t)qqvgaframe1[x_0*2 + 0 + y_0*QQVGA_WIDTH*2];
14
            }
15
            mycolorR = mycolorY + 1.402   * (mycolorCr - 128);
16
            mycolorG = mycolorY - 0.34414 * (mycolorCb - 128) - 0.71414 * (mycolorCr - 128);
17
            mycolorB = mycolorY + 1.772   * (mycolorCb - 128);
18
19
            // For ILI9341:  RGB565 = [RRRRRGGG.GGGBBBBB]
20
            mycolor=(uint32_t)(((mycolorR<<8)&0xF800) | ((mycolorG<<3)&0x07E0) | ((mycolorB>>3)&0x001F));
21
            //TM_ILI9341_DrawPixel(x_0, y_0, mycolor);   // Doesn't work properly - fade/transparent colors
22
            TM_ILI9341_DrawFilledCircle(x_0, y_0, 1, mycolor);  // Works, slowly - has to be changed
23
        }
24
    }

von macload1 (Gast)


Lesenswert?

Ich sehe, dass du da eher die Initialisierungswerte vom OV9655 
verwendest...

Das würde sehr wohl dieses Verhalten erklären.

Mit den Registern vom oben genannten Code, brauchst du auch keine 
"manuelle" Farbumwandlung vornehmen. Der OV7670 kann ganz alleine RGB 
Werte ausgeben anstatt YUV.

Notfalls kannst du auch die folgenden Registerwerte versuchen: 
https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ov7670_regs.h

macload1

von macload1 (Gast)


Lesenswert?

Und wenn man mal die folgenden Begriffe bei Google eingibt, dann bekommt 
man richtig viele Resultate mit sehr viel Source-Code: "stm32 ov7670 
dcim".

Du brauchst ja auch eigentlich nur mehr die Registerwerte zu ändern, da 
der Rest ja schon zu funktionnieren scheint.

von Andreas S. (igel1)


Lesenswert?

macload1 schrieb:
> Ich sehe, dass du da eher die Initialisierungswerte vom OV9655
> verwendest...
>
> Das würde sehr wohl dieses Verhalten erklären.

Oh, sorry - that's just to confuse the russian.

Ich hatte ursprünglich Teile des Codes aus der Standard Peripheral 
Library von ST genommen - dort gibt es Beispiele für die OV9655 Kamera.

Diesen Code habe ich anschließend auf die OV7670 angepasst - nur den 
Namen der Variablen habe ich nicht angepaßt. Das verwirrt natürlich - 
sorry.

Die Initialisierungswerte stammen allen schön brav aus dem QVGA (30 fps 
QVGA YUV mode) Beispiel aus dem "OV7670/OV7171 CMOS VGA (640x480) 
CameraChip™ Implementation Guide", siehe dort S. 9 Mitte:
http://www.haoyuelectronics.com/Attachment/OV7670%20+%20AL422B%28FIFO%29%20Camera%20Module%28V2.0%29/OV7670%20Implementation%20Guide%20%28V1.0%29.pdf

Braver geht's schon gar nicht mehr :-)


>
> Mit den Registern vom oben genannten Code, brauchst du auch keine
> "manuelle" Farbumwandlung vornehmen. Der OV7670 kann ganz alleine RGB
> Werte ausgeben anstatt YUV.

Ja - hast schon recht, aber ich hatte trotzdem erst einmal mit YUV 
begonnen, weil ich zunächst nur die Y-Komponente für ein S/W-Bild 
verwendet hatte. Damit konnte ich mir grundsätzliche Funktion erst 
einmal erarbeiten und dabei möglichst viele Fehlerquellen ausschließen.

Und wo ich so schön bei YUV bin, möchte ich's auch erst einmal per YUV 
bunt bekommen.

>
> Notfalls kannst du auch die folgenden Registerwerte versuchen:
> https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ov7670_regs.h
>
> macload1

Werde ich ausprobieren - Danke für den Tipp!

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

macload1 schrieb:
> Und wenn man mal die folgenden Begriffe bei Google eingibt, dann bekommt
> man richtig viele Resultate mit sehr viel Source-Code: "stm32 ov7670
> dcim".

... von denen ich die meisten im Vorfeld schon gelesen habe - und 
trotzdem frage ich hier, weil viele von denen eben auch keine wirklich 
gute Register-Kombination gefunden haben und die meisten schon froh 
waren "irgendein" Bild aus der Kiste rauszuholen.

> Du brauchst ja auch eigentlich nur mehr die Registerwerte zu ändern, da
> der Rest ja schon zu funktionnieren scheint.

Wenn Du das "nur" wegläßt, so stimme ich Dir zu.
Es sind leider Dutzende von Registern - eins schlechter als das andere 
dokumentiert und ich wollte gerne eine wochenlange Try-and-Error Session 
vermeiden und von Leuten profitieren, die bereits gute Konfigurationen 
herausgefunden haben.

Daher bin ich auch so an Deinem Code interessiert ;-)

Viele Grüße

Igel1

von Andreas S. (igel1)


Lesenswert?

macload1 schrieb:
>
> [...]
>
> Notfalls kannst du auch die folgenden Registerwerte versuchen:
> https://github.com/ArduCAM/Arduino/blob/master/ArduCAM/ov7670_regs.h
>

Hab's ausprobiert - es ergibt leider nur eine tannenbaumgrüne Fläche.
Kein Bild nix.

Viele Grüße

Igel1

von Karl K. (leluno)


Lesenswert?

mit den Werten aus dem von dir angegebenen Tutorial hatte ich farblich 
gute Bilder:

Beitrag "Re: DMA-Datentransfer OV7670 -> ARM-MCU"

von Jan L. (ranzcopter)


Lesenswert?

Andreas S. schrieb:
> Es sind leider Dutzende von Registern - eins schlechter als das andere
> dokumentiert und ich wollte gerne eine wochenlange Try-and-Error Session
> vermeiden und von Leuten profitieren, die bereits gute Konfigurationen
> herausgefunden haben.

...das gerade Bitluni wohl recht erfolgreich hinter sich gebracht:
http://bitluni.net/esp32-i2s-camera-ov7670/

von Andreas S. (igel1)


Lesenswert?

Jan L. schrieb:
> ...das gerade Bitluni wohl recht erfolgreich hinter sich gebracht:
> http://bitluni.net/esp32-i2s-camera-ov7670/

Ja - sein Video kenne ich. Sieht definitiv gut aus.
Habe mir auch gerade seinen Code angeguckt.

Nur leider kann ich die Register-Settings nicht komprimiert an einer 
Stelle in seinem C++-Code finden. Außerdem verwendet er RGB.

Es riecht etwas nach Arbeit, die benötigten OV6770-Register-Settings aus 
seinem Code zu extrahieren (stöööhn ...). Mal sehen, ob ich mir das in 
diesem Jahr noch antun möchte.

Gibt's nicht vielleicht irgendwo doch noch eine einfachere Quelle, wo 
gut funktionierende Register-Settings alle schön an einer Stelle 
aufgeführt sind?

Viele Grüße

Igel1

von Andreas S. (igel1)


Angehängte Dateien:

Lesenswert?

Damit Ihr eine Vorstellung vom Stand meiner Forschungen habt, hier zwei 
Bilder:

- einmal mein Roboter mit einer Samsung Galaxy S7 - Kamera aufgenommen 
und
- einmal mit der OV7670 aufgenommen und auf einem ILI9341 - Display 
ausgegeben. (Achtung: dies ist eine etwas andere Szenerie - ich halte 
hier die Roboter vor die Kamera und im Hintergrund ist kein gemasertes 
Holz sondern die Zimmerdecke).

Gut sichtbar: Rot und Grün sind irgendwie noch vertauscht und auch sonst 
ist noch nicht alles perfekt.

Viele Grüße

Igel1

: Bearbeitet durch User
von Andreas S. (igel1)


Angehängte Dateien:

Lesenswert?

Ich habe mir einmal meinen oben zitierten Programmabschnitt genauer 
angeschaut und dabei festgestellt, dass ich nur jedes zweite Pixel in 
x-Richtung auslese und später auf das Display schreibe. War wohl doch 
etwas spät in der Nacht gewesen, als ich diesen Code zusammengeklopft 
hatte.

Auch erklärt dies, warum meine Farben immer so blass und transparent 
waren: ein testweise zuvor gemalter Vollkreis war nämlich von meinem 
Bild niemals komplett überschrieben worden, sondern "schimmerte" immer 
durch - kein Wunder, wenn ich nur jedes zweite Pixel gesetzt habe ...).

Zum Glück war dieser Fehler schnell gefixed.

Zudem kann ich jetzt die Funktion "TM_ILI9341_DrawPixel" statt der 
Funktion "TM_ILI9341_DrawFilledCircle" verwenden - vormals war DrawPixel 
nicht "farbdeckend" genug. Jetzt weiss ich natürlich warum.
Kleiner Nebeneffekt: das Bild wird nun ca. 10x so schnell gezeichnet.

Sodann habe ich pragmatisch die Formeln zur Berechung von R-Wert und 
G-Wert vertauscht und schwupps - schon stimmen die Farben (jedenfalls 
überwiegend).

Nun habe ich nur noch mit besonders hellen Bildbereichen zu kämpfen - 
dort kommt es zu "Farbsprüngen" - vermutlich irgendein Fehler beim Cast 
von Integern oder bei der Bitschieberei.

Ich freue mich jetzt erst einmal über das nette Zwischenergebnis - siehe 
Bild im Anhang - und widme mich weihnachtlicheren Dingen ...

Viele Grüße

Igel1

von Egon (Gast)


Lesenswert?

Du bist Dir aber schon sicher, das die Farbausgabe auf dem ILI9341 
richtig ist? Da gibt es ja auch diverse Konfigurationsregister bzgl. der 
Farbfolge und Bittiefe...

von Andreas S. (igel1)


Lesenswert?

Egon schrieb:
> Du bist Dir aber schon sicher, das die Farbausgabe auf dem ILI9341
> richtig ist? Da gibt es ja auch diverse Konfigurationsregister bzgl. der
> Farbfolge und Bittiefe...

Ja, relativ sicher:  ich habe die folgenden 3 Bitkombinationen geprüft
und damit testweise alle Pixel meines ILI-Display gefüllt:

- 1111 1000.0000 0000   =>  Rot
- 0000 0111.1110 0000   =>  Grün
- 0000 0000.0001 1111   =>  Blau

Daher denke ich, dass es stimmen müsste.

Viele Grüße

Igel1

von Andreas S. (igel1)


Angehängte Dateien:

Lesenswert?

Bin doch der Versuchung erlegen und habe noch ein bißchen herumprobiert.

Mein Verdacht für die Pixel mit den Fehlfarben waren ja Cast-Fehler, die 
im Falle von Über- bzw. Unterläufen bei der Umwandlung von YUV (bzw. 
YCrCb) nach RGB entstehen.

Also habe ich testweise den Code brute-force-mäßig so angepaßt, dass 
R,G,B-Werte in den Bereich 0 ... 255 "getrimmt" werden. Werte unter 0 
habe ich dafür auf 0 hochgesetzt und Werte über 255 auf 255 
heruntergesetzt.

Schaut selber das Ergebnis an ...    Hübsch gelle?!
Und das Bild sieht in Wirklichkeit deutlich besser aus als auf dem Foto 
- und das trotz einer relativ miesen Ausleuchtung der Motive.


Viele Grüße

Igel1

--------------------------------------------------------------------
1
// Bitte beachten: im Code wird zwar überall QQVGA als Bezeichner verwendet,
2
//                 in Wirklichkeit sind's aber QVGA-Werte, die ich nutze
3
4
5
    for(y_0=0; y_0<QQVGA_HEIGHT; y_0++) {
6
        for(x_0=0; x_0<QQVGA_WIDTH; x_0+=1){
7
            if((x_0%2)==0){
8
                mycolorY  = (int32_t)qqvgaframe1[x_0*2 + 1 + y_0*QQVGA_WIDTH*2];
9
                mycolorCb = (int32_t)qqvgaframe1[x_0*2 + 0 + y_0*QQVGA_WIDTH*2];
10
                mycolorCr = (int32_t)qqvgaframe1[x_0*2 + 2 + y_0*QQVGA_WIDTH*2];
11
            } else {
12
                mycolorY  = (int32_t)qqvgaframe1[x_0*2 + 1 + y_0*QQVGA_WIDTH*2];
13
                mycolorCb = (int32_t)qqvgaframe1[x_0*2 - 2 + y_0*QQVGA_WIDTH*2];
14
                mycolorCr = (int32_t)qqvgaframe1[x_0*2 + 0 + y_0*QQVGA_WIDTH*2];
15
            }
16
            mycolorG = (mycolorY + 1.402   * (mycolorCr - 128));
17
            mycolorR = (mycolorY - 0.34414 * (mycolorCb - 128) - 0.71414 * (mycolorCr - 128));
18
            mycolorB = (mycolorY + 1.772   * (mycolorCb - 128));
19
20
            if (mycolorR<0) mycolorR=0;
21
            if (mycolorG<0) mycolorG=0;
22
            if (mycolorB<0) mycolorB=0;
23
24
            if (mycolorR>255) mycolorR=255;
25
            if (mycolorG>255) mycolorG=255;
26
            if (mycolorB>255) mycolorB=255;
27
28
            // For ILI9341:  RGB565 = [RRRRRGGG.GGGBBBBB]
29
            mycolor  = (uint32_t)((mycolorR<<8 & 0xF800) | (mycolorG<<3 & 0x07E0) | (mycolorB>>3 & 0x001F));
30
            TM_ILI9341_DrawPixel(x_0, y_0, mycolor);
31
        }
32
    }

: Bearbeitet durch User
von Andreas S. (igel1)


Lesenswert?

Karl K. schrieb:
> mit den Werten aus dem von dir angegebenen Tutorial hatte ich farblich
> gute Bilder:

Ich bin verwirrt: welches von mir angegebene Tutorial meinst Du?

Sodann habe ich soeben Deinen Link durchgearbeitet:

> Beitrag "Re: DMA-Datentransfer OV7670 -> ARM-MCU"

Ergebnis: ich kann dort keinen Code finden, der die OV7670-Register 
setzt.
Evtl. müßtest Du mich nochmals mit der Nase darauf stoßen - ich scheine 
blind zu sein.

Viele Grüße

Igel1

von grundschüler (Gast)


Lesenswert?

Andreas S. schrieb:
> welches von mir angegebene Tutorial meinst Du?

http://embeddedprogrammer.blogspot.de/2012/07/hacking-ov7670-camera-module-sccb-cheat.html

Das ist schon länger her. Ich meine, bei diesem tutorial war zumindest 
ein Verweis auf Standard-sccb-code.

von Andreas S. (igel1)


Lesenswert?

grundschüler schrieb:
> Andreas S. schrieb:
>> welches von mir angegebene Tutorial meinst Du?
>
> 
http://embeddedprogrammer.blogspot.de/2012/07/hacking-ov7670-camera-module-sccb-cheat.html
>
> Das ist schon länger her. Ich meine, bei diesem tutorial war zumindest
> ein Verweis auf Standard-sccb-code.


Jetzt bin ich etwas verwirrt, denn da passen ein paar Sachen nicht 
zusammen:

- meine Frage ging an Karl K. (leluno) und es antwortet "gundschüler".
  Bist Du derselbe? => bitte unter demselben Alias schreiben.

- meine Frage bezog sich auf
  > Beitrag "Re: DMA-Datentransfer OV7670 -> ARM-MCU"
  Und Du verweist auf eine ganz andere Seite.

- meine Frage bezog sich auf Register-Settings und Du verweist auf
  SCCB-Code.  SCCB funktioniert bei mir - ich bin hauptsächlich an
  guten Register-Settings interessiert.

Die von Dir zitierte Seite kenne ich - diese Seite ist wirklich gut, 
aber die Register-Settings aus dem referenzierten Code ergaben keine 
allzu dollen Bilder.

Ich will aber nicht undankbar sein, daher: danke für die Mühen - nur 
versucht bitte auch ein bißchen auf meine Fragen einzugehen ...

Viele Grüße

Igel1

von grundschüler (Gast)


Lesenswert?

Andreas S. schrieb:
> aber die Register-Settings aus dem referenzierten Code ergaben keine
> allzu dollen Bilder.

Ich wollte dich lediglich darauf hinweisen, dass die Registersettings 
aus dem von dir  referenzierten Code - die meiner Erinnerung nach per 
sccb-protokoll in die kamera übertragen werden - bei meinen Versuchen 
farblich perfekte Bilder ergeben haben, so dass du mglw. Fehler nicht 
bei den Registersettings suchen musst.

Ich würde dir die damals von mir verwendeten settings gerne zum Abgleich 
zur Verfügung stellen aber die Festplatte gibts leider nicht mehr.


grundschüler karl k.

von Andreas S. (igel1)


Lesenswert?

grundschüler schrieb:
> Ich wollte dich lediglich darauf hinweisen, dass die Registersettings
> aus dem von dir  referenzierten Code - die meiner Erinnerung nach per
> sccb-protokoll in die kamera übertragen werden - bei meinen Versuchen
> farblich perfekte Bilder ergeben haben, so dass du mglw. Fehler nicht
> bei den Registersettings suchen musst.

Ah soooo ...
Jetzt verstehe ich, was Du meinst.

Nein, ich hatte keinerlei Code von dieser Seite verwendet, weil ich dort 
seinerzeit keinen Code gefunden hatte. Außerdem hatte ich anfangs noch 
den Ehrgeiz, alles selbst zu stricken - habe ich aber nicht lange 
durchgehalten. Das ganze endete letztendlich in Code, der von ca. ein 
Dutzend Websites zusammengemoppst und mit ca. 20% eigenem Code 
zusammengeklebt ist - nicht schön, aber läuft.

Nur die Erkläuterungen zur Funktionsweise der Kamera hatte ich mir von 
der genannten Seite angelesen - genial einfach beschrieben von Jorge 
Aparicio.

Nach Deinen Hinweisen und einigem Suchen habe ich nun aber tatsächlich 
den Code von Jorge Aparicio gefunden: Ein winziger Link auf seiner Seite 
verweist auf den Code - verborgen im Satz "You can (hopefully) see 
here (sorry, it's buried among other files) the schematic of the model 
I'm using in this post."

Über den Link, hinter "here" kommt man an eine RAR-Datei OV7670.rar, in 
der sich der Source-Code befindet. Dort wiederum findet sich eine Datei 
"ov7670_init.c" mit zwei Funktionen, in denen jede Menge Register 
initialisiert werden:

- static void init_yuv_12fps(void)
- static void init_yuv_25fps(void)

Damit werde ich in 2018 einmal mein Glück versuchen.

Leider ist so eine Übernahme der Registerwerte in meinen Code recht 
aufwändig, denn ich muss (bzw. möchte) ja jeweils nachlesen, welcher 
Wert nun was genau bewirkt und ob's zu meinem Code passt.

Denn sicherlich werden dort auch Auflösungen und Timings festgelegt, die 
nicht zu meinem Code passen.

Anyway - Du hast mir auf jeden fall schon einmal gute Hoffnung gegeben, 
dass ich dort die gesuchten Register-Settings finden werde. Den Code von 
Jorge Aparicio's hatte ich beim ersten Lesen glatt übersehen.
Danke also ganz herzlich für Deinen Hinweis!

Viele Grüße

Igel1

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.