Arduino icon indicating copy to clipboard operation
Arduino copied to clipboard

Slow Bitmap Speed

Open Davarco opened this issue 5 years ago • 10 comments

When reading a 320x240 bitmap image on an ESP32 (just reading, not writing it over Serial to my computer), it takes a long time, around 2 seconds. I’m guessing this is the limitations of sending the image over SPI from the ArduCAM to the ESP32. I was wondering there was a method to increase the speed of transmission. I might not need a 3-channel image, and a grayscale would work. The main goal is to do real time image processing (something quite simple) on board the ESP32. For reference, I am using a mini module camera shield with OV2640 camera module.

Davarco avatar Dec 06 '19 17:12 Davarco

I'm starting image processing myself with an ArduCAM and ESP8266. Are you sending the image over wifi to your ESP32? I haven't tried that yet, but can offer some thoughts on transmission speed:

  1. Agreed that the SPI transfer is the speed limitation. Yesterday, I posted code modifications to reduce BMP images from 320x240 to 160x120. This will make your transfer four times faster if the reduced image size is acceptable for your application.

  2. Sending the image in grayscale rather than color won't be any faster. I've used the ArduCAM_Host_V2 to capture and write BMP files as Normal (color) and B&W (grayscale), and the image and file sizes are the same. For the OV2640, two bytes (16-bits) per pixel are used to store red, green, and blue channels (each with values from 0 to 31) whether the image is color or grayscale. For grayscale, all three channels would have the same value (grey = [red+green+blue]/3).

  3. Does your application need to constantly send images in real time? Image processing takes place after capture using the image bytes stored in the buffer array. I would think your processing would look for a trigger (e.g. large changes in pixel values between images to indicate motion, or 'blobs' of similar pixel colors to detect objects) before sending an image. I suppose it would be useful during coding to send images to help us see what the computer 'sees' as pixel values. Again, I don't know your application, so this may not be helpful.

BillyBee123 avatar Jan 20 '20 20:01 BillyBee123

Hi BillyBee123, Thanks for adding the code modifications. Based on what you said for part 3, is it possible to do some processing on the ArduCAM itself?

Davarco avatar Jan 23 '20 00:01 Davarco

It doesn't look like much image processing could be done by the ArduCAM itself, beyond setting light and color filters (including grayscale) by registry settings.

BillyBee123 avatar Jan 28 '20 00:01 BillyBee123

Awesome, thanks. Do you know where you posted your modifications to 160x120? I can't find them online.

Davarco avatar Jan 28 '20 18:01 Davarco

https://github.com/ArduCAM/Arduino/issues/456

I've since made the modification for an 80x60 (QQQVGA) BMP. This uses a quarter of the RAM to store the image buffer as compared to 160x120, and has an acceptable resolution for image processing. I can post this as well if you're interested.

BillyBee123 avatar Jan 29 '20 17:01 BillyBee123

@BillyBee123 Yes, we are also interested in image processing or ML on low performance MCU like EPS8266 or ESP32. Do you have any examples or guidelines for this?

ArduCAM avatar Jan 30 '20 14:01 ArduCAM

Hi @BillyBee123. Could you post the one for 80x60 too? Thanks so much!

Davarco avatar Feb 14 '20 19:02 Davarco

Sure. These are the steps to change the BMP resolution from QVGA (320x240) to QQQVGA (80x60):

  1. In the ov2640_regs.h library registry file, make a copy of the struct sensor_reg OV2640_QVGA[] definition, and rename it OV2640_QQQVGA. In the copy, change the hex pairs from {0x5a, 0x50} to {0x5a, 0x14}, and {0x5b, 0x3c} to {0x5b, 0x0f}.

  2. In the ArduCAM.cpp library file, replace wrSensorRegs8_8(OV2640_QVGA) with wrSensorRegs8_8(OV2640_QQQVGA).

  3. In the sketch, replace the bitmap header definition with: const char bmp_header[BMPIMAGEOFFSET] PROGMEM = { 0x42, 0x4D, 0xB6, 0x25, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x42, 0x00, 0x00, 0x00, 0x28, 0x00, 0x00, 0x00, 0x50, 0x00, 0x00, 0x00, 0x3C, 0x00, 0x00, 0x00, 0x01, 0x00, 0x10, 0x00, 0x03, 0x00, 0x00, 0x00, 0x80, 0x25, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0xC4, 0x0E, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xF8, 0x00, 0x00, 0xE0, 0x07, 0x00, 0x00, 0x1F, 0x00, 0x00, 0x00 }; This header is specific to a 80x60 image height, width, image size, and file size.

  4. Again in the sketch, find the nested loop where the image is transferred to serial, and reduce the i and j loop size from 240 and 320, to 60 and 80, respectively.

BillyBee123 avatar Feb 17 '20 01:02 BillyBee123

Lee -- I'm actively developing machine vision for ArduCAM to recognize solid colored quadrilaterals, similar to PixCam. To date, I've done the following:

  1. Read and change pixel red, blue, and green values in the image buffer using masking and bit-shifting bitwise operations.

  2. Average the red, green, and blue values (0 to 31) to manually produce a grayscale image.

  3. Store the grayscale image shades in a matrix, then populate a histogram from 0 to 31 with a count of each shade.

  4. Separate foreground shades from background shades by a threshold analysis on the histogram to minimize the sum of the light and dark shade variance (Otsu's Method).

  5. Implement a row-by-row connected components algorithm to numerically label foreground pixels, then resolve a list of equivalent labels from adjoining labels using a quick union algorithm.

At this point, shapes of a common color can be separated from background pixels. Still to do is assigning each shape to an object class, then defining the axis-aligned or oriented bounding box, and calculating the RGB color of the shape. So far, I've been able to do all this from YouTube tutorials.

BillyBee123 avatar Feb 17 '20 03:02 BillyBee123

@BillyBee123 Hi, Can you help me with changing the resolution to 32x32 bmp image?

Bashima avatar Mar 06 '20 10:03 Bashima