Skip to content

Commit e63d26f

Browse files
committed
feat(opencv): add opencv custom allocator test
1 parent 99c32b4 commit e63d26f

6 files changed

Lines changed: 199 additions & 0 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
# The following five lines of boilerplate have to be in your project's
2+
# CMakeLists in this exact order for cmake to work correctly
3+
cmake_minimum_required(VERSION 3.16)
4+
5+
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
6+
project(opencv_allocator)

examples/vision/opencv/opencv_allocator/README.md

Whitespace-only changes.
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
idf_component_register(SRCS "opencv_allocator.cpp"
2+
INCLUDE_DIRS ".")
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
## IDF Component Manager Manifest File
2+
dependencies:
3+
## Required IDF version
4+
idf:
5+
version: '>=4.1.0'
6+
# # Put list of dependencies here
7+
# # For components maintained by Espressif:
8+
# component: "~1.0.0"
9+
# # For 3rd party components:
10+
# username/component: ">=1.0.0,<2.0.0"
11+
# username2/component2:
12+
# version: "~1.0.0"
13+
# # For transient dependencies `public` flag can be set.
14+
# # `public` flag doesn't have an effect dependencies of the `main` component.
15+
# # All dependencies of `main` are public by default.
16+
# public: true
17+
espressif/opencv: ^4.10.0~3
18+
espressif/esp-lib-utils: ^0.3.0
Lines changed: 165 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,165 @@
1+
#include <stdio.h>
2+
#include <iostream>
3+
#include <esp_log.h>
4+
#include "freertos/FreeRTOS.h"
5+
#include "freertos/task.h"
6+
#include "esp_heap_caps.h"
7+
#include <opencv2/core/utility.hpp>
8+
#include <opencv2/imgproc.hpp>
9+
10+
#define ESP_UTILS_LOG_TAG "UserMatDebugAllocator"
11+
#include "esp_lib_utils.h"
12+
13+
class UserMatDebugAllocator : public cv::MatAllocator {
14+
public:
15+
UserMatDebugAllocator()
16+
{
17+
m_defaultAllocator = cv::Mat::getDefaultAllocator();
18+
m_stdAllocator = cv::Mat::getStdAllocator();
19+
std::cout << "UserMatDebugAllocator: constructor" << std::endl;
20+
}
21+
22+
~UserMatDebugAllocator() override
23+
{
24+
std::cout << "UserMatDebugAllocator: destructor" << std::endl;
25+
}
26+
27+
cv::UMatData *allocate(int dims, const int *sizes, int type,
28+
void *data, size_t *step, cv::AccessFlag flags, cv::UMatUsageFlags usageFlags) const override
29+
{
30+
std::cout << "UserMatDebugAllocator: allocate with heap_caps_malloc (SPIRAM only)" << std::endl;
31+
32+
// Calculate data type size
33+
int typeSize = CV_ELEM_SIZE(type);
34+
35+
// Calculate total memory size and step
36+
size_t totalSize = typeSize;
37+
for (int i = 0; i < dims; i++) {
38+
totalSize *= sizes[i];
39+
}
40+
41+
// Use heap_caps_malloc to allocate memory, force SPIRAM usage
42+
void *ptr = heap_caps_malloc(totalSize, MALLOC_CAP_SPIRAM | MALLOC_CAP_8BIT);
43+
if (ptr == nullptr) {
44+
std::cout << "UserMatDebugAllocator: heap_caps_malloc failed for size " << totalSize << " bytes" << std::endl;
45+
std::cout << "UserMatDebugAllocator: SPIRAM allocation failed, but we won't fallback to default allocator" << std::endl;
46+
return nullptr;
47+
}
48+
49+
std::cout << "UserMatDebugAllocator: allocated " << totalSize << " bytes in SPIRAM at " << ptr << std::endl;
50+
51+
// Calculate step
52+
if (step != nullptr) {
53+
step[dims - 1] = typeSize;
54+
for (int i = dims - 2; i >= 0; i--) {
55+
step[i] = step[i + 1] * sizes[i + 1];
56+
}
57+
}
58+
59+
// Create UMatData object using our allocated memory
60+
cv::UMatData *ret = m_defaultAllocator->allocate(dims, sizes, type, ptr, step, flags, usageFlags);
61+
if (nullptr != ret) {
62+
ret->currAllocator = this;
63+
// Store our allocated memory pointer for later deallocation
64+
ret->userdata = ptr;
65+
} else {
66+
// If creation fails, free our allocated memory
67+
heap_caps_free(ptr);
68+
}
69+
70+
return ret;
71+
}
72+
73+
bool allocate(cv::UMatData *data, cv::AccessFlag accessflags, cv::UMatUsageFlags usageFlags) const override
74+
{
75+
std::cout << "UserMatDebugAllocator: allocate2 (SPIRAM only)" << std::endl;
76+
// For existing UMatData, we still use the default allocator to manage metadata
77+
// But the actual data is already in SPIRAM
78+
return m_defaultAllocator->allocate(data, accessflags, usageFlags);
79+
}
80+
81+
void deallocate(cv::UMatData *data) const override
82+
{
83+
std::cout << "UserMatDebugAllocator: deallocate with heap_caps_free" << std::endl;
84+
85+
// Check if this is memory allocated by us
86+
if (data->userdata != nullptr) {
87+
void *ptr = data->userdata;
88+
std::cout << "UserMatDebugAllocator: freeing memory at " << ptr << std::endl;
89+
heap_caps_free(ptr);
90+
data->userdata = nullptr;
91+
}
92+
93+
// Call the default allocator's deallocate to clean up the UMatData object itself
94+
m_defaultAllocator->deallocate(data);
95+
}
96+
97+
void map(cv::UMatData *data, cv::AccessFlag accessflags) const override
98+
{
99+
std::cout << "UserMatDebugAllocator: map" << std::endl;
100+
return m_defaultAllocator->map(data, accessflags);
101+
}
102+
103+
void unmap(cv::UMatData *data) const override
104+
{
105+
std::cout << "UserMatDebugAllocator: unmap" << std::endl;
106+
if ((data->urefcount == 0) && (data->refcount == 0)) {
107+
deallocate(data);
108+
}
109+
}
110+
111+
void download(cv::UMatData *data, void *dst, int dims, const size_t sz[],
112+
const size_t srcofs[], const size_t srcstep[],
113+
const size_t dststep[]) const override
114+
{
115+
std::cout << "UserMatDebugAllocator: download" << std::endl;
116+
return m_defaultAllocator->download(data, dst, dims, sz, srcofs, srcstep, dststep);
117+
}
118+
119+
void upload(cv::UMatData *data, const void *src, int dims, const size_t sz[],
120+
const size_t dstofs[], const size_t dststep[],
121+
const size_t srcstep[]) const override
122+
{
123+
std::cout << "UserMatDebugAllocator: upload" << std::endl;
124+
return m_defaultAllocator->upload(data, src, dims, sz, dstofs, dststep, srcstep);
125+
}
126+
127+
void copy(cv::UMatData *srcdata, cv::UMatData *dstdata, int dims, const size_t sz[],
128+
const size_t srcofs[], const size_t srcstep[],
129+
const size_t dstofs[], const size_t dststep[], bool sync) const override
130+
{
131+
std::cout << "UserMatDebugAllocator: copy" << std::endl;
132+
return m_defaultAllocator->copy(srcdata, dstdata, dims, sz, srcofs, srcstep, dstofs, dststep, sync);
133+
}
134+
135+
cv::BufferPoolController *getBufferPoolController(const char *id = NULL) const override
136+
{
137+
std::cout << "UserMatDebugAllocator: getBufferPoolController" << std::endl;
138+
return m_defaultAllocator->getBufferPoolController(id);
139+
}
140+
141+
cv::MatAllocator *m_defaultAllocator;
142+
cv::MatAllocator *m_stdAllocator;
143+
};
144+
145+
extern "C" void app_main(void)
146+
{
147+
std::cout << "test start" << std::endl;
148+
149+
UserMatDebugAllocator userAllocator;
150+
cv::Mat::setDefaultAllocator(&userAllocator);
151+
std::cout << "Before allocate" << std::endl;
152+
esp_utils_mem_print_info();
153+
{
154+
cv::Mat mat(1000, 1000, CV_8UC3);
155+
mat.at<cv::Vec3b>(500, 500) = cv::Vec3b(255, 0, 0);
156+
vTaskDelay(1000 / portTICK_PERIOD_MS);
157+
std::cout << "After allocate" << std::endl;
158+
esp_utils_mem_print_info();
159+
}
160+
161+
vTaskDelay(2000 / portTICK_PERIOD_MS);
162+
std::cout << "After delay" << std::endl;
163+
esp_utils_mem_print_info();
164+
std::cout << "test end" << std::endl;
165+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
# This file was generated using idf.py save-defconfig. It can be edited manually.
2+
# Espressif IoT Development Framework (ESP-IDF) 5.5.0 Project Minimal Configuration
3+
#
4+
CONFIG_IDF_TARGET="esp32p4"
5+
CONFIG_SPIRAM=y
6+
CONFIG_SPIRAM_SPEED_200M=y
7+
CONFIG_ESP_MAIN_TASK_STACK_SIZE=10240
8+
CONFIG_IDF_EXPERIMENTAL_FEATURES=y

0 commit comments

Comments
 (0)