https://github.com/cnadler86/mp_jpeg
A very fast micropython jpeg encoder and decoder for the esp32, with precompiled firmware.
https://github.com/cnadler86/mp_jpeg
esp32 jpeg jpeg-decoder jpeg-encoder jpg micropython
Last synced: 3 months ago
JSON representation
A very fast micropython jpeg encoder and decoder for the esp32, with precompiled firmware.
- Host: GitHub
- URL: https://github.com/cnadler86/mp_jpeg
- Owner: cnadler86
- License: mit
- Created: 2025-02-19T04:02:20.000Z (over 1 year ago)
- Default Branch: master
- Last Pushed: 2025-12-13T09:27:35.000Z (7 months ago)
- Last Synced: 2025-12-14T23:33:57.827Z (7 months ago)
- Topics: esp32, jpeg, jpeg-decoder, jpeg-encoder, jpg, micropython
- Language: C
- Homepage:
- Size: 79.1 KB
- Stars: 8
- Watchers: 1
- Forks: 2
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
- awesome-micropython - mp_jpeg - A very fast MicroPython JPEG encoder and decoder for the ESP32. (Libraries / Display)
README
# MicroPython JPEG
A very fast and memory-efficient micropython jpeg decoder and encoder. At the moment only the esp port is supported.
If you are not familiar with building custom firmware, visit the [releases](https://github.com/cnadler86/mp_jpeg/releases) page to download firmware that suits your board!
To get the version of the low level driver, you can use the following code:
```python
import jpeg
print("JPEG Driver Version:", jpeg.version())
```
## Decoder
### API Reference
- `Decoder(format="RGB888", rotation=0, block=False)`: Creates a new decoder object.
- `pixel_format`: Pixel format for output (`RGB565_BE`, `RGB565_LE`, `CbYCrY`, `RGB888`).
- `rotation`: Rotation angle for decoding (0, 90, 180, 270). Default 0.
- `block`: Enable block decoding (default: `False`). Each time decode is called, it outputs 8 or 16 line data depending on the image and output format (see `get_block_counts`). If enabled, scale, clipper and rotation are not supported.
- `scale_width` and `scale_height`: Resize the output image to the prvided scale. Note: the scale needs to be consistent with the input image and be a multiple of 8.
- `clipper_width` and `clipper_height`: This will cut the output image to the specified width and/or height. The clipper_height and clipper_width require integer multiples of 8. The resolution of clipper should be less or equal than scale.
- `return_bytes`: if true, the decoder return a bytes-object, otherwise a memoryview, default is false.
- `get_img_info(jpeg_data)`: Returns a list containing the width and height. If the decoder was constructed with `block` enabled, then it will also return the number of blocks that the decoder will need to decode the full image and the heigh of each block (eihter 8 or 16).
- `jpeg_data`: JPEG data to decode.
- `decode(jpeg_data)`: Decodes the JPEG image and returns the decoded image. With block==True, it decodes the next block of the JPEG image and returns the decoded block. The decoder will give you a block of full width and the height will be either 8 or 16 pixels. You can get the block height by `get_img_info`
- `jpeg_data`: JPEG data to decode.
### Example
```python
import jpeg
# Create a JPEG decoder object
decoder = jpeg.Decoder(rotation=180, pixel_format="RGB888")
# Prepare the JPEG data for decoding
jpeg_data = open("path/to/jpeg/image.jpg", "rb").read()
# Decode the JPEG image
decoded_image = decoder.decode(jpeg_data)
```
## Encoder
### API Reference
- `Encoder(height=240, width=320, format="RGB888", quality=90, rotation=0)`: Creates a new encoder object.
- `height`: Height of the input image. Required.
- `width`: Width of the input image. Required.
- `pixel_format`: Pixel format for input image (RGB888, RGB565, RGBA, YCbYCr, YCbY2YCrY2, CbYCrY, GRAY). Required.
- `quality`: JPEG quality (1-100). Default 90.
- `rotation`: Rotation angle for encoding (0, 90, 180, 270). Default 0.
- `encode(img_data)`: Encodes the image data to JPEG format and returns the encoded JPEG data.
- `img_data`: Raw image data to encode.
- Returns the bytes of the encoded image.
### Example
```python
import jpeg
# Create a JPEG encoder object
encoder = jpeg.Encoder(pixel_format="RGB888", quality=80, rotation=90, width=320, height=240)
# Encode the image data to JPEG format
image_data = open("path/to/raw/image.bin", "rb").read()
encoded_jpeg = encoder.encode(image_data)
```
## Benchmark Results for ESP32S3
The following tables present the results of the JPEG decoding and encoding benchmarks performed on an ESP32S3. The image was 240x320.
### Decoder Benchmark
**Input Image Size:** 26,366 bytes.
| Format | FPS Normal Decode | FPS Block Decode (15) |
|-----------|-------------------|-----------------------|
| RGB565_BE | 21.80 | 34.73 |
| RGB565_LE | 21.79 | 34.72 |
| RGB888 | 17.61 | 32.14 |
| CbYCrY | 21.85 | 38.17 |
**Input Image Size:** 7,941 bytes
| Format | FPS Normal Decode | FPS Block Decode (15) |
|-----------|-------------------|-----------------------|
| RGB565_BE | 29.65 | 62.27 |
| RGB565_LE | 29.66 | 62.27 |
| RGB888 | 22.45 | 55.74 |
| CbYCrY | 29.75 | 74.57 |
Block decode will be faster, because other RAM-type might be used.
### Encoder Benchmark
| Quality | FPS RGB888 |
|---------|------------|
| 100 | 11.90 |
| 90 | 17.58 |
| 80 | 19.62 |
| 70 | 20.55 |
| 60 | 21.35 |
The FPS might vary, depending on the image.
### Build the project
### Setting up the build environment
To build the project, follow these instructions:
- ESP-IDF: I tested it on version 5.2, 5.3 and 5.4, but it might work with other versions.
- Clone the micropython repo and this repo in a folder, e.g. "MyJPEG". I used MicroPython version 1.24 but might work also with older versions.
- You will have to add the ESP JPEG library (I used v0.6.1). To do this, add the following to the dependencies in the respective idf_component.yml file (e.g. in micropython/ports/esp32/main_esp32s3/idf_component.yml):
```yaml
espressif/esp_new_jpeg: "^1.0.0"
```
Alternatively, you can [download the library from the espressif component registry](https://components.espressif.com/components/espressif/esp_new_jpeg/versions/0.6.1?language=en) and unzip the data inside the esp-idf/components folder instead of altering the idf_component.yml file. In this case you might need to rename the folder to "esp_new_jpeg".
### Build the user module
To build the project, you could do it the following way:
```bash
. /esp-idf/export.sh
cd MyJPEG/micropython/ports/esp32
make USER_C_MODULES=../../../../mp_jpeg/micropython.cmake BOARD= clean
make USER_C_MODULES=../../../../mp_jpeg/micropython.cmake BOARD= submodules
make USER_C_MODULES=../../../../mp_jpeg/micropython.cmake BOARD= all
```
You can also pass the MP_JPEG_DIR variable to point to the esp_new_jpeg component folder. in ths case you have to build usinf the idf directly.
Micropython and mp_jpeg folders are at the same level. Note that you need those extra "/../"s while been inside the esp32 port folder. If you experience problems, visit [MicroPython external C modules](https://docs.micropython.org/en/latest/develop/cmodules.html).