Loading Image From SD Card

tahseenabbas52
Posts: 11
Joined: Thu Oct 24, 2024 6:24 am

Loading Image From SD Card

Postby tahseenabbas52 » Thu Oct 24, 2024 8:19 am

I have tested the SD Card Example(SDMMC) on my ESP32 Cam and it is working fine. My objective is to load the jpg image from the SD Card and count the white pixels in it. I have written a script to load the image but when i count the number of white pixels it print out different number each time on the same image. Can any one help me debug this issue ?
Here is the script i am using in C language.

Code: Select all

#include <stdio.h>
#include <stdlib.h>
#include "esp_log.h"
#include "esp_err.h"
#include "sdmmc_cmd.h"
#include "esp_vfs_fat.h"
#include "driver/sdmmc_host.h"

#define TAG "football_detection"
#define WHITE_PIXEL_THRESHOLD 5600
#define MOUNT_POINT "/sdcard"

// Function to detect football in the zoomed-in ROI
bool detect_football_in_zoomed_roi(uint8_t* frame, int width, int height, int white_pixel_threshold) {
    int white_pixel_count = 0;

    // Calculate zoomed-in ROI (30% from each side)
    int x_crop = width * 0.30;
    int y_crop = height * 0.30;
    int roi_width = width - 2 * x_crop;
    int roi_height = height - 2 * y_crop;

    // Debugging info: Print image size and calculated ROI
    ESP_LOGI(TAG, "Image width: %d, height: %d", width, height);
    ESP_LOGI(TAG, "Zoomed-in ROI: x=%d, y=%d, width=%d, height=%d", x_crop, y_crop, roi_width, roi_height);

    // Traverse through the ROI and count white pixels
    for (int y = y_crop; y < y_crop + roi_height; ++y) {
        for (int x = x_crop; x < x_crop + roi_width; ++x) {
            int i = (y * width + x) * 3;  // Position in the RGB frame buffer (3 bytes per pixel)

            uint8_t r = frame[i];         // Red component
            uint8_t g = frame[i + 1];     // Green component
            uint8_t b = frame[i + 2];     // Blue component

            // Detect white pixels: Using a high RGB value as white (e.g., R > 200, G > 200, B > 200)
            if (r > 200 && g > 200 && b > 200) {
                white_pixel_count++;
            }
        }
    }

    // Debugging info: Print the total number of white pixels detected
    ESP_LOGI(TAG, "Total white pixels detected: %d", white_pixel_count);

    // Return true if the number of white pixels exceeds the threshold
    return white_pixel_count > white_pixel_threshold;
}

// Function to read the image file and ensure it loads perfectly into memory
static esp_err_t load_image_file(const char *path, uint8_t **image_buffer, size_t *image_size) {
    FILE *file = fopen(path, "rb");
    if (!file) {
        ESP_LOGE(TAG, "Failed to open image file: %s", path);
        return ESP_FAIL;
    }

    // Get the file size
    fseek(file, 0, SEEK_END);
    *image_size = ftell(file);
    fseek(file, 0, SEEK_SET);

    ESP_LOGI(TAG, "Image file size: %zu bytes", *image_size);

    // Allocate memory for the image buffer
    *image_buffer = (uint8_t*)malloc(*image_size);
    if (!(*image_buffer)) {
        ESP_LOGE(TAG, "Failed to allocate memory for image buffer.");
        fclose(file);
        return ESP_ERR_NO_MEM;
    }

    // Read the image file into the buffer
    size_t bytes_read = fread(*image_buffer, 1, *image_size, file);
    fclose(file);

    // Ensure the file was read fully
    if (bytes_read != *image_size) {
        ESP_LOGE(TAG, "Failed to read the complete image file. Bytes read: %zu", bytes_read);
        free(*image_buffer);
        return ESP_FAIL;
    }

    ESP_LOGI(TAG, "Image loaded successfully.");
    return ESP_OK;
}

// Function to load image and detect football
static esp_err_t process_image(const char *image_path) {
    uint8_t *image_buffer = NULL;
    size_t image_size = 0;

    // Load the image from SD card
    esp_err_t err = load_image_file(image_path, &image_buffer, &image_size);
    if (err != ESP_OK) {
        ESP_LOGE(TAG, "Error loading image.");
        return err;
    }

    // Assuming 320x240 RGB image for simplicity (adjust based on your image)
    int width = 320;
    int height = 240;

    // Detect football in the zoomed-in ROI
    bool football_detected = detect_football_in_zoomed_roi(image_buffer, width, height, WHITE_PIXEL_THRESHOLD);

    if (football_detected) {
        ESP_LOGI(TAG, "football detected.");
    } else {
        ESP_LOGI(TAG, "No football detected.");
    }

    // Free the image buffer after use
    free(image_buffer);
    return ESP_OK;
}

void app_main(void) {
    esp_err_t ret;

    // SD card mount configuration (adapted from Code 1)
    esp_vfs_fat_sdmmc_mount_config_t mount_config = {
        #ifdef CONFIG_EXAMPLE_FORMAT_IF_MOUNT_FAILED
            .format_if_mount_failed = true, // Conditional formatting based on menuconfig
        #else
            .format_if_mount_failed = false, // Do not format if mounting fails
        #endif
            .max_files = 5, // Allow up to 5 files open simultaneously
            .allocation_unit_size = 16 * 1024 // Allocation unit size of 16 KB
    };

    // Configure SDMMC Host and Slot (adapted from Code 1)
    sdmmc_host_t host = SDMMC_HOST_DEFAULT();
    sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
    slot_config.width = 1;
    #ifdef CONFIG_EXAMPLE_SDMMC_BUS_WIDTH_4
        slot_config.width = 4; // Use 4-bit mode if configured
    #else
        slot_config.width = 1; // Default to 1-bit mode
    #endif
    slot_config.flags |= SDMMC_SLOT_FLAG_INTERNAL_PULLUP; // Enable internal pull-ups

    // Mount SD card filesystem
    sdmmc_card_t *card;
    ret = esp_vfs_fat_sdmmc_mount(MOUNT_POINT, &host, &slot_config, &mount_config, &card);
    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to mount SD card filesystem.");
        return;
    }

    ESP_LOGI(TAG, "SD card mounted.");

    // Process the image (change the path if needed)
    const char *image_path = MOUNT_POINT "/noDB1.jpg";
    ret = process_image(image_path);

    if (ret != ESP_OK) {
        ESP_LOGE(TAG, "Failed to process image.");
    }

    // Unmount SD card
    esp_vfs_fat_sdcard_unmount(MOUNT_POINT, card);
    ESP_LOGI(TAG, "SD card unmounted.");
}

MicroController
Posts: 1733
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Loading Image From SD Card

Postby MicroController » Thu Oct 24, 2024 10:43 pm

1) What file/image format does the file on the card contain? If it's not exactly the raw pixel format you're interpreting it as this won't work.
2) The number/range of "pixels" you're looking at is unrelated to the number of bytes actually read from the file. You may be counting random data beyond the end of the loaded data.

tahseenabbas52
Posts: 11
Joined: Thu Oct 24, 2024 6:24 am

Re: Loading Image From SD Card

Postby tahseenabbas52 » Fri Oct 25, 2024 5:28 am

MicroController wrote:
Thu Oct 24, 2024 10:43 pm
1) What file/image format does the file on the card contain? If it's not exactly the raw pixel format you're interpreting it as this won't work.
2) The number/range of "pixels" you're looking at is unrelated to the number of bytes actually read from the file. You may be counting random data beyond the end of the loaded data.
My actual purpose is to load jpg/png/jpeg image from sd card and preprocess it. I have followed different steps, one of them is in the code i provided which you have mentioned, secondly i tried the examples of OpenCV but didn't get any idea how to mount SD Card with those examples though i tried to merge both apps i.e. SDMMC and Hello-World-OpenCV but card was not being mounted.
I have some queries regarding this if you can help on this
1. How to correctly load that image in jpg/png/jpeg format from SD Card?
2. If Point#1 is not possible then what is the appropriate way to load that and process it as an image (AS in the code).
3. Can we use cv2's imread function to load image ? If YES! then how can i mount SD Card on it? I need an example code only.

I would appreciate if you can address these issues, as i have researched a lot but couldn't get any idea.

MicroController
Posts: 1733
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Loading Image From SD Card

Postby MicroController » Sat Oct 26, 2024 6:49 am


tahseenabbas52
Posts: 11
Joined: Thu Oct 24, 2024 6:24 am

Re: Loading Image From SD Card

Postby tahseenabbas52 » Mon Oct 28, 2024 6:59 am

Can you please also answer either we can use cv2's imread function to load image ? If YES! then how can i mount SD Card on it? I need an example code only.
I am not getting how to use this component in my use case ?

MicroController
Posts: 1733
Joined: Mon Oct 17, 2022 7:38 pm
Location: Europe, Germany

Re: Loading Image From SD Card

Postby MicroController » Mon Oct 28, 2024 8:28 am

No, I don't know OpenCV.

tahseenabbas52
Posts: 11
Joined: Thu Oct 24, 2024 6:24 am

Re: Loading Image From SD Card

Postby tahseenabbas52 » Tue Oct 29, 2024 5:49 am

MicroController wrote:
Mon Oct 28, 2024 8:28 am
No, I don't know OpenCV.
Any C++ example to mount SDCard ?

pacucha42
Posts: 33
Joined: Fri Mar 29, 2019 12:56 pm

Re: Loading Image From SD Card

Postby pacucha42 » Wed Oct 30, 2024 7:34 am

Hi @tahseenabbas52,
the issue is actually in your image buffer calculation which is totally wrong, sorry:

1. I assume your JPG is really 320x240 pixels - actual image file size is used only to check the fread() succeeded, the image processing is run with hard-wired width/height. As @microcontroller mentioned, the file size doesn't tell you the actual image size. The image details are available in corresponding file headers, see for instance https://github.com/corkami/formats/blob ... ge/jpeg.md. I recommend learning JPEG internals first, though it may seem "expensive"

2. after reading whole file to memory buffer, you process it using detect_football_in_zoomed_roi(). No matter what the real buffer size is, you try to iterate through expected pixel values:

Code: Select all

// Function to detect football in the zoomed-in ROI
bool detect_football_in_zoomed_roi(uint8_t* frame, int width, int height, int white_pixel_threshold) {
    int white_pixel_count = 0;

    // Calculate zoomed-in ROI (30% from each side)
    int x_crop = width * 0.30;
    int y_crop = height * 0.30;
    int roi_width = width - 2 * x_crop;
    int roi_height = height - 2 * y_crop;

    // Debugging info: Print image size and calculated ROI
    ESP_LOGI(TAG, "Image width: %d, height: %d", width, height);
    ESP_LOGI(TAG, "Zoomed-in ROI: x=%d, y=%d, width=%d, height=%d", x_crop, y_crop, roi_width, roi_height);

    // Traverse through the ROI and count white pixels
    for (int y = y_crop; y < y_crop + roi_height; ++y) {
        for (int x = x_crop; x < x_crop + roi_width; ++x) {
            int i = (y * width + x) * 3;  // Position in the RGB frame buffer (3 bytes per pixel)

            uint8_t r = frame[i];         // Red component
            uint8_t g = frame[i + 1];     // Green component
            uint8_t b = frame[i + 2];     // Blue component

            // Detect white pixels: Using a high RGB value as white (e.g., R > 200, G > 200, B > 200)
            if (r > 200 && g > 200 && b > 200) {
                white_pixel_count++;
            }
        }
    }

    // Debugging info: Print the total number of white pixels detected
    ESP_LOGI(TAG, "Total white pixels detected: %d", white_pixel_count);

    // Return true if the number of white pixels exceeds the threshold
    return white_pixel_count > white_pixel_threshold;
}
which very quickly runs out of you buffer range. The 'frame' buffer size is seemingly 76800 B, but your first 'frame' index 'i' points to offset 69408 [(72 * 320 + 96) * 3]. Thus, you run out of your buffer window in some 9th iteration of 'y' or slightly before [(80 * 320 + 96) * 3 = 77088]. After that point you are reading just random values from the memory, resulting in different output for each code run.

Other than that the code seems ok, your SD card mount/unmount part should work well and can be easily integrated into any C++ wrapper.

Note 1: reading RGB as you do works for 24-bit BMPs (in a way, not literally). I'm not sure there is any JPEG variant designed the same way.... but I'm not image processing specialist so I may be wrong.

Note 2: avoid uploading whole files into the memory, you can easily consume all available RAM like that. Use @microcontroller's link for ESP JPEG processing project to see the recommended approach

Hope this helps.

@microcontroller: thanks for your support!
Last edited by pacucha42 on Wed Oct 30, 2024 12:07 pm, edited 1 time in total.

tahseenabbas52
Posts: 11
Joined: Thu Oct 24, 2024 6:24 am

Re: Loading Image From SD Card

Postby tahseenabbas52 » Wed Oct 30, 2024 8:39 am

pacucha42 wrote:
Wed Oct 30, 2024 7:34 am
Hi @tahseenabbas52,
the issue is actually in your image buffer calculation which is totally wrong, sorry:

1. I assume your JPG is really 320x240 pixels - actual image file size is used only to check the fread() succeeded, the image processing is run with hard-wired width/height. As @microcontroller mentioned, the file size doesn't tell you the actual image size. The image details are available in corresponding file headers, see for instance https://github.com/corkami/formats/blob ... ge/jpeg.md. I recommend learning JPEG internals first, though it may seem "expensive"

2. after reading whole file to memory buffer, you process it using detect_football_in_zoomed_roi(). No matter what the real buffer size is, you try to iterate through expected pixel values:

Code: Select all

// Function to detect football in the zoomed-in ROI
bool detect_football_in_zoomed_roi(uint8_t* frame, int width, int height, int white_pixel_threshold) {
    int white_pixel_count = 0;

    // Calculate zoomed-in ROI (30% from each side)
    int x_crop = width * 0.30;
    int y_crop = height * 0.30;
    int roi_width = width - 2 * x_crop;
    int roi_height = height - 2 * y_crop;

    // Debugging info: Print image size and calculated ROI
    ESP_LOGI(TAG, "Image width: %d, height: %d", width, height);
    ESP_LOGI(TAG, "Zoomed-in ROI: x=%d, y=%d, width=%d, height=%d", x_crop, y_crop, roi_width, roi_height);

    // Traverse through the ROI and count white pixels
    for (int y = y_crop; y < y_crop + roi_height; ++y) {
        for (int x = x_crop; x < x_crop + roi_width; ++x) {
            int i = (y * width + x) * 3;  // Position in the RGB frame buffer (3 bytes per pixel)

            uint8_t r = frame[i];         // Red component
            uint8_t g = frame[i + 1];     // Green component
            uint8_t b = frame[i + 2];     // Blue component

            // Detect white pixels: Using a high RGB value as white (e.g., R > 200, G > 200, B > 200)
            if (r > 200 && g > 200 && b > 200) {
                white_pixel_count++;
            }
        }
    }

    // Debugging info: Print the total number of white pixels detected
    ESP_LOGI(TAG, "Total white pixels detected: %d", white_pixel_count);

    // Return true if the number of white pixels exceeds the threshold
    return white_pixel_count > white_pixel_threshold;
}
which very quickly runs out of you buffer range. The 'frame' buffer size is seemingly 76800 B, but your first 'frame' index 'i' points to offset 69408 [(72 * 320 + 96) * 3]. Thus, you run out of your buffer window in some 9th iteration of 'y' or slightly before [(80 * 320 + 96) * 3 = 77088]. After that point you are reading just random values from the memory, resulting in different output for each code run.

Other than that the code seems ok, your SD card mount/unmount part should work well and can be easily integrated into any C++ wrapper.

Note 1: reading RGB as you do works for 24-bit BMPs (in a way, not literally). I'm not sure there is any JPEG variant designed the same way.... but I'm not image processing specialist so I may be wrong.

Note 2: avoid uploading whole files into the memory, you can easily consume all available RAM like that. Use @microcontroller's link for ESP JTAG processing project to see the recommended approach

Hope this helps.

@microcontroller: thanks for your support!
Thanks for you response,
I'll surely look into it, and get back to you very soon

tahseenabbas52
Posts: 11
Joined: Thu Oct 24, 2024 6:24 am

Re: Loading Image From SD Card

Postby tahseenabbas52 » Wed Oct 30, 2024 1:42 pm

pacucha42 wrote:
Wed Oct 30, 2024 7:34 am
Hi @tahseenabbas52,
the issue is actually in your image buffer calculation which is totally wrong, sorry:

1. I assume your JPG is really 320x240 pixels - actual image file size is used only to check the fread() succeeded, the image processing is run with hard-wired width/height. As @microcontroller mentioned, the file size doesn't tell you the actual image size. The image details are available in corresponding file headers, see for instance https://github.com/corkami/formats/blob ... ge/jpeg.md. I recommend learning JPEG internals first, though it may seem "expensive"

2. after reading whole file to memory buffer, you process it using detect_football_in_zoomed_roi(). No matter what the real buffer size is, you try to iterate through expected pixel values:

Code: Select all

// Function to detect football in the zoomed-in ROI
bool detect_football_in_zoomed_roi(uint8_t* frame, int width, int height, int white_pixel_threshold) {
    int white_pixel_count = 0;

    // Calculate zoomed-in ROI (30% from each side)
    int x_crop = width * 0.30;
    int y_crop = height * 0.30;
    int roi_width = width - 2 * x_crop;
    int roi_height = height - 2 * y_crop;

    // Debugging info: Print image size and calculated ROI
    ESP_LOGI(TAG, "Image width: %d, height: %d", width, height);
    ESP_LOGI(TAG, "Zoomed-in ROI: x=%d, y=%d, width=%d, height=%d", x_crop, y_crop, roi_width, roi_height);

    // Traverse through the ROI and count white pixels
    for (int y = y_crop; y < y_crop + roi_height; ++y) {
        for (int x = x_crop; x < x_crop + roi_width; ++x) {
            int i = (y * width + x) * 3;  // Position in the RGB frame buffer (3 bytes per pixel)

            uint8_t r = frame[i];         // Red component
            uint8_t g = frame[i + 1];     // Green component
            uint8_t b = frame[i + 2];     // Blue component

            // Detect white pixels: Using a high RGB value as white (e.g., R > 200, G > 200, B > 200)
            if (r > 200 && g > 200 && b > 200) {
                white_pixel_count++;
            }
        }
    }

    // Debugging info: Print the total number of white pixels detected
    ESP_LOGI(TAG, "Total white pixels detected: %d", white_pixel_count);

    // Return true if the number of white pixels exceeds the threshold
    return white_pixel_count > white_pixel_threshold;
}
which very quickly runs out of you buffer range. The 'frame' buffer size is seemingly 76800 B, but your first 'frame' index 'i' points to offset 69408 [(72 * 320 + 96) * 3]. Thus, you run out of your buffer window in some 9th iteration of 'y' or slightly before [(80 * 320 + 96) * 3 = 77088]. After that point you are reading just random values from the memory, resulting in different output for each code run.

Other than that the code seems ok, your SD card mount/unmount part should work well and can be easily integrated into any C++ wrapper.

Note 1: reading RGB as you do works for 24-bit BMPs (in a way, not literally). I'm not sure there is any JPEG variant designed the same way.... but I'm not image processing specialist so I may be wrong.

Note 2: avoid uploading whole files into the memory, you can easily consume all available RAM like that. Use @microcontroller's link for ESP JPEG processing project to see the recommended approach

Hope this helps.

@microcontroller: thanks for your support!
I have looked into it and yes that's the main reason behind memory issues, but i am still clueless where to look for it as i am a beginner on it. I have tried following code to load SD Card but file system failed to mount. Can you look into it and lemme know what's the issue ?

Code: Select all

// SDCardManager.cpp
#include <iostream>
#include <string>
#include <fstream>
#include "esp_log.h"

// Wrap C includes in extern "C"
extern "C" {
    #include <string.h>
    #include "esp_vfs_fat.h"
    #include "sdmmc_cmd.h"
    #include "driver/sdmmc_host.h"
}

#define MOUNT_POINT "/sdcard"

class SDCardManager {
public:
    SDCardManager() : card(nullptr) {
        // Configuration for mounting the SD card
        mount_config.format_if_mount_failed = true;
        mount_config.max_files = 5;
        mount_config.allocation_unit_size = 16 * 1024;
    }

    bool mount() {
        ESP_LOGI(TAG, "Initializing SD card");

        sdmmc_host_t host = SDMMC_HOST_DEFAULT();
        sdmmc_slot_config_t slot_config = SDMMC_SLOT_CONFIG_DEFAULT();
        slot_config.width = 1; // or 4 if you want a 4-line bus

        // Mount the filesystem
        esp_err_t ret = esp_vfs_fat_sdmmc_mount(MOUNT_POINT, &host, &slot_config, &mount_config, &card);
        if (ret != ESP_OK) {
            ESP_LOGE(TAG, "Failed to mount filesystem");
            return false;
        }

        ESP_LOGI(TAG, "Filesystem mounted");
        return true;
    }

    void unmount() {
        if (card) {
            esp_vfs_fat_sdcard_unmount(MOUNT_POINT, card);
            ESP_LOGI(TAG, "Card unmounted");
            card = nullptr;
        }
    }

    bool writeFile(const std::string& path, const std::string& data) {
        std::ofstream file(MOUNT_POINT + path);
        if (!file.is_open()) {
            ESP_LOGE(TAG, "Failed to open file for writing");
            return false;
        }
        file << data;
        file.close();
        ESP_LOGI(TAG, "File written successfully");
        return true;
    }

    bool readFile(const std::string& path) {
        std::ifstream file(MOUNT_POINT + path);
        if (!file.is_open()) {
            ESP_LOGE(TAG, "Failed to open file for reading");
            return false;
        }
        std::string line;
        std::getline(file, line);
        ESP_LOGI(TAG, "Read from file: %s", line.c_str());
        return true;
    }

    ~SDCardManager() {
        unmount();
    }

private:
    sdmmc_card_t *card;
    esp_vfs_fat_sdmmc_mount_config_t mount_config;
    static constexpr const char* TAG = "SDCardManager";
};

extern "C" void app_main(void) {
    SDCardManager sdCard;
    if (sdCard.mount()) {
        sdCard.writeFile("/hello.txt", "Hello, ESP32!");
        sdCard.readFile("/hello.txt");
    }
}

Error

Code: Select all

I (385) SDCardManager: Initializing SD card
E (405) sdmmc_common: sdmmc_init_ocr: send_op_cond (1) returned 0x107
E (405) vfs_fat_sdmmc: sdmmc_card_init failed (0x107).
E (405) SDCardManager: Failed to mount filesystem
I (415) main_task: Returned from app_main()

Who is online

Users browsing this forum: No registered users and 57 guests