From d548aa836d4e33600041a6ce3e2689da949fa8e9 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 22:53:58 +0200 Subject: [PATCH 1/8] imgproc: filter: use clamp borders in convolution style filters Zero outside sampling creates artificial responses at image boundaries. Use replicate clamp indexing for spatial filtering paths. Update filter2D_single_channel, sepfilter2D, dogFilter, and logFilter. Add clamp_index() and reuse it in sep_conv_xy_f32 so border indexing stays consistent and duplicated clamp logic is removed. Signed-off-by: Ozan Durgut --- imgproc/filter.c | 77 +++++++++++++++++++++--------------------------- 1 file changed, 33 insertions(+), 44 deletions(-) diff --git a/imgproc/filter.c b/imgproc/filter.c index fce8a9d..d7e07bb 100755 --- a/imgproc/filter.c +++ b/imgproc/filter.c @@ -36,6 +36,15 @@ uint8_t channel_mask[] = { 0xF7, }; +static inline int clamp_index(int idx, int limit) +{ + if (idx < 0) + return 0; + if (idx >= limit) + return limit - 1; + return idx; +} + // i am not sure about the kernel. embeddip_status_t filter2D_single_channel(Image *src, Image *dst, int ch_idx, void *ctx) { @@ -79,16 +88,14 @@ embeddip_status_t filter2D_single_channel(Image *src, Image *dst, int ch_idx, vo for (int fy = -k; fy <= k; ++fy) { for (int fx = -k; fx <= k; ++fx) { - int iy = y + fy; - int ix = x + fx; + int iy = clamp_index(y + fy, height); + int ix = clamp_index(x + fx, width); float val = 0.0f; - if (iy >= 0 && iy < height && ix >= 0 && ix < width) { - if (src->format == IMAGE_FORMAT_GRAYSCALE) { - val = (float)raw[iy * width + ix]; - } else if (src->format == IMAGE_FORMAT_RGB888) { - val = (float)raw[(iy * width + ix) * 3 + ch_idx - 1]; - } + if (src->format == IMAGE_FORMAT_GRAYSCALE) { + val = (float)raw[iy * width + ix]; + } else if (src->format == IMAGE_FORMAT_RGB888) { + val = (float)raw[(iy * width + ix) * 3 + ch_idx - 1]; } sum += val * kernel[(fy + k) * size + (fx + k)]; @@ -120,13 +127,10 @@ embeddip_status_t filter2D_single_channel(Image *src, Image *dst, int ch_idx, vo for (int fy = -k; fy <= k; ++fy) { for (int fx = -k; fx <= k; ++fx) { - int iy = y + fy; - int ix = x + fx; - - float val = 0.0f; - if (iy >= 0 && iy < height && ix >= 0 && ix < width) - val = inCh[iy * width + ix]; + int iy = clamp_index(y + fy, height); + int ix = clamp_index(x + fx, width); + float val = inCh[iy * width + ix]; sum += val * kernel[(fy + k) * size + (fx + k)]; } } @@ -286,9 +290,8 @@ embeddip_status_t sepfilter2D(Image *src, for (int x = 0; x < width; ++x) { float sum = 0.0f; for (int ky = -half; ky <= half; ++ky) { - int iy = y + ky; - if (iy >= 0 && iy < height) - sum += inCh[iy * width + x] * kernel_y[ky + half]; + int iy = clamp_index(y + ky, height); + sum += inCh[iy * width + x] * kernel_y[ky + half]; } temp[y * width + x] = sum; } @@ -299,9 +302,8 @@ embeddip_status_t sepfilter2D(Image *src, for (int x = 0; x < width; ++x) { float sum = 0.0f; for (int kx = -half; kx <= half; ++kx) { - int ix = x + kx; - if (ix >= 0 && ix < width) - sum += temp[y * width + ix] * kernel_x[kx + half]; + int ix = clamp_index(x + kx, width); + sum += temp[y * width + ix] * kernel_x[kx + half]; } sum = (float)(sum * delta); @@ -877,11 +879,9 @@ embeddip_status_t dogFilter(const Image *src, Image *dst, float sigma1, float si float sum = 0.0f; for (int fy = -half1; fy <= half1; ++fy) { for (int fx = -half1; fx <= half1; ++fx) { - int iy = y + fy; - int ix = x + fx; - if (iy >= 0 && iy < height && ix >= 0 && ix < width) - sum += - src_buf[iy * width + ix] * kernel1[(fy + half1) * size1 + (fx + half1)]; + int iy = clamp_index(y + fy, height); + int ix = clamp_index(x + fx, width); + sum += src_buf[iy * width + ix] * kernel1[(fy + half1) * size1 + (fx + half1)]; } } blur1[y * width + x] = sum; @@ -894,11 +894,9 @@ embeddip_status_t dogFilter(const Image *src, Image *dst, float sigma1, float si float sum = 0.0f; for (int fy = -half2; fy <= half2; ++fy) { for (int fx = -half2; fx <= half2; ++fx) { - int iy = y + fy; - int ix = x + fx; - if (iy >= 0 && iy < height && ix >= 0 && ix < width) - sum += - src_buf[iy * width + ix] * kernel2[(fy + half2) * size2 + (fx + half2)]; + int iy = clamp_index(y + fy, height); + int ix = clamp_index(x + fx, width); + sum += src_buf[iy * width + ix] * kernel2[(fy + half2) * size2 + (fx + half2)]; } } blur2[y * width + x] = sum; @@ -1020,10 +1018,9 @@ embeddip_status_t logFilter(const Image *src, Image *dst, float sigma) float sum = 0.0f; for (int fy = -half; fy <= half; ++fy) { for (int fx = -half; fx <= half; ++fx) { - int iy = y + fy; - int ix = x + fx; - if (iy >= 0 && iy < height && ix >= 0 && ix < width) - sum += src_buf[iy * width + ix] * kernel[(fy + half) * ksize + (fx + half)]; + int iy = clamp_index(y + fy, height); + int ix = clamp_index(x + fx, width); + sum += src_buf[iy * width + ix] * kernel[(fy + half) * ksize + (fx + half)]; } } outCh[y * width + x] = sum; @@ -1228,11 +1225,7 @@ static void sep_conv_xy_f32(const float *src, for (int x = 0; x < width; ++x) { float acc = 0.f; for (int i = -rx; i <= rx; ++i) { - int xx = x + i; - if (xx < 0) - xx = 0; - if (xx >= width) - xx = width - 1; + int xx = clamp_index(x + i, width); acc += row[xx] * kx[i + rx]; } trow[x] = acc; @@ -1245,11 +1238,7 @@ static void sep_conv_xy_f32(const float *src, for (int x = 0; x < width; ++x) { float acc = 0.f; for (int j = -ry; j <= ry; ++j) { - int yy = y + j; - if (yy < 0) - yy = 0; - if (yy >= height) - yy = height - 1; + int yy = clamp_index(y + j, height); acc += tmp[yy * width + x] * ky[j + ry]; } drow[x] = acc; From a735045e2a4ac696a95981bed1e4ba8fc31505be Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 22:59:50 +0200 Subject: [PATCH 2/8] board: stm32f7: harden allocator and fix FFT complex channel setup The STM32F7 allocator accepted invalid block headers and could merge free blocks that were not physically adjacent. Add header magic validation, pool range checks, payload pointer verification, double-free guards, and adjacent-only coalescing. Also align realloc requests before size checks. In the FFT path, ensure complex output storage is allocated with createChalsComplex() and reallocated safely in polarToCart(). Signed-off-by: Ozan Durgut --- board/stm32f7/board_stm32f7_fft.c | 17 ++++++-- board/stm32f7/board_stm32f7_memory.c | 63 ++++++++++++++++++++++++---- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/board/stm32f7/board_stm32f7_fft.c b/board/stm32f7/board_stm32f7_fft.c index 14ed166..7c52514 100755 --- a/board/stm32f7/board_stm32f7_fft.c +++ b/board/stm32f7/board_stm32f7_fft.c @@ -96,11 +96,20 @@ embeddip_status_t fourierInv(const Image *inImg, Image *outImg) */ embeddip_status_t polarToCart(const Image *mag_img, const Image *phase_img, Image *dst) { + if (!mag_img || !phase_img || !dst) + return EMBEDDIP_ERROR_NULL_PTR; + int size = mag_img->width * mag_img->height; - if (isChalsEmpty(dst)) { - createChalsComplex(dst, 1); // Complex channel for interleaved (Re, Im) - dst->is_chals = 1; + if (!isChalsEmpty(dst) && dst->chals && dst->chals->ch[0]) { + // Ensure output buffer has complex capacity (2*N floats). + memory_free(dst->chals->ch[0]); + dst->chals->ch[0] = NULL; + } + + embeddip_status_t status = createChalsComplex(dst, 1); + if (status != EMBEDDIP_OK) { + return status; } float *mag = mag_img->chals->ch[0]; @@ -553,7 +562,7 @@ embeddip_status_t ifft__(const Image *inImg, Image *outImg) float *buf0; if (isChalsEmpty(outImg)) { - createChals(outImg, 2); + createChalsComplex(outImg, 1); outImg->is_chals = 1; buf0 = outImg->chals->ch[0]; } else { diff --git a/board/stm32f7/board_stm32f7_memory.c b/board/stm32f7/board_stm32f7_memory.c index 2097942..6278b01 100755 --- a/board/stm32f7/board_stm32f7_memory.c +++ b/board/stm32f7/board_stm32f7_memory.c @@ -21,6 +21,7 @@ static uint8_t *memory_pool = ((uint8_t *)SDRAM_BANK_ADDR + CAMERA_LCD_FRAMEBUFFER_SIZE); typedef struct MemoryBlock { + uint32_t magic; size_t size; struct MemoryBlock *next; int is_free; @@ -28,16 +29,43 @@ typedef struct MemoryBlock { #define ALIGN4(s) (((s) + 3) & ~3) #define BLOCK_SIZE sizeof(MemoryBlock) + #define MEMBLOCK_MAGIC 0xB10C4EADu static MemoryBlock *free_list = NULL; static int initialized = 0; +static inline uintptr_t pool_start_addr(void) +{ + return (uintptr_t)memory_pool; +} + +static inline uintptr_t pool_end_addr(void) +{ + return (uintptr_t)memory_pool + MEMORY_POOL_SIZE; +} + +static inline int ptr_in_pool(const void *p) +{ + uintptr_t a = (uintptr_t)p; + return a >= pool_start_addr() && a < pool_end_addr(); +} + +static inline int block_header_valid(const MemoryBlock *b) +{ + if (!b || !ptr_in_pool(b)) + return 0; + if ((uintptr_t)b + BLOCK_SIZE > pool_end_addr()) + return 0; + return (b->magic == MEMBLOCK_MAGIC); +} + void memory_init() { if (initialized) return; free_list = (MemoryBlock *)memory_pool; + free_list->magic = MEMBLOCK_MAGIC; free_list->size = MEMORY_POOL_SIZE - BLOCK_SIZE; free_list->next = NULL; free_list->is_free = 1; @@ -54,14 +82,15 @@ void *memory_alloc(size_t size) MemoryBlock *curr = free_list; - while (curr) { + while (curr && block_header_valid(curr)) { if (curr->is_free && curr->size >= size) { uintptr_t curr_addr = (uintptr_t)curr; - uintptr_t pool_end = (uintptr_t)memory_pool + MEMORY_POOL_SIZE; + uintptr_t pool_end = pool_end_addr(); uintptr_t next_block_addr = curr_addr + BLOCK_SIZE + size; if (curr->size >= size + BLOCK_SIZE + 4 && next_block_addr + BLOCK_SIZE < pool_end) { MemoryBlock *new_block = (MemoryBlock *)(next_block_addr); + new_block->magic = MEMBLOCK_MAGIC; new_block->size = curr->size - size - BLOCK_SIZE; new_block->next = curr->next; new_block->is_free = 1; @@ -85,8 +114,8 @@ void memory_free(void *ptr) if (!ptr) return; - uintptr_t pool_start = (uintptr_t)memory_pool; - uintptr_t pool_end = pool_start + MEMORY_POOL_SIZE; + uintptr_t pool_start = pool_start_addr(); + uintptr_t pool_end = pool_end_addr(); uintptr_t addr = (uintptr_t)ptr; if (addr < pool_start || addr >= pool_end) @@ -96,12 +125,28 @@ void memory_free(void *ptr) memory_init(); MemoryBlock *block = (MemoryBlock *)((uint8_t *)ptr - BLOCK_SIZE); + + if (!block_header_valid(block)) + return; + + // Ignore double free. + if (block->is_free) + return; + + // Ensure the pointer corresponds exactly to block payload start. + if ((void *)((uint8_t *)block + BLOCK_SIZE) != ptr) + return; + block->is_free = 1; - // Merge adjacent free blocks + // Merge only physically adjacent free blocks. MemoryBlock *curr = free_list; - while (curr && curr->next) { - if (curr->is_free && curr->next->is_free) { + while (curr && block_header_valid(curr) && curr->next) { + if (!block_header_valid(curr->next)) { + break; + } + uintptr_t curr_end = (uintptr_t)curr + BLOCK_SIZE + curr->size; + if (curr->is_free && curr->next->is_free && curr_end == (uintptr_t)curr->next) { curr->size += BLOCK_SIZE + curr->next->size; curr->next = curr->next->next; } else { @@ -119,6 +164,10 @@ void *memory_realloc(void *ptr, size_t new_size) memory_init(); MemoryBlock *block = (MemoryBlock *)((uint8_t *)ptr - BLOCK_SIZE); + if (!block_header_valid(block)) + return NULL; + + new_size = ALIGN4(new_size); if (block->size >= new_size) return ptr; From bdbdbd149e02714edc46c75ffd8065a87eb712b7 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 23:00:35 +0200 Subject: [PATCH 3/8] imgproc: imgwarp: fix resize output log state resize writes results into chals->ch[0], but did not update dst->log accordingly. Set dst->log to IMAGE_DATA_CH0 so downstream code reads output from the correct data location. Signed-off-by: Ozan Durgut --- imgproc/imgwarp.c | 1 + 1 file changed, 1 insertion(+) diff --git a/imgproc/imgwarp.c b/imgproc/imgwarp.c index fbb01f8..37463ae 100644 --- a/imgproc/imgwarp.c +++ b/imgproc/imgwarp.c @@ -51,6 +51,7 @@ embeddip_status_t resize(Image *src, Image *dst, int width, int height) dst->width = (uint32_t)width; dst->height = (uint32_t)height; dst->size = (uint32_t)(width * height); + dst->log = IMAGE_DATA_CH0; return EMBEDDIP_OK; } From ee06b605c27dfe4151064f705d8870c054eb6086 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 23:20:00 +0200 Subject: [PATCH 4/8] .github: fix code quality workflow source paths The code quality workflow referenced parent-project paths that do not exist when running in the embedDIP repository context, causing cppcheck to fail with missing path errors. Use local source directories (core, imgproc, board, device, wrapper) for cppcheck include/source inputs and clang-format checks. Add explicit guards to fail early when no source directories are found. Signed-off-by: Ozan Durgut --- .github/workflows/code-quality.yml | 40 ++++++++++++++++++-------- .github/workflows/doxygen-gh-pages.yml | 4 +-- 2 files changed, 30 insertions(+), 14 deletions(-) diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 7407c34..8a7874f 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -2,7 +2,9 @@ name: Code Quality on: push: + branches: [main] pull_request: + branches: [main] jobs: quality: @@ -22,6 +24,20 @@ jobs: - name: Run Cppcheck run: | echo "Running cppcheck static analysis..." + SRC_DIRS=() + for d in core imgproc board device wrapper; do + [ -d "$d" ] && SRC_DIRS+=("$d") + done + if [ ${#SRC_DIRS[@]} -eq 0 ]; then + echo "No source directories found for cppcheck" + exit 1 + fi + + INCLUDE_ARGS=() + for d in "${SRC_DIRS[@]}"; do + INCLUDE_ARGS+=("-I" "$d") + done + cppcheck \ --enable=warning,style,performance,portability \ --error-exitcode=1 \ @@ -31,16 +47,8 @@ jobs: --suppress=unmatchedSuppression \ --std=c11 \ --force \ - -I embedDIP/core \ - -I embedDIP/imgproc \ - -I embedDIP/board \ - -I embedDIP/device \ - -I embedDIP/wrapper \ - -I Drivers/STM32F7xx_HAL_Driver/Inc \ - -I Drivers/CMSIS/Device/ST/STM32F7xx/Include \ - -I Drivers/CMSIS/Core/Include \ - -I Core/Inc \ - embedDIP/ Core/ \ + "${INCLUDE_ARGS[@]}" \ + "${SRC_DIRS[@]}" \ --xml \ 2> cppcheck-report.xml @@ -63,8 +71,16 @@ jobs: fi # Fail if any file is not properly formatted - find embedDIP Core \( -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) \ - -not -path "*/Drivers/*" \ + SRC_DIRS=() + for d in core imgproc board device wrapper; do + [ -d "$d" ] && SRC_DIRS+=("$d") + done + if [ ${#SRC_DIRS[@]} -eq 0 ]; then + echo "No source directories found for format check" + exit 1 + fi + + find "${SRC_DIRS[@]}" \( -name "*.c" -o -name "*.cpp" -o -name "*.h" -o -name "*.hpp" \) \ -print0 | xargs -0 clang-format --dry-run --Werror - name: Upload Cppcheck Report diff --git a/.github/workflows/doxygen-gh-pages.yml b/.github/workflows/doxygen-gh-pages.yml index e3f2e88..21fce4a 100644 --- a/.github/workflows/doxygen-gh-pages.yml +++ b/.github/workflows/doxygen-gh-pages.yml @@ -10,7 +10,7 @@ on: - '**/*.hpp' - '**/*.c' - '**/*.cpp' - - 'Doxyfile' + - 'docs/Doxyfile' - '.github/workflows/doxygen-gh-pages.yml' workflow_dispatch: # Allow manual trigger @@ -46,7 +46,7 @@ jobs: - name: Generate Doxygen documentation run: | - doxygen Doxyfile + doxygen docs/Doxyfile - name: Check generated documentation run: | From a7d924a0c92e97d7a2c0bf75687d143ca84f8998 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 23:20:55 +0200 Subject: [PATCH 5/8] docs: move Doxygen config under docs Keep documentation config with documentation assets by moving Doxyfile to docs/Doxyfile. Update docs/README.md path references to match the new location. --- .github/workflows/code-quality.yml | 42 +++++++++++++++++++++++++++++- docs/Doxyfile | 27 +++++++++++++++++++ docs/README.md | 4 +-- 3 files changed, 70 insertions(+), 3 deletions(-) create mode 100644 docs/Doxyfile diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 8a7874f..4786776 100644 --- a/.github/workflows/code-quality.yml +++ b/.github/workflows/code-quality.yml @@ -39,7 +39,7 @@ jobs: done cppcheck \ - --enable=warning,style,performance,portability \ + --enable=warning,performance,portability \ --error-exitcode=1 \ --inline-suppr \ --suppress=missingIncludeSystem \ @@ -52,6 +52,37 @@ jobs: --xml \ 2> cppcheck-report.xml + - name: Run Cppcheck Style Report + continue-on-error: true + run: | + echo "Running cppcheck style analysis (non-blocking)..." + SRC_DIRS=() + for d in core imgproc board device wrapper; do + [ -d "$d" ] && SRC_DIRS+=("$d") + done + if [ ${#SRC_DIRS[@]} -eq 0 ]; then + echo "No source directories found for cppcheck style scan" + exit 0 + fi + + INCLUDE_ARGS=() + for d in "${SRC_DIRS[@]}"; do + INCLUDE_ARGS+=("-I" "$d") + done + + cppcheck \ + --enable=style \ + --inline-suppr \ + --suppress=missingIncludeSystem \ + --suppress=unusedFunction \ + --suppress=unmatchedSuppression \ + --std=c11 \ + --force \ + "${INCLUDE_ARGS[@]}" \ + "${SRC_DIRS[@]}" \ + --xml \ + 2> cppcheck-style-report.xml || true + - name: Check Code Formatting run: | echo "Checking code formatting..." @@ -91,3 +122,12 @@ jobs: path: cppcheck-report.xml retention-days: 30 if-no-files-found: warn + + - name: Upload Cppcheck Style Report + if: always() + uses: actions/upload-artifact@v4 + with: + name: cppcheck-style-report + path: cppcheck-style-report.xml + retention-days: 30 + if-no-files-found: warn diff --git a/docs/Doxyfile b/docs/Doxyfile new file mode 100644 index 0000000..72bf305 --- /dev/null +++ b/docs/Doxyfile @@ -0,0 +1,27 @@ +PROJECT_NAME = "embedDIP" +PROJECT_BRIEF = "Embedded Digital Image Processing Library" +OUTPUT_DIRECTORY = docs +CREATE_SUBDIRS = NO + +INPUT = core imgproc board device wrapper embedDIP.h embedDIP.hpp embedDIP_configs.h +FILE_PATTERNS = *.c *.cpp *.h *.hpp +RECURSIVE = YES + +EXTRACT_ALL = YES +EXTRACT_PRIVATE = NO +EXTRACT_STATIC = YES + +GENERATE_HTML = YES +HTML_OUTPUT = html +GENERATE_LATEX = NO + +HAVE_DOT = YES +CALL_GRAPH = YES +CALLER_GRAPH = YES + +QUIET = YES +WARN_IF_UNDOCUMENTED = NO +WARN_IF_DOC_ERROR = YES + +OPTIMIZE_OUTPUT_FOR_C = YES +JAVADOC_AUTOBRIEF = YES diff --git a/docs/README.md b/docs/README.md index 17d364c..95d98d8 100644 --- a/docs/README.md +++ b/docs/README.md @@ -30,7 +30,7 @@ sudo pacman -S doxygen graphviz From the repository root: ```bash -doxygen Doxyfile +doxygen docs/Doxyfile ``` The HTML documentation will be generated in `docs/html/`. @@ -53,7 +53,7 @@ start docs/html/index.html ## Documentation Structure - **HTML Output:** `docs/html/` - Main HTML documentation -- **Configuration:** `Doxyfile` - Doxygen configuration file (at repository root) +- **Configuration:** `docs/Doxyfile` - Doxygen configuration file - **Warnings Log:** `doxygen_warnings.log` - Build warnings (generated during build) ## Automatic Deployment From c91c6c3209210ec1e11265a592c10832464e6084 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 23:28:31 +0200 Subject: [PATCH 6/8] imgproc: fix logFilter allocation null check Check src_buf allocation result in logFilter instead of rechecking src. This fixes a real null-pointer warning path and keeps error handling consistent with other allocation checks. Signed-off-by: Ozan Durgut --- imgproc/filter.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/imgproc/filter.c b/imgproc/filter.c index d7e07bb..9b092ac 100755 --- a/imgproc/filter.c +++ b/imgproc/filter.c @@ -982,7 +982,7 @@ embeddip_status_t logFilter(const Image *src, Image *dst, float sigma) // Allocate float buffer and convert input float *src_buf = (float *)memory_alloc(width * height * sizeof(float)); - if (!src) { + if (!src_buf) { memory_free(kernel); return EMBEDDIP_ERROR_OUT_OF_MEMORY; } From a5b996102790f482089813c6a6e335e09ae77311 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 23:31:19 +0200 Subject: [PATCH 7/8] board/esp32: fix null-dereference order in FFT helpers Run pointer validation before dereferencing image fields in fft() and _abs_(). This resolves cppcheck nullPointerRedundantCheck warnings for inImg and fftImg and makes error handling consistent. --- board/esp32/board_esp32_fft.cpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/board/esp32/board_esp32_fft.cpp b/board/esp32/board_esp32_fft.cpp index efe76b1..d60d7a7 100755 --- a/board/esp32/board_esp32_fft.cpp +++ b/board/esp32/board_esp32_fft.cpp @@ -21,6 +21,10 @@ static bool isValidFFTSize(int w, int h) embeddip_status_t fft(const Image *inImg, Image *outImg) { + if (!inImg || !outImg || !inImg->pixels) { + return EMBEDDIP_ERROR_NULL_PTR; + } + int N = inImg->width; if (!isValidFFTSize(N, N)) { // Serial.println("[ERROR] Invalid FFT size. Only powers of 2 are supported."); @@ -35,12 +39,6 @@ embeddip_status_t fft(const Image *inImg, Image *outImg) // Serial.println("[ERROR] 1pixels are null."); float *buf0 = outImg->chals->ch[0]; float *buf1 = outImg->chals->ch[1]; - // Serial.println("[ERROR] 2or pixels are null."); - if (!inImg || !inImg->pixels) { - // Serial.println("[ERROR]3 or pixels are null."); - return EMBEDDIP_ERROR_NULL_PTR; - } - uint8_t *input = static_cast(inImg->pixels); for (int i = 0; i < N * N; i++) { buf0[2 * i] = (float)input[i]; // real part @@ -233,13 +231,13 @@ embeddip_status_t fftshift(Image *img) embeddip_status_t _abs_(const Image *fftImg, Image *magImg) { - int size = fftImg->width * fftImg->height; - - if (!fftImg || !fftImg->chals) { + if (!fftImg || !fftImg->chals || !magImg) { // Serial.println("[ERROR] Input FFT image or its channels are null."); return EMBEDDIP_ERROR_NULL_PTR; } + int size = fftImg->width * fftImg->height; + float *fft = (fftImg->log == IMAGE_DATA_COMPLEX) ? fftImg->chals->ch[1] : fftImg->chals->ch[0]; if (!fft) { @@ -614,4 +612,4 @@ embeddip_status_t ffilter2D(const Image *fftImg, const Image *filterMask, Image polarToCart(magImg, phaseImg, outImg); return EMBEDDIP_OK; } -#endif \ No newline at end of file +#endif From 4d01031b210e0ab686f591e6b61bafaf85af8951 Mon Sep 17 00:00:00 2001 From: Ozan Durgut Date: Tue, 14 Apr 2026 23:36:54 +0200 Subject: [PATCH 8/8] style: run clang-format across source tree Apply clang-format across C/C++ headers and sources in the repository to satisfy CI. Signed-off-by: Ozan Durgut --- device/camera/camera.h | 1 + device/camera/ov5640/stm32_ov5640.c | 85 +++++++++++++++++------------ device/camera/ov5640/stm32_ov5640.h | 49 +++++++++-------- device/display/display.h | 1 + imgproc/connectedcomponents.c | 1 + imgproc/misc.c | 6 +- imgproc/misc.h | 3 +- wrapper/ImageWrapper.cpp | 5 +- 8 files changed, 88 insertions(+), 63 deletions(-) diff --git a/device/camera/camera.h b/device/camera/camera.h index 29f85cb..ca2be68 100755 --- a/device/camera/camera.h +++ b/device/camera/camera.h @@ -5,6 +5,7 @@ #define CAMERA_H #include "core/image.h" + #include #include diff --git a/device/camera/ov5640/stm32_ov5640.c b/device/camera/ov5640/stm32_ov5640.c index 341baa2..187edf1 100755 --- a/device/camera/ov5640/stm32_ov5640.c +++ b/device/camera/ov5640/stm32_ov5640.c @@ -11,22 +11,23 @@ #ifdef DEVICE_OV5640 -#include "stm32_ov5640.h" -#include "board/stm32f7/configs.h" -#include "device/camera/camera.h" -#include "device/serial/serial.h" -#include "stm32f7xx_hal.h" + #include "board/stm32f7/configs.h" + #include "device/camera/camera.h" + #include "device/serial/serial.h" -#include -#include -#include + #include + #include + #include -/* ============================================================================ - * I2C COMMUNICATION LAYER (Internal - Static Functions) - * ============================================================================ */ + #include "stm32_ov5640.h" + #include "stm32f7xx_hal.h" -/* Private defines */ -#define I2C1_TIMING ((uint32_t)0x40912732) /**< I2C timing for 100kHz at 216MHz */ + /* ============================================================================ + * I2C COMMUNICATION LAYER (Internal - Static Functions) + * ============================================================================ */ + + /* Private defines */ + #define I2C1_TIMING ((uint32_t)0x40912732) /**< I2C timing for 100kHz at 216MHz */ /* Private variables */ static I2C_HandleTypeDef hI2cCamera = {0}; @@ -68,8 +69,7 @@ static void CAMERA_IO_Write(uint16_t DeviceAddr, uint16_t Reg, uint8_t Value) { HAL_StatusTypeDef status; - status = HAL_I2C_Mem_Write(&hI2cCamera, DeviceAddr, Reg, I2C_MEMADD_SIZE_16BIT, - &Value, 1, 100); + status = HAL_I2C_Mem_Write(&hI2cCamera, DeviceAddr, Reg, I2C_MEMADD_SIZE_16BIT, &Value, 1, 100); if (status != HAL_OK) { I2C_Error(DeviceAddr); @@ -87,8 +87,7 @@ static uint8_t CAMERA_IO_Read(uint16_t DeviceAddr, uint16_t Reg) uint8_t value = 0; HAL_StatusTypeDef status; - status = HAL_I2C_Mem_Read(&hI2cCamera, DeviceAddr, Reg, I2C_MEMADD_SIZE_16BIT, - &value, 1, 100); + status = HAL_I2C_Mem_Read(&hI2cCamera, DeviceAddr, Reg, I2C_MEMADD_SIZE_16BIT, &value, 1, 100); if (status != HAL_OK) { I2C_Error(DeviceAddr); @@ -151,7 +150,7 @@ static void I2C_MspInit(void) */ static void I2C_Error(uint8_t Addr) { - (void)Addr; /* Unused parameter */ + (void)Addr; /* Unused parameter */ /* De-initialize and re-initialize I2C to recover from error */ HAL_I2C_DeInit(&hI2cCamera); @@ -216,32 +215,52 @@ const uint16_t OV5640_Init[][2] = { * @brief Resolution configuration for 640x480 (VGA) */ const uint16_t OV5640_VGA[][2] = { - {0x3808, 0x02}, {0x3809, 0x80}, {0x380a, 0x01}, {0x380b, 0xE0}, - {0x4300, 0x6F}, {0x4740, 0x22}, {0x501F, 0x01}, + {0x3808, 0x02}, + {0x3809, 0x80}, + {0x380a, 0x01}, + {0x380b, 0xE0}, + {0x4300, 0x6F}, + {0x4740, 0x22}, + {0x501F, 0x01}, }; /** * @brief Resolution configuration for 480x272 */ const uint16_t OV5640_480x272[][2] = { - {0x3808, 0x01}, {0x3809, 0xE0}, {0x380a, 0x01}, {0x380b, 0x10}, - {0x4300, 0x6F}, {0x4740, 0x22}, {0x501f, 0x01}, + {0x3808, 0x01}, + {0x3809, 0xE0}, + {0x380a, 0x01}, + {0x380b, 0x10}, + {0x4300, 0x6F}, + {0x4740, 0x22}, + {0x501f, 0x01}, }; /** * @brief Resolution configuration for 320x240 (QVGA) */ const uint16_t OV5640_QVGA[][2] = { - {0x3808, 0x01}, {0x3809, 0x40}, {0x380a, 0x00}, {0x380b, 0xF0}, - {0x4300, 0x6F}, {0x4740, 0x22}, {0x501f, 0x01}, + {0x3808, 0x01}, + {0x3809, 0x40}, + {0x380a, 0x00}, + {0x380b, 0xF0}, + {0x4300, 0x6F}, + {0x4740, 0x22}, + {0x501f, 0x01}, }; /** * @brief Resolution configuration for 160x120 (QQVGA) */ const uint16_t OV5640_QQVGA[][2] = { - {0x3808, 0x00}, {0x3809, 0xA0}, {0x380a, 0x00}, {0x380b, 0x78}, - {0x4300, 0x6F}, {0x4740, 0x22}, {0x501f, 0x01}, + {0x3808, 0x00}, + {0x3809, 0xA0}, + {0x380a, 0x00}, + {0x380b, 0x78}, + {0x4300, 0x6F}, + {0x4740, 0x22}, + {0x501f, 0x01}, }; /** @@ -250,7 +269,7 @@ const uint16_t OV5640_QQVGA[][2] = { CAMERA_DrvTypeDef ov5640_drv = { ov5640_Init, ov5640_ReadID, - NULL, /* Config function removed */ + NULL, /* Config function removed */ }; /** @@ -332,7 +351,7 @@ int OV5640_SetPixelFormat(uint16_t DeviceAddr, uint32_t format) { /* Validate format */ if (format != 0x10 && format != 0x6F && format != 0x23 && format != 0x30) { - return -1; /* Invalid format */ + return -1; /* Invalid format */ } /* Write format to register 0x4300 (format control) */ @@ -610,12 +629,10 @@ static int camera_setRes(ImageResolution resolution) /** * @brief STM32 OV5640 camera interface structure */ -camera_t stm32_ov5640 = { - .init = camera_init, - .capture = camera_capture, - .stop = camera_stop, - .setRes = camera_setRes -}; +camera_t stm32_ov5640 = {.init = camera_init, + .capture = camera_capture, + .stop = camera_stop, + .setRes = camera_setRes}; /** * @brief DCMI frame event callback - called when a complete frame is captured diff --git a/device/camera/ov5640/stm32_ov5640.h b/device/camera/ov5640/stm32_ov5640.h index 09f3227..08e8e17 100644 --- a/device/camera/ov5640/stm32_ov5640.h +++ b/device/camera/ov5640/stm32_ov5640.h @@ -13,37 +13,38 @@ #ifdef DEVICE_OV5640 -#ifndef __OV5640_H -#define __OV5640_H + #ifndef __OV5640_H + #define __OV5640_H -#ifdef __cplusplus + #ifdef __cplusplus extern "C" { -#endif + #endif -#include "device/camera/camera.h" + #include "device/camera/camera.h" -/** - * @brief OV5640 chip ID - */ -#define OV5640_ID 0x5640 + /** + * @brief OV5640 chip ID + */ + #define OV5640_ID 0x5640 -/** - * @brief OV5640 I2C address (7-bit address shifted left by 1) - */ -#define CAMERA_I2C_ADDRESS 0x78 + /** + * @brief OV5640 I2C address (7-bit address shifted left by 1) + */ + #define CAMERA_I2C_ADDRESS 0x78 -/** - * @brief OV5640 pixel format constants - */ -#define OV5640_PIXEL_FORMAT_Y8 0x10 /**< 8-bit grayscale */ -#define OV5640_PIXEL_FORMAT_RGB565 0x6F /**< RGB565 format */ -#define OV5640_PIXEL_FORMAT_RGB888 0x23 /**< RGB888 format */ -#define OV5640_PIXEL_FORMAT_YUV422 0x30 /**< YUV422 format */ + /** + * @brief OV5640 pixel format constants + */ + #define OV5640_PIXEL_FORMAT_Y8 0x10 /**< 8-bit grayscale */ + #define OV5640_PIXEL_FORMAT_RGB565 0x6F /**< RGB565 format */ + #define OV5640_PIXEL_FORMAT_RGB888 0x23 /**< RGB888 format */ + #define OV5640_PIXEL_FORMAT_YUV422 0x30 /**< YUV422 format */ /** * @brief Initialize OV5640 camera * @param DeviceAddr: I2C device address (typically 0x78) - * @param resolution: Camera resolution (CAMERA_R160x120, CAMERA_R320x240, CAMERA_R480x272, CAMERA_R640x480) + * @param resolution: Camera resolution (CAMERA_R160x120, CAMERA_R320x240, CAMERA_R480x272, + * CAMERA_R640x480) */ void ov5640_Init(uint16_t DeviceAddr, uint32_t resolution); @@ -77,10 +78,10 @@ extern camera_t stm32_ov5640; */ extern volatile uint8_t frame_capture_complete; -#ifdef __cplusplus + #ifdef __cplusplus } -#endif + #endif -#endif /* __OV5640_H */ + #endif /* __OV5640_H */ #endif /* DEVICE_OV5640 */ diff --git a/device/display/display.h b/device/display/display.h index 7df6ae7..cf24126 100755 --- a/device/display/display.h +++ b/device/display/display.h @@ -5,6 +5,7 @@ #define DISPLAY_H #include "core/image.h" + #include #include diff --git a/imgproc/connectedcomponents.c b/imgproc/connectedcomponents.c index fba9249..9fd62ed 100644 --- a/imgproc/connectedcomponents.c +++ b/imgproc/connectedcomponents.c @@ -2,6 +2,7 @@ // Copyright (c) 2025 EmbedDIP #include "connectedcomponents.h" + #include /* For memset */ embeddip_status_t connectedComponents(const Image *src, Image *dst, uint32_t *label_count) diff --git a/imgproc/misc.c b/imgproc/misc.c index 24bea29..0b0f5f0 100644 --- a/imgproc/misc.c +++ b/imgproc/misc.c @@ -324,13 +324,15 @@ embeddip_status_t convertTo(Image *img) * image to a given reference color. * * @param[in] inImg Pointer to the input RGB image (3 channels, interleaved as RGBRGB...). - * @param[out] outImg Pointer to the output grayscale image (1 channel, same width and height as input). + * @param[out] outImg Pointer to the output grayscale image (1 channel, same width and height as + * input). * @param[in] R_ref Reference Red channel value (0–255). * @param[in] G_ref Reference Green channel value (0–255). * @param[in] B_ref Reference Blue channel value (0–255). * @return EMBEDDIP_OK on success, error code on failure */ -embeddip_status_t dist(const Image *inImg, Image *outImg, uint8_t R_ref, uint8_t G_ref, uint8_t B_ref) +embeddip_status_t +dist(const Image *inImg, Image *outImg, uint8_t R_ref, uint8_t G_ref, uint8_t B_ref) { int totalPixels = inImg->width * inImg->height; diff --git a/imgproc/misc.h b/imgproc/misc.h index 697a9dc..8b66047 100644 --- a/imgproc/misc.h +++ b/imgproc/misc.h @@ -47,7 +47,8 @@ embeddip_status_t convertTo(Image *img); * @param B_ref Reference Blue channel value (0–255) * @return EMBEDDIP_OK on success, error code on failure */ -embeddip_status_t dist(const Image *inImg, Image *outImg, uint8_t R_ref, uint8_t G_ref, uint8_t B_ref); +embeddip_status_t +dist(const Image *inImg, Image *outImg, uint8_t R_ref, uint8_t G_ref, uint8_t B_ref); /** * @brief Bitwise AND operation on binary masks diff --git a/wrapper/ImageWrapper.cpp b/wrapper/ImageWrapper.cpp index cea39db..283297a 100755 --- a/wrapper/ImageWrapper.cpp +++ b/wrapper/ImageWrapper.cpp @@ -571,8 +571,9 @@ void Image::add(const Image &other, Image &out) const /** * @brief Computes color distance from this RGB image to a reference color. */ -void Image::dist(Image &out, uint8_t r, uint8_t g, uint8_t b) const { - ::dist(raw(), out.raw(), r, g, b); +void Image::dist(Image &out, uint8_t r, uint8_t g, uint8_t b) const +{ + ::dist(raw(), out.raw(), r, g, b); } /**