Skip to content

feat(python bindings): enable python bindings and example#307

Merged
Ryan-Millard merged 51 commits into
Ryan-Millard:mainfrom
Krasner:feat/python
Apr 25, 2026
Merged

feat(python bindings): enable python bindings and example#307
Ryan-Millard merged 51 commits into
Ryan-Millard:mainfrom
Krasner:feat/python

Conversation

@Krasner
Copy link
Copy Markdown
Collaborator

@Krasner Krasner commented Apr 10, 2026

Enable python bindings as part of the c/cpp compilation. Update Dockerfile to have necessary python components.

IMPORTANT: Update docker container on dockerhub!

As part of building the c/cpp apps python bindings will be built.


cmake -B build-c-cpp/ .
cmake --build build-c-cpp
cd example_apps/console-py
python3 main.py

Note this will generate the img2num python lib in build-c-cpp/bindings/python/

What was changed & why

Fixes: #

Changes

Testing & Verification

Additional Resources

Summary by CodeRabbit

  • New Features

    • Python bindings for image processing (in-place filters, k-means clustering, and labels→SVG export)
    • Python example app demonstrating end-to-end image processing and SVG/Saved outputs
  • Bug Fixes

    • Corrected LAB→RGB color channel assignment
  • Chores

    • Dev Docker image now includes Python build/runtime tooling and common packages
    • Build now includes native Python bindings, enables position-independent code, and emits Python-related build diagnostics

@Krasner Krasner requested a review from Ryan-Millard as a code owner April 10, 2026 21:22
@github-actions github-actions Bot added c/cpp Changes to C or C++ files example-app Updates to code related to demonstration (example) applications core build-system docs labels Apr 10, 2026
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 10, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Adds native Python bindings (pybind11) and example usage, updates build and dev container to install Python tooling, enables PIC for C targets, and fixes an internal CIELAB→RGB assignment bug.

Changes

Cohort / File(s) Summary
Top-level build
CMakeLists.txt
Conditionally adds bindings/python when EMSCRIPTEN is not set.
Python bindings build
bindings/python/CMakeLists.txt, core/CMakeLists.txt, bindings/c/CMakeLists.txt
New pybind11 module target img2num (output to Python site-arch); emits Python detection messages; sets POSITION_INDEPENDENT_CODE=ON for library targets.
Python bindings implementation
bindings/python/src/img2num_pybind.cpp
New pybind11 module img2num exposing image ops (gaussian_blur_fft, invert_image, threshold_image, black_threshold_image, bilateral_filter), kmeans (returns image + labels), and labels_to_svg (returns SVG string and frees C string).
Dev container
Dockerfile.dev
Installs python3-dev, python3-pybind11, python3-pip and pip-installs numpy and opencv-python in the dev image.
Example app
example-apps/console-py/main.py
New script demonstrating RGBA input, bilateral filter, k-means, and labels→SVG workflow using img2num.
Core implementation fix
core/include/internal/cielab_impl.h
Assigns computed r/g/b values into output RGBA struct in lab_to_rgb.

Sequence Diagram(s)

sequenceDiagram
    participant Py as Python script
    participant NumPy as NumPy ndarray
    participant Module as pybind11 img2num
    participant Lib as Img2Num C library
    participant CAlloc as C allocator

    Py->>NumPy: prepare RGBA ndarray
    Py->>Module: call bilateral_filter(ndarray, w, h, ...)
    Module->>NumPy: get writable data pointer
    Module->>Lib: img2num_bilateral_filter(ptr, w, h, ...)
    Lib-->>Module: in-place modified buffer
    Module-->>Py: returns None (in-place)

    Py->>Module: call kmeans(ndarray, w, h, k, ...)
    Module->>NumPy: read input pointer
    Module->>Lib: img2num_kmeans(in_ptr, out_ptr, labels_ptr, ...)
    Lib-->>Module: fills out buffers
    Module-->>Py: return (out_image_ndarray, labels_ndarray)

    Py->>Module: call labels_to_svg(data, labels, w, h, min_area)
    Module->>Lib: img2num_labels_to_svg(...)
    Lib-->>Module: returns char* svg_cstr
    Module->>CAlloc: free(svg_cstr)
    Module-->>Py: return svg_str
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

Poem

🐰 I hopped through headers, bindings, and C,
NumPy clouds and pybind11 for me,
Filters and clusters in a tidy row,
An SVG tale the labels show,
Hooray — build, import, and let it go!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: enabling Python bindings and adding a Python example application. It directly reflects the core objectives of the PR.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@Krasner Krasner added the python changes to python bindings or python code label Apr 10, 2026
Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🧹 Nitpick comments (5)
core/CMakeLists.txt (1)

22-25: Debug messages may print empty values and use inconsistent log level.

Python3_LIBRARIES and Python3_INCLUDE_DIRS are only populated when find_package(Python3) includes the Development component. Since line 21 only requests Interpreter, these variables will likely be empty. Also, these messages lack the STATUS keyword used elsewhere in this file.

Consider removing these debug messages or using STATUS for consistency:

♻️ Suggested fix
 find_package(Python3 COMPONENTS Interpreter REQUIRED)
-message("Python Version: ${Python3_VERSION}")
-message("Python Version: ${Python3_VERSION_MAJOR}.${Python3_VERSION_MINOR}")
-message("Python Library: ${Python3_LIBRARIES}")
-message("Python Include: ${Python3_INCLUDE_DIRS}")
+message(STATUS "Python Version: ${Python3_VERSION}")
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@core/CMakeLists.txt` around lines 22 - 25, The current debug messages use
message() without STATUS and print variables (Python3_LIBRARIES,
Python3_INCLUDE_DIRS) that will be empty because find_package(Python3) requested
only the Interpreter component; update the messages to either remove the two
library/include lines or change find_package(Python3) to request the Development
component if you need them, and ensure all message calls use message(STATUS ...)
for consistency with other logs (refer to the find_package(Python3) invocation
and the message(...) lines in this diff).
docker-compose.yml (1)

7-7: Reminder: Update the Docker Hub image.

Commenting out the image line forces a local build from Dockerfile.dev. Per the PR description, the Docker container on Docker Hub needs to be updated with the new Python components. Once updated, this line should be uncommented to restore the cached image workflow.

Would you like me to open an issue to track updating the Docker Hub image (ryanmillard/img2num-dev:latest)?

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docker-compose.yml` at line 7, Uncomment and restore the Docker Hub image
line in docker-compose.yml so the service uses ryanmillard/img2num-dev:latest
(or a new versioned tag) instead of forcing a local build; then rebuild and push
an updated Docker Hub image that includes the new Python components (tag it
appropriately, e.g. :latest or a semver tag) and update the image reference if
needed so the compose file pulls the updated image rather than building from
Dockerfile.dev.
example-apps/console-py/main.py (1)

21-28: Consider named constants for algorithm parameters.

Magic numbers make it harder to understand what each parameter controls. For an example script, adding comments or constants improves learnability:

📖 Suggested improvement
+# Bilateral filter parameters
+SIGMA_SPATIAL = 3
+SIGMA_RANGE = 50
+COLOR_SPACE_RGB = 0
+
 # bilateral filter in-place
-img2num.bilateral_filter(img, width, height, 3, 50, 0)
+img2num.bilateral_filter(img, width, height, SIGMA_SPATIAL, SIGMA_RANGE, COLOR_SPACE_RGB)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@example-apps/console-py/main.py` around lines 21 - 28, Replace the numeric
literals passed into img2num.bilateral_filter, img2num.kmeans, and
img2num.labels_to_svg with named constants (e.g., BILATERAL_DIAMETER,
BILATERAL_SIGMA_COLOR, BILATERAL_SIGMA_SPACE, KMEANS_CLUSTERS,
KMEANS_ITERATIONS, KMEANS_ATTEMPTS, SVG_SCALE) and add short inline comments
where those constants are declared to explain their meaning; update calls in the
main function (the usages of img2num.bilateral_filter, img2num.kmeans, and
img2num.labels_to_svg) to use those constant names instead of magic numbers so
the parameters are self-describing and easy to tweak for examples.
Dockerfile.dev (1)

62-64: Consider combining pip installs and pinning versions.

Two separate RUN commands create unnecessary layers. Combining them and pinning versions improves build efficiency and reproducibility:

♻️ Suggested fix
 # Install some pip packages
-RUN python3 -m pip install numpy --break-system-packages
-RUN python3 -m pip install opencv-python --break-system-packages
+RUN python3 -m pip install --break-system-packages \
+    numpy==2.0.0 \
+    opencv-python==4.10.0.84
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@Dockerfile.dev` around lines 62 - 64, Combine the two separate RUN pip
install lines into a single RUN to avoid extra image layers and pin package
versions for reproducibility; replace the two RUN lines that call "python3 -m
pip install numpy --break-system-packages" and "python3 -m pip install
opencv-python --break-system-packages" with one RUN that installs both packages
with explicit version pins (e.g., numpy==<version> opencv-python==<version>) and
include flags like --no-cache-dir (or use a requirements.txt/constraints file)
while preserving --break-system-packages as needed.
CMakeLists.txt (1)

30-32: Consider making Python bindings optional.

Python bindings are built unconditionally for non-Emscripten builds. Users who don't need Python support may prefer to skip this (especially if pybind11 is not installed). Consider adding an option:

♻️ Suggested approach
+option(IMG2NUM_BUILD_PYTHON_BINDINGS "Build Python bindings" ON)
+
 if(EMSCRIPTEN)
   # JS bindings
   add_subdirectory(bindings/js)
 else()
-  # Python bindings
-  add_subdirectory(bindings/python)
+  if(IMG2NUM_BUILD_PYTHON_BINDINGS)
+    # Python bindings
+    add_subdirectory(bindings/python)
+  endif()
   
   if(IMG2NUM_BUILD_EXAMPLES)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CMakeLists.txt` around lines 30 - 32, Add a CMake option to make building
Python bindings conditional: introduce an option like BUILD_PYTHON_BINDINGS
(default ON for non-Emscripten) and wrap the existing
add_subdirectory(bindings/python) call in an if(BUILD_PYTHON_BINDINGS) ...
endif() block; also ensure the option defaults to OFF for Emscripten builds
(detect via CMAKE_SYSTEM_NAME or EMSCRIPTEN variable) and update any README or
top-level help text accordingly so users without pybind11 can skip the Python
bindings.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindings/python/CMakeLists.txt`:
- Around line 11-12: Remove the deprecated find_package(PythonLibs REQUIRED)
call and the hardcoded PYTHON_MODULE_EXTENSION set (".so"); instead rely on the
existing find_package(Python3 COMPONENTS Development) already used and let
pybind11_add_module pick the correct extension for each platform. Edit the
CMakeLists to delete references to find_package(PythonLibs) and remove or stop
using the PYTHON_MODULE_EXTENSION variable so the module build uses
Python3/pybind11 defaults.

In `@bindings/python/src/img2num_pybind.cpp`:
- Around line 46-67: Add input validation at the start of the kmeans binding
lambda: verify data.request().ndim and buffer size match width * height *
channels (derive channels from color_space or data.shape[2]) and throw a
py::value_error if sizes mismatch; validate that k and max_iter are >= 1 (and
cast to unsigned if needed before passing to img2num_kmeans) and throw
py::value_error for invalid values; ensure any invalid color_space is rejected
similarly so img2num_kmeans is only called with well-formed inputs.
- Around line 15-39: The wrappers for gaussian_blur_fft, invert_image,
threshold_image, black_threshold_image, and bilateral_filter lack validation
that the provided py::array_t buffer size matches width*height (and optionally
channels if expected); add a buffer check in each lambda using image.request()
or image.size() and confirm it equals width * height (or width * height *
channels if the C API expects multi-channel data), also ensure the array is
contiguous and writable, and throw a descriptive pybind11::value_error or
std::invalid_argument before calling img2num_* functions (e.g., in the
gaussian_blur_fft, bilateral_filter lambdas) so callers get a clear error
instead of risking OOB memory access.

In `@example-apps/console-py/main.py`:
- Around line 15-16: The code calls cv2.imread("../../IMG_5554.jpg") and
immediately passes img into cv2.cvtColor which will raise a cryptic error if
imread returns None; modify the flow around cv2.imread and cv2.cvtColor so that
after calling cv2.imread you check if img is None (or if the file failed to
load), and if so handle it (e.g., log/raise a clear error or exit) before
calling cv2.cvtColor(img, cv2.COLOR_BGR2RGBA); keep the same variable name img
and ensure the error handling provides a helpful message about the
missing/unreadable image path.

---

Nitpick comments:
In `@CMakeLists.txt`:
- Around line 30-32: Add a CMake option to make building Python bindings
conditional: introduce an option like BUILD_PYTHON_BINDINGS (default ON for
non-Emscripten) and wrap the existing add_subdirectory(bindings/python) call in
an if(BUILD_PYTHON_BINDINGS) ... endif() block; also ensure the option defaults
to OFF for Emscripten builds (detect via CMAKE_SYSTEM_NAME or EMSCRIPTEN
variable) and update any README or top-level help text accordingly so users
without pybind11 can skip the Python bindings.

In `@core/CMakeLists.txt`:
- Around line 22-25: The current debug messages use message() without STATUS and
print variables (Python3_LIBRARIES, Python3_INCLUDE_DIRS) that will be empty
because find_package(Python3) requested only the Interpreter component; update
the messages to either remove the two library/include lines or change
find_package(Python3) to request the Development component if you need them, and
ensure all message calls use message(STATUS ...) for consistency with other logs
(refer to the find_package(Python3) invocation and the message(...) lines in
this diff).

In `@docker-compose.yml`:
- Line 7: Uncomment and restore the Docker Hub image line in docker-compose.yml
so the service uses ryanmillard/img2num-dev:latest (or a new versioned tag)
instead of forcing a local build; then rebuild and push an updated Docker Hub
image that includes the new Python components (tag it appropriately, e.g.
:latest or a semver tag) and update the image reference if needed so the compose
file pulls the updated image rather than building from Dockerfile.dev.

In `@Dockerfile.dev`:
- Around line 62-64: Combine the two separate RUN pip install lines into a
single RUN to avoid extra image layers and pin package versions for
reproducibility; replace the two RUN lines that call "python3 -m pip install
numpy --break-system-packages" and "python3 -m pip install opencv-python
--break-system-packages" with one RUN that installs both packages with explicit
version pins (e.g., numpy==<version> opencv-python==<version>) and include flags
like --no-cache-dir (or use a requirements.txt/constraints file) while
preserving --break-system-packages as needed.

In `@example-apps/console-py/main.py`:
- Around line 21-28: Replace the numeric literals passed into
img2num.bilateral_filter, img2num.kmeans, and img2num.labels_to_svg with named
constants (e.g., BILATERAL_DIAMETER, BILATERAL_SIGMA_COLOR,
BILATERAL_SIGMA_SPACE, KMEANS_CLUSTERS, KMEANS_ITERATIONS, KMEANS_ATTEMPTS,
SVG_SCALE) and add short inline comments where those constants are declared to
explain their meaning; update calls in the main function (the usages of
img2num.bilateral_filter, img2num.kmeans, and img2num.labels_to_svg) to use
those constant names instead of magic numbers so the parameters are
self-describing and easy to tweak for examples.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 11d60ad0-e975-43a4-bccf-c33f2831930c

📥 Commits

Reviewing files that changed from the base of the PR and between b540cd8 and 083131f.

📒 Files selected for processing (8)
  • CMakeLists.txt
  • Dockerfile.dev
  • bindings/c/CMakeLists.txt
  • bindings/python/CMakeLists.txt
  • bindings/python/src/img2num_pybind.cpp
  • core/CMakeLists.txt
  • docker-compose.yml
  • example-apps/console-py/main.py

Comment thread bindings/python/CMakeLists.txt Outdated
Comment thread bindings/python/src/img2num_pybind.cpp Outdated
Comment thread bindings/python/src/img2num_pybind.cpp Outdated
Comment thread example-apps/console-py/main.py Outdated
@Ryan-Millard
Copy link
Copy Markdown
Owner

I'm building it right now.😓

We need a Docker god to help us here. These builds take far too long.

@Ryan-Millard
Copy link
Copy Markdown
Owner

How can I message you privately? As the days go by, I keep feeling the need to have a better way to communicate because this is becoming a bit tough.

A full Discord server might be overkill for us right now, but I'm not sure. I think we need a way to keep others updated and also message privately.

@Krasner
Copy link
Copy Markdown
Collaborator Author

Krasner commented Apr 11, 2026

These builds take far too long.

Yes :( Dawn takes forever

Repository owner deleted a comment from Krasner Apr 11, 2026
@Ryan-Millard
Copy link
Copy Markdown
Owner

Thanks for the information - I’ve deleted your message containing your email address just in case.

I did email you and you can access the DockerHub repo here: https://hub.docker.com/repository/docker/ryanmillard/img2num-dev/general

@Krasner
Copy link
Copy Markdown
Collaborator Author

Krasner commented Apr 11, 2026

Thanks for the information - I’ve deleted your message containing your email address just in case.

I did email you and you can access the DockerHub repo here: https://hub.docker.com/repository/docker/ryanmillard/img2num-dev/general

Thanks! Got your email. Will reply a bit later

@Krasner
Copy link
Copy Markdown
Collaborator Author

Krasner commented Apr 11, 2026

@Ryan-Millard I updated the docker image (on dockerhub). seems to be working fine. It now included the necessary additions for python binding builds

@Ryan-Millard
Copy link
Copy Markdown
Owner

@Ryan-Millard I updated the docker image (on dockerhub). seems to be working fine. It now included the necessary additions for python binding builds

Thanks! I realised that our Dockerfile.dev isn't set up very well, which is slowing us down a lot. I'm busy working on fixing that right now, so I'll only be able to review this once I'm done.

Please write some documentation for this - we need our users to know how to use the library once they import it.

I also noticed that you're referencing the Pybind build files directly from the build-c-cpp folder - that isn't great because other people might not use that folder name (mine is build). Please rather have a look at what I did for the WASM builds. I copied the files into the corresponding package location and created a convenience wrapper package around it in packages/js. See build-wasm:

ryan@Ryans-PC:~/projects/Img2Num$ ls packages/js/
build-wasm  imageToUint8ClampedArray.js  imageToUint8ClampedArray.test.js  index.js  package.json  safeWasmWrappers.js  tsconfig.typedoc.json  wasmClient.js  wasmWorker.js
ryan@Ryans-PC:~/projects/Img2Num$

@Krasner
Copy link
Copy Markdown
Collaborator Author

Krasner commented Apr 11, 2026

Yeah I wasn't sure how to place the generated python lib... maybe i'll place it in packages/python

@Krasner
Copy link
Copy Markdown
Collaborator Author

Krasner commented Apr 11, 2026

OK i made it so that the python library builds to the system (docker) python path:

Linking CXX shared module /usr/local/lib/python3.13/dist-packages/img2num.so

That way in main.py I can direct just do import img2num without specifying any path

@Ryan-Millard
Copy link
Copy Markdown
Owner

I don't think it should build as a .so or be specific to the Docker image because downstream users won't use that.

I'll clean it up - don't worry.

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@bindings/python/CMakeLists.txt`:
- Around line 23-33: The CMakeLists currently sets LIBRARY_OUTPUT_DIRECTORY to
the system Python site-packages (${Python3_SITEARCH}), which writes build
artifacts into the install tree; revert that by removing or resetting the
LIBRARY_OUTPUT_DIRECTORY change for the img2num target and instead add an
install(TARGETS img2num ...) stanza to perform deployment to
${Python3_SITEARCH}. Specifically, stop assigning LIBRARY_OUTPUT_DIRECTORY
"${Python3_SITEARCH}" for target img2num, ensure build outputs go to the default
build tree, and add/install the commented install(TARGETS img2num COMPONENT
python LIBRARY DESTINATION "${Python3_SITEARCH}" RUNTIME DESTINATION
"${Python3_SITEARCH}") (and re-enable include(GNUInstallDirs) if needed) so
installation is performed via CMake install with CMAKE_INSTALL_PREFIX.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: ed1fe177-3f28-4ef5-a4b6-7c145e8816e2

📥 Commits

Reviewing files that changed from the base of the PR and between 4ed412e and 84e7087.

📒 Files selected for processing (2)
  • bindings/python/CMakeLists.txt
  • example-apps/console-py/main.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • example-apps/console-py/main.py

Comment thread bindings/python/CMakeLists.txt Outdated
@Ryan-Millard
Copy link
Copy Markdown
Owner

Sorry, #320 doesn't cover that, but it's the same thing.

@Ryan-Millard
Copy link
Copy Markdown
Owner

/docker-build 2b48c4c

@Krasner
Copy link
Copy Markdown
Collaborator Author

Krasner commented Apr 23, 2026

@Ryan-Millard python ci build works now!

@Ryan-Millard
Copy link
Copy Markdown
Owner

/docker-build e9cc8c4

@Krasner
Copy link
Copy Markdown
Collaborator Author

Krasner commented Apr 24, 2026

Let's merge this?

@Ryan-Millard
Copy link
Copy Markdown
Owner

Let's merge this?

Sure. We can handle the below in another PR.

@coderabbitai, plase make issues for the below (they are all Python related for PR #307):

TODO:

  • Documentation (including docstrings)
  • numpy img.shape default support
  • .coderabbit.yaml updates

Also, do you think we need anything else?

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Create issue 1: Documentation and docstrings for Python bindings
gh issue create \
  --repo Ryan-Millard/Img2Num \
  --title "docs(python): add documentation and docstrings for Python bindings" \
  --assignee Ryan-Millard \
  --body "## Summary
Add user-facing documentation and inline docstrings for the Python bindings introduced in `#307`.

## Tasks
- Add docstrings to all exported functions in the \`img2num\` Python module (e.g. \`gaussian_blur_fft\`, \`invert_image\`, \`threshold_image\`, \`black_threshold_image\`, \`bilateral_filter\`, \`kmeans\`, \`labels_to_svg\`)
- Document expected input/output array shapes, dtypes, and parameter semantics
- Add a usage guide explaining how to import and use the \`img2num\` Python package after \`uv sync\`
- Consider automation via a docstring extraction tool (similar to Doxygen for C++ and Docusaurus for JS)

## References
- PR: `#307`
- Comment: https://github.com/Ryan-Millard/Img2Num/pull/307#issuecomment-2824074520

## Requested by
`@Ryan-Millard`"

echo "---"

# Create issue 2: numpy img.shape default support
gh issue create \
  --repo Ryan-Millard/Img2Num \
  --title "feat(python): support numpy img.shape for automatic width/height inference in Python bindings" \
  --assignee Ryan-Millard \
  --body "## Summary
Currently all Python binding functions (e.g. \`invert_image\`, \`bilateral_filter\`, etc.) require explicit \`width\` and \`height\` integer arguments to be passed alongside the NumPy array. Since NumPy arrays carry shape metadata via \`img.shape\`, the bindings should be able to infer \`width\` and \`height\` automatically when the caller does not supply them.

## Proposed change
Allow each binding function to accept either:
- Explicit \`width\` and \`height\` (current behaviour, for backwards compatibility), or
- No \`width\`/\`height\` arguments, inferring them from \`img.shape[1]\` and \`img.shape[0]\` respectively

## References
- PR: `#307`
- Comment: https://github.com/Ryan-Millard/Img2Num/pull/307#issuecomment-2824074520

## Requested by
`@Ryan-Millard`"

echo "---"

# Create issue 3: .coderabbit.yaml updates for Python
gh issue create \
  --repo Ryan-Millard/Img2Num \
  --title "chore(config): update .coderabbit.yaml for Python bindings support" \
  --assignee Ryan-Millard \
  --body "## Summary
Now that Python bindings have been added to the repo via `#307`, the \`.coderabbit.yaml\` configuration needs to be updated to enable appropriate linting and review instructions for Python files.

## Tasks
- Enable \`ruff\` under \`tools:\` (\`ruff: enabled: true\`)
- Enable \`pylint\` under \`tools:\` (\`pylint: enabled: true\`)
- Disable \`flake8\` if currently enabled (\`flake8: enabled: false\`) to avoid duplication with Ruff
- Add a \`path_instructions\` entry for \`**/*.py\` covering:
  - PEP 8 style compliance
  - Type annotations
  - Exception handling best practices
  - Resource management (context managers, \`with\` statements)
  - Mutable default argument warnings
  - Docker-first command suggestions via the \`./img2num\` wrapper

## References
- PR: `#307`
- Comment: https://github.com/Ryan-Millard/Img2Num/pull/307#issuecomment-2824074520

## Requested by
`@Ryan-Millard`"

Length of output: 161


🏁 Script executed:

#!/bin/bash
# Create issue 4: CI workflow for Python bindings build
gh issue create \
  --repo Ryan-Millard/Img2Num \
  --title "ci(python): add CI workflow to build and test Python bindings" \
  --assignee Ryan-Millard \
  --body "## Summary
There is currently no CI workflow that builds and tests the Python bindings introduced in `#307`. Without this, a broken Docker image (missing pybind11 additions) will not be caught automatically.

## Tasks
- Add a GitHub Actions workflow that:
  - Runs \`uv sync\` inside the dev container (or equivalent environment)
  - Builds the \`img2num\` Python package via scikit-build-core
  - Executes a basic smoke test (e.g. \`import img2num\` and call \`invert_image\`)
- Ensure the workflow runs on PRs that modify \`bindings/python/\`, \`packages/py/\`, or \`Dockerfile.dev\`

## References
- PR: `#307` — Krasner raised this concern: \"this needs a Build Python CI test otherwise we won't catch an error if the docker image doesn't contain the pybind additions\"
- Comment: https://github.com/Ryan-Millard/Img2Num/pull/307#issuecomment-2813937019

## Requested by
`@Ryan-Millard`"

Length of output: 51


@Ryan-Millard — Done! Here are the four issues created:

# Issue Your TODO
#328 docs(python): add documentation and docstrings for Python bindings ✅ Documentation (including docstrings)
#329 feat(python): support numpy img.shape for automatic width/height inference ✅ numpy img.shape default support
#330 chore(config): update .coderabbit.yaml for Python bindings support .coderabbit.yaml updates
#331 ci(python): add CI workflow to build and test Python bindings ➕ Suggested addition (see below)

On your question about whether anything else is needed — yes, I'd suggest one more:

  • ci(python): add CI workflow to build and test Python bindings #331 — Python CI build workflow: @Krasner explicitly flagged this during the PR: "this needs a Build Python CI test otherwise we won't catch an error if the docker image doesn't contain the pybind additions." You confirmed it was needed but deferred it. Without a CI test, a Docker image that's missing pybind11/numpy headers would silently pass all checks. I went ahead and created it.

Beyond that, the PR looks solid. The pybind11 module is well-structured, uv packaging is working, the example app runs, and the labels_to_svg refactor was correctly split out.


✏️ Learnings added
Learnt from: Ryan-Millard
URL: https://github.com/Ryan-Millard/Img2Num/pull/307

Timestamp: 2026-04-25T16:20:34.348Z
Learning: In the Ryan-Millard/Img2Num repository, the following follow-up issues were created after merging PR `#307` (Python bindings): `#328` (Python docstrings/docs), `#329` (numpy img.shape inference for width/height), `#330` (.coderabbit.yaml Python tooling config), `#331` (CI workflow for Python bindings build test).

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:48:02.692Z
Learning: In the Ryan-Millard/Img2Num repository, PR `#311` adds `.coderabbit.yaml` to main. PR `#307` (adding Python .py files) must rebase onto main after `#311` merges so that `.coderabbit.yaml` exists and can be updated with: `ruff: enabled: true`, `pylint: enabled: true`, `flake8: enabled: false` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions via the `./img2num` wrapper. The "CodeRabbit Config Needs Update" custom check will automatically flag `#307` if the Python config is missing.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:48:02.692Z
Learning: In the Ryan-Millard/Img2Num repository, PR `#311` adds `.coderabbit.yaml` to main. PR `#307` (adding Python .py files) must rebase onto main after `#311` merges so that `.coderabbit.yaml` exists and can be updated with: `ruff: enabled: true`, `pylint: enabled: true`, `flake8: enabled: false` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions via the `./img2num` wrapper. The "CodeRabbit Config Needs Update" custom check will automatically flag `#307` if the Python config is missing.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:35:01.215Z
Learning: In the Ryan-Millard/Img2Num repository, Python (.py files) is being added to the codebase via PR `#307`. The `.coderabbit.yaml` should include `ruff: enabled: true` and `pylint: enabled: true` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions via the `./img2num` wrapper.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:35:01.215Z
Learning: In the Ryan-Millard/Img2Num repository, Python (.py files) is being added to the codebase via PR `#307`. The `.coderabbit.yaml` should include `ruff: enabled: true` and `pylint: enabled: true` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-18T17:26:42.396Z
Learning: In the Ryan-Millard/Img2Num repository, the `.coderabbit.yaml` includes a `pre_merge_checks.custom_checks` entry named "CodeRabbit Config Needs Update" (mode: warning) that checks if a PR introduces new languages, linter config files, new file extensions, or new CI tooling without a corresponding update to `.coderabbit.yaml`.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-01T20:45:20.732Z
Learning: In the Ryan-Millard/Img2Num repository, PRs that introduce documentation or code examples referencing functions or symbols not present in the codebase (e.g., a `vectorize` function from `img2num`) should be flagged as potential AI-generated slop. Always cross-reference newly introduced API names/symbols against actual codebase exports.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-01T20:45:20.732Z
Learning: In the Ryan-Millard/Img2Num repository, a PR body containing generic boilerplate such as "This contribution was made as part of an open-source documentation improvement initiative. All changes are meant to be helpful additions to the project." is a strong signal of an AI-generated slop PR and should be explicitly flagged during review.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-01-16T21:25:24.556Z
Learning: When generating docstrings for the Img2Num repository, post them as review comments on the PR rather than creating a separate pull request.

@Ryan-Millard
Copy link
Copy Markdown
Owner

@coderabbitai

Please suggest a conventional commit for this PR.

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Apr 25, 2026

Tip

For best results, initiate chat on the files or code changes.

@Ryan-Millard — Here's a suggested conventional commit for this PR:

feat(python): enable Python bindings via pybind11 and add console-py example

- Add bindings/python/ with CMakeLists.txt and img2num_pybind.cpp, exposing
  gaussian_blur_fft, invert_image, threshold_image, black_threshold_image,
  bilateral_filter, kmeans, and labels_to_svg to Python via pybind11
- Integrate Python bindings into the root CMake build (non-Emscripten only)
- Set POSITION_INDEPENDENT_CODE=ON on Img2Num and CImg2Num targets to support
  the shared .so required by pybind11
- Add packages/py/ with scikit-build-core + uv packaging for `import img2num`
- Add example-apps/console-py/ with a bilateral_filter → kmeans → labels_to_svg
  pipeline and a uv `dev` script
- Update Dockerfile.dev with python3-dev, python3-pybind11, numpy, and
  opencv-python
- Add build-py CI job (uv venv, uv sync, smoke test, artifact upload)
- Fix lab_to_rgb in cielab_impl.h: assign computed r/g/b back into rgba struct

A few notes on the choices:

  • feat(python) — the scope is lowercase and single-word, consistent with conventional commits convention; Ryan-Millard's own suggestion of python bindings works too if that's the style preferred in this repo.
  • The ! (breaking change marker) is not used here since the C++ and JS APIs are unchanged; this is purely additive.
  • The cielab_impl.h bug fix is noted at the bottom since it's bundled in but not the primary purpose of the PR.

🧠 Learnings used
Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-25T16:31:13.184Z
Learning: In the Ryan-Millard/Img2Num repository, issue `#331` (CI workflow for Python bindings build test) was addressed in PR `#307` via the `build-py` job in `.github/workflows/cmake-build.yml`. That job runs `uv venv`, `uv sync --no-build-isolation`, a smoke test (`uv run python3 -c "import img2num;"`), and uploads artifacts.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-25T16:20:34.348Z
Learning: In the Ryan-Millard/Img2Num repository, the following follow-up issues were created after merging PR `#307` (Python bindings): `#328` (Python docstrings/docs), `#329` (numpy img.shape inference for width/height), `#330` (.coderabbit.yaml Python tooling config), `#331` (CI workflow for Python bindings build test).

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:48:02.692Z
Learning: In the Ryan-Millard/Img2Num repository, PR `#311` adds `.coderabbit.yaml` to main. PR `#307` (adding Python .py files) must rebase onto main after `#311` merges so that `.coderabbit.yaml` exists and can be updated with: `ruff: enabled: true`, `pylint: enabled: true`, `flake8: enabled: false` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions via the `./img2num` wrapper. The "CodeRabbit Config Needs Update" custom check will automatically flag `#307` if the Python config is missing.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:48:02.692Z
Learning: In the Ryan-Millard/Img2Num repository, PR `#311` adds `.coderabbit.yaml` to main. PR `#307` (adding Python .py files) must rebase onto main after `#311` merges so that `.coderabbit.yaml` exists and can be updated with: `ruff: enabled: true`, `pylint: enabled: true`, `flake8: enabled: false` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions via the `./img2num` wrapper. The "CodeRabbit Config Needs Update" custom check will automatically flag `#307` if the Python config is missing.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:35:01.215Z
Learning: In the Ryan-Millard/Img2Num repository, Python (.py files) is being added to the codebase via PR `#307`. The `.coderabbit.yaml` should include `ruff: enabled: true` and `pylint: enabled: true` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-23T18:35:01.215Z
Learning: In the Ryan-Millard/Img2Num repository, Python (.py files) is being added to the codebase via PR `#307`. The `.coderabbit.yaml` should include `ruff: enabled: true` and `pylint: enabled: true` under `tools:`, and a `path_instructions` entry for `**/*.py` covering PEP 8, type annotations, exception handling, resource management, mutable default arguments, and Docker-first command suggestions via the `./img2num` wrapper.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-18T17:26:42.396Z
Learning: In the Ryan-Millard/Img2Num repository, the `.coderabbit.yaml` includes a `pre_merge_checks.custom_checks` entry named "CodeRabbit Config Needs Update" (mode: warning) that checks if a PR introduces new languages, linter config files, new file extensions, or new CI tooling without a corresponding update to `.coderabbit.yaml`.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-01T20:45:20.732Z
Learning: In the Ryan-Millard/Img2Num repository, PRs that introduce documentation or code examples referencing functions or symbols not present in the codebase (e.g., a `vectorize` function from `img2num`) should be flagged as potential AI-generated slop. Always cross-reference newly introduced API names/symbols against actual codebase exports.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-01T20:45:20.732Z
Learning: In the Ryan-Millard/Img2Num repository, a PR body containing generic boilerplate such as "This contribution was made as part of an open-source documentation improvement initiative. All changes are meant to be helpful additions to the project." is a strong signal of an AI-generated slop PR and should be explicitly flagged during review.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-01-16T21:25:24.556Z
Learning: When generating docstrings for the Img2Num repository, post them as review comments on the PR rather than creating a separate pull request.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-18T17:26:42.396Z
Learning: In the Ryan-Millard/Img2Num repository, the `.coderabbit.yaml` includes a `path_instructions` entry for `**/*.wgsl` files (WebGPU Shading Language shaders) covering address spaces, entry point attributes, recursion prohibition, binding/group annotations, alignment/layout, atomics, performance, portability (f16), and style conventions.

Learnt from: Ryan-Millard
Repo: Ryan-Millard/Img2Num PR: 0
File: :0-0
Timestamp: 2026-04-25T16:43:10.456Z
Learning: In Ryan-Millard/Img2Num, the Python documentation tool chosen is pdoc, mirroring the Doxygen pattern for C/C++. The plan: install python3-pip and pdoc in a new Dockerfile.dev layer (between layer 4 Doxygen and layer 5 pnpm), create docs/scripts/pdoc.js (mirrors doxygen.js), output to docs/static/docs/python/api/, update prebuild in docs/package.json to "pnpm doxygen && pnpm pdoc", scaffold docs/docs/python/index.md and docs/docs/python/api-reference.md (uses FullscreenIframe component pointing to /Img2Num/info/docs/python/api/).

@Ryan-Millard Ryan-Millard merged commit 294ae53 into Ryan-Millard:main Apr 25, 2026
11 checks passed
Ryan-Millard added a commit that referenced this pull request Apr 29, 2026
…iles (#311)

Add a full `.coderabbit.yaml` configuration for CodeRabbit, covering:
- Assertive review profile with request-changes workflow enabled
- Granular path_instructions for C/C++ core, WGSL shaders, Docusaurus docs,
  GitHub Actions workflows, scripts, Dockerfiles, bindings, and example-apps
- Pre-merge checks for Conventional Commit titles, non-boilerplate descriptions,
  and a custom "CodeRabbit Config Needs Update" check that warns when new
  languages or tooling are introduced without updating .coderabbit.yaml
- Tool enablement scoped to the repo's stack (clang, cppcheck, shellcheck,
  hadolint, eslint, actionlint, etc.); Python tooling deferred to `#307`
- Knowledge base, code generation (Doxygen/JSDoc), and unit test guidance

Refactor `.github/dependabot.yml` from a single root npm entry to scoped
per-directory groups (root, packages/js, example-apps/react-js, docs), with
separate production/development dependency groups and updated commit prefixes.

Extend `.github/CODEOWNERS` to assign ownership of example-apps subtrees by
language (example-apps/*-c, *-cpp, *-py) and the Python bindings directory
(/bindings/py) to `@Ryan-Millard` and `@Krasner`.

Consolidate licensing: rewrite root `LICENSE` to the full standard MIT License
text, remove the now-redundant `LICENSE-MIT` file, and strip the informational
MIT preamble notes from `.github/LICENSE`, `docs/LICENSE`, and
`example-apps/react-js/LICENSE`. Update `README.md` to replace the
bullet-based licensing summary with a table linking to per-directory AGPLv3
license files.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

build-system c/cpp Changes to C or C++ files ci core docs example-app Updates to code related to demonstration (example) applications python changes to python bindings or python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants