JPG Colourspace Problem in Ardiuno


Hi there,

As seen above, I am having a very large and persistent problem with generating jpg images from the nicla vision raw rgb565 bytes.

The first (normal) image is one taken in OpenMV, and the second one from Arduino. I have tried the CaptureRawBytes in ardiuno with writing to serial monitor and this works completely normally. The making of a .jpg file I think is causing the problems.

I have tested my rgb565->rbg888->jpeg encoding pipeline and it works normally when given locally generated pure rgb565 raw images, but when I try to then do this with the camera frame buffer in Arduino, I get psychedelic looking images.

Thanks in advance for any help!

Below is the relevant code;

// ENCODING FUNCTION (WORKS WITH GENERATED IMAGES)
JPEGENC jpeg;
JPEGENCODE jpe;
uint16_t u16Temp[320 * 16]; // hold 16 lines for capturing the MCUs (16x16)

int encodeJPEGFromRGB565(const uint8_t* rgb565Data, int width, int height, File& outFile) 
{
    int rc;
    int iOutputSize = 65536; //all possible colours

    uint8_t* pOutput = (uint8_t*)malloc(iOutputSize);
    if (!pOutput) return -1;

    rc = jpeg.open(pOutput, iOutputSize);
    rc = jpeg.encodeBegin(&jpe, width, height, JPEGE_PIXEL_RGB565, JPEGE_SUBSAMPLE_420, JPEGE_Q_MED);
    if (rc != JPEGE_SUCCESS) {
        free(pOutput);
        return -1;
    }

    const uint16_t* src = (const uint16_t*)rgb565Data;
    for (int y = 0; y < height; y += 16) {
        for (int k = 0; k < 16 && (y + k) < height; k++) {
            memcpy(&u16Temp[k * width], &src[(y + k) * width], width * sizeof(uint16_t));
        }
        for (int x = 0; x < width; x += 16) {
            rc = jpeg.addMCU(&jpe, (uint8_t*)&u16Temp[x], width * sizeof(uint16_t));
            if (rc != JPEGE_SUCCESS) {
                free(pOutput);
                return -1;
            }
        }
    }

    int jpegSize = jpeg.close();
    outFile.write(pOutput, jpegSize);
    free(pOutput);
    return jpegSize;

}
// FAKE IMAGE GENERATOR (SURVIVES JPG ENCODING)
uint8_t* generateImage() {
  int width = 320;
  int height = 240;
  //uint16_t redPixel = 0xF800; //red
  //uint16_t pixel = 0x07E0; //green
  uint16_t pixel = 0x001F; //blue

  uint16_t* image = (uint16_t*)malloc(width * height * sizeof(uint16_t));

  for (int i = 0; i < width * height; ++i) {
    image[i] = pixel;
  }

  return (uint8_t*)image;
}
// STANDARD OTHER CAMERA SETUP
GC2145 galaxyCore;
Camera cam(galaxyCore);
#define IMAGE_MODE CAMERA_RGB565

FrameBuffer fb;

...

bool initCamera() {
  Serial.println("Initialising cam...");
  return cam.begin(CAMERA_R320x240, IMAGE_MODE, 30); //quality, colour/graycale, fps
}

...

void get_jpeg(const char* filepath) {

  Serial.println("Taking pic");
  if (cam.grabFrame(fb, 3000) == 0) {
    Serial.println("Image captured successfully.");

    if (SD.exists("image.jpg")) {
      Serial.println("File exists, deleting first...");
      SD.remove(filepath);
    } else {
      Serial.println("File does not exist, creating new file...");
    }

    File imFile = SD.open(filepath, FILE_WRITE);
    
    if (imFile) {

      uint8_t* frameData = fb.getBuffer();

      // convert rbg565 to jpeg using func
      int jpegSize = encodeJPEGFromRGB565(frameData, 320, 240, imFile);

      // test of pizels in jpeg
      uint16_t* data = (uint16_t*)fb.getBuffer();
      for (int i = 0; i < 8; ++i) {
        Serial.print("Pixel ");
        Serial.print(i);
        Serial.print(": 0x");
        if (data[i] < 0x1000) Serial.print("0"); // pad for 4 digits
        if (data[i] < 0x100) Serial.print("0");
        if (data[i] < 0x10) Serial.print("0");
        Serial.println(data[i], HEX);
      }



      imFile.flush();
      imFile.close();

      if (jpegSize > 0) {
        Serial.println("JPEG saved successfully (" + String(jpegSize) + " bytes)\n");
      } 
      else {
        Serial.println("JPEG encoding failed.");
      }
      Serial.print("Image saved as: ");
      Serial.println(filepath);
    } 
    else {
      Serial.println("Failed to save image.");
    }
  } 
  else {
    Serial.println("Failed to capture image.");
  }
  
}

See Rgb565 to rgb888 error in the Nicla Vision

Needed to convert to rgb888 first, and after some messing around it now works.