add some code

This commit is contained in:
2025-09-05 13:25:11 +08:00
parent 9ff0a99e7a
commit 3cf1229a85
8911 changed files with 2535396 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
idf_component_register(SRCS "tjpgd_test.c" "test_tjpgd_main.c"
INCLUDE_DIRS "."
PRIV_REQUIRES "unity"
WHOLE_ARCHIVE
EMBED_FILES "logo.jpg" "usb_camera.jpg" "usb_camera_2.jpg")

View File

@@ -0,0 +1,4 @@
dependencies:
espressif/esp_jpeg:
version: "*"
override_path: "../../"

View File

@@ -0,0 +1,64 @@
from PIL import Image
def jpg_to_rgb888_hex_c_array(input_filename: str, output_filename: str) -> str:
"""
Convert a .jpg file to RGB888 hex data and format it as a C-style array.
Parameters:
input_filename (str): The path to the JPEG file.
Returns:
str: A string representing the RGB888 hex data formatted as a C array.
"""
# Open the image file
with Image.open(input_filename) as img:
# Ensure the image is in RGB mode
rgb_img = img.convert("RGB")
# Get image dimensions
width, height = rgb_img.size
# List to store hex values as C-style entries
hex_data = []
# Iterate over each pixel to get RGB values
for y in range(height):
for x in range(width):
r, g, b = rgb_img.getpixel((x, y))
# Format each RGB value as C-style hex (e.g., 0xRRGGBB)
hex_data.append(f"0x{r:02X}{g:02X}{b:02X}")
# Format as a C-style array with line breaks for readability
hex_array = ",\n ".join(hex_data)
c_array = f"unsigned int image_data[{width * height}] = {{\n {hex_array}\n}};"
# Write the C array to the output file
with open(output_filename, "w") as file:
file.write(c_array)
print(f"C-style RGB888 hex array saved to {output_filename}")
return c_array
def main():
"""
Main function to convert a JPEG file to an RGB888 C-style hex array.
Instructions:
1. Replace 'input.jpg' with the path to your JPEG file.
2. Run the script to get the C-style array output.
"""
# Input JPEG file path
input_filename = "usb_camera.jpg" # Replace with your JPEG file path
# Output file path for the C array
output_filename = "output_array.c" # Specify your desired output filename
# Convert JPEG to C-style RGB888 hex array
jpg_to_rgb888_hex_c_array(input_filename, output_filename)
if __name__ == "__main__":
main()

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

View File

@@ -0,0 +1,7 @@
// JPEG encoded image 46x46, 7561 bytes
extern const unsigned char logo_jpg[] asm("_binary_logo_jpg_start");
extern char _binary_logo_jpg_start;
extern char _binary_logo_jpg_end;
// Must be defined as macro because extern variables are not known at compile time (but at link time)
#define logo_jpg_len (&_binary_logo_jpg_end - &_binary_logo_jpg_start)

File diff suppressed because one or more lines are too long

View File

@@ -0,0 +1,29 @@
/*
* SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Apache-2.0
*/
#include "unity.h"
#include "unity_test_runner.h"
#include "esp_heap_caps.h"
#include "esp_newlib.h"
#include "unity_test_utils_memory.h"
void setUp(void)
{
unity_utils_record_free_mem();
}
void tearDown(void)
{
esp_reent_cleanup(); //clean up some of the newlib's lazy allocations
unity_utils_evaluate_leaks_direct(0);
}
void app_main(void)
{
printf("Running esp_jpeg component tests\n");
unity_run_menu();
}

View File

@@ -0,0 +1,12 @@
/*
Raw data from Logitech C170 USB camera was reconstructed to usb_camera_2.jpg
It was converted to RGB888 array with jpg_to_rgb888_hex.py
*/
// JPEG encoded frame 160x120, 1384 bytes, has broken 0xFFFF marker
extern const unsigned char camera_2_jpg[] asm("_binary_usb_camera_2_jpg_start");
extern char _binary_usb_camera_2_jpg_start;
extern char _binary_usb_camera_2_jpg_end;
// Must be defined as macro because extern variables are not known at compile time (but at link time)
#define camera_2_jpg_len (&_binary_usb_camera_2_jpg_end - &_binary_usb_camera_2_jpg_start)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,12 @@
/*
Raw data from Logitech C270 USB camera was reconstructed to usb_camera.jpg
It was converted to RGB888 array with jpg_to_rgb888_hex.py
*/
// JPEG encoded frame 160x120, 2632 bytes, no huffman tables, double block size (16x8 pixels)
extern const unsigned char jpeg_no_huffman[] asm("_binary_usb_camera_jpg_start");
extern char _binary_usb_camera_jpg_start;
extern char _binary_usb_camera_jpg_end;
// Must be defined as macro because extern variables are not known at compile time (but at link time)
#define jpeg_no_huffman_len (&_binary_usb_camera_jpg_end - &_binary_usb_camera_jpg_start)

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,328 @@
/*
* SPDX-FileCopyrightText: 2021-2025 Espressif Systems (Shanghai) CO LTD
*
* SPDX-License-Identifier: Unlicense OR CC0-1.0
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <stdio.h>
#include "sdkconfig.h"
#include "unity.h"
#include "jpeg_decoder.h"
#include "test_logo_jpg.h"
#include "test_logo_rgb888.h"
#include "test_usb_camera_2_jpg.h"
#include "test_usb_camera_2_rgb888.h"
#define TESTW 46
#define TESTH 46
void esp_jpeg_print_ascii(unsigned char *rgb888, esp_jpeg_image_output_t *outimg)
{
char aapix[] = " .:;+=xX$$";
unsigned char *p = rgb888 + 2;
for (int y = 0; y < outimg->width; y++) {
for (int x = 0; x < outimg->height; x++) {
int v = ((*p) * (sizeof(aapix) - 2) * 2) / 256;
printf("%c%c", aapix[v / 2], aapix[(v + 1) / 2]);
p += 3;
}
printf("%c%c", ' ', '\n');
}
}
TEST_CASE("Test JPEG decompression library", "[esp_jpeg]")
{
unsigned char *decoded, *p;
const unsigned char *o;
int decoded_outsize = TESTW * TESTH * 3;
decoded = malloc(decoded_outsize);
for (int x = 0; x < decoded_outsize; x += 2) {
decoded[x] = 0;
decoded[x + 1] = 0xff;
}
/* JPEG decode */
esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = (uint8_t *)logo_jpg,
.indata_size = logo_jpg_len,
.outbuf = decoded,
.outbuf_size = decoded_outsize,
.out_format = JPEG_IMAGE_FORMAT_RGB888,
.out_scale = JPEG_IMAGE_SCALE_0,
.flags = {
.swap_color_bytes = 0,
}
};
esp_jpeg_image_output_t outimg;
esp_err_t err = esp_jpeg_decode(&jpeg_cfg, &outimg);
TEST_ASSERT_EQUAL(err, ESP_OK);
/* Decoded image size */
TEST_ASSERT_EQUAL(outimg.width, TESTW);
TEST_ASSERT_EQUAL(outimg.height, TESTH);
p = decoded;
o = logo_rgb888;
for (int x = 0; x < outimg.width * outimg.height; x++) {
/* The color can be +- 2 */
TEST_ASSERT_UINT8_WITHIN(2, o[0], p[0]);
TEST_ASSERT_UINT8_WITHIN(2, o[1], p[1]);
TEST_ASSERT_UINT8_WITHIN(2, o[2], p[2]);
p += 3;
o += 3;
}
esp_jpeg_print_ascii(decoded, &outimg);
free(decoded);
}
/**
* @brief JPEG unknown size test
*
* This test case verifies the functionality of the JPEG decompression library
* when decoding an image with unknown size. The image is decoded from a
* JPEG file, and the output size is determined dynamically. The test checks
* that the decoded image dimensions match the expected values and that the
* pixel data is within an acceptable tolerance range.
*/
TEST_CASE("Test JPEG unknown size", "[esp_jpeg]")
{
unsigned char *decoded, *p;
const unsigned char *o;
/* JPEG decode */
esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = (uint8_t *)logo_jpg,
.indata_size = logo_jpg_len,
.out_format = JPEG_IMAGE_FORMAT_RGB888,
};
// 1. Get required output size
esp_jpeg_image_output_t outimg;
esp_err_t err = esp_jpeg_get_image_info(&jpeg_cfg, &outimg);
TEST_ASSERT_EQUAL(err, ESP_OK);
TEST_ASSERT_EQUAL(TESTW * TESTH * 3, outimg.output_len);
TEST_ASSERT_EQUAL(outimg.width, TESTW);
TEST_ASSERT_EQUAL(outimg.height, TESTH);
// 2. Allocate output buffer and assign it to the config
decoded = malloc(outimg.output_len);
TEST_ASSERT_NOT_NULL(decoded);
jpeg_cfg.outbuf = decoded;
jpeg_cfg.outbuf_size = outimg.output_len;
// 3. Decode the image
err = esp_jpeg_decode(&jpeg_cfg, &outimg);
TEST_ASSERT_EQUAL(err, ESP_OK);
/* Decoded image size */
TEST_ASSERT_EQUAL(TESTW * TESTH * 3, outimg.output_len);
TEST_ASSERT_EQUAL(outimg.width, TESTW);
TEST_ASSERT_EQUAL(outimg.height, TESTH);
p = decoded;
o = logo_rgb888;
for (int x = 0; x < outimg.width * outimg.height; x++) {
/* The color can be +- 2 */
TEST_ASSERT_UINT8_WITHIN(2, o[0], p[0]);
TEST_ASSERT_UINT8_WITHIN(2, o[1], p[1]);
TEST_ASSERT_UINT8_WITHIN(2, o[2], p[2]);
p += 3;
o += 3;
}
free(decoded);
}
#define WORKING_BUFFER_SIZE 4096
TEST_CASE("Test JPEG decompression library: User defined working buffer", "[esp_jpeg]")
{
unsigned char *decoded, *p;
const unsigned char *o;
int decoded_outsize = TESTW * TESTH * 3;
decoded = malloc(decoded_outsize);
uint8_t *working_buf = malloc(WORKING_BUFFER_SIZE);
assert(decoded);
assert(working_buf);
for (int x = 0; x < decoded_outsize; x += 2) {
decoded[x] = 0;
decoded[x + 1] = 0xff;
}
/* JPEG decode */
esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = (uint8_t *)logo_jpg,
.indata_size = logo_jpg_len,
.outbuf = decoded,
.outbuf_size = decoded_outsize,
.out_format = JPEG_IMAGE_FORMAT_RGB888,
.out_scale = JPEG_IMAGE_SCALE_0,
.flags = {
.swap_color_bytes = 0,
},
.advanced = {
.working_buffer = working_buf,
.working_buffer_size = WORKING_BUFFER_SIZE,
},
};
esp_jpeg_image_output_t outimg;
esp_err_t err = esp_jpeg_decode(&jpeg_cfg, &outimg);
TEST_ASSERT_EQUAL(err, ESP_OK);
/* Decoded image size */
TEST_ASSERT_EQUAL(outimg.width, TESTW);
TEST_ASSERT_EQUAL(outimg.height, TESTH);
p = decoded;
o = logo_rgb888;
for (int x = 0; x < outimg.width * outimg.height; x++) {
/* The color can be +- 2 */
TEST_ASSERT_UINT8_WITHIN(2, o[0], p[0]);
TEST_ASSERT_UINT8_WITHIN(2, o[1], p[1]);
TEST_ASSERT_UINT8_WITHIN(2, o[2], p[2]);
p += 3;
o += 3;
}
free(working_buf);
free(decoded);
}
#if CONFIG_JD_DEFAULT_HUFFMAN
#include "test_usb_camera_jpg.h"
#include "test_usb_camera_rgb888.h"
/**
* @brief Test for JPEG decompression without Huffman tables
*
* This test case verifies the functionality of the JPEG decompression library
* when decoding an image that lacks Huffman tables, such as a USB frame
* from a Logitech C270 USB camera. The image was reconstructed from raw USB data
* (using `hex_to_jpg.py`) and then converted into an RGB888 C-style array
* (using `jpg_to_rgb888_hex.py`).
*
* Due to the unique structure of the JPEG data (double block size, 16x8 pixels)
* and absence of Huffman tables, this test assesses whether the decompression
* library correctly decodes the image and outputs RGB888 pixel data within
* an acceptable tolerance range.
*
* The test performs the following steps:
* - Allocates a buffer for the decoded image.
* - Configures and runs the JPEG decoder with the RGB888 output format.
* - Checks that the decoded image dimensions match expected values.
* - Compares the decompressed image data against the reference RGB888 data,
* allowing a tolerance of ±16 in each color component due to potential
* differences in Huffman tables or decompression accuracy.
*
* @note This test allows a margin of error in pixel values due to potential
* differences in how color data is interpreted across different decoders.
*
* @param None
*
* @return None
*
* @test Requirements:
* - JPEG decompression library support for images without Huffman tables.
* - JPEG decompression accuracy within acceptable error margins.
*/
TEST_CASE("Test JPEG decompression library: No Huffman tables", "[esp_jpeg]")
{
unsigned char *decoded, *p;
const unsigned int *o;
int decoded_outsize = 160 * 120 * 3;
decoded = malloc(decoded_outsize);
/* JPEG decode */
esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = (uint8_t *)jpeg_no_huffman,
.indata_size = jpeg_no_huffman_len,
.outbuf = decoded,
.outbuf_size = decoded_outsize,
.out_format = JPEG_IMAGE_FORMAT_RGB888,
.out_scale = JPEG_IMAGE_SCALE_0,
.flags = {
.swap_color_bytes = 0,
}
};
esp_jpeg_image_output_t outimg;
esp_err_t err = esp_jpeg_decode(&jpeg_cfg, &outimg);
TEST_ASSERT_EQUAL(err, ESP_OK);
/* Decoded image size */
TEST_ASSERT_EQUAL(outimg.width, 160);
TEST_ASSERT_EQUAL(outimg.height, 120);
p = decoded;
o = jpeg_no_huffman_rgb888;
for (int x = 0; x < outimg.width * outimg.height; x++) {
/* The color can be +- 16 */
// Here we allow bigger decoding error
// It might be that the Windows decoder used slightly different Huffman tables
TEST_ASSERT_UINT8_WITHIN(16, (*o) & 0xff, p[0]);
TEST_ASSERT_UINT8_WITHIN(16, (*o >> 8) & 0xff, p[1]);
TEST_ASSERT_UINT8_WITHIN(16, (*o >> 16) & 0xff, p[2]);
p += 3; // this is uint8_t
o ++; // this is unt32_t
}
free(decoded);
}
#endif
/**
* @brief Invalid JPEG marker test
*
* This test case verifies the behavior of the JPEG decompression library
* when encountering an invalid marker (0xFFFF) in the JPEG data stream.
* The test uses a known JPEG image (camera_2_jpg) that contains this invalid
* marker. The test checks whether the library can handle the invalid marker
* gracefully and still decode the image correctly.
*/
TEST_CASE("Test JPEG invalid marker 0xFFFF", "[esp_jpeg]")
{
unsigned char *decoded;
int decoded_outsize = 160 * 120 * 3;
decoded = malloc(decoded_outsize);
assert(decoded);
for (int x = 0; x < decoded_outsize; x += 2) {
decoded[x] = 0;
decoded[x + 1] = 0xff;
}
/* JPEG decode */
esp_jpeg_image_cfg_t jpeg_cfg = {
.indata = (uint8_t *)camera_2_jpg,
.indata_size = camera_2_jpg_len,
.outbuf = decoded,
.outbuf_size = decoded_outsize,
.out_format = JPEG_IMAGE_FORMAT_RGB888,
.out_scale = JPEG_IMAGE_SCALE_0,
.flags = {
.swap_color_bytes = 0,
}
};
esp_jpeg_image_output_t outimg;
esp_err_t err = esp_jpeg_decode(&jpeg_cfg, &outimg);
TEST_ASSERT_EQUAL(ESP_OK, err);
/* Decoded image size */
TEST_ASSERT_EQUAL(160, outimg.width);
TEST_ASSERT_EQUAL(120, outimg.height);
free(decoded);
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB