grayscale and binary cannot send uxga resolution images. Error Capture Failed UXGA
// Function to clamp values between 0 and 255
inline uint8_t clamp(int value) {
if (value < 0) return 0;
if (value > 255) return 255;
return (uint8_t)value;
}
// Function to convert YUV422 to RGB for high resolution (UXGA)
void yuv422_to_rgb(uint8_t* yuv_image, uint8_t* rgb_image, int width, int height) {
int num_pixels = width * height;
for (int i = 0; i < num_pixels; i += 2) {
int base = i * 2;
uint8_t y0 = yuv_image[base];
uint8_t u = yuv_image[base + 1];
uint8_t y1 = yuv_image[base + 2];
uint8_t v = yuv_image[base + 3];
// Konversi piksel pertama
int c = y0 - 16;
int d = u - 128;
int e = v - 128;
int r = (298 * c + 409 * e + 128) >> 8;
int g = (298 * c - 100 * d - 208 * e + 128) >> 8;
int b = (298 * c + 516 * d + 128) >> 8;
rgb_image[i * 3] = clamp(r);
rgb_image[i * 3 + 1] = clamp(g);
rgb_image[i * 3 + 2] = clamp(b);
// Konversi piksel kedua
c = y1 - 16;
// d dan e tetap sama untuk kedua piksel
r = (298 * c + 409 * e + 128) >> 8;
g = (298 * c - 100 * d - 208 * e + 128) >> 8;
b = (298 * c + 516 * d + 128) >> 8;
rgb_image[(i + 1) * 3] = clamp(r);
rgb_image[(i + 1) * 3 + 1] = clamp(g);
rgb_image[(i + 1) * 3 + 2] = clamp(b);
}
}
// Function to convert RGB to Grayscale using integer arithmetic
void convertToGrayscale(uint8_t* rgb_image, uint8_t* gray_image, int width, int height) {
int num_pixels = width * height;
for (int i = 0; i < num_pixels; i++) {
uint8_t r = rgb_image[i * 3];
uint8_t g = rgb_image[i * 3 + 1];
uint8_t b = rgb_image[i * 3 + 2];
// Using integer approximation: 0.299*R + 0.587*G + 0.114*B ≈ (77*R + 150*G + 29*B) >> 8
gray_image[i] = (77 * r + 150 * g + 29 * b) >> 8;
}
}
// Function to perform PCA on grayscale image
int16_t* pca_grayscale(uint8_t* gray_image, int width, int height) {
int num_pixels = width * height;
int16_t* pca_result = new int16_t[num_pixels];
// Calculate mean
long sum = 0;
for (int i = 0; i < num_pixels; i++) {
sum += gray_image[i];
}
float mean = (float)sum / num_pixels;
// Center the data
for (int i = 0; i < num_pixels; i++) {
pca_result[i] = gray_image[i] - (int16_t)mean;
}
// Note: This is still a simplified PCA implementation.
// A full PCA would require computing eigenvectors, etc.
return pca_result;
}
// Function to perform Otsu's thresholding
uint8_t* otsu_threshold(uint8_t* gray_image, int width, int height) {
uint8_t* binary_image = new uint8_t[width * height];
// Calculate histogram
int histogram[256] = {0};
for (int i = 0; i < width * height; i++) {
histogram[gray_image[i]]++;
}
// Calculate total number of pixels
int total = width * height;
float sum = 0;
for (int i = 0; i < 256; i++) {
sum += i * histogram[i];
}
float sumB = 0;
int wB = 0;
int wF = 0;
float varMax = 0;
int threshold = 0;
for (int i = 0; i < 256; i++) {
wB += histogram[i];
if (wB == 0) continue;
wF = total - wB;
if (wF == 0) break;
sumB += i * histogram[i];
float mB = sumB / wB;
float mF = (sum - sumB) / wF;
float varBetween = wB * wF * (mB - mF) * (mB - mF);
if (varBetween > varMax) {
varMax = varBetween;
threshold = i;
}
}
// Apply threshold
for (int i = 0; i < width * height; i++) {
binary_image[i] = (gray_image[i] > threshold) ? 255 : 0;
}
return binary_image;
}
bool jpeg_to_bmp(uint8_t *jpeg_buffer, size_t jpeg_len, int width, int height, pixformat_t format, uint8_t **bmp_buffer, size_t *bmp_len) {
// Alokasikan buffer untuk gambar RGB
uint8_t *rgb_image = (uint8_t *)malloc(width * height * 3); // 3 bytes per pixel for RGB
if (!rgb_image) {
Serial.println("Failed to allocate memory for RGB image");
return false;
}
// Dekompresi JPEG menjadi RGB tanpa argumen width dan height, hanya kirimkan buffer hasil
if (!fmt2rgb888(jpeg_buffer, jpeg_len, format, rgb_image)) {
Serial.println("JPEG decompression failed");
free(rgb_image); // Jangan lupa untuk membersihkan memori yang dialokasikan
return false;
}
// BMP file header (14 bytes)
uint8_t bmp_file_header[14] = {
'B', 'M', // Signature
0, 0, 0, 0, // File size (akan diisi nanti)
0, 0, 0, 0, // Reserved
54, 0, 0, 0 // Offset ke data gambar
};
// BMP info header (40 bytes)
uint8_t bmp_info_header[40] = {
40, 0, 0, 0, // Header size
0, 0, 0, 0, // Lebar gambar (akan diisi nanti)
0, 0, 0, 0, // Tinggi gambar (akan diisi nanti)
1, 0, // Jumlah planes warna
24, 0, // Bits per pixel (24 untuk RGB)
0, 0, 0, 0, // Tidak ada kompresi
0, 0, 0, 0, // Ukuran gambar (bisa 0 untuk tanpa kompresi)
0x13, 0x0B, 0, 0, // Resolusi horizontal (2835 pixels/meter)
0x13, 0x0B, 0, 0, // Resolusi vertikal (2835 pixels/meter)
0, 0, 0, 0, // Jumlah warna dalam palet (0 = default)
0, 0, 0, 0 // Warna penting (0 = semua)
};
// Isi width dan height ke header BMP
bmp_info_header[4] = (uint8_t)(width);
bmp_info_header[5] = (uint8_t)(width >> 8);
bmp_info_header[6] = (uint8_t)(width >> 16);
bmp_info_header[7] = (uint8_t)(width >> 24);
bmp_info_header[8] = (uint8_t)(height);
bmp_info_header[9] = (uint8_t)(height >> 8);
bmp_info_header[10] = (uint8_t)(height >> 16);
bmp_info_header[11] = (uint8_t)(height >> 24);
// Ukuran total file BMP (header + data gambar)
int row_size = (width * 3 + 3) & (~3); // Lebar baris harus kelipatan 4 byte
int image_size = row_size * height;
int file_size = 54 + image_size;
// Isi ukuran file di file header BMP
bmp_file_header[2] = (uint8_t)(file_size);
bmp_file_header[3] = (uint8_t)(file_size >> 8);
bmp_file_header[4] = (uint8_t)(file_size >> 16);
bmp_file_header[5] = (uint8_t)(file_size >> 24);
// Alokasikan buffer BMP
*bmp_len = file_size;
*bmp_buffer = (uint8_t *)malloc(file_size);
if (*bmp_buffer == NULL) {
Serial.println("Failed to allocate memory for BMP");
free(rgb_image);
return false;
}
// Salin header file BMP ke buffer
memcpy(*bmp_buffer, bmp_file_header, 14);
memcpy(*bmp_buffer + 14, bmp_info_header, 40);
// Salin data gambar dengan membalikkan secara vertikal (karena format BMP)
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
int src_index = ((height - 1 - y) * width + x) * 3; // Urutan terbalik
int dst_index = 54 + y * row_size + x * 3;
(*bmp_buffer)[dst_index] = rgb_image[src_index + 2]; // Biru
(*bmp_buffer)[dst_index + 1] = rgb_image[src_index + 1]; // Hijau
(*bmp_buffer)[dst_index + 2] = rgb_image[src_index]; // Merah
}
}
// Bersihkan memori sementara
free(rgb_image);
return true;
}
esp_err_t pca_grayscale_handler() {
// Inisialisasi kamera ke mode YUV422
init_camera(FRAMESIZE_SVGA, PIXFORMAT_YUV422);
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return ESP_FAIL;
}
int width = fb->width;
int height = fb->height;
int num_pixels = width * height;
// Alokasikan memori untuk gambar RGB dan grayscale
uint8_t* rgb_image = new uint8_t[num_pixels * 3];
uint8_t* gray_image = new uint8_t[num_pixels];
// Konversi YUV422 ke RGB
yuv422_to_rgb(fb->buf, rgb_image, width, height);
// Konversi RGB ke Grayscale
convertToGrayscale(rgb_image, gray_image, width, height);
// Lakukan PCA pada gambar grayscale
int16_t* pca_result = pca_grayscale(gray_image, width, height);
// Cari nilai min dan max dari hasil PCA untuk normalisasi
int16_t min_val = pca_result[0];
int16_t max_val = pca_result[0];
for (int i = 1; i < num_pixels; i++) {
if (pca_result[i] < min_val) min_val = pca_result[i];
if (pca_result[i] > max_val) max_val = pca_result[i];
}
// Normalisasi nilai PCA ke rentang 0-255
uint8_t* normalized_pca = new uint8_t[num_pixels];
float range = max_val - min_val;
if (range == 0) range = 1; // Hindari pembagian dengan nol
for (int i = 0; i < num_pixels; i++) {
normalized_pca[i] = (uint8_t)(((pca_result[i] - min_val) / range) * 255);
}
// Konversi buffer JPEG ke BMP
uint8_t *bmp_buffer = NULL;
size_t bmp_len = 0;
bool bmp_success = jpeg_to_bmp(normalized_pca, num_pixels, width, height, PIXFORMAT_GRAYSCALE, &bmp_buffer, &bmp_len);
if (!bmp_success) {
server.send(500, "text/plain", "BMP conversion failed");
} else {
// Set header konten BMP
server.sendHeader("Content-Type", "image/bmp");
server.sendHeader("Content-Disposition", "inline; filename=pca_grayscale.bmp");
// Kirim gambar BMP
server.sendContent((const char *)bmp_buffer, bmp_len);
}
// Bersihkan memori yang dialokasikan
delete[] rgb_image;
delete[] gray_image;
delete[] pca_result;
delete[] normalized_pca;
free(bmp_buffer);
esp_camera_fb_return(fb);
// Kembalikan kamera ke mode JPEG untuk streaming
init_camera(FRAME_SIZE_STREAM, PIXFORMAT_JPEG);
return ESP_OK;
}
// Handler for binary image
esp_err_t binary_handler() {
// Inisialisasi kamera ke mode YUV422
init_camera(FRAMESIZE_SVGA, PIXFORMAT_YUV422);
camera_fb_t *fb = esp_camera_fb_get();
if (!fb) {
Serial.println("Camera capture failed");
return ESP_FAIL;
}
int width = fb->width;
int height = fb->height;
int num_pixels = width * height;
uint8_t* rgb_image = new uint8_t[num_pixels * 3];
uint8_t* gray_image = new uint8_t[num_pixels];
yuv422_to_rgb(fb->buf, rgb_image, width, height);
convertToGrayscale(rgb_image, gray_image, width, height);
uint8_t* binary_image = otsu_threshold(gray_image, width, height);
// Konversi buffer JPEG ke BMP
uint8_t *bmp_buffer = NULL;
size_t bmp_len = 0;
bool bmp_success = jpeg_to_bmp(binary_image, num_pixels, width, height, PIXFORMAT_GRAYSCALE, &bmp_buffer, &bmp_len);
if (!bmp_success) {
server.send(500, "text/plain", "BMP conversion failed");
} else {
// Set header konten BMP
server.sendHeader("Content-Type", "image/bmp");
server.sendHeader("Content-Disposition", "inline; filename=pca_grayscale.bmp");
// Kirim gambar BMP
server.sendContent((const char *)bmp_buffer, bmp_len);
}
// Free allocated memory
delete[] rgb_image;
delete[] gray_image;
delete[] binary_image;
free(bmp_buffer);
esp_camera_fb_return(fb);
// Revert camera back to JPEG for streaming
init_camera(FRAME_SIZE_STREAM, PIXFORMAT_JPEG);
return ESP_OK;
}