diff --git a/.github/workflows/code-quality.yml b/.github/workflows/code-quality.yml index 7407c34..4786776 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,8 +24,22 @@ 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 \ + --enable=warning,performance,portability \ --error-exitcode=1 \ --inline-suppr \ --suppress=missingIncludeSystem \ @@ -31,19 +47,42 @@ 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 + - 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..." @@ -63,8 +102,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 @@ -75,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/.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: | 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 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; 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/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 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/filter.c b/imgproc/filter.c index fce8a9d..9b092ac 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; @@ -984,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; } @@ -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; 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; } 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); } /**