Go Down

Topic: OV7670 with both arduino uno and now mega (Read 494111 times) previous topic - next topic

jlsilicon

#870
Dec 02, 2017, 03:28 am Last Edit: Dec 02, 2017, 04:14 pm by jlsilicon
Well, I turned around and mounted the Ov7670 Camera onto the Arduino DUE Board.

I was having a hard time with the YUV Colors yesterday, only Green and Purple images.
I tried shifting over 1 byte or more, reversing sequence, etc - no luck.

So, I tried GRB422 and converted it into RGB in the Arduino Program , while outputng it onto the UART.
The problem now is : all I see is Black and White. 

For example (GRB422 to RGB-Bmp24) :
- The image below actually includes: Red and Yellow and Green Games on the shelf,
  besides the Yellow Hardwood Floors.





I used the standard Register Settings, both YUV422 and GRB422 modes.

Anybody have any ideas ?

jlsilicon

#871
Dec 08, 2017, 07:06 pm Last Edit: Dec 11, 2017, 12:13 am by jlsilicon
I used these Examples :

https://www.instructables.com/id/How-to-Connect-OV7670-to-Arduino-Due/

http://www.commenthow.com/article/display/13456/0/How+to+connect+OV7670+to+Arduino+Due

http://privateblog.info/arduino-due-i-kamera-ov7670-primer-ispolzovaniya/

I used the following Schematic for wiring :



I would judge, comparing the Code from these to the Uno Example in this Forum,
- that the Registers are Wrong.

jlsilicon

#872
Dec 09, 2017, 01:55 am Last Edit: Dec 09, 2017, 04:22 am by jlsilicon
I modified the code, to tell ov7670 to output as RGB565,
 and send the data back as a BMP image. 
Just save as a BMP when capturing the Serial data. 

But, still only Black & White.

Any suggestions ?

zoomx

The image should be a RAW image not a color image, you have to translate in colour format. There is a bayer filter.
From the first post
I also made a data convert that allows you to convert raw data from the camera into a PNG file
https://github.com/ComputerNerd/RawCamera-data-converter

jlsilicon

#874
Dec 10, 2017, 02:34 am Last Edit: Dec 10, 2017, 11:31 pm by jlsilicon
Thanks for the reference !

Actually though, I put a Converter in the Arduino Code.
So, this should work directly from the Serial out to BMP file.

But I only get Black & White Images.

jlsilicon

#875
Dec 11, 2017, 02:03 am Last Edit: Dec 11, 2017, 08:00 pm by jlsilicon
Latest using the RGB565 format :





It looks better.
The Red Stick is Red. 
But the Green Stick shows as Yellow ... (Red and Green pixels) ?
And the Blue the Stick shows as Green ... (Green pixels) ?
Notice the marble effect on the Fingers.
And, the Blue and Purple (besides Black) background.

Below is a Snapshot of a  box of Pastels:





Pastels don't look too bad.
Left is Browns Column.
Central is Purple, Blue, Greens Column.
Right is Yellow, Orange, Pink, Reds Column.
But the box is Black , why the mixed marble colors again (Blue and Yellow) (- is it reflection) ?

(Code is linked below.)

jlsilicon

I tried using the Bayer(888) ,
 but I only get back the Black & White again.

krzysiekk

I have problem with my camera.
I dont use Arduino. I use Nucleo stm32f446re + DCMI+DMA.
I use XCLK from uC, 16MHz. Frame rate are not important to me
in this level of my work.

I send my framebuffer from uC to PC by UART, previously adding to this header of bitmap.
It is interesting that when I set the QCIF format I get 172x144 format (not 176x144).


Code: [Select]
// send header of file
 usart2_send('B'); // BM send
 usart2_send('M');

 uint32_t size_of_file=54+bitmap_size; // size of file - 4 bytes

 usart2_send((file_size)&0xFF);
 usart2_send((file_size>>8)&0xFF);
 usart2_send((file_size>>16)&0xFF);
 usart2_send((file_size>>24)&0xFF);

 usart2_send(0); // bfReserved1 bfReserved2 - 4 bytes
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 usart2_send(54); // bfOffBits - 4 bytes
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 // BITMAPINFOHEADER ----------------------------------------------------------------

 usart2_send(40); // biSize (from this to end of header) - 4 bytes
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 usart2_send(width & 0xFF); // biWidth - 4 bytes
 usart2_send((width>>8)& 0xFF);
 usart2_send(0);
 usart2_send(0);

 usart2_send(height & 0xFF); // biHeight - 4 bytes
 usart2_send((height>>8)& 0xFF);
 usart2_send(0);
 usart2_send(0);

 usart2_send(1); // biPlanes - count of color layers - 2 bytes
 usart2_send(0);

 usart2_send(24); // biBitCount - bits/pixel - 2 bytes
 usart2_send(0);
// ------------------------------------------ No important but must by - 24 bytes
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 usart2_send(0);
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 usart2_send(0);
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 usart2_send(0);
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 usart2_send(0);
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);

 usart2_send(0);
 usart2_send(0);
 usart2_send(0);
 usart2_send(0);


Nex I convert my data from RGB565 to RGB888


Code: [Select]
for (word=0; word<words; word++) // word select
 {
 uint16_t pixel = obraz[word] & 0xFFFF;

 uint8_t red5=((pixel >> 11) & 0x1F);
 uint8_t green5 = ((pixel >> 5) & 0x3F);
 uint8_t blue5=(pixel & 0x1F);

 uint8_t red8 =(red5 << 3) | (red5 >> 2);
 uint8_t green8 =  (green5 << 2) | (green5 >> 4);
 uint8_t blue8 = (blue5 << 3) | (blue5 >> 2);

 usart2_send(blue8);
 usart2_send(green8);
 usart2_send(red8);

 pixel = (obraz[word] & 0xFFFF0000)>>16;

 red5=((pixel >> 11) & 0x1F);
 green5 = ((pixel >> 5) & 0x3F);
 blue5=(pixel & 0x1F);

 red8 =(red5 << 3) | (red5 >> 2);
 green8 =  (green5 << 2) | (green5 >> 4);
 blue8 = (blue5 << 3) | (blue5 >> 2);

 usart2_send(blue8);
 usart2_send(green8);
 usart2_send(red8);
 }



I configure My camera by seting register like this:

Code: [Select]
cam_send(0x12, 0x80); // reset all CAM


 cam_send(0x0C, 0x08); // enable scaling
 cam_send(0x12, tmp|(1<<3)|(1<<1)|(1<<2)); // GRB,  QCIF, color bar
// cam_send(0x12, tmp|(1<<3)|(1<<2)); // GRB,  QCIF, WITOUT color bar
 cam_send(0x40, 0xD0); //RGB565
// cam_send(0x70,0x3a);
// cam_send(0x71,0xB5);
 cam_send(0xB0, 0x84); // must be to propertly color display
// cam_send(0xB0, 0x8c); // must be to propertly color display
// cam_send(0x42, (1<<3)); //DSP color bar
 cam_send(0x10, (1<<5)); // PCLK does not toggle during horizontal blank



When I enable DSP color bar COM17[3] then I get image:

https://zapodaj.net/e4c94db077e7a.bmp.html

For my, this lock good, but I'm not sure that colors can overlap. (?)

When disable  DSP color bar and enable color bar in register COM7[1]
then I get this:

https://zapodaj.net/997a6be935877.bmp.html

In this case, I see, that from right bars begins from narrow black bar. But in
my opinion this bar will be wide. All colors are shifted right and rounded.

This makes, that my pictures  do not look good. Colors are mishmash.

Where is problem?

Joe Engineer

I have spent 2 days reading the entire 59 page thread...  But I have not seen a clear way to use the internal registers to resize the readout frame.

I have successfully gotten the OV7670 (w/o FIFO) to interface with the UNO, and can receive the data on a PC using the 'instructables' instructions:

https://www.instructables.com/id/OV7670-Without-FIFO-Very-Simple-Framecapture-With-/

I modified my circuit to use the bi-directional level shifters that I found on ebay.  (I can't imagine that connecting 5V and 3.3V electronics is a good idea without buffering.)  This method works, at least at low pixel rates.

I am even able to rewrite some registers and change the frame size, and intensity.

My goal is create a very low cost camera system.  I figure that for the cost of the UNO, the OV7670 (no FIFO), and the level shifters, the price cannot be beat.  Also, the UNO board provides regulated 3.3V required for the camera.  If someone has other suggestions for cheaper hardware, please comment back.

Now, I would like to readout the frame as 40x30 pixels so that I can actually process a frame of pixels (8-bit intensity, and probably not color) on the UNO.  I have seen only 1 reference as to someone getting the 80x60 pixel frame to work (I have not tried it yet). 

I realize that I could read in the larger frame, and then aggregate pixels myself, but if the camera can do it natively, why bother.

So if someone has gotten the 40x30 pixel readout to work, please post your register settings.


jlsilicon

#879
Mar 16, 2018, 01:51 am Last Edit: Mar 16, 2018, 02:04 am by jlsilicon
If you want a smaller resolution size, you might try looking into the Gameboy Cameras.
The GB Cam has aresolution og 100x100 in B&W.
They are easier to use, and can still be found on ebay.




tazzo

Hello all,

I have found definitive solution to bad colors!

but before WHY there are bad colors:

Quantum Efficiency of OV7670 is not the same for every channel (R,G,B) so sensor is more sensible to specific wavelenghts. Sensor also use OmniPixel technology that is not BSI (Back-Side Illuminated) and so optical crosstalk is more marked. These are the principal problems that cause bad colors so they should be corrected by software, no perfect color is possible without correction.

now HOW to correct colors:

Industry standard technique is using a Color Correction Matrix that compensate color of every pixel, now to calculate this matrix a Color Checker is needed. Color Checker is a reference card with known colors that is used as reference for given illumination conditions. Standard illuminant is D50.

but in practice?

The faster way is buy a Color Checker like X-Rite, Grey White Balance Colour Card, etc.. and buy an app like Color Corrector (https://www.microsoft.com/store/productId/9PNVVHVWZB85) to automatically calculate the color correction matrix (CCM) for some different illumination conditions, then just implement the CCM in firmware or in pc software to display image with better colors, correction is just multiplication/ addition as described by the matrix. In firmware is better to disable automatic (digital) color gain and white balance that is also done with CCM.

Please stick these info in the first post if you find it useful

Thank you

jlsilicon

#881
Apr 02, 2018, 03:38 pm Last Edit: Apr 25, 2018, 10:26 pm by jlsilicon
This is a Paid for Windows Tool. 
How do you insert it into the Sketch ?

> "Please stick these info in the first post if you find it useful"

tazzo

Hello JSilicon,

Yes, app and color checker board are paid tools (software and "hardware") but are also accessible to hobbyist and allow to get much better colors. Also OV7670 CMOS sensor cost money, nothing is free.

CCM matrix is in this form:
[ Rr Rg Rb ]
[ Gr Gg Gb ]
[ Br Bg Bb ]

Once that you have calculated the right CCM you can simply correct images pixels (in RGB format) with
R=R*Rr+G*Rg+B*Rb
G=R*Gr+G*Gg+B*Gb
B=R*Br+G*Bg+B*Bb

example: if you have this CCM

[ 2 1 1 ]
[ 0 1 0 ]
[ 0 1 1 ]

then you have for each pixel
R=R*2 + G*1 + B*1
G=G
B=G*1 + B*1

PS: App is free now, get it before promotion end

Lord_Gaara

#883
Oct 24, 2018, 01:09 am Last Edit: Oct 24, 2018, 03:43 pm by Lord_Gaara
This little code is the result of a great deal of research...
Bear in mind....the 7 pixels to the left and the right may look like good pixels...they are not. The true purpose of these pixles should be obvious when downsampled and averaged at QQQVGA using the averaging avg feature. these pixles protect the pixle data when zoomed in at 8x. But if you do not use those features then you can use those pixles. THEY ARE NOT FOR ADJUSTING...off center will distort the edge colors when at max zoom.
Some of the variables are global control variables in my program. Sorry I will not reveal more of the code..or edit it further....or reveal my purpose.

This function is used to zoom when using 160x120 QQVGA
if: screenZoomCenterX = 320, screenZoomCenterY = 240
then: then screen will be centered when zooming,

Code: [Select]
void ov7670QQVGAzoom(byte level)
{
  int32_t hstart, hstop;
  byte hEdgeOffset = 3;
  int32_t ystart, ystop;
  switch ( level )
  {
    case 0b00:
      { //max depth
        WriteOV7670(0x0C, 0x00);//COM3 - Enable Scaling
        WriteOV7670(0x3E, 0b00011000 | level); //COM14
        hstart = 240;
        hstop = 400;
        hEdgeOffset = 3;
        ystart = 180;
        ystop = 300;
        WriteOV7670(0x72, level | (level << 4));
        WriteOV7670(0x73, 0b11110000 | level);
        WriteOV7670(0xA2, 0x02);//scaling Pixel Clock Delay
        WriteOV7670(0x11, 0b0);
        break;
      }
    case 0b01:
      {
        WriteOV7670(0x0C, 0x04);//COM3 - Enable Scaling
        WriteOV7670(0x3E, 0b00011000 | level); //COM14
        hstart = 160;
        hstop = 480;
        hEdgeOffset = 2;
        ystart = 120;
        ystop = 360;
        WriteOV7670(0x72, level | (level << 4));
        WriteOV7670(0x73, 0b11110000 | level);
        WriteOV7670(0xA2, 0x02);//scaling Pixel Clock Delay
        WriteOV7670(0x11, 0b0);
        break;
      }
    case 0b10:
      { //normal view
        WriteOV7670(0x0C, 0b00000100);//COM3 - Enable Scaling
        WriteOV7670(0x3E, 0b00011000 | level); //COM14
        hstart = 0;
        hstop = 640;
        hEdgeOffset = 2;
        ystart = 0;
        ystop = 480;
        WriteOV7670(0x72, level | (level << 4));
        WriteOV7670(0x73, 0b11110000 | level);
        WriteOV7670(0xA2, 0x02);//scaling Pixel Clock Delay
        WriteOV7670(0x11, 0b0);
        break;
      }
  }
  hstart += (screenZoomCenterX-320);
  hstop += (screenZoomCenterX-320);
  ystart += (screenZoomCenterY-240);
  ystop += (screenZoomCenterY-240);
  if(hstart < 0)
  {
    hstop += (0-hstart);
    hstart = 0;
  }
  if(hstop > 640)
  {
    hstart -= (hstop-640);
    hstop = 640;
  }
  if(ystart < 0)
  {
    ystop += (0-ystart);
    ystart = 0;
  }
  if(ystop > 480)
  {
    ystart -= (ystop-480);
    ystop = 480;
  }
  hstart += (imageColors==0?174:188);
  if(hstart >= 784){hstart-=784;}
  hstop += (imageColors==0?174:188);
  if(hstop >= 784){hstop-=784;}
  ystart += 10;
  ystop += 10;
  Serial.print( hstart );
  Serial.print( ", " );
  Serial.print( hstop );
  Serial.print( ", " );
  Serial.print( ystart );
  Serial.print( ", " );
  Serial.println( ystop );
  WriteOV7670(0x17, hstart >> 3); //hstop
  WriteOV7670(0x18, hstop >> 3); //HSTOP
  WriteOV7670(0x32, (hEdgeOffset << 6) | ((0b111 & hstop) << 3) | (0b111 & hstop)); //HREF
  WriteOV7670(0x19, ystart >> 2); //VSTART
  WriteOV7670(0x1a, ystop >> 2); //VSTOP
  WriteOV7670(0x03, (0b00001111 & (((0b11 & ystop) << 2) | (0b11 & ystart)))); //VREF
}

Mahdiyar

Dear Mr_arduino

Thank you for sharing this informative project.

Would you please explain how to run this code in windows environment and also how do you get the photos?
Do I have to run another code for receiving the photo on PC? How do you define the destination directory to save the photo?


Many thanks

Go Up