Add support for multiple regions of interest (ROI) for batch JPEG decoding (#126)

This commit is contained in:
Aryan Salmanpour
2025-03-05 17:06:04 -05:00
zatwierdzone przez GitHub
rodzic 863eafcdb6
commit 692d4bb72c
8 zmienionych plików z 43 dodań i 46 usunięć
+2 -1
Wyświetl plik
@@ -3,10 +3,11 @@
Documentation for rocJPEG is available at
[https://rocm.docs.amd.com/projects/rocJPEG/en/latest/](https://rocm.docs.amd.com/projects/rocJPEG/en/latest/)
## (Unreleased) rocJPEG 0.9.0
## (Unreleased) rocJPEG 0.10.0
## Changed
* Readme - cleanup and updates to pre-reqs
* The `decode_params` argument of the `rocJpegDecodeBatched` API is now an array of `RocJpegDecodeParams` structs representing the decode parameters for the batch of JPEG images.
## Removed
* Dev Package - No longer installs pkg-config
+1 -1
Wyświetl plik
@@ -40,7 +40,7 @@ set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED On)
# NOTE: Match version with api/rocjpeg_version.h
set(VERSION "0.9.0")
set(VERSION "0.10.0")
# Set Project Version and Language
project(rocjpeg VERSION ${VERSION} LANGUAGES CXX)
+2 -2
Wyświetl plik
@@ -319,12 +319,12 @@ RocJpegStatus ROCJPEGAPI rocJpegDecode(RocJpegHandle handle, RocJpegStreamHandle
* @ingroup group_amd_rocjpeg
* @brief Decodes a batch of JPEG images using the rocJPEG library.
*
* Decodes a batch of JPEG images using the rocJPEG library.
* This function decodes a batch of JPEG images using the rocJPEG library.
*
* @param handle The rocJPEG handle.
* @param jpeg_stream_handles An array of rocJPEG stream handles representing the input JPEG streams.
* @param batch_size The number of JPEG streams in the batch.
* @param decode_params The decode parameters for the JPEG decoding process.
* @param decode_params An array of RocJpegDecodeParams structs representing the decode parameters for the batch of JPEG images.
* @param destinations An array of rocJPEG images representing the output decoded images.
* @return The status of the JPEG decoding operation.
*/
+1 -1
Wyświetl plik
@@ -34,7 +34,7 @@ THE SOFTWARE.
extern "C" {
#endif
#define ROCJPEG_MAJOR_VERSION 0
#define ROCJPEG_MINOR_VERSION 9
#define ROCJPEG_MINOR_VERSION 10
#define ROCJPEG_MICRO_VERSION 0
@@ -49,6 +49,7 @@ int main(int argc, char **argv) {
RocJpegImage output_image = {};
std::vector<RocJpegImage> output_images;
RocJpegDecodeParams decode_params = {};
std::vector<RocJpegDecodeParams> decode_params_batch;
RocJpegUtils rocjpeg_utils;
std::vector<std::string> base_file_names;
std::vector<RocJpegStreamHandle> rocjpeg_stream_handles_for_current_batch;
@@ -61,15 +62,11 @@ int main(int argc, char **argv) {
uint64_t num_jpegs_with_unknown_subsampling = 0;
uint64_t num_jpegs_with_unsupported_resolution = 0;
int current_batch_size = 0;
RocJpegUtils::ParseCommandLine(input_path, output_file_path, save_images, device_id, rocjpeg_backend, decode_params, nullptr, &batch_size, argc, argv);
bool is_roi_valid = false;
uint32_t roi_width;
uint32_t roi_height;
roi_width = decode_params.crop_rectangle.right - decode_params.crop_rectangle.left;
roi_height = decode_params.crop_rectangle.bottom - decode_params.crop_rectangle.top;
RocJpegUtils::ParseCommandLine(input_path, output_file_path, save_images, device_id, rocjpeg_backend, decode_params, nullptr, &batch_size, argc, argv);
if (!RocJpegUtils::GetFilePaths(input_path, file_paths, is_dir, is_file)) {
std::cerr << "ERROR: Failed to get input file paths!" << std::endl;
return EXIT_FAILURE;
@@ -89,6 +86,7 @@ int main(int argc, char **argv) {
batch_images.resize(batch_size);
output_images.resize(batch_size);
decode_params_batch.resize(batch_size, decode_params);
prior_channel_sizes.resize(batch_size, std::vector<uint32_t>(ROCJPEG_MAX_COMPONENT, 0));
widths.resize(batch_size, std::vector<uint32_t>(ROCJPEG_MAX_COMPONENT, 0));
heights.resize(batch_size, std::vector<uint32_t>(ROCJPEG_MAX_COMPONENT, 0));
@@ -134,10 +132,6 @@ int main(int argc, char **argv) {
CHECK_ROCJPEG(rocJpegGetImageInfo(rocjpeg_handle, rocjpeg_stream_handles[index], &num_components, &temp_subsampling, temp_widths.data(), temp_heights.data()));
if (roi_width > 0 && roi_height > 0 && roi_width <= widths[index][0] && roi_height <= heights[index][0]) {
is_roi_valid = true;
}
rocjpeg_utils.GetChromaSubsamplingStr(temp_subsampling, chroma_sub_sampling);
if (temp_widths[0] < 64 || temp_heights[0] < 64) {
if (is_dir) {
@@ -164,7 +158,7 @@ int main(int argc, char **argv) {
}
}
if (rocjpeg_utils.GetChannelPitchAndSizes(decode_params, temp_subsampling, temp_widths.data(), temp_heights.data(), num_channels, output_images[current_batch_size], channel_sizes)) {
if (rocjpeg_utils.GetChannelPitchAndSizes(decode_params_batch[index], temp_subsampling, temp_widths.data(), temp_heights.data(), num_channels, output_images[current_batch_size], channel_sizes)) {
std::cerr << "ERROR: Failed to get the channel pitch and sizes" << std::endl;
return EXIT_FAILURE;
}
@@ -191,7 +185,7 @@ int main(int argc, char **argv) {
double time_per_batch_in_milli_sec = 0;
if (current_batch_size > 0) {
auto start_time = std::chrono::high_resolution_clock::now();
CHECK_ROCJPEG(rocJpegDecodeBatched(rocjpeg_handle, rocjpeg_stream_handles_for_current_batch.data(), current_batch_size, &decode_params, output_images.data()));
CHECK_ROCJPEG(rocJpegDecodeBatched(rocjpeg_handle, rocjpeg_stream_handles_for_current_batch.data(), current_batch_size, decode_params_batch.data(), output_images.data()));
auto end_time = std::chrono::high_resolution_clock::now();
time_per_batch_in_milli_sec = std::chrono::duration<double, std::milli>(end_time - start_time).count();
}
@@ -207,12 +201,15 @@ int main(int argc, char **argv) {
for (int b = 0; b < current_batch_size; b++) {
std::string image_save_path = output_file_path;
//if ROI is present, need to pass roi_width and roi_height
roi_width = decode_params_batch[b].crop_rectangle.right - decode_params_batch[b].crop_rectangle.left;
roi_height = decode_params_batch[b].crop_rectangle.bottom - decode_params_batch[b].crop_rectangle.top;
is_roi_valid = (roi_width > 0 && roi_height > 0 && roi_width <= widths[b][0] && roi_height <= heights[b][0]) ? true : false;
uint32_t width = is_roi_valid ? roi_width : widths[b][0];
uint32_t height = is_roi_valid ? roi_height : heights[b][0];
if (is_dir) {
rocjpeg_utils.GetOutputFileExt(decode_params.output_format, base_file_names[b], width, height, subsamplings[b], image_save_path);
rocjpeg_utils.GetOutputFileExt(decode_params_batch[b].output_format, base_file_names[b], width, height, subsamplings[b], image_save_path);
}
rocjpeg_utils.SaveImage(image_save_path, &output_images[b], width, height, subsamplings[b], decode_params.output_format);
rocjpeg_utils.SaveImage(image_save_path, &output_images[b], width, height, subsamplings[b], decode_params_batch[b].output_format);
}
}
@@ -50,8 +50,6 @@ void DecodeImages(DecodeInfo &decode_info, RocJpegUtils rocjpeg_utils, RocJpegDe
bool is_roi_valid = false;
uint32_t roi_width;
uint32_t roi_height;
roi_width = decode_params.crop_rectangle.right - decode_params.crop_rectangle.left;
roi_height = decode_params.crop_rectangle.bottom - decode_params.crop_rectangle.top;
uint8_t num_components;
uint32_t channel_sizes[ROCJPEG_MAX_COMPONENT] = {};
std::string chroma_sub_sampling = "";
@@ -65,6 +63,7 @@ void DecodeImages(DecodeInfo &decode_info, RocJpegUtils rocjpeg_utils, RocJpegDe
std::vector<std::vector<uint32_t>> prior_channel_sizes(batch_size, std::vector<uint32_t>(ROCJPEG_MAX_COMPONENT, 0));
std::vector<RocJpegChromaSubsampling> subsamplings(batch_size);
std::vector<RocJpegImage> output_images(batch_size);
std::vector<RocJpegDecodeParams> decode_params_batch(batch_size, decode_params);
std::vector<std::string> base_file_names(batch_size);
std::vector<RocJpegStreamHandle> rocjpeg_stream_handles(batch_size);
std::vector<uint32_t> temp_widths(ROCJPEG_MAX_COMPONENT, 0);
@@ -105,9 +104,6 @@ void DecodeImages(DecodeInfo &decode_info, RocJpegUtils rocjpeg_utils, RocJpegDe
}
CHECK_ROCJPEG(rocJpegGetImageInfo(decode_info.rocjpeg_handle, decode_info.rocjpeg_stream_handles[index], &num_components, &temp_subsampling, temp_widths.data(), temp_heights.data()));
if (roi_width > 0 && roi_height > 0 && roi_width <= temp_widths[0] && roi_height <= temp_heights[0]) {
is_roi_valid = true;
}
rocjpeg_utils.GetChromaSubsamplingStr(temp_subsampling, chroma_sub_sampling);
if (temp_widths[0] < 64 || temp_heights[0] < 64) {
@@ -125,7 +121,7 @@ void DecodeImages(DecodeInfo &decode_info, RocJpegUtils rocjpeg_utils, RocJpegDe
continue;
}
if (rocjpeg_utils.GetChannelPitchAndSizes(decode_params, temp_subsampling, temp_widths.data(), temp_heights.data(), num_channels, output_images[current_batch_size], channel_sizes)) {
if (rocjpeg_utils.GetChannelPitchAndSizes(decode_params_batch[index], temp_subsampling, temp_widths.data(), temp_heights.data(), num_channels, output_images[current_batch_size], channel_sizes)) {
std::cerr << "ERROR: Failed to get the channel pitch and sizes" << std::endl;
return;
}
@@ -153,7 +149,7 @@ void DecodeImages(DecodeInfo &decode_info, RocJpegUtils rocjpeg_utils, RocJpegDe
double time_per_batch_in_milli_sec = 0;
if (current_batch_size > 0) {
auto start_time = std::chrono::high_resolution_clock::now();
CHECK_ROCJPEG(rocJpegDecodeBatched(decode_info.rocjpeg_handle, rocjpeg_stream_handles.data(), current_batch_size, &decode_params, output_images.data()));
CHECK_ROCJPEG(rocJpegDecodeBatched(decode_info.rocjpeg_handle, rocjpeg_stream_handles.data(), current_batch_size, decode_params_batch.data(), output_images.data()));
auto end_time = std::chrono::high_resolution_clock::now();
time_per_batch_in_milli_sec = std::chrono::duration<double, std::milli>(end_time - start_time).count();
}
@@ -169,6 +165,9 @@ void DecodeImages(DecodeInfo &decode_info, RocJpegUtils rocjpeg_utils, RocJpegDe
for (int b = 0; b < current_batch_size; b++) {
std::string image_save_path = output_file_path;
//if ROI is present, need to pass roi_width and roi_height
roi_width = decode_params_batch[b].crop_rectangle.right - decode_params_batch[b].crop_rectangle.left;
roi_height = decode_params_batch[b].crop_rectangle.bottom - decode_params_batch[b].crop_rectangle.top;
is_roi_valid = (roi_width > 0 && roi_height > 0 && roi_width <= widths[b][0] && roi_height <= heights[b][0]) ? true : false;
uint32_t width = is_roi_valid ? roi_width : widths[b][0];
uint32_t height = is_roi_valid ? roi_height : heights[b][0];
rocjpeg_utils.GetOutputFileExt(decode_params.output_format, base_file_names[b], width, height, subsamplings[b], image_save_path);
+12 -12
Wyświetl plik
@@ -216,7 +216,7 @@ RocJpegStatus RocJpegDecoder::DecodeBatched(RocJpegStreamHandle *jpeg_streams, i
jpeg_streams_params[j] = std::move(*jpeg_stream_params);
}
CHECK_ROCJPEG(jpeg_vaapi_decoder_.SubmitDecodeBatched(jpeg_streams_params.data() + i, current_batch_size, decode_params, current_surface_ids.data() + i));
CHECK_ROCJPEG(jpeg_vaapi_decoder_.SubmitDecodeBatched(jpeg_streams_params.data() + i, current_batch_size, &decode_params[i], current_surface_ids.data() + i));
for (int k = 0; k < current_batch_size; k++) {
HipInteropDeviceMem hip_interop_dev_mem = {};
@@ -231,8 +231,8 @@ RocJpegStatus RocJpegDecoder::DecodeBatched(RocJpegStreamHandle *jpeg_streams, i
bool is_roi_valid = false;
uint32_t roi_width;
uint32_t roi_height;
roi_width = decode_params->crop_rectangle.right - decode_params->crop_rectangle.left;
roi_height = decode_params->crop_rectangle.bottom - decode_params->crop_rectangle.top;
roi_width = decode_params[k + i].crop_rectangle.right - decode_params[k + i].crop_rectangle.left;
roi_height = decode_params[k + i].crop_rectangle.bottom - decode_params[k + i].crop_rectangle.top;
if (roi_width > 0 && roi_height > 0 && roi_width <= jpeg_stream_params->picture_parameter_buffer.picture_width && roi_height <= jpeg_stream_params->picture_parameter_buffer.picture_height) {
is_roi_valid = true;
@@ -247,38 +247,38 @@ RocJpegStatus RocJpegDecoder::DecodeBatched(RocJpegStreamHandle *jpeg_streams, i
is_roi_valid = false;
}
switch (decode_params->output_format) {
switch (decode_params[k + i].output_format) {
case ROCJPEG_OUTPUT_NATIVE:
// Copy the native decoded output buffers from interop memory directly to the destination buffers
CHECK_ROCJPEG(GetChromaHeight(hip_interop_dev_mem.surface_format, picture_height, chroma_height));
// Copy Luma (first channel) for any surface format
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, picture_height, 0, &destinations[k + i], decode_params, is_roi_valid));
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, picture_height, 0, &destinations[k + i], &decode_params[k + i], is_roi_valid));
if (hip_interop_dev_mem.surface_format == VA_FOURCC_NV12) {
// Copy the second channel (UV interleaved) for NV12
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, chroma_height, 1, &destinations[k + i], decode_params, is_roi_valid));
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, chroma_height, 1, &destinations[k + i], &decode_params[k + i], is_roi_valid));
} else if (hip_interop_dev_mem.surface_format == VA_FOURCC_444P ||
hip_interop_dev_mem.surface_format == VA_FOURCC_422V) {
// Copy the second and third channels for YUV444 and YUV440 (i.e., YUV422V)
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, chroma_height, 1, &destinations[k + i], decode_params, is_roi_valid));
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, chroma_height, 2, &destinations[k + i], decode_params, is_roi_valid));
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, chroma_height, 1, &destinations[k + i], &decode_params[k + i], is_roi_valid));
CHECK_ROCJPEG(CopyChannel(hip_interop_dev_mem, chroma_height, 2, &destinations[k + i], &decode_params[k + i], is_roi_valid));
}
break;
case ROCJPEG_OUTPUT_YUV_PLANAR:
CHECK_ROCJPEG(GetChromaHeight(hip_interop_dev_mem.surface_format, picture_height, chroma_height));
CHECK_ROCJPEG(GetPlanarYUVOutputFormat(hip_interop_dev_mem, picture_width,
picture_height, chroma_height, &destinations[k + i], decode_params, is_roi_valid));
picture_height, chroma_height, &destinations[k + i], &decode_params[k + i], is_roi_valid));
break;
case ROCJPEG_OUTPUT_Y:
CHECK_ROCJPEG(GetYOutputFormat(hip_interop_dev_mem, picture_width,
picture_height, &destinations[k + i], decode_params, is_roi_valid));
picture_height, &destinations[k + i], &decode_params[k + i], is_roi_valid));
break;
case ROCJPEG_OUTPUT_RGB:
CHECK_ROCJPEG(ColorConvertToRGB(hip_interop_dev_mem, picture_width,
picture_height, &destinations[k + i], decode_params, is_roi_valid));
picture_height, &destinations[k + i], &decode_params[k + i], is_roi_valid));
break;
case ROCJPEG_OUTPUT_RGB_PLANAR:
CHECK_ROCJPEG(ColorConvertToRGBPlanar(hip_interop_dev_mem, picture_width,
picture_height, &destinations[k + i], decode_params, is_roi_valid));
picture_height, &destinations[k + i], &decode_params[k + i], is_roi_valid));
break;
default:
break;
+8 -8
Wyświetl plik
@@ -711,11 +711,11 @@ RocJpegStatus RocJpegVappiDecoder::SubmitDecodeBatched(JpegStreamParameters *jpe
return ROCJPEG_STATUS_JPEG_NOT_SUPPORTED;
}
if ((decode_params->output_format == ROCJPEG_OUTPUT_RGB || decode_params->output_format == ROCJPEG_OUTPUT_RGB_PLANAR) && current_vcn_jpeg_spec_.can_convert_to_rgb && jpeg_streams_params[i].chroma_subsampling != CSS_440) {
if (decode_params->output_format == ROCJPEG_OUTPUT_RGB) {
if ((decode_params[i].output_format == ROCJPEG_OUTPUT_RGB || decode_params[i].output_format == ROCJPEG_OUTPUT_RGB_PLANAR) && current_vcn_jpeg_spec_.can_convert_to_rgb && jpeg_streams_params[i].chroma_subsampling != CSS_440) {
if (decode_params[i].output_format == ROCJPEG_OUTPUT_RGB) {
jpeg_stream_key.surface_format = VA_RT_FORMAT_RGB32;
jpeg_stream_key.pixel_format = VA_FOURCC_RGBA;
} else if (decode_params->output_format == ROCJPEG_OUTPUT_RGB_PLANAR) {
} else if (decode_params[i].output_format == ROCJPEG_OUTPUT_RGB_PLANAR) {
jpeg_stream_key.surface_format = VA_RT_FORMAT_RGBP;
jpeg_stream_key.pixel_format = VA_FOURCC_RGBP;
}
@@ -770,8 +770,6 @@ RocJpegStatus RocJpegVappiDecoder::SubmitDecodeBatched(JpegStreamParameters *jpe
uint32_t roi_width;
uint32_t roi_height;
roi_width = decode_params->crop_rectangle.right - decode_params->crop_rectangle.left;
roi_height = decode_params->crop_rectangle.bottom - decode_params->crop_rectangle.top;
// Iterate through all entries of jpeg_stream_groups.
// Check if there is a matching entry in the memory pool.
@@ -805,16 +803,18 @@ RocJpegStatus RocJpegVappiDecoder::SubmitDecodeBatched(JpegStreamParameters *jpe
for (int idx : indices) {
// if the HW JPEG decoder has a built-in ROI-decode capability then fill the requested crop rectangle to the picture parameter buffer
void* picture_parameter_buffer = &jpeg_streams_params[idx].picture_parameter_buffer;
roi_width = decode_params[idx].crop_rectangle.right - decode_params[idx].crop_rectangle.left;
roi_height = decode_params[idx].crop_rectangle.bottom - decode_params[idx].crop_rectangle.top;
if (current_vcn_jpeg_spec_.can_roi_decode && roi_width > 0 && roi_height > 0 &&
roi_width <= jpeg_streams_params[idx].picture_parameter_buffer.picture_width &&
roi_height <= jpeg_streams_params[idx].picture_parameter_buffer.picture_height) {
#if VA_CHECK_VERSION(1, 21, 0)
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->crop_rectangle.x = decode_params->crop_rectangle.left;
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->crop_rectangle.y = decode_params->crop_rectangle.top;
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->crop_rectangle.x = decode_params[idx].crop_rectangle.left;
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->crop_rectangle.y = decode_params[idx].crop_rectangle.top;
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->crop_rectangle.width = roi_width;
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->crop_rectangle.height = roi_height;
#else
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->va_reserved[0] = decode_params->crop_rectangle.top << 16 | decode_params->crop_rectangle.left;
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->va_reserved[0] = decode_params[idx].crop_rectangle.top << 16 | decode_params[idx].crop_rectangle.left;
reinterpret_cast<VAPictureParameterBufferJPEGBaseline*>(picture_parameter_buffer)->va_reserved[1] = roi_height << 16 | roi_width;
#endif
}