Skip to content

Updates #53

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Jan 2, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
105 changes: 101 additions & 4 deletions benchmark/benchmark_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@

#include <benchmark/benchmark.h>

#include "ultrahdr/jpegr.h"
#include "ultrahdr/jpegrutils.h"

using namespace ultrahdr;

Expand Down Expand Up @@ -88,6 +88,20 @@ std::vector<std::pair<std::string, std::string>> kEncodeApi3TestImages3MpName{
{"mountain_lake_3mp.p010", "mountain_lake_3mp.jpg"},
};

std::vector<std::string> kEncodeApi4TestImages12MpName{
// 12mp test vectors
"mountains.jpg",
"mountain_lake.jpg",
"desert_wanda.jpg",
};

std::vector<std::string> kEncodeApi4TestImages3MpName{
// 3mp test vectors
"mountains_3mp.jpg",
"mountain_lake_3mp.jpg",
"desert_wanda_3mp.jpg",
};

std::string ofToString(const ultrahdr_output_format of) {
switch (of) {
case ULTRAHDR_OUTPUT_SDR:
Expand Down Expand Up @@ -205,9 +219,7 @@ static void BM_Decode(benchmark::State& s) {
compData.reset(reinterpret_cast<uint8_t*>(jpegImgR.data));

JpegR jpegHdr;
std::vector<uint8_t> iccData(0);
std::vector<uint8_t> exifData(0);
jpegr_info_struct info{0, 0, &iccData, &exifData};
jpegr_info_struct info{};
status_t status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
if (JPEGR_NO_ERROR != status) {
s.SkipWithError("getJPEGRInfo returned with error " + std::to_string(status));
Expand Down Expand Up @@ -444,6 +456,77 @@ static void BM_Encode_Api3(benchmark::State& s,
}
}

static void BM_Encode_Api4(benchmark::State& s) {
std::string srcFileName = kTestImagesPath + "jpegr/" + kDecodeAPITestImages[s.range(0)];

std::ifstream ifd(srcFileName.c_str(), std::ios::binary | std::ios::ate);
if (!ifd.good()) {
s.SkipWithError("unable to open file " + srcFileName);
return;
}
int size = ifd.tellg();

jpegr_compressed_struct inpJpegImgR{};
inpJpegImgR.length = size;
inpJpegImgR.maxLength = size;
inpJpegImgR.data = nullptr;
inpJpegImgR.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
ifd.close();
if (!loadFile(srcFileName.c_str(), inpJpegImgR.data, size)) {
s.SkipWithError("unable to load file " + srcFileName);
return;
}
std::unique_ptr<uint8_t[]> inpJpegImgRData;
inpJpegImgRData.reset(reinterpret_cast<uint8_t*>(inpJpegImgR.data));

JpegR jpegHdr;
jpeg_info_struct primaryImgInfo;
jpeg_info_struct gainmapImgInfo;
jpegr_info_struct info{};
info.primaryImgInfo = &primaryImgInfo;
info.gainmapImgInfo = &gainmapImgInfo;
status_t status = jpegHdr.getJPEGRInfo(&inpJpegImgR, &info);
if (JPEGR_NO_ERROR != status) {
s.SkipWithError("getJPEGRInfo returned with error " + std::to_string(status));
return;
}

jpegr_compressed_struct jpegImgR{};
jpegImgR.maxLength = (std::max)(static_cast<size_t>(8 * 1024) /* min size 8kb */,
info.width * info.height * 3 * 2);
jpegImgR.data = new uint8_t[jpegImgR.maxLength];
if (jpegImgR.data == nullptr) {
s.SkipWithError("unable to allocate memory to store compressed image");
return;
}
std::unique_ptr<uint8_t[]> jpegImgRData;
jpegImgRData.reset(reinterpret_cast<uint8_t*>(jpegImgR.data));

jpegr_compressed_struct primaryImg;
primaryImg.data = primaryImgInfo.imgData.data();
primaryImg.maxLength = primaryImg.length = primaryImgInfo.imgData.size();
primaryImg.colorGamut = static_cast<ultrahdr_color_gamut>(s.range(1));
jpegr_compressed_struct gainmapImg;
gainmapImg.data = gainmapImgInfo.imgData.data();
gainmapImg.maxLength = gainmapImg.length = gainmapImgInfo.imgData.size();
gainmapImg.colorGamut = ULTRAHDR_COLORGAMUT_UNSPECIFIED;
ultrahdr_metadata_struct uhdr_metadata;
if (!getMetadataFromXMP(gainmapImgInfo.xmpData.data(), gainmapImgInfo.xmpData.size(),
&uhdr_metadata)) {
s.SkipWithError("getMetadataFromXMP returned with error");
return;
}
for (auto _ : s) {
status = jpegHdr.encodeJPEGR(&primaryImg, &gainmapImg, &uhdr_metadata, &jpegImgR);
if (JPEGR_NO_ERROR != status) {
s.SkipWithError("encodeJPEGR returned with error " + std::to_string(status));
return;
}
}

s.SetLabel(srcFileName + ", " + std::to_string(info.width) + "x" + std::to_string(info.height));
}

BENCHMARK(BM_Decode)
->ArgsProduct({{benchmark::CreateDenseRange(0, kDecodeAPITestImages.size() - 1, 1)},
{ULTRAHDR_OUTPUT_HDR_HLG, ULTRAHDR_OUTPUT_HDR_PQ, ULTRAHDR_OUTPUT_SDR}})
Expand Down Expand Up @@ -539,4 +622,18 @@ BENCHMARK_CAPTURE(BM_Encode_Api3, TestVectorName, kEncodeApi3TestImages3MpName)
}})
->Unit(benchmark::kMillisecond);

BENCHMARK(BM_Encode_Api4)
->ArgsProduct({
{benchmark::CreateDenseRange(0, kEncodeApi4TestImages12MpName.size() - 1, 1)},
{ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100},
})
->Unit(benchmark::kMillisecond);

BENCHMARK(BM_Encode_Api4)
->ArgsProduct({
{benchmark::CreateDenseRange(0, kEncodeApi4TestImages3MpName.size() - 1, 1)},
{ULTRAHDR_COLORGAMUT_BT709, ULTRAHDR_COLORGAMUT_P3, ULTRAHDR_COLORGAMUT_BT2100},
})
->Unit(benchmark::kMillisecond);

BENCHMARK_MAIN();
2 changes: 1 addition & 1 deletion examples/ultrahdr_app.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -367,7 +367,7 @@ bool UltraHdrAppInput::decode() {
if (mMode == 1 && !fillJpegRImageHandle()) return false;
std::vector<uint8_t> iccData(0);
std::vector<uint8_t> exifData(0);
jpegr_info_struct info{0, 0, &iccData, &exifData};
jpegr_info_struct info{};
JpegR jpegHdr;
status_t status = jpegHdr.getJPEGRInfo(&mJpegImgR, &info);
if (JPEGR_NO_ERROR == status) {
Expand Down
4 changes: 1 addition & 3 deletions fuzzer/ultrahdr_dec_fuzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,9 +42,7 @@ void UltraHdrDecFuzzer::process() {
jpegr_compressed_struct jpegImgR{buffer.data(), (int)buffer.size(), (int)buffer.size(),
ULTRAHDR_COLORGAMUT_UNSPECIFIED};

std::vector<uint8_t> iccData(0);
std::vector<uint8_t> exifData(0);
jpegr_info_struct info{0, 0, &iccData, &exifData};
jpegr_info_struct info{};
JpegR jpegHdr;
(void)jpegHdr.getJPEGRInfo(&jpegImgR, &info);
//#define DUMP_PARAM
Expand Down
4 changes: 1 addition & 3 deletions fuzzer/ultrahdr_enc_fuzzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -289,9 +289,7 @@ void UltraHdrEncFuzzer::process() {
}
}
if (status == JPEGR_NO_ERROR) {
std::vector<uint8_t> iccData(0);
std::vector<uint8_t> exifData(0);
jpegr_info_struct info{0, 0, &iccData, &exifData};
jpegr_info_struct info{};
status = jpegHdr.getJPEGRInfo(&jpegImgR, &info);
if (status == JPEGR_NO_ERROR) {
size_t outSize = info.width * info.height * ((of == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
Expand Down
14 changes: 10 additions & 4 deletions lib/include/ultrahdr/jpegdecoderhelper.h
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ static const int kMaxWidth = 8192;
static const int kMaxHeight = 8192;

namespace ultrahdr {

typedef enum {
PARSE_ONLY = 0, // Dont decode. Parse for dimensions, EXIF, ICC, XMP
DECODE_TO_RGBA = 1, // Parse and decode to rgba
DECODE_TO_YCBCR = 2, // Parse and decode to YCBCR or Grayscale
} decode_mode_t;

/*
* Encapsulates a converter from JPEG to raw image (YUV420planer or grey-scale) format.
* This class is not thread-safe.
Expand All @@ -54,7 +61,7 @@ class JpegDecoderHelper {
* calling this method, call getDecompressedImage() to get the image.
* Returns false if decompressing the image fails.
*/
bool decompressImage(const void* image, int length, bool decodeToRGBA = false);
bool decompressImage(const void* image, int length, decode_mode_t decodeTo = DECODE_TO_YCBCR);
/*
* Returns the decompressed raw image buffer pointer. This method must be called only after
* calling decompressImage().
Expand Down Expand Up @@ -117,11 +124,10 @@ class JpegDecoderHelper {
/*
* Decompresses metadata of the image. All vectors are owned by the caller.
*/
bool getCompressedImageParameters(const void* image, int length, size_t* pWidth, size_t* pHeight,
std::vector<uint8_t>* iccData, std::vector<uint8_t>* exifData);
bool getCompressedImageParameters(const void* image, int length);

private:
bool decode(const void* image, int length, bool decodeToRGBA);
bool decode(const void* image, int length, decode_mode_t decodeTo);
// Returns false if errors occur.
bool decompress(jpeg_decompress_struct* cinfo, const uint8_t* dest, bool isSingleChannel);
bool decompressYUV(jpeg_decompress_struct* cinfo, const uint8_t* dest);
Expand Down
35 changes: 31 additions & 4 deletions lib/include/ultrahdr/jpegr.h
Original file line number Diff line number Diff line change
Expand Up @@ -67,13 +67,25 @@ typedef enum {
} status_t;

/*
* Holds information of jpegr image
* Holds information of jpeg image
*/
struct jpegr_info_struct {
struct jpeg_info_struct {
std::vector<uint8_t> imgData = std::vector<uint8_t>(0);
std::vector<uint8_t> iccData = std::vector<uint8_t>(0);
std::vector<uint8_t> exifData = std::vector<uint8_t>(0);
std::vector<uint8_t> xmpData = std::vector<uint8_t>(0);
size_t width;
size_t height;
std::vector<uint8_t>* iccData;
std::vector<uint8_t>* exifData;
};

/*
* Holds information of jpegr image
*/
struct jpegr_info_struct {
size_t width; // copy of primary image width (for easier access)
size_t height; // copy of primary image height (for easier access)
jpeg_info_struct* primaryImgInfo = nullptr;
jpeg_info_struct* gainmapImgInfo = nullptr;
};

/*
Expand Down Expand Up @@ -134,6 +146,7 @@ struct jpegr_exif_struct {
typedef struct jpegr_uncompressed_struct* jr_uncompressed_ptr;
typedef struct jpegr_compressed_struct* jr_compressed_ptr;
typedef struct jpegr_exif_struct* jr_exif_ptr;
typedef struct jpeg_info_struct* j_info_ptr;
typedef struct jpegr_info_struct* jr_info_ptr;

class JpegR {
Expand Down Expand Up @@ -374,6 +387,20 @@ class JpegR {
jr_compressed_ptr primary_jpg_image_ptr,
jr_compressed_ptr gainmap_jpg_image_ptr);

/*
* Gets Info from JPEG image without decoding it.
*
* The output is filled jpeg_info structure
* @param jpegr_image_ptr compressed JPEG image
* @param jpeg_image_info_ptr pointer to jpeg info struct. Members of jpeg_info_struct
* are owned by the caller
* @param img_width (optional) pointer to store width of jpeg image
* @param img_height (optional) pointer to store height of jpeg image
* @return NO_ERROR if JPEGR parsing succeeds, error code otherwise
*/
status_t parseJpegInfo(jr_compressed_ptr jpeg_image_ptr, j_info_ptr jpeg_image_info_ptr,
size_t* img_width = nullptr, size_t* img_height = nullptr);

/*
* This method is called in the encoding pipeline. It will take the standard 8-bit JPEG image,
* the compressed gain map and optionally the exif package as inputs, and generate the XMP
Expand Down
81 changes: 11 additions & 70 deletions lib/src/jpegdecoderhelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,14 @@ JpegDecoderHelper::JpegDecoderHelper() {}

JpegDecoderHelper::~JpegDecoderHelper() {}

bool JpegDecoderHelper::decompressImage(const void* image, int length, bool decodeToRGBA) {
bool JpegDecoderHelper::decompressImage(const void* image, int length, decode_mode_t decodeTo) {
if (image == nullptr || length <= 0) {
ALOGE("Image size can not be handled: %d", length);
return false;
}
mResultBuffer.clear();
mXMPBuffer.clear();
return decode(image, length, decodeToRGBA);
return decode(image, length, decodeTo);
}

void* JpegDecoderHelper::getDecompressedImagePtr() { return mResultBuffer.data(); }
Expand Down Expand Up @@ -187,7 +187,7 @@ bool JpegDecoderHelper::extractEXIF(const void* image, int length) {
return true;
}

bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA) {
bool JpegDecoderHelper::decode(const void* image, int length, decode_mode_t decodeTo) {
bool status = true;
jpeg_decompress_struct cinfo;
jpegrerror_mgr myerr;
Expand Down Expand Up @@ -256,7 +256,7 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
goto CleanUp;
}

if (decodeToRGBA) {
if (decodeTo == DECODE_TO_RGBA) {
// The primary image is expected to be yuv420 sampling
if (cinfo.jpeg_color_space != JCS_YCbCr) {
status = false;
Expand All @@ -273,7 +273,7 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
// 4 bytes per pixel
mResultBuffer.resize(cinfo.image_width * cinfo.image_height * 4);
cinfo.out_color_space = JCS_EXT_RGBA;
} else {
} else if (decodeTo == DECODE_TO_YCBCR) {
if (cinfo.jpeg_color_space == JCS_YCbCr) {
if (cinfo.comp_info[0].h_samp_factor != 2 || cinfo.comp_info[0].v_samp_factor != 2 ||
cinfo.comp_info[1].h_samp_factor != 1 || cinfo.comp_info[1].v_samp_factor != 1 ||
Expand All @@ -292,6 +292,10 @@ bool JpegDecoderHelper::decode(const void* image, int length, bool decodeToRGBA)
}
cinfo.out_color_space = cinfo.jpeg_color_space;
cinfo.raw_data_out = TRUE;
} else {
status = decodeTo == PARSE_ONLY;
jpeg_destroy_decompress(&cinfo);
return status;
}

cinfo.dct_method = JDCT_ISLOW;
Expand All @@ -316,71 +320,8 @@ bool JpegDecoderHelper::decompress(jpeg_decompress_struct* cinfo, const uint8_t*
: decompressYUV(cinfo, dest));
}

bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length, size_t* pWidth,
size_t* pHeight, std::vector<uint8_t>* iccData,
std::vector<uint8_t>* exifData) {
jpeg_decompress_struct cinfo;
jpegrerror_mgr myerr;
cinfo.err = jpeg_std_error(&myerr.pub);
myerr.pub.error_exit = jpegrerror_exit;
myerr.pub.output_message = output_message;

if (setjmp(myerr.setjmp_buffer)) {
jpeg_destroy_decompress(&cinfo);
return false;
}
jpeg_create_decompress(&cinfo);

jpeg_save_markers(&cinfo, kAPP1Marker, 0xFFFF);
jpeg_save_markers(&cinfo, kAPP2Marker, 0xFFFF);

jpegr_source_mgr mgr(static_cast<const uint8_t*>(image), length);
cinfo.src = &mgr;
if (jpeg_read_header(&cinfo, TRUE) != JPEG_HEADER_OK) {
jpeg_destroy_decompress(&cinfo);
return false;
}

if (pWidth != nullptr) {
*pWidth = cinfo.image_width;
}
if (pHeight != nullptr) {
*pHeight = cinfo.image_height;
}

if (iccData != nullptr) {
for (jpeg_marker_struct* marker = cinfo.marker_list; marker; marker = marker->next) {
if (marker->marker != kAPP2Marker) {
continue;
}
if (marker->data_length <= kICCMarkerHeaderSize ||
memcmp(marker->data, kICCSig, sizeof(kICCSig)) != 0) {
continue;
}

iccData->insert(iccData->end(), marker->data, marker->data + marker->data_length);
}
}

if (exifData != nullptr) {
bool exifAppears = false;
for (jpeg_marker_struct* marker = cinfo.marker_list; marker && !exifAppears;
marker = marker->next) {
if (marker->marker != kAPP1Marker) {
continue;
}

const unsigned int len = marker->data_length;
if (len >= sizeof(kExifIdCode) && !memcmp(marker->data, kExifIdCode, sizeof(kExifIdCode))) {
exifData->resize(len, 0);
memcpy(static_cast<void*>(exifData->data()), marker->data, len);
exifAppears = true;
}
}
}

jpeg_destroy_decompress(&cinfo);
return true;
bool JpegDecoderHelper::getCompressedImageParameters(const void* image, int length) {
return decode(image, length, PARSE_ONLY);
}

bool JpegDecoderHelper::decompressRGBA(jpeg_decompress_struct* cinfo, const uint8_t* dest) {
Expand Down
Loading