Binding NCNN C API to MicroPython #6271
inspireMeNow
started this conversation in
Show and tell
Replies: 0 comments
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
This patch adds a complete MicroPython binding layer for the ncnn C API, exposing commonly used components (Option, Allocator, Mat, Net, Layer, Extractor, ParamDict, ModelBin, DataReader) together with pixel I/O, preprocessing, some utilities, and optional drawing support. Basic tests and documentation are included to demonstrate API usage and feature-configurable builds.
Core Design Principles
xyz_destroy()methods for explicit cleanup.Changes Introduced
1. Build System Setup (CMake and Make)
The project allows compilation via CMake or Make. For this purpose, micropython.cmake and micropython.mk are provided, where the NCNN header files and libraries are explicitly specified.
2. Wrapping NCNN Functions with MicroPython
Implement
ncnn_module.cpp, defining variousmp_xyz()functions. Each function is wrapped for MicroPython using theMP_DEFINE_CONST_FUN_OBJ_macro (with suffixes like_0,_1,_2indicating the number of arguments). Functions that need to accept a variable number of arguments use MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN (with min/max bounds) or MP_DEFINE_CONST_FUN_OBJ_VAR (with only minimum). These function objects are then added to the module's global dictionary, e.g.,3. Defining the Module Symbol Table
Define the module symbol table
ncnn_module_globals_tableand use theMP_DEFINE_CONST_DICTmacro to createncnn_module_globals. This dictionary contains all functions, constants, and classes exposed by the module.4. Assigning the Globals Dictionary to the Module Object
Create the module object:
The
.globalsfield of a MicroPython module object is a pointer to the dictionary that stores all the module’s attributes. MicroPython uses this dictionary to resolve module attributes such as functions and constants. Setting.globalsis essential for exposing the module’s content to Python.5. Registering the Module
The module is registered using the
MP_REGISTER_MODULEmacro:This macro makes the
ncnnmodule fully accessible from Python code. Internally, it registers the module and links its.globalsto the previously defined dictionary, completing the binding.Challenges & Resolutions
1. Handling Python Lists for Mean and Normalize Values
Challenge:
The
ncnn_mat_substract_mean_normalizefunction requires arrays of floats for mean and normalization values, while in Python these are passed as lists (mp_obj_t). Correctly converting them is essential.Resolution:
The binding extracts the Python list, converts items to floats, and allocates temporary arrays:
After passing
mean_valsandnorm_valstoncnn_mat_substract_mean_normalize, the arrays are freed:2. Accessing Buffer Objects
Challenge:
NCNN functions operate on raw data buffers. MicroPython buffers (
mp_obj_t) need to be accessed safely for reading or writing.Resolution:
The binding uses
mp_get_buffer_raiseto access the buffer, specifying read or write access, and casts it to the proper type:3. Understanding the Structure of Weight Arrays
Problem:
The binding function
ncnn_modelbin_create_from_mat_arrayreceives aweightsand an integern. Initially, it is unclear whetherweightsis a single matrix, a list of matrices, or something else, and what exactlynrepresents.Resolution:
By reading the underlying implementation in
src/c_api.cpp, it was confirmed thatweightsis a list ofncnn_mat_tobjects (as integer handles) andnspecifies the number of elements in this list:Thus, each element is retrieved with
mp_obj_get_intand stored in a dynamically allocated C array before being passed toncnn_modelbin_create_from_mat_array:Running Test Examples
Example test cases for the MicroPython NCNN binding can be found here.
Practical Insights & Limitations
Working on this project required a amount of effort. On the surface, it mostly involves calling APIs, so the technical complexity may not seem very high. However, the implementation demands careful attention, especially when dealing with arrays, pointers, and error handling. Small mistakes in these areas can easily lead to runtime errors or memory issues, so meticulous coding and testing are essential.
However, a key limitation of the current binding is that C++ structures such as
ncnn_mat_tand other related types are handled as raw integer handles. This approach is prone to memory leaks because Python's garbage collector cannot manage the lifecycle of these objects. Users must manually call cleanup methods (such asmat_destroy()) to properly release native resources.Beta Was this translation helpful? Give feedback.
All reactions