Skip to content

Commit 4d380e0

Browse files
Merge pull request #439 from espressif/feature/esp_jpeg_default_huffman
esp_jpeg: Add support for default Huffman tables
2 parents ca95694 + e793c31 commit 4d380e0

17 files changed

+19571
-668
lines changed

.idf_build_apps.toml

-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
target = "all"
2-
paths = "."
31
recursive = true
42
exclude = [
53
".github",

esp_jpeg/CMakeLists.txt

+4
Original file line numberDiff line numberDiff line change
@@ -7,4 +7,8 @@ if(NOT CONFIG_JD_USE_ROM)
77
list(APPEND includes "tjpgd")
88
endif()
99

10+
if(CONFIG_JD_DEFAULT_HUFFMAN)
11+
list(APPEND sources "jpeg_default_huffman_table.c")
12+
endif()
13+
1014
idf_component_register(SRCS ${sources} INCLUDE_DIRS ${includes})

esp_jpeg/Kconfig

+12-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ menu "JPEG Decoder"
22

33
config JD_USE_ROM
44
bool "Use TinyJPG Decoder from ROM"
5-
depends on IDF_TARGET_ESP32 || IDF_TARGET_ESP32C3 || IDF_TARGET_ESP32C6 || IDF_TARGET_ESP32S3
5+
depends on ESP_ROM_HAS_JPEG_DECODE
66
default y
77
help
88
By default, Espressif SoCs use TJpg decoder implemented in ROM code.
@@ -66,4 +66,15 @@ menu "JPEG Decoder"
6666
config JD_FASTDECODE_TABLE
6767
bool "+ Table conversion for huffman decoding (wants 6 << HUFF_BIT bytes of RAM)"
6868
endchoice
69+
70+
config JD_DEFAULT_HUFFMAN
71+
bool "Support images without Huffman table"
72+
depends on !JD_USE_ROM
73+
default n
74+
help
75+
Enable this option to support decoding JPEG images that lack an embedded Huffman table.
76+
When enabled, a default Huffman table is used during decoding, allowing the JPEG decoder to handle
77+
images without explicitly provided Huffman tables.
78+
79+
Note: Enabling this option increases ROM usage due to the inclusion of default Huffman tables.
6980
endmenu

esp_jpeg/README.md

+25-18
Original file line numberDiff line numberDiff line change
@@ -2,55 +2,62 @@
22

33
[![Component Registry](https://components.espressif.com/components/espressif/esp_jpeg/badge.svg)](https://components.espressif.com/components/espressif/esp_jpeg)
44

5-
TJpgDec is a generic JPEG image decompressor that is highly optimized for small embedded systems. It works with very low memory consumption.
5+
TJpgDec is a lightweight JPEG image decompressor optimized for embedded systems with minimal memory consumption.
66

7-
Some microcontrollers have TJpg decoder in ROM, it is used, if there is. [^1] Using ROM code can be disabled in menuconfig.
7+
On some microcontrollers, TJpgDec is available in ROM and will be used by default, though this can be disabled in menuconfig if desired[^1].
88

99
[^1]: **_NOTE:_** When the ROM decoder is used, the configuration can't be changed. The configuration is fixed.
1010

1111
## Features
1212

1313
**Compilation configuration:**
14-
- Size of stream input buffer (default 512)
15-
- Output pixel format (default RGB888): RGB888/RGB565
16-
- Switches output descaling feature (default enabled)
17-
- Use table conversion for saturation arithmetic (default enabled)
18-
- Three optimization levels (default basic): 8/16-bit MCUs, 32-bit MCUs, Table conversion for huffman decoding
14+
- Stream input buffer size (default: 512 bytes)
15+
- Output pixel format (default: RGB888; options: RGB888/RGB565)
16+
- Enable/disable output descaling (default: enabled)
17+
- Use table-based saturation for arithmetic operations (default: enabled)
18+
- Use default Huffman tables: Useful from decoding frames from cameras, that do not provide Huffman tables (default: disabled to save ROM)
19+
- Three optimization levels (default: 32-bit MCUs) for different CPU types:
20+
- 8/16-bit MCUs
21+
- 32-bit MCUs
22+
- Table-based Huffman decoding
1923

2024
**Runtime configuration:**
21-
- Pixel Format: RGB888, RGB565
22-
- Scaling Ratio: 1/1, 1/2, 1/4 or 1/8 Selectable on Decompression
23-
- Allow swap the first and the last byte of the color
25+
- Pixel format options: RGB888, RGB565
26+
- Selectable scaling ratios: 1/1, 1/2, 1/4, or 1/8 (chosen at decompression)
27+
- Option to swap the first and last bytes of color values
2428

2529
## TJpgDec in ROM
2630

27-
Some microcontrollers have TJpg decoder in ROM. It is used as default, but it can be disabled in menuconfig. Then there will be used code saved in this component.
31+
On certain microcontrollers, TJpgDec is available in ROM and used by default. This can be disabled in menuconfig if you prefer to use the library code provided in this component.
2832

2933
### List of MCUs, which have TJpgDec in ROM
3034
- ESP32
3135
- ESP32-S3
3236
- ESP32-C3
3337
- ESP32-C6
38+
- ESP32-C5
39+
- ESP32-C61
3440

3541
### Fixed compilation configuration of the ROM code
36-
- Stream input buffer: 512
42+
The ROM version uses the following fixed settings:
43+
- Stream input buffer: 512 bytes
3744
- Output pixel format: RGB888
38-
- Descaling feature for output: Enabled
39-
- Table for saturation: Enabled
45+
- Output descaling: enabled
46+
- Saturation table: enabled
4047
- Optimization level: Basic (JD_FASTDECODE = 0)
4148

4249
### Pros and cons using ROM code
4350

4451
**Advantages:**
45-
- Save 5k of the flash memory (in the same configuration)
52+
- Saves approximately 5 KB of flash memory with the same configuration
4653

4754
**Disadvantages:**
48-
- Cannot be changed compilation configuration
49-
- Some configuration can be faster in some cases
55+
- Compilation configuration cannot be changed
56+
- Certain configurations may provide faster performance
5057

5158
## Speed comparison
5259

53-
In this table are examples of speed decoding JPEG image with this configuration:
60+
The table below shows example decoding times for a JPEG image using various configurations:
5461
* Image size: 320 x 180 px
5562
* Output format: RGB565
5663
* CPU: ESP32-S3

esp_jpeg/idf_component.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
version: "1.0.5~3"
1+
version: "1.1.0"
22
description: "JPEG Decoder: TJpgDec"
33
url: https://github.com/espressif/idf-extra-components/tree/master/esp_jpeg/
44
dependencies:
5-
idf: ">=4.4"
5+
idf: ">=5.0"

esp_jpeg/jpeg_decoder.c

+3-3
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* SPDX-FileCopyrightText: 2015-2022 Espressif Systems (Shanghai) CO LTD
2+
* SPDX-FileCopyrightText: 2015-2024 Espressif Systems (Shanghai) CO LTD
33
*
44
* SPDX-License-Identifier: Apache-2.0
55
*/
@@ -86,7 +86,7 @@ esp_err_t esp_jpeg_decode(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *im
8686

8787
/* Prepare image */
8888
res = jd_prepare(&JDEC, jpeg_decode_in_cb, workbuf, JPEG_WORK_BUF_SIZE, cfg);
89-
ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in preparing JPEG image!");
89+
ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in preparing JPEG image! %d", res);
9090

9191
uint8_t scale_div = jpeg_get_div_by_scale(cfg->out_scale);
9292
uint8_t out_color_bytes = jpeg_get_color_bytes(cfg->out_format);
@@ -101,7 +101,7 @@ esp_err_t esp_jpeg_decode(esp_jpeg_image_cfg_t *cfg, esp_jpeg_image_output_t *im
101101

102102
/* Decode JPEG */
103103
res = jd_decomp(&JDEC, jpeg_decode_out_cb, cfg->out_scale);
104-
ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in decoding JPEG image!");
104+
ESP_GOTO_ON_FALSE((res == JDR_OK), ESP_FAIL, err, TAG, "Error in decoding JPEG image! %d", res);
105105

106106
err:
107107
if (workbuf) {

esp_jpeg/jpeg_default_huffman_table.c

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/*
2+
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
3+
*
4+
* SPDX-License-Identifier: Apache-2.0
5+
*/
6+
7+
// Default Huffman tables for baseline JPEG
8+
9+
// These values are taken directly from CCITT Rec. T.81 (1992 E) Appendix K.3.3
10+
// The *_num_bits array always contains exactly 16 elements.
11+
// Each element represents the number of Huffman codes of a specific length:
12+
// - The first element corresponds to codes of length 1 bit,
13+
// - The second element to codes of length 2 bits, and so forth up to 16 bits.
14+
//
15+
// The *_values array has a length equal to the sum of all elements in the *_num_bits array,
16+
// representing the actual values associated with each Huffman code in order.
17+
18+
// Luminance DC Table
19+
const unsigned char esp_jpeg_lum_dc_num_bits[16] = {0, 1, 5, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0};
20+
const unsigned esp_jpeg_lum_dc_codes_total = 12;
21+
const unsigned char esp_jpeg_lum_dc_values[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
22+
23+
// Chrominance DC Table
24+
const unsigned char esp_jpeg_chrom_dc_num_bits[16] = {0, 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0};
25+
const unsigned esp_jpeg_chrom_dc_codes_total = 12;
26+
const unsigned char esp_jpeg_chrom_dc_values[12] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11};
27+
28+
// Luminance AC Table
29+
const unsigned char esp_jpeg_lum_ac_num_bits[16] = {0, 2, 1, 3, 3, 2, 4, 3, 5, 5, 4, 4, 0, 0, 1, 125};
30+
const unsigned esp_jpeg_lum_ac_codes_total = 162;
31+
const unsigned char esp_jpeg_lum_ac_values[162] = {
32+
0x01, 0x02, 0x03, 0x00, 0x04, 0x11, 0x05, 0x12, 0x21, 0x31, 0x41, 0x06, 0x13, 0x51, 0x61, 0x07,
33+
0x22, 0x71, 0x14, 0x32, 0x81, 0x91, 0xA1, 0x08, 0x23, 0x42, 0xB1, 0xC1, 0x15, 0x52, 0xD1, 0xF0,
34+
0x24, 0x33, 0x62, 0x72, 0x82, 0x09, 0x0A, 0x16, 0x17, 0x18, 0x19, 0x1A, 0x25, 0x26, 0x27, 0x28,
35+
0x29, 0x2A, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49,
36+
0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69,
37+
0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89,
38+
0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6, 0xA7,
39+
0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3, 0xC4, 0xC5,
40+
0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA, 0xE1, 0xE2,
41+
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
42+
0xF9, 0xFA
43+
};
44+
45+
// Chrominance AC Table
46+
const unsigned char esp_jpeg_chrom_ac_num_bits[16] = {0, 2, 1, 2, 4, 4, 3, 4, 7, 5, 4, 4, 0, 1, 2, 119};
47+
const unsigned esp_jpeg_chrom_ac_codes_total = 162;
48+
const unsigned char esp_jpeg_chrom_ac_values[162] = {
49+
0x00, 0x01, 0x02, 0x03, 0x11, 0x04, 0x05, 0x21, 0x31, 0x06, 0x12, 0x41, 0x51, 0x07, 0x61, 0x71,
50+
0x13, 0x22, 0x32, 0x81, 0x08, 0x14, 0x42, 0x91, 0xA1, 0xB1, 0xC1, 0x09, 0x23, 0x33, 0x52, 0xF0,
51+
0x15, 0x62, 0x72, 0xD1, 0x0A, 0x16, 0x24, 0x34, 0xE1, 0x25, 0xF1, 0x17, 0x18, 0x19, 0x1A, 0x26,
52+
0x27, 0x28, 0x29, 0x2A, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48,
53+
0x49, 0x4A, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68,
54+
0x69, 0x6A, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,
55+
0x88, 0x89, 0x8A, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A, 0xA2, 0xA3, 0xA4, 0xA5,
56+
0xA6, 0xA7, 0xA8, 0xA9, 0xAA, 0xB2, 0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xC2, 0xC3,
57+
0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6, 0xD7, 0xD8, 0xD9, 0xDA,
58+
0xE2, 0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8,
59+
0xF9, 0xFA
60+
};
+2-1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
idf_component_register(SRCS "tjpgd_test.c" "test_tjpgd_main.c"
22
INCLUDE_DIRS "."
33
PRIV_REQUIRES "unity"
4-
WHOLE_ARCHIVE)
4+
WHOLE_ARCHIVE
5+
EMBED_FILES "logo.jpg" "usb_camera.jpg")
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,64 @@
1+
from PIL import Image
2+
3+
4+
def jpg_to_rgb888_hex_c_array(input_filename: str, output_filename: str) -> str:
5+
"""
6+
Convert a .jpg file to RGB888 hex data and format it as a C-style array.
7+
8+
Parameters:
9+
input_filename (str): The path to the JPEG file.
10+
11+
Returns:
12+
str: A string representing the RGB888 hex data formatted as a C array.
13+
"""
14+
# Open the image file
15+
with Image.open(input_filename) as img:
16+
# Ensure the image is in RGB mode
17+
rgb_img = img.convert("RGB")
18+
19+
# Get image dimensions
20+
width, height = rgb_img.size
21+
22+
# List to store hex values as C-style entries
23+
hex_data = []
24+
25+
# Iterate over each pixel to get RGB values
26+
for y in range(height):
27+
for x in range(width):
28+
r, g, b = rgb_img.getpixel((x, y))
29+
# Format each RGB value as C-style hex (e.g., 0xRRGGBB)
30+
hex_data.append(f"0x{r:02X}{g:02X}{b:02X}")
31+
32+
# Format as a C-style array with line breaks for readability
33+
hex_array = ",\n ".join(hex_data)
34+
c_array = f"unsigned int image_data[{width * height}] = {{\n {hex_array}\n}};"
35+
36+
# Write the C array to the output file
37+
with open(output_filename, "w") as file:
38+
file.write(c_array)
39+
40+
print(f"C-style RGB888 hex array saved to {output_filename}")
41+
42+
return c_array
43+
44+
45+
def main():
46+
"""
47+
Main function to convert a JPEG file to an RGB888 C-style hex array.
48+
49+
Instructions:
50+
1. Replace 'input.jpg' with the path to your JPEG file.
51+
2. Run the script to get the C-style array output.
52+
"""
53+
# Input JPEG file path
54+
input_filename = "usb_camera.jpg" # Replace with your JPEG file path
55+
56+
# Output file path for the C array
57+
output_filename = "output_array.c" # Specify your desired output filename
58+
59+
# Convert JPEG to C-style RGB888 hex array
60+
jpg_to_rgb888_hex_c_array(input_filename, output_filename)
61+
62+
63+
if __name__ == "__main__":
64+
main()

0 commit comments

Comments
 (0)