OV7670 YUV demystified


Ok, been there, done that! For past few hours I was struggling to get an image from YUV output but keep failing (planning to use this format for a better colour segmentation). Anyway, managed to get it done once read the manual (several times). Is actually pretty simple once you get the idea.

First, need to be sure that the proper register are set the way are supposed to be set:

{TSLB,     0x14}
{COM13,    0x88}
{COM7,     0x00}

Then, on the client side, you will need to handle the YUV422. Question is: what is this YUV422 format about? The now is when it start the annoying part: searching the web…

Anyway, to cut it short, is simple: YUV422 looks like YUYV, where this is definition for 2 pixels, so it become Y0 U0 Y1 V0, where Y0U0V0 is for pixel1 and Y1U0V0 for pixel2. Add now the magic formula to change from YUV to RGB and you get something like bellow (in PROCESSING):

Y  = pixel1;
U  = pixel2;
Y1 = pixel3;
V  = pixel4;

R = Y + 1.4075 * (V - 128);
G = Y - 0.3455 * (U - 128) - (0.7169 * (V - 128));
B = Y + 1.7790 * (U - 128);
fill(R, G, B); 
rect(x*2, y, 1, 1);        // draw first pixel
R = Y1 + 1.4075 * (V - 128);
G = Y1 - 0.3455 * (U - 128) - (0.7169 * (V - 128));
B = Y1 + 1.7790 * (U - 128);
fill(R, G, B); 
rect(x*2+1, y, 1, 1);      // draw second pixel

And now, voila! you get your image the way it should be. To make it more “visual appealing”, I add a picture with Y, UV and RGB result.

Advertisements

38 thoughts on “OV7670 YUV demystified

  1. Hello,

    I use a OV7670 camera with a FIFO ram (AL422) and an ATMega644.
    I use the format Y0U0Y1V0 with the magic formula as you to change from YUV to RGB.
    But instead of having the red color, I have a green color and pink instead of green.
    In the UV image, the green color appears white and the red color black. In your test it is inverted (black for green and white for red).
    My Y image is good.

    I try to have an image from the RGB444 format of the camera : I have the same color as the YUV format, even if I change the color Matrix and the Gamma curve.

    I don’t understand this problem. Do you have an idea ? is it possible to have the part of your program where there is the camera configuration ?
    I can send you my image test.

    Thanks for your help.
    (Sorry for my bad english)

    Best regards

    • Hi, did you try to swap U and V and see if make a difference? There is a register that does that, but for start try to do this in your code. If your Y is fine this may be the problem. Hope this helps!

      • Hello Nasulica,

        Thanks for your fast answer !
        I have already swap U and V in my code and in the COM13 register too, it make a difference but I have not the real colors : red is always green and green is pink.
        I tried to invert RGB pixel in the bmp file, there is differernces but not the real colors.
        I have swapped the data bus on my board and in the COM3 register too : no significant changes.
        Maybe, I have missed to configure a register. Is it possible to have the list of the register you have configured ?
        Do you want I send you the images (UV, Y and RGB) ?

        Thanks in advance.

      • Hi elecdev, can you please send me the piece of code you use to capture pixel data and the code where you process it? I’ll take a look and try to see where it might be the problem.

    • I working on ov7670 +al422b camera for two months with atmega 32 but not getting the image via usart, i have checked href, Vsync Pclk,everything is ok..
      I am able to configure the ov7670 camera sucessfully.

      So, i request you to please please please mail your complete code to my e-mail
      shubhamgarg.arg@gmail.com.
      It will be grateful if you even send the GUI using on the PC side for viewing the image.

      waiting for your response!!!!!!!

      • Hi Shubham, first of all, did you tried to look on the posts and the comments i advised before? There are all the details you need on how to do the connectivity between your MCU and OV7670 module. Also, plenty of details on how to get the data out from the OV7670 (e.g.: pseudocode, pieces of code, etc) and how to get the UART communication done. Basically there is everything you need to see things working. Btw, the GUI is useless unless you are using my CTS modules.

        Already explained before that I will not be able to provide you the code as I do have an agreement on this with the company doing the distribution for this CTS modules.

  2. Hi nasulica,

    Thanks for your help !

    I have tried to change lens : I used my webcam lens with a filter but it change nothing.

    I try 2 code, first using the QVGA YUV camera configuration, the second using the QVGA RGB444 camera configuration.
    With those 2 codes, the result is the same : I have a picture with not correct colors (green instead of red and pink instead of green).

    I use a CF7670C-V2 camera (OV7670 + FIFO ram)

    First test of code using the QVGA YUV camera configuration:

    // configuration register : the other registers have the default values

    OV7670::I2C_WriteValue(COM10, 0x02); //set VSYNC negative
    OV7670::I2C_WriteValue(COM7, 0x00);
    OV7670::I2C_WriteValue(CLKRC, 0x00);
    OV7670::I2C_WriteValue(COM3, 0x04);
    OV7670::I2C_WriteValue(COM14, 0x19);
    OV7670::I2C_WriteValue(SCALING_XSC, 0x3A);
    OV7670::I2C_WriteValue(SCALING_YSC, 0x35);
    OV7670::I2C_WriteValue(SCALING_DCWCTR, 0x11);
    OV7670::I2C_WriteValue(SCALING_PCLK_DIV, 0xF1);
    OV7670::I2C_WriteValue(SCALING_PCLK_DELAY, 0x02);
    OV7670::I2C_WriteValue(COM15, 0xC0);
    OV7670::I2C_WriteValue(RGB444, 0x00);
    OV7670::I2C_WriteValue(TSLB, 0x0C);
    OV7670::I2C_WriteValue(COM13, 0x88);
    OV7670::I2C_WriteValue(MVFP, 0x20);

    OV7670::I2C_WriteValue(HSTART, 0x16);
    OV7670::I2C_WriteValue(HSTOP, 0x04);
    OV7670::I2C_WriteValue(HREF, 0x80);
    OV7670::I2C_WriteValue(VSTART, 0x03);
    OV7670::I2C_WriteValue(VSTOP, 0x7B);
    OV7670::I2C_WriteValue(VREF, 0x00);

    // make an image using the QVGA YUV camera configuration

    byte Y;
    byte U;
    byte Y1;
    byte V;

    byte R;
    byte G;
    byte B;

    for(int lc = 0; lc < 240; lc++) // QVGA_HEIGHT
    {
    for(int pc = 0; pc < 160; pc++) // QVGA_WIDTH / 2
    {

    U = Fifo.ReadVideoByte(); // read 4 bytes from the FIFO
    Y = Fifo.ReadVideoByte();
    V = Fifo.ReadVideoByte();
    Y1 = Fifo.ReadVideoByte();

    R = Y + 1.4075 * (V – 128); // magic formula to change from YUV to RGB
    G = Y – 0.3455 * (U – 128) – (0.7169 * (V – 128));
    B = Y + 1.7790 * (U – 128);

    SendByte(B); // send the first pixel (3 bytes BGR to make a 24bit bmp file)
    SendByte(G); // even if I invert B and R : not really differences !
    SendByte(R);

    R = Y1 + 1.4075 * (V – 128); // magic formula to change from YUV to RGB
    G = Y1 – 0.3455 * (U – 128) – (0.7169 * (V – 128));
    B = Y1 + 1.7790 * (U – 128);

    SendByte(B); // send the second pixel (3 bytes BGR to make a 24bit pixel in bmp file)
    SendByte(G);
    SendByte(R);

    }
    }

    Second test of code using the QVGA RGB444 camera configuration:

    // configuration register : the other registers have the default values

    OV7670::I2C_WriteValue(COM10, 0x02); //set VSYNC negative
    OV7670::I2C_WriteValue(COM7, 0x14);
    OV7670::I2C_WriteValue(CLKRC, 0x01);
    OV7670::I2C_WriteValue(COM3, 0x04);
    OV7670::I2C_WriteValue(COM14, 0x19);
    OV7670::I2C_WriteValue(SCALING_XSC, 0x3A);
    OV7670::I2C_WriteValue(SCALING_YSC, 0x35);
    OV7670::I2C_WriteValue(SCALING_DCWCTR, 0x11);
    OV7670::I2C_WriteValue(SCALING_PCLK_DIV, 0xF1);
    OV7670::I2C_WriteValue(SCALING_PCLK_DELAY, 0x02);
    OV7670::I2C_WriteValue(COM15, 0xF0);
    OV7670::I2C_WriteValue(RGB444, 0x02);
    OV7670::I2C_WriteValue(TSLB, 0x0D);
    OV7670::I2C_WriteValue(COM13, 0xC3);
    OV7670::I2C_WriteValue(MVFP, 0x20);

    OV7670::I2C_WriteValue(HSTART, 0x16);
    OV7670::I2C_WriteValue(HSTOP, 0x04);
    OV7670::I2C_WriteValue(HREF, 0x80);
    OV7670::I2C_WriteValue(VSTART, 0x03);
    OV7670::I2C_WriteValue(VSTOP, 0x7B);
    OV7670::I2C_WriteValue(VREF, 0x00);

    // make an image using the QVGA RGB444 camera configuration
    // first FIFO Byte : x x x x R7 R6 R5 R4
    // second FIFO Byte : G7 G6 G5 G4 B7 B6 B5 B4

    byte Byte1;
    byte Byte2;
    byte S;

    byte R;
    byte G;
    byte B;

    for(int lc = 0; lc < 240; lc++) // QVGA_HEIGHT
    {
    for(int pc = 0; pc < 320; pc++) // QVGA_WIDTH
    {

    Byte1 = Fifo.ReadVideoByte(); // read 2 bytes from the FIFO
    Byte2 = Fifo.ReadVideoByte();

    S = Byte1;
    R = (S<<4) & B11110000; // to have the Red byte value R7 R6 R5 R4 0 0 0 0

    S = Byte2;
    G = S & B11110000; // to have the Green byte value G7 G6 G5 G4 0 0 0 0

    S = Byte2;
    B = (S<<4) & B11110000; // to have the Blue byte value B7 B6 B5 B4 0 0 0 0

    SendByte(B); // send the pixel (3 bytes BGR to make a 24bit pixel in bmp file)
    SendByte(G); // even if I invert B and R : not really differences !
    SendByte(R);

    }
    }

    I try to modify the color matrix and the gamma curve too : no color changes.

    Best regards

    • Hi elecdev,
      For YUV, I am not touching COM15, CLKRC should be 0x01, the rest of your registries look fine. Also, COM13 works with TLSB for YUV control.
      For RGB444 I can see you still playing with YUV settings, also your COM7 i guess should be 0x04 since you are using SCALLING* options to set the format. Can you try something in RGB44 mode? Can you display an image generated only using first byte then another one using second byte for pixel value?

  3. Hi nasulica,

    For YUV, COM15 has the default value (0xC0).
    If I keep this default value in RGB444 mode, there is a lot of noise in the image and not when COM15=0xF0. Indeed, RGB444 mode is valid only if COM15[4] is high (the result is the same if I put 0xD0 instead of 0xF0 in COM15).
    When CLKRC is 0x01 in YUV, the image is the same.

    Exactly, for RGB444, the result is the same if COM7 = 0x04 or 0x14.
    I have generated an image using only the first byte and an other image using the second byte : the two images are in black and white and exactly the same !!! (green is darker than red).

    Thanks !
    Best regards.

    • Hi elecdev, if looks the same means is something wrong. One should be very dark as only the last 4 bits will contain image data and the second byte have MSB for green and LSB for blue. Seems there are other things that may cause your problem. I’ll try to run your configuration on my side one of these days and let you know if get same results but need to get an environment smiler with yours up and running first. Will get back to you with findings.

      • Hi nasulica,

        I have at last managed to have a good picture with the real colors !
        After a lot of tests and some search on internet, I have set those registers:

        /* Almost all of these are magic “reserved” values. */
        { REG_COM5, 0x61 }, { REG_COM6, 0x4b },
        { 0x16, 0x02 }, { REG_MVFP, 0x07 },
        { 0x21, 0x02 }, { 0x22, 0x91 },
        { 0x29, 0x07 }, { 0x33, 0x0b },
        { 0x35, 0x0b }, { 0x37, 0x1d },
        { 0x38, 0x71 }, { 0x39, 0x2a },
        { REG_COM12, 0x78 }, { 0x4d, 0x40 },
        { 0x4e, 0x20 }, { REG_GFIX, 0 },
        { 0x6b, 0x4a }, { 0x74, 0x10 },
        { 0x8d, 0x4f }, { 0x8e, 0 },
        { 0x8f, 0 }, { 0x90, 0 },
        { 0x91, 0 }, { 0x96, 0 },
        { 0x9a, 0 }, { 0xb0, 0x84 },
        { 0xb1, 0x0c }, { 0xb2, 0x0e },
        { 0xb3, 0x82 }, { 0xb8, 0x0a },

        I don’t understand, because a lot of thoses registers are reserved registers !
        It is curious !
        Have you got a complete OV7670 advanced information datasheet ?

        Thanks again for taking the time to help me !
        Best regards

      • Hi elecdev, unfortunately this is one issue with OV camera sensors, a lot of magic numbers in reserved registers. I’m glad that you got your answer. Try to do a google on “OV7670 implementation guide”, you should find a document on a site in China but you can only see it, you can not download it or copy it… Anyway, will answer some of your questions about OV configuration. Good luck with your project!

    • Hi rebelión, maybe you can take a look on the comments attached to some of the posts, there are discussions on how to connect the OV7670 to any MCU and how to control it in your code. If have any specific question feel free to ask and I will try to answer.

  4. Thanks,
    my experience in arduino is too poor to wire the camera right..i think… :(
    Alright i will try to figure it, with your help :)
    pins D0-D7 will be used for reading the frame?
    3 pin is a SCCB/i2c clock, what i need to send on this pin from arduino?
    4 is a SCCB/i2c data pin, through it i have to configure the camera registers?

    Sorry for my English

    • Hi there, Arduino should work, got it running on Atmega8, on your Mega256 should not be an issue.
      Basically, yes: pins 3 and 4 are the I2C/SSCB bus used to configure the camera, there will be another pins to control the AL422 chip and then the D0-7 databus to capture the frame. You may also want to take a look on this post’s comment, may help.

  5. hI NASULICA please I need your help, im getting images, but where the light is bright the image looks purple, im viewing the image on an LCD TFT 320*240, this is my code

    {0x6B,0x4A},//pll para el reloj
    {0x11,0x81},//preescalizar para el reloj
    {0x12, 0x14},//rgb565,qvga 14
    {0x8C, 0x00},//rgb 444 OFF
    {0x0c, 0x0C},//com3 habilita escalado (se va a trabajar con QVGA)
    {0x3e, 0x00},//com14 divisor de pclk y escalado manual, ambos en off es
    {0x40, 0xD0},//COM15 RGB565 formato de datos completo 0-255
    /******estos parametros son los que estabilizan la imagen******/

    {0x17, 0x13},//hstart
    {0x18, 0x01},//hstop
    {0x32, 0xB6},//href
    {0x19, 0x02},//vstart
    {0x1a, 0x7A},//vstop ESTE PARAMETRO PRODUCE SCROLLING
    {0x03, 0x0A},//vref ESTE PARAMETRO PRODUCE SCROLLING

    // {0x14,0x20},//limite de ganancia del AGC
    {0x3D,0xC0},// habilita gamma y ajuste automatico de UV, trabaja junto al registro 3A
    {0x1E,0x37},//habilitar flip mode y black sun mode
    {0x0F,0x4B},//resetea todos los timers cuando el formato cambia <——hasta este punto la imagen se ve correcta en cuanto a posicion
    /*****************************PARAMETROS A MODIFICAR PARA EL COLOR DE LA IMAGEN*******************************************/

    {0x4f,0x80},
    {0x50,0x80},
    {0x51,0x00},
    {0x52,0x22},
    {0x53,0x5e},
    {0x54,0x80},
    {0x56,0x40},
    {0x58,0x9e},
    {0x59,0x88},
    {0x5a,0x88},
    {0x5b,0x44},
    {0x5c,0x67},
    {0x5d,0x49},
    {0x5e,0x0e},
    {0x69,0x00},
    {0x6a,0x40},

    {0x6c,0x0a},
    {0x6d,0x55},
    {0x6e,0x11},
    {0x6f,0x9f},

    nothing seems to change this behavior
    please help

    • Hi Alex, usually this may be caused by using incorrect values for gamma curve in your registries. Did you try to use the official values provided by Omnivision? That should give a decent picture already.
      I’ll try to check on my side to see what is missing from you registry list, however, can you post a picture with the image you getting?

    • Hi Alex, looks more like the colors you display are wrong. Can you check your code? by any chance you have an extra read? This may mess up your order and use a byte from one pixel with a byte from another pixel…

  6. Hi nasulica, definitely colors are wrong im using DCMI to capture data, it takes 4 bytes one after other and then I send it to the LCD using DMA so it is not doing extra readings or mixing bytes. does the camera make the YUV422 to RGB convertion or I must doit by using the matrix registers? if so, how do I do that?

    could you please use only this initialization registers without color settings

    {0x6B,0x4A},//pll para el reloj
    {0x11,0x81},//preescalizar para el reloj
    {0x12, 0x14},//rgb565,qvga 14, 04 es solo rgb mode (COM7)
    {0x8C, 0x00},//rgb 444 OFF
    {0x0c, 0x0C},//com3 habilita escalado (se va a trabajar con QVGA)
    {0x3e, 0x00},//com14 divisor de pclk y escalado manual
    {0x40, 0xD0},//COM15 RGB565 formato de datos completo 0-255
    /******estos parametros son los que estabilizan la imagen******/

    {0x17, 0x13},//hstart
    {0x18, 0x01},//hstop
    {0x32, 0xB6},//href
    {0x19, 0x02},//vstart
    {0x1a, 0x7A},//vstop ESTE PARAMETRO PRODUCE SCROLLING
    {0x03, 0x0A},//vref ESTE PARAMETRO PRODUCE SCROLLING

    // {0x14,0x20},//limite de ganancia del AGC
    // { 0x3A, 0x05 },//uyvy o vyuy depente del registro 3D
    // {0x3D,0xC1},// habilita gamma y ajuste automatico de UV, trabaja junto al registro 3A
    {0x1E,0x37},//habilitar flip mode y black sun mode
    {0x0F,0x4B},//resetea todos los timers cuando el formato cambia

    the reason is to verify if my sensor is damaged or not, if your image is purple in some places then the problem is the color settings but if it is not then my sensor is damage
    I hope not to bother you and thank you so much.

    • Hi Alex, i’ll try to do a test, but may take few days. However, i don’t think is something wrong with your image, you may want to check in the meantime using published OV registry values instead?

  7. Hi nasullica its me again, Im trying to capture images with fifo instead of DCMI and Im getting good pictures but the problem is that the image doesnt stay still, its moving all around on the LCD, my cam is V1.0 so vsync its not connected to WRST, this is part of my code

    /*inicializacion de la FIFO de la camara*/
    camerafifo();
    fifo_gpio();
    FIFO_PWDN_L();
    FIFO_CS_L();

    Clear_Screen(0x0000);

    vsyncint(); //INICIALIZACION DE LA INTERRUPCION VSYNC

    while(1)
    {

    if(aux1==2)
    {
    FIFO_WE_H();
    Set_Cursor(0, 319);
    Write_GDDRAM_Prepare();

    FIFO_WRST_L();
    FIFO_WRST_H();

    FIFO_RD_L();
    FIFO_RRST_L();
    FIFO_RD_H();
    FIFO_RRST_H();
    FIFO_RD_L();

    for( count = 0; count IDR<<8) & 0xff00; /* ( GPIO_ReadInputData(GPIOC) <IDR) & 0x00ff; /* ( GPIO_ReadInputData(GPIOC) ) & 0x00ff; */
    FIFO_RD_H();

    Write_Data(data);
    }
    aux1=0;
    FIFO_WE_L();

    }

    }
    }

    //funcion que se activa en la interrupcion
    void EXTI9_5_IRQHandler(void)
    {
    if(EXTI_GetITStatus(EXTI_Line7))
    {
    aux1++;

    EXTI_ClearITPendingBit(EXTI_Line7);
    }
    }

    do I need to put a delay between WRST and the read of the data because of the 128 cycles of WCLK to read new data?

    • Hi Alex, looking at your code I can see quite a few issues:
      – if you read AL422 manual, page 15, “read operation”section, there is a mention about write-to-read timing restriction;
      – in manual, page 9, timing diagrams, read cycle timings, you will see what is the correct sequence for reading data;
      – why not handle WE and WRST in your interrupt handler?

      The picture you posted shows that your image is not synchronised, so you seems to have problems with your timing. Hope this helps.

  8. Hi,

    I am trying to interface OV7670 + AL422 chip with an AVR ATmega16. With all the above notes and discussion I have implemented the code. But now I don’t know which software should I use to get the display on PC. (Windows Box).

    Please suggest me some software which I can use for this implementation.

    Thanks

    • Hi there, you’ll need to build your own client, however, I guess will be easier to use PROCESSING if you just want to see the output. Some code samples can be found on this site…

  9. Hi nasullica, well the image its ok and stable thank you so much, I want the red color looks stronger than the blue and green, which registers do you think I need to change? I’ve had probe gainchannel registers but it doesnt make any change.

  10. Hi!

    I am trying to interface OV7670 with STM32F4 uC. I can get the complete image, and show it in matlab, but something is wrong with the colors. I think there is no blue component.
    Here is an image: http://s16.postimg.org/hb6n2mctx/untitled.jpg The ball and the beer can is blue. The color around “Orbit” text is green, and the green object behind the ball is red.

    I use the following configuration:
    {0x12, 0x00},
    {0x3A, 0x04},
    {0x3D, 0x88},
    {0x0C,0x04}, // Enable down sampling
    {0x72,0x22}, // Down sample by 4
    {0x73,0x02}, // Clock div4
    {0x3E,0x1A}, // Clock div4

    The other problem is that, the image resolution is 156*120 instead of 160*120, and there is a black line on the left side.

    Do you have an idea?
    Thanks for your help!

    (Sorry for my bad english..)

  11. Hello,

    I solved my color problems by writing into OV7670 Register 0xB0 the value 0x84.

    C# Code to convert the YUYV pair (y1/u/y2/v) in a RGB pair:
    r1 = (int)((float)y1 – 1.4075 * (float)(v – 128));
    g1 = (int)((float)y1 – 0.3455 * (float)(u – 128) + 0.7169 * (float)(v – 128));
    b1 = (int)((float)y1 + 1.7790 * (float)(u – 128));
    r2 = (int)((float)y2 – 1.4075 * (float)(v – 128));
    g2 = (int)((float)y2 – 0.3455 * (float)(u – 128) + 0.7169 * (float)(v – 128));
    b2 = (int)((float)y2 + 1.7790 * (float)(u – 128));

  12. Hello,

    Maybe is too old post but I’ll give a try ;)
    First of all thanks for share your great work.
    I’ve been looking the camera output sequence in different places and seems to be different approaches, I mean, here you say the output is Y0 U0 Y1 V0 and I saw in other sources something like Cb0 Y0 Cr0 Y1 I’m really confuse with that which one is the correct camera output?

    Thanks.

    • Hi Jose, if you check the datasheet, there is a sequence of 3 RCLK pulses that need to be sent first before you start reading the data. Also, one of the registries can change the order of the bytes while you are reading it.

      • Oh thank you very much for your reply. What I’m trying to do at the moment is get the YUV data as they come from the camera default setting (I’m not interfacing the registers).
        I’m using VHDL to interface the camera.

        The thing is that I’ve got the camera byte clock which is in my case 25MHz wich with the YUV format means that each 4 clock cycle I’ll have two pixel data (if I’m not wrong).
        I want a pixel clock of the same frequency (25MHz) for that I’m saving this data in a async fifo and in the other side I’m running a HDL which is reading each pixel converting to RGB and generating the vsync and hsync signals.

        At the moment I’m getting wrong output on the VGA screen…

        Is it wrong approach?

        thanks again.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s