mirror of
https://github.com/espressif/esp-idf.git
synced 2026-09-22 13:01:16 +03:00
feat(jpeg): simplify decoder example and add pytest coverage
This commit is contained in:
@@ -97,7 +97,9 @@ esp_err_t jpeg_decoder_get_info(const uint8_t *bit_stream, uint32_t stream_size,
|
||||
* returned through the `out_size` pointer.
|
||||
*
|
||||
* @note 1.Please make sure that the content of `bit_stream` pointer cannot be modified until this function returns.
|
||||
* 2.Please note that the output size of image is always the multiple of 16 depends on protocol of JPEG.
|
||||
* 2.For JPEGs encoded with YUV420 or YUV422 sampling, the decoded output dimensions can be padded
|
||||
* to 16-pixel boundaries by the JPEG block layout. Make sure `decode_outbuf` is large enough for
|
||||
* that padded output size, not only for the visible width and height.
|
||||
*
|
||||
* @param[in] decoder_engine Handle of the JPEG decoder instance to use for processing.
|
||||
* @param[in] decode_cfg Config structure of decoder.
|
||||
|
||||
@@ -9,8 +9,8 @@ set(EXTRA_COMPONENT_DIRS "$ENV{IDF_PATH}/tools/test_apps/components")
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
project(jpeg_test)
|
||||
|
||||
target_add_binary_data(jpeg_test.elf "${IDF_PATH}/examples/peripherals/jpeg/jpeg_decode/resources/esp720.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "${IDF_PATH}/examples/peripherals/jpeg/jpeg_decode/resources/esp1080.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "resources/esp720.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "resources/esp1080.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "resources/no_huff.jpg" BINARY)
|
||||
target_add_binary_data(jpeg_test.elf "resources/esp480.rgb" BINARY)
|
||||
|
||||
|
||||
|
Before Width: | Height: | Size: 48 KiB After Width: | Height: | Size: 48 KiB |
|
Before Width: | Height: | Size: 24 KiB After Width: | Height: | Size: 24 KiB |
@@ -130,18 +130,13 @@ Overall, You can take following code as reference, the code is going to decode a
|
||||
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
|
||||
};
|
||||
|
||||
size_t tx_buffer_size;
|
||||
size_t rx_buffer_size;
|
||||
|
||||
jpeg_decode_memory_alloc_cfg_t rx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER,
|
||||
};
|
||||
|
||||
jpeg_decode_memory_alloc_cfg_t tx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER,
|
||||
};
|
||||
|
||||
uint8_t *bit_stream = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size, &tx_mem_cfg, &tx_buffer_size);
|
||||
const uint8_t *bit_stream = embedded_jpeg_start;
|
||||
uint8_t *out_buf = (uint8_t*)jpeg_alloc_decoder_mem(1920 * 1088 * 3, &rx_mem_cfg, &rx_buffer_size);
|
||||
|
||||
jpeg_decode_picture_info_t header_info;
|
||||
@@ -152,11 +147,11 @@ Overall, You can take following code as reference, the code is going to decode a
|
||||
|
||||
There are some tips that can help you use this driver more accurately:
|
||||
|
||||
1. In above code, you should make sure the `bit_stream` and `out_buf` should be aligned by certain rules. We provide a helper function :cpp:func:`jpeg_alloc_decoder_mem` to help you malloc a buffer which is aligned in both size and address.
|
||||
1. In above code, you should make sure the output buffer `out_buf` follows the driver's alignment requirements. We provide a helper function :cpp:func:`jpeg_alloc_decoder_mem` to help you allocate a buffer with aligned size and address.
|
||||
|
||||
2. The content of `bit_stream` buffer should not be changed until :cpp:func:`jpeg_decoder_process` returns.
|
||||
2. The content of `bit_stream` should not be changed until :cpp:func:`jpeg_decoder_process` returns. This input buffer can come directly from flash-mapped embedded data or any other memory region that stays readable for the full call.
|
||||
|
||||
3. The width and height of output picture would be 16 bytes aligned if original picture is compressed by YUV420 or YUV422. For example, if the input picture is 1080*1920, the output picture will be 1088*1920. That is the restriction of jpeg protocol. Please provide sufficient output buffer memory.
|
||||
3. If the source JPEG uses YUV420 or YUV422 sampling, the decoded output dimensions can be padded up to 16-pixel boundaries. For example, if the visible image size is 1080*1920, the decoder may require an output buffer sized for 1088*1920 pixels. This comes from the JPEG block layout, so please provide enough output buffer memory for the padded image, not only for the visible width and height.
|
||||
|
||||
JPEG Encoder Engine
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
@@ -455,7 +450,7 @@ The JPEG driver usage of hardware resources and its dependency status are shown
|
||||
Application Examples
|
||||
--------------------
|
||||
|
||||
- :example:`peripherals/jpeg/jpeg_decode` demonstrates how to use the JPEG hardware decoder to decode JPEG pictures of different sizes (1080p and 720p) into RGB format, showcasing the flexibility and speed of hardware decoding.
|
||||
- :example:`peripherals/jpeg/jpeg_decode` demonstrates how to use the JPEG hardware decoder to parse one embedded JPEG, decode it into RGB888, stream the raw output as base64 over UART, and validate the result with pytest.
|
||||
|
||||
- :example:`peripherals/jpeg/jpeg_encode` demonstrates how to use the JPEG hardware encoder to encode an embedded 720p raw picture, stream the JPEG as base64 over UART, and validate the result with pytest.
|
||||
|
||||
|
||||
@@ -130,18 +130,13 @@ JPEG 解码器引擎
|
||||
.rgb_order = JPEG_DEC_RGB_ELEMENT_ORDER_BGR,
|
||||
};
|
||||
|
||||
size_t tx_buffer_size;
|
||||
size_t rx_buffer_size;
|
||||
|
||||
jpeg_decode_memory_alloc_cfg_t rx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_OUTPUT_BUFFER,
|
||||
};
|
||||
|
||||
jpeg_decode_memory_alloc_cfg_t tx_mem_cfg = {
|
||||
.buffer_direction = JPEG_DEC_ALLOC_INPUT_BUFFER,
|
||||
};
|
||||
|
||||
uint8_t *bit_stream = (uint8_t*)jpeg_alloc_decoder_mem(jpeg_size, &tx_mem_cfg, &tx_buffer_size);
|
||||
const uint8_t *bit_stream = embedded_jpeg_start;
|
||||
uint8_t *out_buf = (uint8_t*)jpeg_alloc_decoder_mem(1920 * 1088 * 3, &rx_mem_cfg, &rx_buffer_size);
|
||||
|
||||
jpeg_decode_picture_info_t header_info;
|
||||
@@ -152,11 +147,11 @@ JPEG 解码器引擎
|
||||
|
||||
参考以下提示,可以更准确地使用该驱动程序:
|
||||
|
||||
1. 在上述代码中,应确保 `bit_stream` 和 `out_buf` 按照一定的规则对齐。可以通过 :cpp:func:`jpeg_alloc_decoder_mem` 函数来分配一个在大小和地址上都对齐的缓冲区。
|
||||
1. 在上述代码中,应确保输出缓冲区 `out_buf` 满足驱动的对齐要求。可以通过 :cpp:func:`jpeg_alloc_decoder_mem` 函数来分配一个在大小和地址上都对齐的缓冲区。
|
||||
|
||||
2. 在 :cpp:func:`jpeg_decoder_process` 返回前, `bit_stream` 缓冲区的内容不应有更改。
|
||||
2. 在 :cpp:func:`jpeg_decoder_process` 返回前, `bit_stream` 指向的输入内容不应有更改。该输入缓冲区既可以直接来自映射到 flash 的嵌入式数据,也可以来自其他在整个调用期间保持可读的内存区域。
|
||||
|
||||
3. 如果原始图片以 YUV420 或 YUV422 格式压缩,则输出图片的宽度和高度将会以 16 字节对齐。例如,如果输入图片大小为 1080*1920,则输出图片大小为 1088*1920。这是 jpeg 协议的限制,所以请准备足够的输出缓冲区内存。
|
||||
3. 如果源 JPEG 使用 YUV420 或 YUV422 采样方式,解码后的输出图像尺寸可能会被补齐到 16 像素边界。例如,当可见图像大小为 1080*1920 时,解码器可能需要按 1088*1920 像素来分配输出缓冲区。这来自 JPEG 的块布局限制,因此请按补齐后的图像尺寸而不是仅按可见宽高准备足够的输出缓冲区内存。
|
||||
|
||||
JPEG 编码器引擎
|
||||
^^^^^^^^^^^^^^^
|
||||
@@ -455,7 +450,7 @@ Kconfig 选项
|
||||
应用程序示例
|
||||
------------
|
||||
|
||||
- :example:`peripherals/jpeg/jpeg_decode` 演示了如何使用 JPEG 硬件解码器将不同大小的 JPEG 图片(1080p 和 720p)解码为 RGB 格式,展示了硬件解码的速度和灵活性。
|
||||
- :example:`peripherals/jpeg/jpeg_decode` 演示了如何使用 JPEG 硬件解码器解析一张嵌入式 JPEG,将其解码为 RGB888,通过 UART 输出 base64 原始结果,并使用 pytest 做回归校验。
|
||||
|
||||
- :example:`peripherals/jpeg/jpeg_encode` 演示了如何使用 JPEG 硬件编码器对一张嵌入式 720p 原始图像进行编码,并通过 UART 输出 base64 JPEG,再用 pytest 做结果校验。
|
||||
|
||||
|
||||
@@ -241,7 +241,7 @@ examples/peripherals/isp/multi_pipelines:
|
||||
|
||||
examples/peripherals/jpeg/jpeg_decode:
|
||||
disable:
|
||||
- if: SOC_JPEG_CODEC_SUPPORTED != 1
|
||||
- if: SOC_JPEG_DECODE_SUPPORTED != 1
|
||||
depends_components:
|
||||
- *common_components
|
||||
- esp_driver_jpeg
|
||||
|
||||
@@ -5,4 +5,4 @@ cmake_minimum_required(VERSION 3.22)
|
||||
include($ENV{IDF_PATH}/tools/cmake/project.cmake)
|
||||
# "Trim" the build. Include the minimal set of components, main, and anything it depends on.
|
||||
idf_build_set_property(MINIMAL_BUILD ON)
|
||||
project(jpeg_decode)
|
||||
project(jpeg_decode_example)
|
||||
|
||||
@@ -5,21 +5,25 @@
|
||||
|
||||
## Overview
|
||||
|
||||
This example demonstrates how to use the JPEG hardware decoder to decode a 1080p and a 720p picture:
|
||||
This example demonstrates how to use the JPEG hardware decoder to decode one embedded JPEG image into `RGB888` raw bytes in default `BGR24` order.
|
||||
|
||||
If you have a bunch of big JPEG picture need to be decoded, such as `*.jpg` -> `*.rgb`, and this example uses hardware JPEG decoder to accelerate the decoding.
|
||||
The example performs:
|
||||
|
||||
## How to use example
|
||||
- Embedding `main/assets/image.jpg` into the application image
|
||||
- Letting the JPEG decoder read the embedded JPEG bitstream directly from flash
|
||||
- Parsing the JPEG header with `jpeg_decoder_get_info()`
|
||||
- Decoding the image into an `RGB888` output buffer with default `BGR24` byte order
|
||||
- Allocating the output buffer for padded dimensions when the JPEG block layout rounds width or height up to 16-pixel boundaries
|
||||
- Base64-encoding the decoded raw pixels and printing them with machine-parseable UART markers
|
||||
- Letting pytest rebuild `jpeg_decode_result.ppm` for inspection and compare it against `golden_output.ppm`
|
||||
|
||||
### Prerequisites Required
|
||||
## Hardware Required
|
||||
|
||||
This example demonstrates the flexibility of decoding pictures by decoding two different sizes: one in 1080p and another in 720p. It showcases how you can easily modify the code to meet your specific requirements, such as only decoding 1080p photos.
|
||||
Any board based on a supported target can be used. No SD card or external storage setup is required.
|
||||
|
||||
### Build and Flash
|
||||
## Build and Flash
|
||||
|
||||
Before you start build and flash this example, please put the image `esp720.jpg` and `esp1080.jpg` in your sdcard.
|
||||
|
||||
Enter `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
Run `idf.py -p PORT flash monitor` to build, flash and monitor the project.
|
||||
|
||||
(To exit the serial monitor, type ``Ctrl-]``.)
|
||||
|
||||
@@ -27,32 +31,53 @@ See the [Getting Started Guide](https://docs.espressif.com/projects/esp-idf/en/l
|
||||
|
||||
## Example Output
|
||||
|
||||
```bash
|
||||
I (1116) jpeg.example: Initializing SD card
|
||||
I (1116) gpio: GPIO[43]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1126) gpio: GPIO[44]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1136) gpio: GPIO[39]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1146) gpio: GPIO[40]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1156) gpio: GPIO[41]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
I (1166) gpio: GPIO[42]| InputEn: 0| OutputEn: 1| OpenDrain: 0| Pullup: 0| Pulldown: 0| Intr:0
|
||||
I (1416) gpio: GPIO[42]| InputEn: 0| OutputEn: 0| OpenDrain: 0| Pullup: 1| Pulldown: 0| Intr:0
|
||||
Name: SD64G
|
||||
Type: SDHC/SDXC
|
||||
Speed: 40.00 MHz (limit: 40.00 MHz)
|
||||
Size: 60906MB
|
||||
CSD: ver=2, sector_size=512, capacity=124735488 read_bl_len=9
|
||||
SSR: bus_width=4
|
||||
I (1436) jpeg.example: jpg_file_1080:/sdcard/esp1080.jpg
|
||||
I (1696) jpeg.example: jpg_file_1080:/sdcard/esp720.jpg
|
||||
I (1796) jpeg.example: header parsed, width is 1920, height is 1080
|
||||
I (1846) jpeg.example: raw_file_1080:/sdcard/out.rgb
|
||||
I (11836) jpeg.example: raw_file_720:/sdcard/out2.rgb
|
||||
I (13336) jpeg.example: Card unmounted
|
||||
I (13336) main_task: Returned from app_main()
|
||||
```text
|
||||
Loading embedded JPEG from flash...
|
||||
Embedded JPEG size: 43700 bytes
|
||||
JPEG header parsed: width=320 height=240
|
||||
Decoding JPEG -> RGB888...
|
||||
Decoded RGB888 size: 245760 bytes
|
||||
JPEG_DECODE_INFO width=320 height=240 padded_width=320 padded_height=256 format=RGB888 encoding=base64 size=245760
|
||||
JPEG_DECODE_BASE64_BEGIN
|
||||
JPEG_DECODE_BASE64 ...
|
||||
JPEG_DECODE_BASE64 ...
|
||||
JPEG_DECODE_BASE64_END
|
||||
JPEG decode demo done.
|
||||
```
|
||||
|
||||
Also, the helper script [open_raw_picture.py](./open_raw_picture.py) simplifies the visualization of the output on your computer. For this to work, go to `examples/peripheral/jpeg/jpeg_decode` and install the requirements by running `pip install -r requirements.txt`.
|
||||
`padded_width` and `padded_height` report the actual decoded buffer dimensions. For JPEGs whose block layout pads the output to 16-pixel boundaries, these values can be larger than the visible `width` and `height`, so the output buffer must be sized for the padded image.
|
||||
|
||||
## Pytest Regression Check
|
||||
|
||||
The accompanying `pytest_jpeg_decode.py` script waits for the `JPEG_DECODE_INFO` and `JPEG_DECODE_BASE64` markers, reconstructs the decoded raw pixel output, crops away padded rows, and saves the visible image as:
|
||||
|
||||
- `dut.logdir/jpeg_decode_result.ppm`
|
||||
|
||||
The test writes the `PPM` file and compares it with `golden_output.ppm`. This makes the example both a functional regression test and a host-side artifact generator for inspection.
|
||||
|
||||
## Running Pytest Locally And Viewing The Image
|
||||
|
||||
To run the pytest helper locally on hardware, build the example for your target first, then invoke the test script with the target and serial port:
|
||||
|
||||
```bash
|
||||
idf.py set-target esp32p4 build
|
||||
pytest --target esp32p4 --port PORT pytest_jpeg_decode.py
|
||||
```
|
||||
|
||||
Replace `esp32p4` with another supported target such as `esp32s31` when needed.
|
||||
|
||||
`pytest-embedded` stores per-test logs under `$IDF_PATH/pytest-embedded/`. The script writes the reconstructed image to `jpeg_decode_result.ppm` inside that test log directory, so after the test finishes you can open the generated `PPM` file locally with an image viewer that supports `PPM` to inspect the decoded output.
|
||||
|
||||
## Replacing The Embedded JPEG Asset
|
||||
|
||||
If you want to try another input image, replace:
|
||||
|
||||
- `main/assets/image.jpg`
|
||||
|
||||
Keep the replacement as a baseline JPEG with the same general scale if you want UART log volume and test runtime to stay small.
|
||||
|
||||
After replacing the asset, rerun the example and update `golden_output.ppm` from the generated `dut.logdir/jpeg_decode_result.ppm` artifact if the new output should become the expected result.
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
||||
(For any technical queries, please open an [issue](https://github.com/espressif/esp-idf/issues) on GitHub. We will get back to you as soon as possible.)
|
||||
|
||||
BIN
examples/peripherals/jpeg/jpeg_decode/golden_output.ppm
Normal file
BIN
examples/peripherals/jpeg/jpeg_decode/golden_output.ppm
Normal file
Binary file not shown.
@@ -1,3 +1,5 @@
|
||||
idf_component_register(SRCS "jpeg_decode_main.c"
|
||||
PRIV_REQUIRES fatfs esp_driver_jpeg esp_psram
|
||||
INCLUDE_DIRS ".")
|
||||
idf_component_register(SRCS "jpeg_decode_example_main.c"
|
||||
PRIV_REQUIRES esp_driver_jpeg mbedtls
|
||||
INCLUDE_DIRS ".")
|
||||
|
||||
target_add_binary_data(${COMPONENT_LIB} "${CMAKE_CURRENT_LIST_DIR}/assets/image.jpg" BINARY RENAME_TO "example_jpeg")
|
||||
|
||||
@@ -1,18 +0,0 @@
|
||||
menu "JPEG Decode Example menu"
|
||||
|
||||
config EXAMPLE_FORMAT_IF_MOUNT_FAILED
|
||||
bool "Format the card if mount failed"
|
||||
default n
|
||||
help
|
||||
If this config item is set, format_if_mount_failed will be set to true and the card will be formatted if
|
||||
the mount has failed.
|
||||
|
||||
config EXAMPLE_SDMMC_IO_POWER_INTERNAL_LDO
|
||||
depends on SOC_SDMMC_IO_POWER_EXTERNAL
|
||||
bool "SDMMC IO power supply comes from internal LDO (READ HELP!)"
|
||||
default y
|
||||
help
|
||||
Please read the schematic first and check if the SDMMC VDD is connected to any internal LDO output.
|
||||
If the SDMMC is powered by an external supplier, unselect me
|
||||
|
||||
endmenu
|
||||
BIN
examples/peripherals/jpeg/jpeg_decode/main/assets/image.jpg
Normal file
BIN
examples/peripherals/jpeg/jpeg_decode/main/assets/image.jpg
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 43 KiB |
@@ -1,189 +0,0 @@
|
||||
# SPDX-FileCopyrightText: 2024 Espressif Systems (Shanghai) CO LTD
|
||||
# SPDX-License-Identifier: Unlicense OR CC0-1.0
|
||||
import argparse
|
||||
|
||||
import cv2 as cv
|
||||
import numpy as np
|
||||
from numpy.typing import NDArray
|
||||
|
||||
|
||||
def open_picture(path): # type: (str) -> list[int]
|
||||
with open(path, 'rb') as f:
|
||||
data = f.read()
|
||||
f.close()
|
||||
new_data = [int(x) for x in data]
|
||||
return new_data
|
||||
|
||||
|
||||
def picture_show_rgb888(data, h, w): # type: (list[int], int, int) -> None
|
||||
data = np.array(data).reshape(h, w, 3).astype(np.uint8)
|
||||
cv.imshow('data', data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def picture_show_rgb565(data, h, w): # type: (list[int], int, int) -> None
|
||||
|
||||
new_data = [0] * ((len(data) // 2) * 3)
|
||||
for i in range(len(data)):
|
||||
if i % 2 != 0:
|
||||
new_data[3 * (i - 1) // 2 + 2] = (data[i] & 0xf8)
|
||||
new_data[3 * (i - 1) // 2 + 1] |= (data[i] & 0x7) << 5
|
||||
else:
|
||||
new_data[3 * i // 2] = (data[i] & 0x1f) << 3
|
||||
new_data[3 * i // 2 + 1] |= (data[i] & 0xe0) >> 3
|
||||
|
||||
new_data = np.array(new_data).reshape(h, w, 3).astype(np.uint8)
|
||||
cv.imshow('data', new_data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def picture_show_gray(data, h, w): # type: (list[int], int, int) -> None
|
||||
new_data = np.array(data).reshape(h, w, 1).astype(np.uint8)
|
||||
cv.imshow('data', new_data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def convert_YUV_to_RGB(Y, U, V): # type: (NDArray, NDArray, NDArray) -> tuple[NDArray, NDArray, NDArray]
|
||||
B = np.clip(Y + 1.7790 * (U - 128), 0, 255).astype(np.uint8)
|
||||
G = np.clip(Y - 0.3455 * (U - 128) - 0.7169 * (V - 128), 0, 255).astype(np.uint8)
|
||||
R = np.clip(Y + 1.4075 * (V - 128), 0, 255).astype(np.uint8)
|
||||
|
||||
return B, G, R
|
||||
|
||||
|
||||
def picture_show_yuv420(data, h, w): # type: (list[int], int, int) -> None
|
||||
new_u = [0] * (h * w)
|
||||
new_v = [0] * (h * w)
|
||||
new_y = [0] * (h * w)
|
||||
|
||||
for i in range(int(h * w * 1.5)):
|
||||
is_even_row = ((i // (w * 1.5)) % 2 == 0)
|
||||
if is_even_row:
|
||||
if (i % 3 == 0):
|
||||
new_u[(i // 3) * 2] = data[i]
|
||||
new_u[(i // 3) * 2 + 1] = data[i]
|
||||
else:
|
||||
if (i % 3 == 0):
|
||||
new_u[(i // 3) * 2] = new_u[int((i - (w * 1.5)) // 3) * 2]
|
||||
new_u[(i // 3) * 2 + 1] = new_u[int((i - (w * 1.5)) // 3) * 2 + 1]
|
||||
|
||||
for i in range(int(h * w * 1.5)):
|
||||
if (i // (w * 1.5)) % 2 != 0 and (i % 3 == 0):
|
||||
idx = (i // 3) * 2
|
||||
new_v[idx] = data[i]
|
||||
new_v[idx + 1] = data[i]
|
||||
|
||||
for i in range(int(h * w * 1.5)):
|
||||
if (i // (w * 1.5)) % 2 == 0 and (i % 3 == 0):
|
||||
idx = (i // 3) * 2
|
||||
new_v[idx] = new_v[int((i + (w * 1.5)) // 3) * 2]
|
||||
new_v[idx + 1] = new_v[int((i + (w * 1.5)) // 3) * 2 + 1]
|
||||
|
||||
new_y = [data[i] for i in range(int(h * w * 1.5)) if i % 3 != 0]
|
||||
|
||||
Y = np.array(new_y)
|
||||
U = np.array(new_u)
|
||||
V = np.array(new_v)
|
||||
|
||||
B, G, R = convert_YUV_to_RGB(Y, U, V)
|
||||
# Merge channels
|
||||
new_data = np.stack((B, G, R), axis=-1)
|
||||
new_data = np.array(new_data).reshape(h, w, 3).astype(np.uint8)
|
||||
|
||||
# Display the image
|
||||
cv.imshow('data', new_data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def picture_show_yuv422(data, h, w): # type: (list[int], int, int) -> None
|
||||
# Reshape the input data to a 2D array
|
||||
data_array = np.array(data).reshape(h, w * 2)
|
||||
|
||||
# Separate Y, U, and V channels
|
||||
Y = data_array[:, 1::2]
|
||||
U = data_array[:, 0::4].repeat(2, axis=1)
|
||||
V = data_array[:, 2::4].repeat(2, axis=1)
|
||||
|
||||
# Convert YUV to RGB
|
||||
B, G, R = convert_YUV_to_RGB(Y, U, V)
|
||||
|
||||
# Merge channels
|
||||
new_data = np.stack((B, G, R), axis=-1)
|
||||
|
||||
# Display the image
|
||||
cv.imshow('data', new_data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def picture_show_yuv444(data, h, w): # type: (list[int], int, int) -> None
|
||||
# Reshape the input data to a 2D array
|
||||
data_array = np.array(data).reshape(h, w * 3)
|
||||
|
||||
# Separate Y, U, and V channels
|
||||
Y = data_array[:, 2::3]
|
||||
U = data_array[:, 1::3]
|
||||
V = data_array[:, 0::3]
|
||||
|
||||
# Convert YUV to RGB
|
||||
B, G, R = convert_YUV_to_RGB(Y, U, V)
|
||||
|
||||
# Merge channels
|
||||
new_data = np.stack((B, G, R), axis=-1)
|
||||
|
||||
# Display the image
|
||||
cv.imshow('data', new_data)
|
||||
cv.waitKey()
|
||||
|
||||
|
||||
def main(): # type: () -> None
|
||||
parser = argparse.ArgumentParser(description='which mode need to show')
|
||||
|
||||
parser.add_argument(
|
||||
'--pic_path',
|
||||
type=str,
|
||||
help='What is the path of your picture',
|
||||
required=True)
|
||||
|
||||
parser.add_argument(
|
||||
'--pic_type',
|
||||
type=str,
|
||||
help='What type you want to show',
|
||||
required=True,
|
||||
choices=['rgb565', 'rgb888', 'gray', 'yuv422', 'yuv420', 'yuv444'])
|
||||
|
||||
parser.add_argument(
|
||||
'--height',
|
||||
type=int,
|
||||
help='the picture height',
|
||||
default=480)
|
||||
|
||||
parser.add_argument(
|
||||
'--width',
|
||||
type=int,
|
||||
help='the picture width',
|
||||
default=640)
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
height = args.height
|
||||
width = args.width
|
||||
|
||||
data = open_picture(args.pic_path)
|
||||
if (args.pic_type == 'rgb565'):
|
||||
picture_show_rgb565(data, height, width)
|
||||
elif (args.pic_type == 'rgb888'):
|
||||
picture_show_rgb888(data, height, width)
|
||||
elif (args.pic_type == 'gray'):
|
||||
picture_show_gray(data, height, width)
|
||||
elif (args.pic_type == 'yuv420'):
|
||||
picture_show_yuv420(data, height, width)
|
||||
elif (args.pic_type == 'yuv422'):
|
||||
picture_show_yuv422(data, height, width)
|
||||
elif (args.pic_type == 'yuv444'):
|
||||
picture_show_yuv444(data, height, width)
|
||||
else:
|
||||
print('This type is not supported in this script!')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
||||
@@ -1,2 +0,0 @@
|
||||
opencv-python
|
||||
numpy
|
||||
@@ -1,6 +1 @@
|
||||
# SPIRAM configurations
|
||||
|
||||
CONFIG_IDF_EXPERIMENTAL_FEATURES=y
|
||||
CONFIG_SPIRAM=y
|
||||
CONFIG_SPIRAM_MODE_HEX=y
|
||||
CONFIG_SPIRAM_SPEED_200M=y
|
||||
|
||||
@@ -50,6 +50,19 @@ The accompanying `pytest_jpeg_encode.py` script captures the `JPEG_META` and `JP
|
||||
|
||||
It also compares the generated JPEG with `golden_output.jpeg`. This turns the example into both a functional regression test and a host-side artifact generator that makes the encoded result easy to inspect.
|
||||
|
||||
## Running Pytest Locally And Viewing The Image
|
||||
|
||||
To run the pytest helper locally on hardware, build the example for your target first, then invoke the test script with the target and serial port:
|
||||
|
||||
```bash
|
||||
idf.py set-target esp32p4 build
|
||||
pytest --target esp32p4 --port PORT pytest_jpeg_encode.py
|
||||
```
|
||||
|
||||
Replace `esp32p4` with another supported target such as `esp32s31` when needed.
|
||||
|
||||
`pytest-embedded` stores per-test logs under `$IDF_PATH/pytest-embedded/`. The script writes the reconstructed image to `jpeg_encode_result.jpeg` inside that test log directory, so after the test finishes you can open the generated JPEG locally with any image viewer to inspect the encoded output.
|
||||
|
||||
## Replacing The Embedded RGB Asset
|
||||
|
||||
If you want to regenerate a compatible raw frame from another input image, one simple workflow is:
|
||||
|
||||
Reference in New Issue
Block a user