aboutsummaryrefslogtreecommitdiff
path: root/tests
diff options
context:
space:
mode:
authorDichenZhang1 <140119224+DichenZhang1@users.noreply.github.com>2023-09-11 12:36:13 -0700
committerGitHub <noreply@github.com>2023-09-11 12:36:13 -0700
commit6ac33eab4d6e3f2e773e039fa4f0bec55dccac17 (patch)
treea3ac4836a2e595e47e3023329d0e7192b2bcdbcc /tests
parent6a5e8b98edff198b58070834853b74861573d06a (diff)
downloadlibultrahdr-6ac33eab4d6e3f2e773e039fa4f0bec55dccac17.tar.gz
Add files via upload
Diffstat (limited to 'tests')
-rw-r--r--tests/AndroidTest.xml26
-rw-r--r--tests/ultrahdr_app.cpp755
2 files changed, 781 insertions, 0 deletions
diff --git a/tests/AndroidTest.xml b/tests/AndroidTest.xml
new file mode 100644
index 0000000..1754a5c
--- /dev/null
+++ b/tests/AndroidTest.xml
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2023 The Android Open Source Project
+
+ Licensed under the Apache License, Version 2.0 (the"License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an"AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
+-->
+<configuration description="Unit test configuration for ultrahdr_unit_test">
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push-file" key="ultrahdr_unit_test" value="/data/local/tmp/ultrahdr_unit_test" />
+ <option name="push" value="data/*->/data/local/tmp/" />
+ </target_preparer>
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="ultrahdr_unit_test" />
+ </test>
+</configuration>
diff --git a/tests/ultrahdr_app.cpp b/tests/ultrahdr_app.cpp
new file mode 100644
index 0000000..9414c5b
--- /dev/null
+++ b/tests/ultrahdr_app.cpp
@@ -0,0 +1,755 @@
+/*
+ * Copyright 2023 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <unistd.h>
+
+#include <algorithm>
+#include <cmath>
+#include <fstream>
+#include <iostream>
+
+#include "ultrahdr/ultrahdrcommon.h"
+#include "ultrahdr/gainmapmath.h"
+#include "ultrahdr/jpegr.h"
+
+using namespace ultrahdr;
+
+const float BT601YUVtoRGBMatrix[9] =
+ {1, 0, 1.402, 1, (-0.202008 / 0.587), (-0.419198 / 0.587), 1.0, 1.772, 0.0};
+const float BT709YUVtoRGBMatrix[9] =
+ {1, 0, 1.5748, 1, (-0.13397432 / 0.7152), (-0.33480248 / 0.7152), 1.0, 1.8556, 0.0};
+const float BT2020YUVtoRGBMatrix[9] =
+ {1, 0, 1.4746, 1, (-0.11156702 / 0.6780), (-0.38737742 / 0.6780), 1, 1.8814, 0};
+
+const float BT601RGBtoYUVMatrix[9] = {0.299,
+ 0.587,
+ 0.114,
+ (-0.299 / 1.772),
+ (-0.587 / 1.772),
+ 0.5,
+ 0.5,
+ (-0.587 / 1.402),
+ (-0.114 / 1.402)};
+const float BT709RGBtoYUVMatrix[9] = {0.2126,
+ 0.7152,
+ 0.0722,
+ (-0.2126 / 1.8556),
+ (-0.7152 / 1.8556),
+ 0.5,
+ 0.5,
+ (-0.7152 / 1.5748),
+ (-0.0722 / 1.5748)};
+const float BT2020RGBtoYUVMatrix[9] = {0.2627,
+ 0.6780,
+ 0.0593,
+ (-0.2627 / 1.8814),
+ (-0.6780 / 1.8814),
+ 0.5,
+ 0.5,
+ (-0.6780 / 1.4746),
+ (-0.0593 / 1.4746)};
+
+static bool loadFile(const char* filename, void*& result, int length) {
+ std::ifstream ifd(filename, std::ios::binary | std::ios::ate);
+ if (ifd.good()) {
+ int size = ifd.tellg();
+ if (size < length) {
+ std::cerr << "requested to read " << length << " bytes from file : " << filename
+ << ", file contains only " << length << " bytes" << std::endl;
+ return false;
+ }
+ ifd.seekg(0, std::ios::beg);
+ result = malloc(length);
+ if (result == nullptr) {
+ std::cerr << "failed to allocate memory to store contents of file : " << filename
+ << std::endl;
+ return false;
+ }
+ ifd.read(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to open file : " << filename << std::endl;
+ return false;
+}
+
+static bool writeFile(const char* filename, void*& result, int length) {
+ std::ofstream ofd(filename, std::ios::binary);
+ if (ofd.is_open()) {
+ ofd.write(static_cast<char*>(result), length);
+ return true;
+ }
+ std::cerr << "unable to write to file : " << filename << std::endl;
+ return false;
+}
+
+class UltraHdrAppInput {
+public:
+ UltraHdrAppInput(const char* p010File, const char* yuv420File, int width, int height,
+ ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709,
+ ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709,
+ ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG, int quality = 100,
+ ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG)
+ : mP010File(p010File),
+ mYuv420File(yuv420File),
+ mWidth(width),
+ mHeight(height),
+ mP010Cg(p010Cg),
+ mYuv420Cg(yuv420Cg),
+ mTf(tf),
+ mQuality(quality),
+ mOf(of){};
+
+ ~UltraHdrAppInput() {
+ if (mRawP010Image.data) free(mRawP010Image.data);
+ if (mRawP010Image.chroma_data) free(mRawP010Image.chroma_data);
+ if (mRawRgba1010102Image.data) free(mRawRgba1010102Image.data);
+ if (mRawRgba1010102Image.chroma_data) free(mRawRgba1010102Image.chroma_data);
+ if (mRawYuv420Image.data) free(mRawYuv420Image.data);
+ if (mRawYuv420Image.chroma_data) free(mRawYuv420Image.chroma_data);
+ if (mRawRgba8888Image.data) free(mRawRgba8888Image.data);
+ if (mRawRgba8888Image.chroma_data) free(mRawRgba8888Image.chroma_data);
+ if (mJpegImgR.data) free(mJpegImgR.data);
+ if (mDestImage.data) free(mDestImage.data);
+ if (mDestImage.chroma_data) free(mDestImage.chroma_data);
+ if (mDestYUV444Image.data) free(mDestYUV444Image.data);
+ if (mDestYUV444Image.chroma_data) free(mDestYUV444Image.chroma_data);
+ }
+
+ bool fillP010ImageHandle();
+ bool convertP010ToRGBImage();
+ bool fillYuv420ImageHandle();
+ bool convertYuv420ToRGBImage();
+ bool convertRgba8888ToYUV444Image();
+ bool convertRgba1010102ToYUV444Image();
+ bool encode();
+ bool decode();
+ void computeRGBHdrPSNR();
+ void computeRGBSdrPSNR();
+ void computeYUVHdrPSNR();
+ void computeYUVSdrPSNR();
+
+ const char* mP010File;
+ const char* mYuv420File;
+ const int mWidth;
+ const int mHeight;
+ const ultrahdr_color_gamut mP010Cg;
+ const ultrahdr_color_gamut mYuv420Cg;
+ const ultrahdr_transfer_function mTf;
+ const int mQuality;
+ const ultrahdr_output_format mOf;
+ jpegr_uncompressed_struct mRawP010Image{};
+ jpegr_uncompressed_struct mRawRgba1010102Image{};
+ jpegr_uncompressed_struct mRawYuv420Image{};
+ jpegr_uncompressed_struct mRawRgba8888Image{};
+ jpegr_compressed_struct mJpegImgR{};
+ jpegr_uncompressed_struct mDestImage{};
+ jpegr_uncompressed_struct mDestYUV444Image{};
+ double mPsnr[3]{};
+};
+
+bool UltraHdrAppInput::fillP010ImageHandle() {
+ const int bpp = 2;
+ int p010Size = mWidth * mHeight * bpp * 1.5;
+ mRawP010Image.width = mWidth;
+ mRawP010Image.height = mHeight;
+ mRawP010Image.colorGamut = mP010Cg;
+ return loadFile(mP010File, mRawP010Image.data, p010Size);
+}
+
+bool UltraHdrAppInput::fillYuv420ImageHandle() {
+ int yuv420Size = mWidth * mHeight * 1.5;
+ mRawYuv420Image.width = mWidth;
+ mRawYuv420Image.height = mHeight;
+ mRawYuv420Image.colorGamut = mYuv420Cg;
+ return loadFile(mYuv420File, mRawYuv420Image.data, yuv420Size);
+}
+
+bool UltraHdrAppInput::encode() {
+ if (!fillP010ImageHandle()) return false;
+ if (mYuv420File != nullptr && !fillYuv420ImageHandle()) return false;
+
+ mJpegImgR.maxLength = std::max(8 * 1024 /* min size 8kb */,
+ mRawP010Image.width * mRawP010Image.height * 3 * 2);
+ mJpegImgR.data = malloc(mJpegImgR.maxLength);
+ if (mJpegImgR.data == nullptr) {
+ std::cerr << "unable to allocate memory to store compressed image" << std::endl;
+ return false;
+ }
+
+ JpegR jpegHdr;
+ status_t status = UNKNOWN_ERROR;
+ if (mYuv420File == nullptr) { // api-0
+ status = jpegHdr.encodeJPEGR(&mRawP010Image, mTf, &mJpegImgR, mQuality, nullptr);
+ if (OK != status) {
+ std::cerr << "Encountered error during encodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+ } else { // api-1
+ status = jpegHdr.encodeJPEGR(&mRawP010Image, &mRawYuv420Image, mTf, &mJpegImgR, mQuality,
+ nullptr);
+ if (OK != status) {
+ std::cerr << "Encountered error during encodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+ }
+ writeFile("out.jpeg", mJpegImgR.data, mJpegImgR.length);
+ return true;
+}
+
+bool UltraHdrAppInput::decode() {
+ std::vector<uint8_t> iccData(0);
+ std::vector<uint8_t> exifData(0);
+ jpegr_info_struct info{0, 0, &iccData, &exifData};
+ JpegR jpegHdr;
+ status_t status = jpegHdr.getJPEGRInfo(&mJpegImgR, &info);
+ if (OK == status) {
+ size_t outSize = info.width * info.height * ((mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) ? 8 : 4);
+ mDestImage.data = malloc(outSize);
+ if (mDestImage.data == nullptr) {
+ std::cerr << "failed to allocate memory to store decoded output" << std::endl;
+ return false;
+ }
+ status = jpegHdr.decodeJPEGR(&mJpegImgR, &mDestImage, FLT_MAX, nullptr, mOf, nullptr,
+ nullptr);
+ if (OK != status) {
+ std::cerr << "Encountered error during decodeJPEGR call, error code " << status
+ << std::endl;
+ return false;
+ }
+ writeFile("outrgb.raw", mDestImage.data, outSize);
+ } else {
+ std::cerr << "Encountered error during getJPEGRInfo call, error code " << status
+ << std::endl;
+ return false;
+ }
+ return true;
+}
+
+bool UltraHdrAppInput::convertP010ToRGBImage() {
+ const float* coeffs = BT2020YUVtoRGBMatrix;
+ if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
+ coeffs = BT709YUVtoRGBMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
+ coeffs = BT2020YUVtoRGBMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
+ coeffs = BT601YUVtoRGBMatrix;
+ } else {
+ std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
+ << std::endl;
+ }
+
+ mRawRgba1010102Image.data = malloc(mRawP010Image.width * mRawP010Image.height * 4);
+ if (mRawRgba1010102Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store Rgba1010102" << std::endl;
+ return false;
+ }
+ mRawRgba1010102Image.width = mRawP010Image.width;
+ mRawRgba1010102Image.height = mRawP010Image.height;
+ mRawRgba1010102Image.colorGamut = mRawP010Image.colorGamut;
+ uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba1010102Image.data);
+ uint16_t* y = static_cast<uint16_t*>(mRawP010Image.data);
+ uint16_t* u = y + mRawP010Image.width * mRawP010Image.height;
+ uint16_t* v = u + 1;
+
+ for (int i = 0; i < mRawP010Image.height; i++) {
+ for (int j = 0; j < mRawP010Image.width; j++) {
+ float y0 = float(y[mRawP010Image.width * i + j] >> 6);
+ float u0 = float(u[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
+ float v0 = float(v[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6);
+
+ y0 = CLIP3(y0, 64.0f, 940.0f);
+ u0 = CLIP3(u0, 64.0f, 960.0f);
+ v0 = CLIP3(v0, 64.0f, 960.0f);
+
+ y0 = (y0 - 64.0f) / 876.0f;
+ u0 = (u0 - 64.0f) / 896.0f - 0.5f;
+ v0 = (v0 - 64.0f) / 896.0f - 0.5f;
+
+ float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
+ float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
+ float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
+
+ r = CLIP3(r * 1023.0f + 0.5f, 0.0f, 1023.0f);
+ g = CLIP3(g * 1023.0f + 0.5f, 0.0f, 1023.0f);
+ b = CLIP3(b * 1023.0f + 0.5f, 0.0f, 1023.0f);
+
+ int32_t r0 = int32_t(r);
+ int32_t g0 = int32_t(g);
+ int32_t b0 = int32_t(b);
+ *rgbData = (0x3ff & r0) | ((0x3ff & g0) << 10) | ((0x3ff & b0) << 20) |
+ (0x3 << 30); // Set alpha to 1.0
+
+ rgbData++;
+ }
+ }
+ writeFile("inRgba1010102.raw", mRawRgba1010102Image.data,
+ mRawP010Image.width * mRawP010Image.height * 4);
+ return true;
+}
+
+bool UltraHdrAppInput::convertYuv420ToRGBImage() {
+ mRawRgba8888Image.data = malloc(mRawYuv420Image.width * mRawYuv420Image.height * 4);
+ if (mRawRgba8888Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store rgba888" << std::endl;
+ return false;
+ }
+ mRawRgba8888Image.width = mRawYuv420Image.width;
+ mRawRgba8888Image.height = mRawYuv420Image.height;
+ mRawRgba8888Image.colorGamut = mRawYuv420Image.colorGamut;
+ uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba8888Image.data);
+ uint8_t* y = static_cast<uint8_t*>(mRawYuv420Image.data);
+ uint8_t* u = y + (mRawYuv420Image.width * mRawYuv420Image.height);
+ uint8_t* v = u + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
+
+ const float* coeffs = BT601YUVtoRGBMatrix;
+ for (int i = 0; i < mRawYuv420Image.height; i++) {
+ for (int j = 0; j < mRawYuv420Image.width; j++) {
+ float y0 = float(y[mRawYuv420Image.width * i + j]);
+ float u0 = float(u[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
+ float v0 = float(v[mRawYuv420Image.width / 2 * (i / 2) + (j / 2)] - 128);
+
+ y0 /= 255.0f;
+ u0 /= 255.0f;
+ v0 /= 255.0f;
+
+ float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
+ float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
+ float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
+
+ r = r * 255.0f + 0.5f;
+ g = g * 255.0f + 0.5f;
+ b = b * 255.0f + 0.5f;
+
+ r = CLIP3(r, 0.0f, 255.0f);
+ g = CLIP3(g, 0.0f, 255.0f);
+ b = CLIP3(b, 0.0f, 255.0f);
+
+ int32_t r0 = int32_t(r);
+ int32_t g0 = int32_t(g);
+ int32_t b0 = int32_t(b);
+ *rgbData = r0 | (g0 << 8) | (b0 << 16) | (255 << 24); // Set alpha to 1.0
+
+ rgbData++;
+ }
+ }
+ writeFile("inRgba8888.raw", mRawRgba8888Image.data,
+ mRawYuv420Image.width * mRawYuv420Image.height * 4);
+ return true;
+}
+
+bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
+ mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3);
+ if (mDestYUV444Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store yuv444" << std::endl;
+ return false;
+ }
+ mDestYUV444Image.width = mDestImage.width;
+ mDestYUV444Image.height = mDestImage.height;
+ mDestYUV444Image.colorGamut = mDestImage.colorGamut;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
+
+ uint8_t* yData = static_cast<uint8_t*>(mDestYUV444Image.data);
+ uint8_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint8_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ const float* coeffs = BT601RGBtoYUVMatrix;
+ for (int i = 0; i < mDestImage.height; i++) {
+ for (int j = 0; j < mDestImage.width; j++) {
+ float r0 = float(rgbData[mDestImage.width * i + j] & 0xff);
+ float g0 = float((rgbData[mDestImage.width * i + j] >> 8) & 0xff);
+ float b0 = float((rgbData[mDestImage.width * i + j] >> 16) & 0xff);
+
+ r0 /= 255.0f;
+ g0 /= 255.0f;
+ b0 /= 255.0f;
+
+ float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
+ float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
+ float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
+
+ y = y * 255.0f + 0.5f;
+ u = u * 255.0f + 0.5f + 128.0f;
+ v = v * 255.0f + 0.5f + 128.0f;
+
+ y = CLIP3(y, 0.0f, 255.0f);
+ u = CLIP3(u, 0.0f, 255.0f);
+ v = CLIP3(v, 0.0f, 255.0f);
+
+ yData[mDestYUV444Image.width * i + j] = uint8_t(y);
+ uData[mDestYUV444Image.width * i + j] = uint8_t(u);
+ vData[mDestYUV444Image.width * i + j] = uint8_t(v);
+ }
+ }
+ writeFile("outyuv444.yuv", mDestYUV444Image.data,
+ mDestYUV444Image.width * mDestYUV444Image.height * 3);
+ return true;
+}
+
+bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
+ const float* coeffs = BT2020RGBtoYUVMatrix;
+ if (mP010Cg == ULTRAHDR_COLORGAMUT_BT709) {
+ coeffs = BT709RGBtoYUVMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_BT2100) {
+ coeffs = BT2020RGBtoYUVMatrix;
+ } else if (mP010Cg == ULTRAHDR_COLORGAMUT_P3) {
+ coeffs = BT601RGBtoYUVMatrix;
+ } else {
+ std::cerr << "color matrix not present for gamut " << mP010Cg << " using BT2020Matrix"
+ << std::endl;
+ }
+
+ mDestYUV444Image.data = malloc(mDestImage.width * mDestImage.height * 3 * 2);
+ if (mDestYUV444Image.data == nullptr) {
+ std::cerr << "failed to allocate memory to store yuv444" << std::endl;
+ return false;
+ }
+ mDestYUV444Image.width = mDestImage.width;
+ mDestYUV444Image.height = mDestImage.height;
+ mDestYUV444Image.colorGamut = mDestImage.colorGamut;
+
+ uint32_t* rgbData = static_cast<uint32_t*>(mDestImage.data);
+
+ uint16_t* yData = static_cast<uint16_t*>(mDestYUV444Image.data);
+ uint16_t* uData = yData + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint16_t* vData = uData + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ for (int i = 0; i < mDestImage.height; i++) {
+ for (int j = 0; j < mDestImage.width; j++) {
+ float r0 = float(rgbData[mDestImage.width * i + j] & 0x3ff);
+ float g0 = float((rgbData[mDestImage.width * i + j] >> 10) & 0x3ff);
+ float b0 = float((rgbData[mDestImage.width * i + j] >> 20) & 0x3ff);
+
+ r0 /= 1023.0f;
+ g0 /= 1023.0f;
+ b0 /= 1023.0f;
+
+ float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
+ float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
+ float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
+
+ y = (y * 876.0f) + 64.0f + 0.5f;
+ u = (u * 896.0f) + 64.0f + 512.0f + 0.5f;
+ v = (v * 896.0f) + 64.0f + 512.0f + 0.5f;
+
+ y = CLIP3(y, 64.0f, 940.0f);
+ u = CLIP3(u, 64.0f, 960.0f);
+ v = CLIP3(v, 64.0f, 960.0f);
+
+ yData[mDestYUV444Image.width * i + j] = uint16_t(y);
+ uData[mDestYUV444Image.width * i + j] = uint16_t(u);
+ vData[mDestYUV444Image.width * i + j] = uint16_t(v);
+ }
+ }
+ writeFile("outyuv444.yuv", mDestYUV444Image.data,
+ mDestYUV444Image.width * mDestYUV444Image.height * 3 * 2);
+ return true;
+}
+
+void UltraHdrAppInput::computeRGBHdrPSNR() {
+ if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+ uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba1010102Image.data);
+ uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
+ if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
+ std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
+ return;
+ }
+ if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
+ (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
+ std::cout << "input transfer function and output format are not compatible, psnr results "
+ "may be unreliable"
+ << std::endl;
+ }
+ uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
+ for (int i = 0; i < mRawP010Image.width * mRawP010Image.height; i++) {
+ int rSrc = *rgbDataSrc & 0x3ff;
+ int rDst = *rgbDataDst & 0x3ff;
+ rSqError += (rSrc - rDst) * (rSrc - rDst);
+
+ int gSrc = (*rgbDataSrc >> 10) & 0x3ff;
+ int gDst = (*rgbDataDst >> 10) & 0x3ff;
+ gSqError += (gSrc - gDst) * (gSrc - gDst);
+
+ int bSrc = (*rgbDataSrc >> 20) & 0x3ff;
+ int bDst = (*rgbDataDst >> 20) & 0x3ff;
+ bSqError += (bSrc - bDst) * (bSrc - bDst);
+
+ rgbDataSrc++;
+ rgbDataDst++;
+ }
+ double meanSquareError = (double)rSqError / (mRawP010Image.width * mRawP010Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)gSqError / (mRawP010Image.width * mRawP010Image.height);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)bSqError / (mRawP010Image.width * mRawP010Image.height);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
+ << std::endl;
+}
+
+void UltraHdrAppInput::computeRGBSdrPSNR() {
+ if (mOf != ULTRAHDR_OUTPUT_SDR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+ uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba8888Image.data);
+ uint32_t* rgbDataDst = static_cast<uint32_t*>(mDestImage.data);
+ if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
+ std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
+ return;
+ }
+
+ uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
+ for (int i = 0; i < mRawYuv420Image.width * mRawYuv420Image.height; i++) {
+ int rSrc = *rgbDataSrc & 0xff;
+ int rDst = *rgbDataDst & 0xff;
+ rSqError += (rSrc - rDst) * (rSrc - rDst);
+
+ int gSrc = (*rgbDataSrc >> 8) & 0xff;
+ int gDst = (*rgbDataDst >> 8) & 0xff;
+ gSqError += (gSrc - gDst) * (gSrc - gDst);
+
+ int bSrc = (*rgbDataSrc >> 16) & 0xff;
+ int bDst = (*rgbDataDst >> 16) & 0xff;
+ bSqError += (bSrc - bDst) * (bSrc - bDst);
+
+ rgbDataSrc++;
+ rgbDataDst++;
+ }
+ double meanSquareError = (double)rSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)gSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)bSqError / (mRawYuv420Image.width * mRawYuv420Image.height);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
+ << std::endl;
+}
+
+void UltraHdrAppInput::computeYUVHdrPSNR() {
+ if (mOf == ULTRAHDR_OUTPUT_SDR || mOf == ULTRAHDR_OUTPUT_HDR_LINEAR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+ uint16_t* yuvDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
+ uint16_t* yuvDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
+ if (yuvDataSrc == nullptr || yuvDataDst == nullptr) {
+ std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
+ return;
+ }
+ if ((mOf == ULTRAHDR_OUTPUT_HDR_PQ && mTf != ULTRAHDR_TF_PQ) ||
+ (mOf == ULTRAHDR_OUTPUT_HDR_HLG && mTf != ULTRAHDR_TF_HLG)) {
+ std::cout << "input transfer function and output format are not compatible, psnr results "
+ "may be unreliable"
+ << std::endl;
+ }
+
+ uint16_t* yDataSrc = static_cast<uint16_t*>(mRawP010Image.data);
+ uint16_t* uDataSrc = yDataSrc + (mRawP010Image.width * mRawP010Image.height);
+ uint16_t* vDataSrc = uDataSrc + 1;
+
+ uint16_t* yDataDst = static_cast<uint16_t*>(mDestYUV444Image.data);
+ uint16_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint16_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
+ for (int i = 0; i < mDestYUV444Image.height; i++) {
+ for (int j = 0; j < mDestYUV444Image.width; j++) {
+ int ySrc = (yDataSrc[mRawP010Image.width * i + j] >> 6) & 0x3ff;
+ ySrc = CLIP3(ySrc, 64, 940);
+ int yDst = yDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
+ ySqError += (ySrc - yDst) * (ySrc - yDst);
+
+ if (i % 2 == 0 && j % 2 == 0) {
+ int uSrc = (uDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
+ uSrc = CLIP3(uSrc, 64, 960);
+ int uDst = uDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ uDst = (uDst + 2) >> 2;
+ uSqError += (uSrc - uDst) * (uSrc - uDst);
+
+ int vSrc = (vDataSrc[mRawP010Image.width * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
+ vSrc = CLIP3(vSrc, 64, 960);
+ int vDst = vDataDst[mDestYUV444Image.width * i + j] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.width * i + j + 1] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1] & 0x3ff;
+ vDst = (vDst + 2) >> 2;
+ vSqError += (vSrc - vDst) * (vSrc - vDst);
+ }
+ }
+ }
+
+ double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
+
+ std::cout << "psnr y :: " << mPsnr[0] << " psnr u :: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
+ << std::endl;
+}
+
+void UltraHdrAppInput::computeYUVSdrPSNR() {
+ if (mOf != ULTRAHDR_OUTPUT_SDR) {
+ std::cout << "psnr not supported for output format " << mOf << std::endl;
+ return;
+ }
+
+ uint8_t* yDataSrc = static_cast<uint8_t*>(mRawYuv420Image.data);
+ uint8_t* uDataSrc = yDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height);
+ uint8_t* vDataSrc = uDataSrc + (mRawYuv420Image.width * mRawYuv420Image.height / 4);
+
+ uint8_t* yDataDst = static_cast<uint8_t*>(mDestYUV444Image.data);
+ uint8_t* uDataDst = yDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+ uint8_t* vDataDst = uDataDst + (mDestYUV444Image.width * mDestYUV444Image.height);
+
+ uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
+ for (int i = 0; i < mDestYUV444Image.height; i++) {
+ for (int j = 0; j < mDestYUV444Image.width; j++) {
+ int ySrc = yDataSrc[mRawYuv420Image.width * i + j];
+ int yDst = yDataDst[mDestYUV444Image.width * i + j];
+ ySqError += (ySrc - yDst) * (ySrc - yDst);
+
+ if (i % 2 == 0 && j % 2 == 0) {
+ int uSrc = uDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
+ int uDst = uDataDst[mDestYUV444Image.width * i + j];
+ uDst += uDataDst[mDestYUV444Image.width * i + j + 1];
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j];
+ uDst += uDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
+ uDst = (uDst + 2) >> 2;
+ uSqError += (uSrc - uDst) * (uSrc - uDst);
+
+ int vSrc = vDataSrc[mRawYuv420Image.width / 2 * (i / 2) + j / 2];
+ int vDst = vDataDst[mDestYUV444Image.width * i + j];
+ vDst += vDataDst[mDestYUV444Image.width * i + j + 1];
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j];
+ vDst += vDataDst[mDestYUV444Image.width * (i + 1) + j + 1];
+ vDst = (vDst + 2) >> 2;
+ vSqError += (vSrc - vDst) * (vSrc - vDst);
+ }
+ }
+ }
+ double meanSquareError = (double)ySqError / (mDestYUV444Image.width * mDestYUV444Image.height);
+ mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)uSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ meanSquareError = (double)vSqError / (mDestYUV444Image.width * mDestYUV444Image.height / 4);
+ mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
+
+ std::cout << "psnr y :: " << mPsnr[0] << " psnr u:: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
+ << std::endl;
+}
+
+static void usage(const char* name) {
+ fprintf(stderr, "Usage: %s \n", name);
+ fprintf(stderr, "ultra hdr demo application \n");
+ fprintf(stderr, " -p p010 file path, mandatory \n");
+ fprintf(stderr, " -y yuv420 file path, optional \n");
+ fprintf(stderr, " -w input width, mandatory \n");
+ fprintf(stderr, " -h input height, mandatory \n");
+ fprintf(stderr, " -C p010 color gamut, optional [0:bt709, 1:p3, 2:bt2100] \n");
+ fprintf(stderr, " -c yuv420 color gamut, optional [0:bt709, 1:p3, 2:bt2100] \n");
+ fprintf(stderr, " -t input transfer function, optional [0:linear, 1:hlg, 2:pq] \n");
+ fprintf(stderr, " -q quality factor, optional [0-100] \n");
+ fprintf(stderr,
+ " -o output transfer function, optional [0:sdr, 1:hdr_linear, 2:hdr_pq, "
+ "3:hdr_hlg] \n");
+}
+
+int main(int argc, char* argv[]) {
+ char *p010_file = nullptr, *yuv420_file = nullptr;
+ int width = 0, height = 0;
+ ultrahdr_color_gamut p010Cg = ULTRAHDR_COLORGAMUT_BT709;
+ ultrahdr_color_gamut yuv420Cg = ULTRAHDR_COLORGAMUT_BT709;
+ ultrahdr_transfer_function tf = ULTRAHDR_TF_HLG;
+ int quality = 100;
+ ultrahdr_output_format of = ULTRAHDR_OUTPUT_HDR_HLG;
+ int ch;
+ while ((ch = getopt(argc, argv, "p:y:w:h:C:c:t:q:o:")) != -1) {
+ switch (ch) {
+ case 'p':
+ p010_file = optarg;
+ break;
+ case 'y':
+ yuv420_file = optarg;
+ break;
+ case 'w':
+ width = atoi(optarg);
+ break;
+ case 'h':
+ height = atoi(optarg);
+ break;
+ case 'C':
+ p010Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg));
+ break;
+ case 'c':
+ yuv420Cg = static_cast<ultrahdr_color_gamut>(atoi(optarg));
+ break;
+ case 't':
+ tf = static_cast<ultrahdr_transfer_function>(atoi(optarg));
+ break;
+ case 'q':
+ quality = atoi(optarg);
+ break;
+ case 'o':
+ of = static_cast<ultrahdr_output_format>(atoi(optarg));
+ break;
+ default:
+ usage(argv[0]);
+ return -1;
+ }
+ }
+ if (width <= 0 || height <= 0 || p010_file == nullptr) {
+ usage(argv[0]);
+ return -1;
+ }
+
+ UltraHdrAppInput appInput(p010_file, yuv420_file, width, height, p010Cg, yuv420Cg, tf, quality,
+ of);
+ if (!appInput.encode()) return -1;
+ if (!appInput.decode()) return -1;
+ if (of == ULTRAHDR_OUTPUT_SDR && yuv420_file != nullptr) {
+ appInput.convertYuv420ToRGBImage();
+ appInput.computeRGBSdrPSNR();
+ appInput.convertRgba8888ToYUV444Image();
+ appInput.computeYUVSdrPSNR();
+ } else if (of == ULTRAHDR_OUTPUT_HDR_HLG || of == ULTRAHDR_OUTPUT_HDR_PQ) {
+ appInput.convertP010ToRGBImage();
+ appInput.computeRGBHdrPSNR();
+ appInput.convertRgba1010102ToYUV444Image();
+ appInput.computeYUVHdrPSNR();
+ }
+ return 0;
+}