Skip to content
Closed
Show file tree
Hide file tree
Changes from 13 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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ Try the [live demo](https://ryan-millard.github.io/Img2Num/)!
- [C++](https://ryan-millard.github.io/Img2Num/info/docs/next/cpp/)
- [C](https://ryan-millard.github.io/Img2Num/info/docs/next/c/)
- [JavaScript](https://ryan-millard.github.io/Img2Num/info/docs/next/js/)
- [Python](https://ryan-millard.github.io/Img2Num/info/docs/next/python/)
- [Internal docs](https://ryan-millard.github.io/Img2Num/info/docs/next/internal/) (for contributors)

### Community
Expand Down
1 change: 0 additions & 1 deletion bindings/c/include/cimg2num.h
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@ extern "C" {
/// @brief Configuration options for image_to_svg.
/// @ingroup CIMG2NUM_H
typedef struct img2num_ImageToSvgConfig {

/// Configuration settings for the bilateral filter in image_to_svg.
struct BilateralFilterConfig {
/// Standard deviation for spatial Gaussian (proximity weight).
Expand Down
39 changes: 12 additions & 27 deletions bindings/c/src/cimg2num.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,33 +8,19 @@
extern "C" {

static img2num::ImageToSvgConfig to_cpp(const img2num_ImageToSvgConfig &c) {
return {
.bilateral_filter{
.sigma_spatial = c.bilateral_filter.sigma_spatial,
.sigma_range = c.bilateral_filter.sigma_range
},
.kmeans{
.k = c.kmeans.k,
.max_iter = c.kmeans.max_iter
},
.min_cluster_area = c.min_cluster_area,
.color_space = c.color_space
};
return {.bilateral_filter{.sigma_spatial = c.bilateral_filter.sigma_spatial,
.sigma_range = c.bilateral_filter.sigma_range},
.kmeans{.k = c.kmeans.k, .max_iter = c.kmeans.max_iter},
.min_cluster_area = c.min_cluster_area,
.color_space = c.color_space};

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify C++ standard/toolchain config and scan for this initializer pattern.
rg -n "CMAKE_CXX_STANDARD|set\\(CMAKE_CXX_STANDARD" CMakeLists.txt **/CMakeLists.txt
rg -nP '\.\w+\s*\{' bindings/c/src/cimg2num.cpp

Repository: Ryan-Millard/Img2Num

Length of output: 586


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Check for any C++ standard overrides beyond the first matches.
rg -n "CMAKE_CXX_STANDARD|CMAKE_CXX_FLAGS|std=c\\+\\+|gnu\\+\\+|CXX_STANDARD|target_compile_features|set\\(CXX_STANDARD" \
  **/CMakeLists.txt **/*.cmake CMakeLists.txt || true

# 2) See whether this (non-`=`) nested designated-initializer pattern appears elsewhere.
rg -nP '\.\w+\s*\{[^}]*\.' bindings/c/src/cimg2num.cpp || true
rg -nP '\.\w+\s*\{' bindings/c/src/cimg2num.cpp || true

# 3) Confirm there are only these two specific return statements (context).
rg -n "return \\{\\." -n bindings/c/src/cimg2num.cpp && sed -n '1,140p' bindings/c/src/cimg2num.cpp || true

Repository: Ryan-Millard/Img2Num

Length of output: 5350


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# 1) Find other occurrences of ".name { ... }" style aggregates in C++.
# Use narrower regex to avoid too many brace-init false positives.
rg -nP '\.\w+\s*\{' . || true

# 2) Look for any -std= or CXXFLAGS/EM_FLAGS overrides that could bump the standard.
rg -n "(-std=c\\+\\+\\d+|CXXFLAGS|EMSDK|EMSCRIPTEN|EM_CXXFLAGS|EM_FLAGS|EM_LDFLAGS)" . || true

# 3) If the repo has JS/WASM build tooling, search for C++ standard settings there too.
rg -n "c\\+\\+\\d+|CXX_STANDARD|CMAKE_CXX_STANDARD|-std=" . || true

Repository: Ryan-Millard/Img2Num

Length of output: 24179


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Search for C++20-style designated initializers with '=' inside brace-init-lists.
# Limit to C/C++ sources to avoid noise.
rg -nP '\{\s*\.\w+\s*=\s*' --glob '*.{c,cc,cpp,cxx,h,hpp,hxx}' . || true

Repository: Ryan-Millard/Img2Num

Length of output: 502


🌐 Web query:

C++ designated initializer syntax without '=' .member{...} is it valid and which compilers/standards support it?

💡 Result:

Yes, the designated initializer syntax using braces without an equals sign, such as.member{value}, is valid in C++20 and later standards [1][2]. In C++20, designated initializers were introduced for aggregate initialization [1][3]. The syntax allows for both the assignment-style (.member = value) and the brace-initialization-style (.member{value}) [2]. Key points regarding this syntax: - Standard Support: This is a standard feature of C++20 and subsequent versions [1][2]. - Aggregate Initialization: It is strictly limited to aggregate types [1][2]. - Syntax Flexibility: You can use either.member = value or.member{value} [2]. These can also be mixed within the same initializer list if desired [2]. - Compiler Support: Modern compilers that support the C++20 standard (such as GCC, Clang, and MSVC) support this syntax when the appropriate standard flag (e.g., -std=c++20) is enabled [2][3]. Note that this differs from the C programming language, which requires the equals sign (.member = value) and does not support the brace-only syntax for designated initializers [1][4]. Additionally, C++ designated initializers have stricter requirements than C, such as requiring members to be initialized in their declaration order [1][2].

Citations:


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "CMAKE_CXX_STANDARD\s*20|CMAKE_CXX_STANDARD\s*2[0-9]|CXX_STANDARD\s*20|target_compile_options\\(|CMAKE_CXX_FLAGS.*-std=|CXXFLAGS.*-std=|-std=c\\+\\+20|-std=gnu\\+\\+20|c\\+\\+20|EM.*CXXFLAGS|EM_FLAGS" . --glob '*.{cmake,CMakeLists.txt,sh,txt,md,js,ts,py,c,h,cc,cpp,cxx,hpp,hxx}'

Repository: Ryan-Millard/Img2Num

Length of output: 1279


🏁 Script executed:

#!/bin/bash
set -euo pipefail

rg -n "struct\\s+ImageToSvgConfig|ImageToSvgConfig\\b" core/include bindings/c/include bindings/c/src bindings/c/CMakeLists.txt --glob '*.{h,hpp,cpp,c,cc}'

Repository: Ryan-Millard/Img2Num

Length of output: 1202


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Print the C++ struct definition for ImageToSvgConfig
nl -ba core/include/img2num.h | sed -n '1,140p'

Repository: Ryan-Millard/Img2Num

Length of output: 107


🏁 Script executed:

#!/bin/bash
set -euo pipefail

cat -n core/include/img2num.h | sed -n '1,160p'

Repository: Ryan-Millard/Img2Num

Length of output: 3868


Fix C++17 build break in bindings/c/src/cimg2num.cpp

return {.member{...}, ...} uses C++20 designated-initializer syntax (.field = ... / brace form), but this repo forces CMAKE_CXX_STANDARD 17, so it can fail to compile on C++17 toolchains. Replace with C++17-compatible aggregate initialization (or explicit assignments) for img2num::ImageToSvgConfig.

Proposed fix
// to_cpp(...)
return {{c.bilateral_filter.sigma_spatial, c.bilateral_filter.sigma_range},
        {c.kmeans.k, c.kmeans.max_iter},
        c.min_cluster_area,
        c.color_space};

// to_c(...)
return {{cpp.bilateral_filter.sigma_spatial, cpp.bilateral_filter.sigma_range},
        {cpp.kmeans.k, cpp.kmeans.max_iter},
        cpp.min_cluster_area,
        cpp.color_space};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bindings/c/src/cimg2num.cpp` around lines 11 - 15, The return uses C++20
designated-initializer syntax which breaks C++17 builds; update the construction
of img2num::ImageToSvgConfig in the to_cpp/to_c conversion functions to use
C++17-compatible aggregate initialization or explicit member assignment instead
of `.member{...}` syntax — e.g. replace `return {.bilateral_filter{...},
.kmeans{...}, .min_cluster_area, .color_space}` with a plain braced aggregate
`return {{...}, {...}, ..., ...}` or build a local ImageToSvgConfig and assign
bilateral_filter, kmeans, min_cluster_area, color_space members before
returning.

}

static img2num_ImageToSvgConfig to_c(const img2num::ImageToSvgConfig &cpp) {
return {
.bilateral_filter{
.sigma_spatial = cpp.bilateral_filter.sigma_spatial,
.sigma_range = cpp.bilateral_filter.sigma_range
},
.kmeans{
.k = cpp.kmeans.k,
.max_iter = cpp.kmeans.max_iter
},
.min_cluster_area = cpp.min_cluster_area,
.color_space = cpp.color_space
};
return {.bilateral_filter{.sigma_spatial = cpp.bilateral_filter.sigma_spatial,
.sigma_range = cpp.bilateral_filter.sigma_range},
.kmeans{.k = cpp.kmeans.k, .max_iter = cpp.kmeans.max_iter},
.min_cluster_area = cpp.min_cluster_area,
.color_space = cpp.color_space};
}

img2num_ImageToSvgConfig img2num_ImageToSvgConfig_default(void) {
Expand Down Expand Up @@ -101,16 +87,15 @@ char *img2num_image_to_svg(const uint8_t *data, const int width, const int heigh

img2num::clear_last_error_and_catch(
[&](const uint8_t *d, const int w, const int h) {
std::string svg{ img2num::image_to_svg(d, w, h, to_cpp(cfg)) };
std::string svg{img2num::image_to_svg(d, w, h, to_cpp(cfg))};

result = static_cast<char *>(std::malloc(svg.size() + 1));
if (!result) {
return; // Allocation failed
}
std::memcpy(result, svg.c_str(), svg.size() + 1);
},
data, width, height
);
data, width, height);

return result;
}
Expand Down
2 changes: 1 addition & 1 deletion bindings/js/doxygen/home_page.dox
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
* To build the JavaScript/WASM bindings, follow these steps:
*
* \code{.sh}
* emcmake cmake -B build-wasm
* emcmake cmake -DCMAKE_BUILD_TYPE=Release -B build-wasm
* cmake --build build-wasm
* \endcode
*
Expand Down
14 changes: 14 additions & 0 deletions bindings/py/Doxyfile.internal
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
@INCLUDE = ../../doxygen/Doxyfile.base

PROJECT_NAME = "Img2Num Python Bindings (Internal Developer Docs)"

PROJECT_NUMBER = dev

INPUT = src ../../doxygen/*.dox doxygen/home_page.dox
OUTPUT_DIRECTORY = ../../docs/static/docs/internal/bindings/py/api

RECURSIVE = YES

EXTRACT_PRIVATE = YES
EXTRACT_STATIC = YES
INTERNAL_DOCS = YES
95 changes: 95 additions & 0 deletions bindings/py/doxygen/home_page.dox
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/*! \mainpage Img2Num Python Bindings
*
* \section intro_sec Introduction
*
* The **Img2Num Python Bindings** provide a Python interface to the core Img2Num
* image processing library via pybind11. These bindings wrap the underlying C++
* functionality, exposing image processing operations such as filtering, clustering,
* thresholding, and SVG conversion to Python code.
*
* All functions operate on `numpy.ndarray` buffers and return new image data, making
* them easy to integrate into Python-based image processing pipelines.
*
* \section features_sec Key Features
*
* The Python bindings expose the following core capabilities:
* - **FFT-based Gaussian blur** — Fast frequency-domain blurring
* - **Image inversion** — Invert pixel values
* - **Thresholding** — Standard and black-thresholding operations
* - **K-means clustering** — Pixel clustering for color quantization
* - **Bilateral filtering** — Edge-preserving smoothing
* - **Label-to-SVG conversion** — Convert labeled images to SVG strings
* - **Image-to-SVG conversion** — Full image vectorization with configurable parameters
* - **ImageToSvgConfig** — Python-accessible configuration for SVG generation
*
* \section building_sec Building the Python Bindings
*
* The Python bindings are built using CMake from the project root:
*
* \code{.sh}
* cmake -B build .
* cmake --build build
* \endcode
*
* \section install_sec Installation
*
* Install the bindings to your system:
*
* \code{.sh}
* cmake --install build
* \endcode
*
* The compiled `_img2num` module will be available for import in Python:
*
* \code{.py}
* import _img2num
* \endcode
*
* \section usage_sec Usage Example
*
* \code{.py}
* import numpy as np
* import _img2num
*
* # Load image as uint8 numpy array
* image = np.array([...], dtype=np.uint8).reshape((height, width, 3))
*
* # Apply Gaussian blur
* blurred = _img2num.gaussian_blur_fft(image, width, height, sigma=2.0)
*
* # Invert colors
* inverted = _img2num.invert_image(image, width, height)
*
* # Threshold
* thresholded = _img2num.threshold_image(image, width, height, num_thresholds=4)
*
* # K-means clustering
* clustered_data, labels = _img2num.kmeans(image, width, height, k=8, max_iter=50)
Comment thread
Ryan-Millard marked this conversation as resolved.
Resolved
*
* # Bilateral filtering
* filtered = _img2num.bilateral_filter(image, width, height, sigma_spatial=3.0, sigma_range=50.0)
Comment thread
Ryan-Millard marked this conversation as resolved.
Resolved
*
* # SVG conversion
* config = _img2num.ImageToSvgConfig()
* svg_str = _img2num.image_to_svg(image, width, height, config)
* \endcode
*
* \section api_sec API Reference
*
* Browse the detailed function and class documentation:
* - \ref img2num_functions "Core Image Processing Functions"
* - \ref img2num_classes "Configuration Classes"
*/

/** \defgroup img2num_functions Image Processing Functions
* @ingroup mainpage
* @brief Image processing functions exposed via Python bindings.
* @{
*/

/** \defgroup img2num_classes Configuration Classes
* @ingroup mainpage
* @brief Configuration classes for image processing and SVG generation.
* @{
*/

Loading
Loading