Skip to content

Commit 00a80ac

Browse files
committed
Getting rid of image conversions
1 parent 9407004 commit 00a80ac

File tree

4 files changed

+24
-179
lines changed

4 files changed

+24
-179
lines changed

README.md

Lines changed: 16 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ The API is stable, but it might change without previous announce.
1414
- [Creating a camera object](#creating-a-camera-object)
1515
- [Initializing the camera](#initializing-the-camera)
1616
- [Capture image](#capture-image)
17-
- [Convert image to another format](#convert-image-to-another-format)
1817
- [Camera reconfiguration](#camera-reconfiguration)
1918
- [Additional methods](#additional-methods)
2019
- [Additional information](#additional-information)
@@ -93,7 +92,6 @@ cam = Camera(
9392
- fb_count: Frame buffer count
9493
- grab_mode: Grab mode as GrabMode
9594
- init: Initialize camera at construction time (default: True)
96-
- bmp_out: Image captured output converted to bitmap (default: False)
9795

9896
**Default values:**
9997

@@ -123,28 +121,6 @@ cam.init()
123121
img = cam.capture()
124122
```
125123

126-
Arguments for capture
127-
128-
- out_format: Output format as PixelFormat (optional)
129-
130-
### Convert image to another format
131-
132-
You can either convert the image with the `capture` method directly passing the desired output format:
133-
```python
134-
img_rgb888 = cam.capture(PixelFormat.RGB888) #capture image as configured (e.g. JPEG), convert it to RGB888 and return the converted image
135-
```
136-
Or you can first capture the image and then convert it to the desired PixelFormat with the `convert` method.
137-
Doing so you can have both, the captured and the converted image. Note that more memory will be used.
138-
```python
139-
img = cam.capture()
140-
img_rgb888 = cam.convert(PixelFormat.RGB888) #converts the last captured image to RGB888 and returns the converted image
141-
```
142-
143-
Convertion supported
144-
- from JPEG to RGB565
145-
- to RGB888 in general
146-
- to JPEG in gerenal (use the `set_quality` method to set the desired JPEG quality)
147-
148124
### Camera reconfiguration
149125

150126
```python
@@ -164,7 +140,6 @@ Here are just a few examples:
164140

165141
```python
166142
cam.set_quality(90) # The quality goes from 0% to 100%, meaning 100% is the highest but has probably no compression
167-
cam.set_bmp_out(True) # Enables convertion to bmp when capturing image
168143
camera.get_brightness()
169144
camera.set_vflip(True) #Enable vertical flip
170145
```
@@ -296,25 +271,22 @@ If you experience problems, visit [MicroPython external C modules](https://docs.
296271
I didn't use a calibrated osziloscope, but here is a FPS benchmark with my ESP32S3 (xclck_freq = 20MHz, GrabMode=LATEST, fb_count = 1, jpeg_quality=85%) and OV2640.
297272
Using fb_count=2 theoretically can double the FPS (see JPEG with fb_count=2). This might also aplly for other PixelFormats.
298273

299-
| Frame Size | GRAYSCALE | RGB565 | YUV422 | JPEG | JPEG -> RGB565 | JPEG -> RGB888 | JPEG (fb=2) |
300-
|------------|-----------|--------|--------|--------|----------------|----------------|-------------|
301-
| R96X96 | 12.5 | 12.5 | 12.5 | No img | No img | No img | No img |
302-
| QQVGA | 12.5 | 12.5 | 12.5 | 25 | 25 | 25 | 50 |
303-
| QCIF | 11 | 11 | 11.5 | 25 | 25 | 25 | 50 |
304-
| HQVGA | 12.5 | 12.5 | 12.5 | 25 | 16.7 | 16.7 | 50 |
305-
| R240X240 | 12.5 | 12.5 | 11.5 | 25 | 16.7 | 12.5 | 50 |
306-
| QVGA | 12 | 11 | 12 | 25 | 25 | 25 | 50 |
307-
| CIF | 12.5 | No img | No img | 6.3 | 8.3 | 8.3 | 12.5 |
308-
| HVGA | 3 | 3 | 2.5 | 12.5 | 6.3 | 6.3 | 25 |
309-
| VGA | 3 | 3 | 3 | 12.5 | 3.6 | 3.6 | 25 |
310-
| SVGA | 3 | 3 | 3 | 12.5 | 2.8 | 2.5 | 25 |
311-
| XGA | No img | No img | No img | 6.3 | 1.6 | 1.6 | 12.5 |
312-
| HD | No img | No img | No img | 6.3 | 1.4 | 1.3 | 12.5 |
313-
| SXGA | 2 | 2 | 2 | 6.3 | 1 | 1 | 12.5 |
314-
| UXGA | No img | No img | No img | 6.3 | 0.7 | 0.7 | 12.5 |
315-
316-
317-
Looking at the results: image conversion make only sense for frame sized below QVGA or if capturing the image in the intended pixelformat and frame size combination fails.
274+
| Frame Size | GRAYSCALE | RGB565 | YUV422 | JPEG | JPEG (fb=2) |
275+
|------------|-----------|--------|--------|--------|-------------|
276+
| R96X96 | 12.5 | 12.5 | 12.5 | No img | No img |
277+
| QQVGA | 12.5 | 12.5 | 12.5 | 25 | 50 |
278+
| QCIF | 11 | 11 | 11.5 | 25 | 50 |
279+
| HQVGA | 12.5 | 12.5 | 12.5 | 25 | 50 |
280+
| R240X240 | 12.5 | 12.5 | 11.5 | 25 | 50 |
281+
| QVGA | 12 | 11 | 12 | 25 | 50 |
282+
| CIF | 12.5 | No img | No img | 6.3 | 12.5 |
283+
| HVGA | 3 | 3 | 2.5 | 12.5 | 25 |
284+
| VGA | 3 | 3 | 3 | 12.5 | 25 |
285+
| SVGA | 3 | 3 | 3 | 12.5 | 25 |
286+
| XGA | No img | No img | No img | 6.3 | 12.5 |
287+
| HD | No img | No img | No img | 6.3 | 12.5 |
288+
| SXGA | 2 | 2 | 2 | 6.3 | 12.5 |
289+
| UXGA | No img | No img | No img | 6.3 | 12.5 |
318290

319291
## Troubleshooting
320292

src/modcamera.c

Lines changed: 2 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,6 @@
2828
#include "modcamera.h"
2929
#include "esp_err.h"
3030
#include "esp_log.h"
31-
#include "img_converters.h"
3231
#include "mphalport.h"
3332

3433
#define TAG "MPY_CAMERA"
@@ -153,10 +152,6 @@ void mp_camera_hal_construct(
153152

154153
self->initialized = false;
155154
self->captured_buffer = NULL;
156-
self->converted_buffer.len = 0;
157-
self->converted_buffer.buf = NULL;
158-
self->converted_buffer.typecode = 'B';
159-
self->bmp_out = false;
160155
}
161156

162157
void mp_camera_hal_init(mp_camera_obj_t *self) {
@@ -175,11 +170,6 @@ void mp_camera_hal_init(mp_camera_obj_t *self) {
175170

176171
void mp_camera_hal_deinit(mp_camera_obj_t *self) {
177172
if (self->initialized) {
178-
if (self->converted_buffer.buf) {
179-
free(self->converted_buffer.buf);
180-
self->converted_buffer.buf = NULL;
181-
self->converted_buffer.len = 0;
182-
}
183173
if (self->captured_buffer) {
184174
esp_camera_return_all();
185175
self->captured_buffer = NULL;
@@ -214,62 +204,7 @@ void mp_camera_hal_reconfigure(mp_camera_obj_t *self, mp_camera_framesize_t fram
214204
ESP_LOGI(TAG, "Camera reconfigured successfully");
215205
}
216206

217-
static bool ensure_buffer(mp_camera_obj_t *self, size_t req_len) {
218-
if (self->converted_buffer.len == req_len) {
219-
return true;
220-
}
221-
if (self->converted_buffer.len > 0 || self->converted_buffer.buf) {
222-
free(self->converted_buffer.buf);
223-
}
224-
self->converted_buffer.buf = (uint8_t *)malloc(req_len);
225-
if (!self->converted_buffer.buf) {
226-
self->converted_buffer.len = 0;
227-
ESP_LOGE(TAG, "converted_buffer malloc failed");
228-
return false;
229-
}
230-
self->converted_buffer.len = req_len;
231-
return true;
232-
}
233-
234-
static bool mp_camera_convert(mp_camera_obj_t *self, mp_camera_pixformat_t out_format) {
235-
ESP_LOGI(TAG, "Converting image to pixel format: %d", out_format);
236-
237-
switch (out_format) {
238-
case PIXFORMAT_JPEG:
239-
return frame2jpg(self->captured_buffer, self->camera_config.jpeg_quality, self->converted_buffer.buf, &self->converted_buffer.len);
240-
241-
case PIXFORMAT_RGB888:
242-
if (ensure_buffer(self, self->captured_buffer->width * self->captured_buffer->height * 3)) {
243-
return fmt2rgb888(self->captured_buffer->buf, self->captured_buffer->len, self->captured_buffer->format, self->converted_buffer.buf);
244-
} else {
245-
return false;
246-
}
247-
248-
case PIXFORMAT_RGB565:
249-
if (self->camera_config.pixel_format == PIXFORMAT_JPEG) {
250-
if (ensure_buffer(self, self->captured_buffer->width * self->captured_buffer->height * 2)) {
251-
return jpg2rgb565(self->captured_buffer->buf, self->captured_buffer->len, self->converted_buffer.buf, JPG_SCALE_NONE);
252-
} else {
253-
return false;
254-
}
255-
} else {
256-
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Can only convert JPEG to RGB565"));
257-
}
258-
259-
default:
260-
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Unsupported pixel format for conversion"));
261-
}
262-
}
263-
mp_obj_t mp_camera_hal_convert(mp_camera_obj_t *self, int8_t out_format) {
264-
check_init(self);
265-
if (mp_camera_convert(self, (mp_camera_pixformat_t)out_format)) {
266-
return mp_obj_new_memoryview('b', self->converted_buffer.len, self->converted_buffer.buf);
267-
} else {
268-
return mp_const_none;
269-
}
270-
}
271-
272-
mp_obj_t mp_camera_hal_capture(mp_camera_obj_t *self, int8_t out_format) {
207+
mp_obj_t mp_camera_hal_capture(mp_camera_obj_t *self) {
273208
check_init(self);
274209
if (self->captured_buffer) {
275210
esp_camera_fb_return(self->captured_buffer);
@@ -282,33 +217,8 @@ mp_obj_t mp_camera_hal_capture(mp_camera_obj_t *self, int8_t out_format) {
282217
ESP_LOGE(TAG, "Failed to capture image");
283218
return mp_const_none;
284219
}
285-
286-
if (out_format >= 0 && (mp_camera_pixformat_t)out_format != self->camera_config.pixel_format) {
287-
if (mp_camera_convert(self, (mp_camera_pixformat_t)out_format)) {
288-
esp_camera_fb_return(self->captured_buffer);
289-
mp_obj_t result = mp_obj_new_memoryview('b', self->converted_buffer.len, self->converted_buffer.buf);
290-
return result;
291-
} else {
292-
return mp_const_none;
293-
}
294-
}
220+
return mp_obj_new_memoryview('b', self->captured_buffer->len, self->captured_buffer->buf);
295221

296-
if (self->bmp_out == false) {
297-
ESP_LOGI(TAG, "Returning image without conversion");
298-
return mp_obj_new_memoryview('b', self->captured_buffer->len, self->captured_buffer->buf);
299-
} else {
300-
ESP_LOGI(TAG, "Returning image as bitmap");
301-
if (frame2bmp(self->captured_buffer, self->converted_buffer.buf, &self->converted_buffer.len)) {
302-
esp_camera_fb_return(self->captured_buffer);
303-
mp_obj_t result = mp_obj_new_memoryview('b', self->converted_buffer.len, self->converted_buffer.buf);
304-
return result;
305-
} else {
306-
free(self->converted_buffer.buf);
307-
self->converted_buffer.buf = NULL;
308-
self->converted_buffer.len = 0;
309-
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to convert image to BMP"));
310-
}
311-
}
312222
} // mp_camera_hal_capture
313223

314224
bool mp_camera_hal_initialized(mp_camera_obj_t *self){

src/modcamera.h

Lines changed: 1 addition & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -96,8 +96,6 @@ typedef struct hal_camera_obj {
9696
camera_config_t camera_config;
9797
bool initialized;
9898
camera_fb_t *captured_buffer;
99-
bool bmp_out;
100-
mp_buffer_info_t converted_buffer;
10199
} hal_camera_obj_t;
102100

103101
#endif // CONFIG_IDF_TARGET_ESP32 || CONFIG_IDF_TARGET_ESP32S2 || CONFIG_IDF_TARGET_ESP32S3
@@ -184,19 +182,9 @@ extern void mp_camera_hal_reconfigure(mp_camera_obj_t *self, mp_camera_framesize
184182
* @brief Captures an image and returns it as mp_obj_t (e.g. mp_obj_new_memoryview).
185183
*
186184
* @param self Pointer to the camera object.
187-
* @param out_format Output pixelformat format.
188185
* @return Captured image as micropython object.
189186
*/
190-
extern mp_obj_t mp_camera_hal_capture(mp_camera_obj_t *self, int8_t out_format);
191-
192-
/**
193-
* @brief Converts an image from one pixelformat to another.
194-
*
195-
* @param self Pointer to the camera object.
196-
* @param out_format Output pixelformat format.
197-
* @return Converted image as micropython object.
198-
*/
199-
extern mp_obj_t mp_camera_hal_convert(mp_camera_obj_t *self, int8_t out_format);
187+
extern mp_obj_t mp_camera_hal_capture(mp_camera_obj_t *self);
200188

201189
/**
202190
* @brief Frees the buffer of the camera object.

src/modcamera_api.c

Lines changed: 5 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const mp_obj_type_t camera_type;
4040

4141
//Constructor
4242
static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, size_t n_kw, const mp_obj_t *all_args) {
43-
enum { ARG_data_pins, ARG_pixel_clock_pin, ARG_vsync_pin, ARG_href_pin, ARG_sda_pin, ARG_scl_pin, ARG_xclock_pin, ARG_xclock_frequency, ARG_powerdown_pin, ARG_reset_pin, ARG_pixel_format, ARG_frame_size, ARG_jpeg_quality, ARG_fb_count, ARG_grab_mode, ARG_init, ARG_bmp_out, NUM_ARGS };
43+
enum { ARG_data_pins, ARG_pixel_clock_pin, ARG_vsync_pin, ARG_href_pin, ARG_sda_pin, ARG_scl_pin, ARG_xclock_pin, ARG_xclock_frequency, ARG_powerdown_pin, ARG_reset_pin, ARG_pixel_format, ARG_frame_size, ARG_jpeg_quality, ARG_fb_count, ARG_grab_mode, ARG_init, NUM_ARGS };
4444
static const mp_arg_t allowed_args[] = {
4545
#ifdef MICROPY_CAMERA_ALL_REQ_PINS_DEFINED
4646
{ MP_QSTR_data_pins, MP_ARG_OBJ | MP_ARG_KW_ONLY , { .u_obj = MP_ROM_NONE } },
@@ -68,7 +68,6 @@ static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, siz
6868
{ MP_QSTR_fb_count, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = MICROPY_CAMERA_FB_COUNT } },
6969
{ MP_QSTR_grab_mode, MP_ARG_INT | MP_ARG_KW_ONLY, { .u_int = MICROPY_CAMERA_GRAB_MODE } },
7070
{ MP_QSTR_init, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = true } },
71-
{ MP_QSTR_bmp_out, MP_ARG_BOOL | MP_ARG_KW_ONLY, { .u_bool = false } },
7271
};
7372

7473
mp_arg_val_t args[MP_ARRAY_SIZE(allowed_args)];
@@ -129,13 +128,12 @@ static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, siz
129128

130129
mp_camera_obj_t *self = mp_obj_malloc_with_finaliser(mp_camera_obj_t, &camera_type);
131130
self->base.type = &camera_type;
132-
self->bmp_out = args[ARG_bmp_out].u_bool;
133131

134132
mp_camera_hal_construct(self, data_pins, xclock_pin, pixel_clock_pin, vsync_pin, href_pin, powerdown_pin, reset_pin,
135133
sda_pin, scl_pin, xclock_frequency, pixel_format, frame_size, jpeg_quality, fb_count, grab_mode);
136134

137135
mp_camera_hal_init(self);
138-
if (mp_camera_hal_capture(self, -1) == mp_const_none){
136+
if (mp_camera_hal_capture(self) == mp_const_none){
139137
mp_camera_hal_deinit(self);
140138
mp_raise_msg(&mp_type_OSError, MP_ERROR_TEXT("Failed to capture initial frame. Construct a new object with appropriate configuration."));
141139
} else {
@@ -147,19 +145,11 @@ static mp_obj_t mp_camera_make_new(const mp_obj_type_t *type, size_t n_args, siz
147145
} // camera_construct
148146

149147
// Main methods
150-
static mp_obj_t camera_capture(size_t n_args, const mp_obj_t *args){
151-
mp_camera_obj_t *self = MP_OBJ_TO_PTR(args[0]);
152-
int8_t out_format = n_args < 2 ? -1 : mp_obj_get_int(args[1]);
153-
return mp_camera_hal_capture(self, out_format);
154-
}
155-
static MP_DEFINE_CONST_FUN_OBJ_VAR_BETWEEN(camera_capture_obj, 1, 2, camera_capture);
156-
157-
static mp_obj_t camera_convert(mp_obj_t self_in, mp_obj_t arg) {
148+
static mp_obj_t camera_capture(mp_obj_t self_in){
158149
mp_camera_obj_t *self = MP_OBJ_TO_PTR(self_in);
159-
int8_t out_format = mp_obj_get_int(arg);
160-
return mp_camera_hal_convert(self, out_format);
150+
return mp_camera_hal_capture(self);
161151
}
162-
static MP_DEFINE_CONST_FUN_OBJ_2(camera_convert_obj, camera_convert);
152+
static MP_DEFINE_CONST_FUN_OBJ_1(camera_capture_obj, camera_capture);
163153

164154
static mp_obj_t camera_free_buf(mp_obj_t self_in) {
165155
mp_camera_obj_t *self = MP_OBJ_TO_PTR(self_in);
@@ -217,19 +207,6 @@ static mp_obj_t mp_camera_deinit(mp_obj_t self_in) {
217207
}
218208
static MP_DEFINE_CONST_FUN_OBJ_1(mp_camera_deinit_obj, mp_camera_deinit);
219209

220-
static mp_obj_t camera_get_bmp_out(mp_obj_t self_in) {
221-
mp_camera_obj_t *self = MP_OBJ_TO_PTR(self_in);
222-
return mp_obj_new_bool(self->bmp_out);
223-
}
224-
static MP_DEFINE_CONST_FUN_OBJ_1(camera_get_bmp_out_obj, camera_get_bmp_out);
225-
226-
static mp_obj_t camera_set_bmp_out(mp_obj_t self_in, mp_obj_t arg) {
227-
mp_camera_obj_t *self = MP_OBJ_TO_PTR(self_in);
228-
self->bmp_out = mp_obj_is_true(arg);
229-
return mp_const_none;
230-
}
231-
static MP_DEFINE_CONST_FUN_OBJ_2(camera_set_bmp_out_obj, camera_set_bmp_out);
232-
233210
// Destructor
234211
static mp_obj_t mp_camera_obj___exit__(size_t n_args, const mp_obj_t *args) {
235212
(void)n_args;
@@ -304,7 +281,6 @@ CREATE_GETSET_FUNCTIONS(lenc, mp_obj_new_bool, mp_obj_is_true);
304281
static const mp_rom_map_elem_t camera_camera_locals_table[] = {
305282
{ MP_ROM_QSTR(MP_QSTR_reconfigure), MP_ROM_PTR(&camera_reconfigure_obj) },
306283
{ MP_ROM_QSTR(MP_QSTR_capture), MP_ROM_PTR(&camera_capture_obj) },
307-
{ MP_ROM_QSTR(MP_QSTR_convert), MP_ROM_PTR(&camera_convert_obj) },
308284
{ MP_ROM_QSTR(MP_QSTR_free_buffer), MP_ROM_PTR(&camera_free_buf_obj) },
309285
{ MP_ROM_QSTR(MP_QSTR_init), MP_ROM_PTR(&camera_init_obj) },
310286
{ MP_ROM_QSTR(MP_QSTR_deinit), MP_ROM_PTR(&mp_camera_deinit_obj) },
@@ -344,7 +320,6 @@ static const mp_rom_map_elem_t camera_camera_locals_table[] = {
344320
ADD_PROPERTY_TO_TABLE(wpc),
345321
ADD_PROPERTY_TO_TABLE(raw_gma),
346322
ADD_PROPERTY_TO_TABLE(lenc),
347-
ADD_PROPERTY_TO_TABLE(bmp_out),
348323
};
349324
static MP_DEFINE_CONST_DICT(camera_camera_locals_dict, camera_camera_locals_table);
350325

0 commit comments

Comments
 (0)