Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 46 additions & 17 deletions board/common.c
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
#include "core/error.h"
#include "core/image.h"

#include <limits.h>
#include <stdlib.h>
#include <string.h>

Expand All @@ -16,6 +17,11 @@ embeddip_status_t createImage(ImageResolution resolution, ImageFormat format, Im
return EMBEDDIP_ERROR_NULL_PTR;
}

if ((int)resolution < 0 || (int)resolution >= IMAGE_RES_COUNT ||
resolution == IMAGE_RES_INVALID || resolution == IMAGE_RES_CUSTOM) {
return EMBEDDIP_ERROR_INVALID_SIZE;
}

// Allocate Image structure
Image *image = (Image *)memory_alloc(sizeof(Image));
if (image == NULL) {
Expand All @@ -25,7 +31,7 @@ embeddip_status_t createImage(ImageResolution resolution, ImageFormat format, Im
// Determine the resolution (width and height) based on the provided size argument
image->width = RES_WIDTH_LOOKUP[resolution];
image->height = RES_HEIGHT_LOOKUP[resolution];
image->size = image->width * image->height;
image->size = (uint32_t)((size_t)image->width * (size_t)image->height);
image->log = IMAGE_DATA_PIXELS;
image->format = format;

Expand Down Expand Up @@ -92,10 +98,16 @@ embeddip_status_t createImageWH(int width, int height, ImageFormat format, Image
return EMBEDDIP_ERROR_OUT_OF_MEMORY;
}

size_t pixel_count = (size_t)width * (size_t)height;
if (pixel_count > UINT32_MAX) {
memory_free(image);
return EMBEDDIP_ERROR_INVALID_SIZE;
}

// Determine the resolution (width and height) based on the provided size argument
image->width = width;
image->height = height;
image->size = width * height;
image->size = (uint32_t)pixel_count;
image->format = format;
image->log = IMAGE_DATA_PIXELS;

Expand Down Expand Up @@ -160,7 +172,7 @@ void deleteImage(Image *image)

// Free channel buffers if present
if (image->is_chals && image->chals != NULL) {
for (uint8_t i = 0; i < 3; i++) {
for (uint8_t i = 0; i < 6; i++) {
if (image->chals->ch[i] != NULL) {
memory_free(image->chals->ch[i]);
image->chals->ch[i] = NULL;
Expand Down Expand Up @@ -188,37 +200,47 @@ embeddip_status_t createChals(Image *inImg, uint8_t numChals)
return EMBEDDIP_ERROR_NULL_PTR;
}

if (numChals == 0 || numChals > 3) {
if (numChals == 0 || numChals > 6) {
return EMBEDDIP_ERROR_INVALID_ARG;
}

// RGB-like data uses ch[1], ch[2], ch[3], so requesting 3 channels
// must allocate indices 0..3.
uint8_t channels_to_alloc = (numChals == 3) ? 4 : numChals;
bool created_chals_struct = false;
uint8_t allocated_in_call[6] = {0};

if (inImg->chals == NULL) {
inImg->chals = (channels_t *)memory_alloc(sizeof(channels_t));
if (inImg->chals == NULL) {
return EMBEDDIP_ERROR_OUT_OF_MEMORY;
}
memset(inImg->chals, 0, sizeof(channels_t));
created_chals_struct = true;
}

for (uint8_t i = 0; i < numChals; i++) {
for (uint8_t i = 0; i < channels_to_alloc; i++) {
if (inImg->chals->ch[i] == NULL) {
// NOTE: Removed "* 2U" multiplier - was allocating double the required memory
// Correct size: width × height × sizeof(float) for a single channel
// For WQVGA (480×272): 522,240 bytes per channel (not 1,044,480!)
size_t bufSize = (size_t)inImg->width * (size_t)inImg->height * sizeof(float);
inImg->chals->ch[i] = (float *)memory_alloc(bufSize);
if (inImg->chals->ch[i] == NULL) {
// Roll back allocations for already-created channels
for (uint8_t j = 0; j < i; j++) {
if (inImg->chals->ch[j] != NULL) {
// Roll back only channels allocated in this call.
for (uint8_t j = 0; j < channels_to_alloc; j++) {
if (allocated_in_call[j] && inImg->chals->ch[j] != NULL) {
memory_free(inImg->chals->ch[j]);
inImg->chals->ch[j] = NULL;
}
}
memory_free(inImg->chals);
inImg->chals = NULL;
if (created_chals_struct) {
memory_free(inImg->chals);
inImg->chals = NULL;
}
return EMBEDDIP_ERROR_OUT_OF_MEMORY;
}
allocated_in_call[i] = 1;
}
}

Expand All @@ -242,16 +264,20 @@ embeddip_status_t createChalsComplex(Image *inImg, uint8_t numChals)
return EMBEDDIP_ERROR_NULL_PTR;
}

if (numChals == 0 || numChals > 3) {
if (numChals == 0 || numChals > 6) {
return EMBEDDIP_ERROR_INVALID_ARG;
}

bool created_chals_struct = false;
uint8_t allocated_in_call[6] = {0};

if (inImg->chals == NULL) {
inImg->chals = (channels_t *)memory_alloc(sizeof(channels_t));
if (inImg->chals == NULL) {
return EMBEDDIP_ERROR_OUT_OF_MEMORY;
}
memset(inImg->chals, 0, sizeof(channels_t));
created_chals_struct = true;
}

for (uint8_t i = 0; i < numChals; i++) {
Expand All @@ -262,17 +288,20 @@ embeddip_status_t createChalsComplex(Image *inImg, uint8_t numChals)
size_t bufSize = (size_t)inImg->width * (size_t)inImg->height * 2U * sizeof(float);
inImg->chals->ch[i] = (float *)memory_alloc(bufSize);
if (inImg->chals->ch[i] == NULL) {
// Roll back allocations for already-created channels
for (uint8_t j = 0; j < i; j++) {
if (inImg->chals->ch[j] != NULL) {
// Roll back only channels allocated in this call.
for (uint8_t j = 0; j < numChals; j++) {
if (allocated_in_call[j] && inImg->chals->ch[j] != NULL) {
memory_free(inImg->chals->ch[j]);
inImg->chals->ch[j] = NULL;
}
}
memory_free(inImg->chals);
inImg->chals = NULL;
if (created_chals_struct) {
memory_free(inImg->chals);
inImg->chals = NULL;
}
return EMBEDDIP_ERROR_OUT_OF_MEMORY;
}
allocated_in_call[i] = 1;
}
}

Expand Down Expand Up @@ -302,4 +331,4 @@ Image *createImageWH_legacy(int width, int height, ImageFormat format)
return NULL;
}
return img;
}
}
4 changes: 2 additions & 2 deletions board/common.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ bool isChalsEmpty(const Image *inImg);
* width × height × 2 floats, supporting complex data if needed.
*
* @param[in,out] inImg Pointer to the Image structure.
* @param[in] numChals Number of channels to allocate (max 3).
* @param[in] numChals Number of channels to allocate (1..6).
*
* @return EMBEDDIP_OK on success, error code on failure.
*/
Expand All @@ -103,7 +103,7 @@ embeddip_status_t createChals(Image *inImg, uint8_t numChals);
* Used by FFT operations: width × height × 2 × sizeof(float) per channel.
*
* @param[in,out] inImg Target image to allocate complex channels for.
* @param[in] numChals Number of complex channels to allocate (max 3).
* @param[in] numChals Number of complex channels to allocate (1..6).
*
* @return EMBEDDIP_OK on success, error code on failure.
*/
Expand Down
14 changes: 8 additions & 6 deletions board/esp32/board_esp32eye_memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
#ifdef EMBED_DIP_BOARD_ESP32

#include <stdlib.h>
#include <string.h>

#include "esp_heap_caps.h" // Required for ps_malloc
#include <Arduino.h> // For Serial
Expand Down Expand Up @@ -61,11 +60,14 @@ void *memory_realloc(void *ptr, size_t new_size)
return memory_alloc(new_size);
}

void *new_ptr = ps_malloc(new_size);
if (new_ptr) {
memcpy(new_ptr, ptr, new_size); // WARNING: if old size is unknown, this can overread
free(ptr);
} else {
if (new_size == 0) {
memory_free(ptr);
return NULL;
}

// Use realloc so the runtime copies only the old allocation size.
void *new_ptr = realloc(ptr, new_size);
if (!new_ptr) {
Serial.printf("[memory_realloc] Failed to reallocate %u bytes from %p\n",
(unsigned int)new_size,
ptr);
Expand Down
25 changes: 18 additions & 7 deletions device/camera/stm32_ov5640.c
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ static void I2C_MspInit(void)
gpio_init.Pin = GPIO_PIN_8;
gpio_init.Mode = GPIO_MODE_AF_OD;
gpio_init.Pull = GPIO_NOPULL;
gpio_init.Speed = GPIO_SPEED_FAST;
gpio_init.Speed = GPIO_SPEED_FREQ_HIGH;
gpio_init.Alternate = GPIO_AF4_I2C1;
HAL_GPIO_Init(GPIOB, &gpio_init);

Expand Down Expand Up @@ -410,7 +410,7 @@ static void CAMERA_PwrUp(void)
gpio_init_structure.Pin = GPIO_PIN_13;
gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_structure.Pull = GPIO_NOPULL;
gpio_init_structure.Speed = GPIO_SPEED_HIGH;
gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOH, &gpio_init_structure);

/* De-assert the camera POWER_DOWN pin (active high) */
Expand All @@ -435,7 +435,7 @@ static void CAMERA_PwrDown(void)
gpio_init_structure.Pin = GPIO_PIN_13;
gpio_init_structure.Mode = GPIO_MODE_OUTPUT_PP;
gpio_init_structure.Pull = GPIO_NOPULL;
gpio_init_structure.Speed = GPIO_SPEED_HIGH;
gpio_init_structure.Speed = GPIO_SPEED_FREQ_VERY_HIGH;
HAL_GPIO_Init(GPIOH, &gpio_init_structure);

/* Assert the camera POWER_DOWN pin (active high) */
Expand Down Expand Up @@ -540,6 +540,13 @@ static int camera_init(ImageResolution resolution, ImageFormat format)
*/
static int camera_capture(captureMode mode, Image *inImg)
{
if (inImg == NULL || inImg->pixels == NULL) {
return -1;
}
if (inImg->size == 0U || inImg->depth == 0U) {
return -1;
}

// Clear the frame completion flag
frame_capture_complete = 0;

Expand All @@ -551,10 +558,14 @@ static int camera_capture(captureMode mode, Image *inImg)
uint32_t dma_size_words = total_bytes / 4;

// Start DMA transfer
HAL_DCMI_Start_DMA(&hdcmi,
mode == CONTINUOUS ? DCMI_MODE_CONTINUOUS : DCMI_MODE_SNAPSHOT,
(uint32_t)inImg->pixels,
dma_size_words);
HAL_StatusTypeDef hal_status =
HAL_DCMI_Start_DMA(&hdcmi,
mode == CONTINUOUS ? DCMI_MODE_CONTINUOUS : DCMI_MODE_SNAPSHOT,
(uint32_t)inImg->pixels,
dma_size_words);
if (hal_status != HAL_OK) {
return -1;
}

// For SINGLE mode, wait for frame completion
if (mode == SINGLE) {
Expand Down
24 changes: 16 additions & 8 deletions device/serial/stm32_uart.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,10 +80,14 @@ static int serial_capture(Image *img)
huart1.gState = HAL_UART_STATE_READY;

// Calculate block parameters
uint16_t blockSize = ((img->size * img->depth) < UART_BLOCK_SIZE_MAX) ? (img->size * img->depth)
: UART_BLOCK_SIZE_MAX;
uint32_t blockCount = (img->size * img->depth) / blockSize;
uint16_t lastBlockSize = (img->size * img->depth) % blockSize;
uint32_t totalBytes = img->size * img->depth;
if (totalBytes == 0U) {
return EMBEDDIP_ERROR_INVALID_SIZE;
}
uint16_t blockSize =
(totalBytes < UART_BLOCK_SIZE_MAX) ? (uint16_t)totalBytes : UART_BLOCK_SIZE_MAX;
uint32_t blockCount = totalBytes / blockSize;
uint16_t lastBlockSize = (uint16_t)(totalBytes % blockSize);

// Send capture request header
HAL_UART_Transmit(&huart1, request_start_sequence, 3, 5000);
Expand Down Expand Up @@ -131,10 +135,14 @@ static int serial_send(const Image *img)
CHECK_NULL_INT(img->pixels);
uint8_t request_start_sequence[3] = "STW";
// Calculate block transmission parameters
uint16_t blockSize = ((img->size * img->depth) < UART_BLOCK_SIZE_MAX) ? (img->size * img->depth)
: UART_BLOCK_SIZE_MAX;
uint32_t blockCount = (img->size * img->depth) / blockSize;
uint16_t lastBlockSize = (img->size * img->depth) % blockSize;
uint32_t totalBytes = img->size * img->depth;
if (totalBytes == 0U) {
return EMBEDDIP_ERROR_INVALID_SIZE;
}
uint16_t blockSize =
(totalBytes < UART_BLOCK_SIZE_MAX) ? (uint16_t)totalBytes : UART_BLOCK_SIZE_MAX;
uint32_t blockCount = totalBytes / blockSize;
uint16_t lastBlockSize = (uint16_t)(totalBytes % blockSize);

// Step 1: Send command header
HAL_UART_Transmit(&huart1, request_start_sequence, 3, HAL_MAX_DELAY);
Expand Down
8 changes: 7 additions & 1 deletion imgproc/color.c
Original file line number Diff line number Diff line change
Expand Up @@ -613,7 +613,13 @@ embeddip_status_t cvtColor(const Image *src, Image *dst, ColorConversionCode cod
if (src->format != dst->format) {
return EMBEDDIP_ERROR_INVALID_FORMAT;
}
memcpy(dst->pixels, src->pixels, src->size * src->depth);
if (src->width != dst->width || src->height != dst->height) {
return EMBEDDIP_ERROR_INVALID_SIZE;
}
if ((size_t)dst->size * (size_t)dst->depth < (size_t)src->size * (size_t)src->depth) {
return EMBEDDIP_ERROR_INVALID_SIZE;
}
memcpy(dst->pixels, src->pixels, (size_t)src->size * (size_t)src->depth);
break;

default:
Expand Down
12 changes: 8 additions & 4 deletions imgproc/fft.c
Original file line number Diff line number Diff line change
Expand Up @@ -356,13 +356,15 @@ embeddip_status_t multiply(const Image *img1, const Image *img2, Image *outImg)

float *in1 = NULL;
float *in2 = NULL;
const uint8_t *pix1 = NULL;
const uint8_t *pix2 = NULL;

if (img1->log == IMAGE_DATA_CH0) {
in1 = img1->chals ? img1->chals->ch[0] : NULL;
} else if (img1->log == IMAGE_DATA_COMPLEX) {
in1 = img1->chals ? img1->chals->ch[1] : NULL;
} else if (img1->log == IMAGE_DATA_PIXELS) {
in1 = (float *)img1->pixels;
pix1 = (const uint8_t *)img1->pixels;
} else {
return EMBEDDIP_ERROR_INVALID_ARG;
}
Expand All @@ -372,18 +374,20 @@ embeddip_status_t multiply(const Image *img1, const Image *img2, Image *outImg)
} else if (img2->log == IMAGE_DATA_COMPLEX) {
in2 = img2->chals ? img2->chals->ch[1] : NULL;
} else if (img2->log == IMAGE_DATA_PIXELS) {
in2 = (float *)img2->pixels;
pix2 = (const uint8_t *)img2->pixels;
} else {
return EMBEDDIP_ERROR_INVALID_ARG;
}

if (!in1 || !in2 || !outImg->chals || !outImg->chals->ch[0])
if ((!in1 && !pix1) || (!in2 && !pix2) || !outImg->chals || !outImg->chals->ch[0])
return EMBEDDIP_ERROR_NULL_PTR;

float *out = outImg->chals->ch[0];
int size = img1->width * img1->height;
for (int i = 0; i < size; ++i) {
out[i] = in1[i] * in2[i];
float v1 = in1 ? in1[i] : (float)pix1[i];
float v2 = in2 ? in2[i] : (float)pix2[i];
out[i] = v1 * v2;
}

outImg->log = IMAGE_DATA_CH0;
Expand Down
6 changes: 3 additions & 3 deletions imgproc/imgwarp.c
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,10 @@ embeddip_status_t resize(Image *src, Image *dst, int width, int height)

// Allocate channels if missing
if (isChalsEmpty(dst)) {
if (!createChals(dst, 1)) { // only 1 channel for grayscale
return EMBEDDIP_ERROR_OUT_OF_MEMORY;
embeddip_status_t status = createChals(dst, 1); // only 1 channel for grayscale
if (status != EMBEDDIP_OK) {
return status;
}
dst->is_chals = 1;
}

float width_ratio = (float)src->width / (float)width;
Expand Down
Loading
Loading