11// C++ headers
22#include " opencv2/core.hpp"
3+ #include " opencv2/imgcodecs.hpp"
34#include " convert.h"
45#include " numpy.h"
56
@@ -11,46 +12,46 @@ extern "C" {
1112
1213using namespace cv ;
1314
14- // Fix for https://github.com/sparkfun/micropython-opencv/issues/13
15+ // The function below is a workaround for memory management issues between
16+ // OpenCV and the MicroPython GC. OpenCV allocates some objects on the heap,
17+ // whenever the first function that needs the objects happen to be called. That
18+ // only happens from the user's code after the GC has been initialized, meaning
19+ // they get allocated on the GC heap (see `__wrap_malloc()`). If a soft reset
20+ // occurs, the GC gets reset and the memory locations get overwritten, but the
21+ // same memory locations are still referenced for the objects, resulting in bad
22+ // values and problems (crashes and freezes, `CV_Assert()` calls fail, etc.).
1523//
16- // TLDR; The CoreTLSData object gets allocated once, whenever the first OpenCV
17- // function that needs it happens to be called. That will only happen from the
18- // user's code, after the GC has been initialized, meaning it gets allocated on
19- // the GC heap (see `__wrap_malloc()`). If a soft reset occurs, the GC gets
20- // reset and overwrites the memory location, but the same memory location is
21- // still referenced for the CoreTLSData object, resulting in bogus values and
22- // subsequent `CV_Assert()` calls fail
24+ // The solution here is to ensure those objects are allocated in the C heap
25+ // instead of the GC heap. The function below calls various OpenCV functions
26+ // that subsequently allocate the problematic objects. To ensure they are
27+ // allocated on the C heap, this needs to happen before the GC is initialized
28+ // (before `main()` is called), so __wrap_malloc() will use __real_malloc()
29+ // instead of the GC.
2330//
24- // The solution here is to create a global variable that subsequently calls
25- // `getCoreTlsData()` to allocate the CoreTLSData object before the GC has
26- // been initialized, so it gets allocated on the C heap and persists through
27- // soft resets. `getCoreTlsData()` is not publicly exposed, but `theRNG()` is
28- // exposed, which just runs `return getCoreTlsData().rng`
29- volatile RNG rng = theRNG();
30-
31- // Fix for https://github.com/sparkfun/micropython-opencv/issues/17
32- //
33- // TLDR; The `StdMatAllocator` gets allocated once, whenever the first time a
34- // Mat object is created without the NumpyAllocator being set (OpenCV creates
35- // internal Mat objects for various operations that use whatever the default
36- // allocator is). Similar to above, the `StdMatAllocator` gets allocated on the
37- // GC heap, so if a soft reset occurs, the GC gets reset and overwrites the
38- // memory location, causing problems
39- //
40- // Instead of ensuring the `StdMatAllocator` is allocated on the C heap, we just
41- // set the NumpyAllocator as the default allocator. `Mat::setDefaultAllocator()`
42- // does not return anything, so this wrapper function returns a dummy value so
43- // we can use it to initialize a global variable, ensuring it gets run before
44- // `main()` gets called
45- bool setNumpyAllocator () {
31+ // The function below returns a dummy value that we use to initialize a global
32+ // variable, ensuring it gets run before `main()` gets called. This also means
33+ // it can be used as a general boot function for anything else that needs to
34+ // happen before `main()` is called, such as setting the default Mat allocator.
35+ bool upyOpenCVBoot () {
4636 try {
37+ // Initializes `CoreTLSData` on the C heap, see:
38+ // https://github.com/sparkfun/micropython-opencv/issues/13
39+ theRNG ();
40+
41+ // Initializes all image codecs on the C heap, see:
42+ // https://github.com/sparkfun/micropython-opencv/issues/17
43+ haveImageWriter (" .bmp" );
44+
45+ // Sets the NumpyAllocator as the default Mat object allocator, see
46+ // https://github.com/sparkfun/micropython-opencv/issues/17
4747 Mat::setDefaultAllocator (&GetNumpyAllocator ());
48+
4849 return true ;
4950 } catch (const Exception& e) {
5051 return false ;
5152 }
5253}
53- volatile bool defaultAllocatorSet = setNumpyAllocator ();
54+ volatile bool bootSuccess = upyOpenCVBoot ();
5455
5556mp_obj_t cv2_core_convertScaleAbs (size_t n_args, const mp_obj_t *pos_args, mp_map_t *kw_args) {
5657 // Define the arguments
0 commit comments