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.");
}
}

