1 /*
2 * Copyright 2023 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #ifdef _WIN32
18 #include <Windows.h>
19 #else
20 #include <sys/time.h>
21 #endif
22
23 #include <string.h>
24
25 #include <algorithm>
26 #include <cmath>
27 #include <cstdint>
28 #include <fstream>
29 #include <iostream>
30 #include <sstream>
31
32 #include "ultrahdr_api.h"
33
34 const float BT601YUVtoRGBMatrix[9] = {
35 1, 0, 1.402, 1, (-0.202008 / 0.587), (-0.419198 / 0.587), 1.0, 1.772, 0.0};
36 const float BT709YUVtoRGBMatrix[9] = {
37 1, 0, 1.5748, 1, (-0.13397432 / 0.7152), (-0.33480248 / 0.7152), 1.0, 1.8556, 0.0};
38 const float BT2020YUVtoRGBMatrix[9] = {
39 1, 0, 1.4746, 1, (-0.11156702 / 0.6780), (-0.38737742 / 0.6780), 1, 1.8814, 0};
40
41 const float BT601RGBtoYUVMatrix[9] = {
42 0.299, 0.587, 0.114, (-0.299 / 1.772), (-0.587 / 1.772), 0.5, 0.5, (-0.587 / 1.402),
43 (-0.114 / 1.402)};
44 const float BT709RGBtoYUVMatrix[9] = {0.2126,
45 0.7152,
46 0.0722,
47 (-0.2126 / 1.8556),
48 (-0.7152 / 1.8556),
49 0.5,
50 0.5,
51 (-0.7152 / 1.5748),
52 (-0.0722 / 1.5748)};
53 const float BT2020RGBtoYUVMatrix[9] = {0.2627,
54 0.6780,
55 0.0593,
56 (-0.2627 / 1.8814),
57 (-0.6780 / 1.8814),
58 0.5,
59 0.5,
60 (-0.6780 / 1.4746),
61 (-0.0593 / 1.4746)};
62
63 // remove these once introduced in ultrahdr_api.h
64 const int UHDR_IMG_FMT_48bppYCbCr444 = 101;
65
66 int optind_s = 1;
67 int optopt_s = 0;
68 char* optarg_s = nullptr;
69
getopt_s(int argc,char * const argv[],char * ostr)70 int getopt_s(int argc, char* const argv[], char* ostr) {
71 if (optind_s >= argc) return -1;
72
73 const char* arg = argv[optind_s];
74 if (arg[0] != '-' || !arg[1]) {
75 std::cerr << "invalid option " << arg << std::endl;
76 return '?';
77 }
78 optopt_s = arg[1];
79 char* oindex = strchr(ostr, optopt_s);
80 if (!oindex) {
81 std::cerr << "unsupported option " << arg << std::endl;
82 return '?';
83 }
84 if (oindex[1] != ':') {
85 optarg_s = nullptr;
86 return optopt_s;
87 }
88
89 if (argc > ++optind_s) {
90 optarg_s = (char*)argv[optind_s++];
91 } else {
92 std::cerr << "option " << arg << " requires an argument" << std::endl;
93 optarg_s = nullptr;
94 return '?';
95 }
96 return optopt_s;
97 }
98
99 // #define PROFILE_ENABLE 1
100 #ifdef _WIN32
101 class Profiler {
102 public:
timerStart()103 void timerStart() { QueryPerformanceCounter(&mStartingTime); }
104
timerStop()105 void timerStop() { QueryPerformanceCounter(&mEndingTime); }
106
elapsedTime()107 int64_t elapsedTime() {
108 LARGE_INTEGER frequency;
109 LARGE_INTEGER elapsedMicroseconds;
110 QueryPerformanceFrequency(&frequency);
111 elapsedMicroseconds.QuadPart = mEndingTime.QuadPart - mStartingTime.QuadPart;
112 return (double)elapsedMicroseconds.QuadPart / (double)frequency.QuadPart * 1000000;
113 }
114
115 private:
116 LARGE_INTEGER mStartingTime;
117 LARGE_INTEGER mEndingTime;
118 };
119 #else
120 class Profiler {
121 public:
timerStart()122 void timerStart() { gettimeofday(&mStartingTime, nullptr); }
123
timerStop()124 void timerStop() { gettimeofday(&mEndingTime, nullptr); }
125
elapsedTime()126 int64_t elapsedTime() {
127 struct timeval elapsedMicroseconds;
128 elapsedMicroseconds.tv_sec = mEndingTime.tv_sec - mStartingTime.tv_sec;
129 elapsedMicroseconds.tv_usec = mEndingTime.tv_usec - mStartingTime.tv_usec;
130 return elapsedMicroseconds.tv_sec * 1000000 + elapsedMicroseconds.tv_usec;
131 }
132
133 private:
134 struct timeval mStartingTime;
135 struct timeval mEndingTime;
136 };
137 #endif
138
139 #define READ_BYTES(DESC, ADDR, LEN) \
140 DESC.read(static_cast<char*>(ADDR), (LEN)); \
141 if (DESC.gcount() != (LEN)) { \
142 std::cerr << "failed to read : " << (LEN) << " bytes, read : " << DESC.gcount() << " bytes" \
143 << std::endl; \
144 return false; \
145 }
146
loadFile(const char * filename,void * & result,int length)147 static bool loadFile(const char* filename, void*& result, int length) {
148 std::ifstream ifd(filename, std::ios::binary | std::ios::ate);
149 if (ifd.good()) {
150 int size = ifd.tellg();
151 if (size < length) {
152 std::cerr << "requested to read " << length << " bytes from file : " << filename
153 << ", file contains only " << size << " bytes" << std::endl;
154 return false;
155 }
156 ifd.seekg(0, std::ios::beg);
157 result = malloc(length);
158 if (result == nullptr) {
159 std::cerr << "failed to allocate memory to store contents of file : " << filename
160 << std::endl;
161 return false;
162 }
163 READ_BYTES(ifd, result, length)
164 return true;
165 }
166 std::cerr << "unable to open file : " << filename << std::endl;
167 return false;
168 }
169
loadFile(const char * filename,uhdr_raw_image_t * handle)170 static bool loadFile(const char* filename, uhdr_raw_image_t* handle) {
171 std::ifstream ifd(filename, std::ios::binary);
172 if (ifd.good()) {
173 if (handle->fmt == UHDR_IMG_FMT_24bppYCbCrP010) {
174 const int bpp = 2;
175 READ_BYTES(ifd, handle->planes[UHDR_PLANE_Y], handle->w * handle->h * bpp)
176 READ_BYTES(ifd, handle->planes[UHDR_PLANE_UV], (handle->w / 2) * (handle->h / 2) * bpp * 2)
177 return true;
178 } else if (handle->fmt == UHDR_IMG_FMT_32bppRGBA1010102 ||
179 handle->fmt == UHDR_IMG_FMT_32bppRGBA8888) {
180 const int bpp = 4;
181 READ_BYTES(ifd, handle->planes[UHDR_PLANE_PACKED], handle->w * handle->h * bpp)
182 return true;
183 } else if (handle->fmt == UHDR_IMG_FMT_12bppYCbCr420) {
184 READ_BYTES(ifd, handle->planes[UHDR_PLANE_Y], handle->w * handle->h)
185 READ_BYTES(ifd, handle->planes[UHDR_PLANE_U], (handle->w / 2) * (handle->h / 2))
186 READ_BYTES(ifd, handle->planes[UHDR_PLANE_V], (handle->w / 2) * (handle->h / 2))
187 return true;
188 }
189 return false;
190 }
191 std::cerr << "unable to open file : " << filename << std::endl;
192 return false;
193 }
194
writeFile(const char * filename,void * & result,int length)195 static bool writeFile(const char* filename, void*& result, int length) {
196 std::ofstream ofd(filename, std::ios::binary);
197 if (ofd.is_open()) {
198 ofd.write(static_cast<char*>(result), length);
199 return true;
200 }
201 std::cerr << "unable to write to file : " << filename << std::endl;
202 return false;
203 }
204
writeFile(const char * filename,uhdr_raw_image_t * img)205 static bool writeFile(const char* filename, uhdr_raw_image_t* img) {
206 std::ofstream ofd(filename, std::ios::binary);
207 if (ofd.is_open()) {
208 if (img->fmt == UHDR_IMG_FMT_32bppRGBA8888 || img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ||
209 img->fmt == UHDR_IMG_FMT_32bppRGBA1010102) {
210 char* data = static_cast<char*>(img->planes[UHDR_PLANE_PACKED]);
211 int bpp = img->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat ? 8 : 4;
212 const size_t stride = img->stride[UHDR_PLANE_PACKED] * bpp;
213 const size_t length = img->w * bpp;
214 for (unsigned i = 0; i < img->h; i++, data += stride) {
215 ofd.write(data, length);
216 }
217 return true;
218 } else if ((int)img->fmt == UHDR_IMG_FMT_24bppYCbCr444 ||
219 (int)img->fmt == UHDR_IMG_FMT_48bppYCbCr444) {
220 char* data = static_cast<char*>(img->planes[UHDR_PLANE_Y]);
221 int bpp = (int)img->fmt == UHDR_IMG_FMT_48bppYCbCr444 ? 2 : 1;
222 size_t stride = img->stride[UHDR_PLANE_Y] * bpp;
223 size_t length = img->w * bpp;
224 for (unsigned i = 0; i < img->h; i++, data += stride) {
225 ofd.write(data, length);
226 }
227 data = static_cast<char*>(img->planes[UHDR_PLANE_U]);
228 stride = img->stride[UHDR_PLANE_U] * bpp;
229 for (unsigned i = 0; i < img->h; i++, data += stride) {
230 ofd.write(data, length);
231 }
232 data = static_cast<char*>(img->planes[UHDR_PLANE_V]);
233 stride = img->stride[UHDR_PLANE_V] * bpp;
234 for (unsigned i = 0; i < img->h; i++, data += stride) {
235 ofd.write(data, length);
236 }
237 return true;
238 }
239 return false;
240 }
241 std::cerr << "unable to write to file : " << filename << std::endl;
242 return false;
243 }
244
245 class UltraHdrAppInput {
246 public:
UltraHdrAppInput(const char * hdrIntentRawFile,const char * sdrIntentRawFile,const char * sdrIntentCompressedFile,const char * gainmapCompressedFile,const char * gainmapMetadataCfgFile,const char * outputFile,size_t width,size_t height,uhdr_img_fmt_t hdrCf=UHDR_IMG_FMT_32bppRGBA1010102,uhdr_img_fmt_t sdrCf=UHDR_IMG_FMT_32bppRGBA8888,uhdr_color_gamut_t hdrCg=UHDR_CG_DISPLAY_P3,uhdr_color_gamut_t sdrCg=UHDR_CG_BT_709,uhdr_color_transfer_t hdrTf=UHDR_CT_HLG,int quality=95,uhdr_color_transfer_t oTf=UHDR_CT_HLG,uhdr_img_fmt_t oFmt=UHDR_IMG_FMT_32bppRGBA1010102)247 UltraHdrAppInput(const char* hdrIntentRawFile, const char* sdrIntentRawFile,
248 const char* sdrIntentCompressedFile, const char* gainmapCompressedFile,
249 const char* gainmapMetadataCfgFile, const char* outputFile, size_t width,
250 size_t height, uhdr_img_fmt_t hdrCf = UHDR_IMG_FMT_32bppRGBA1010102,
251 uhdr_img_fmt_t sdrCf = UHDR_IMG_FMT_32bppRGBA8888,
252 uhdr_color_gamut_t hdrCg = UHDR_CG_DISPLAY_P3,
253 uhdr_color_gamut_t sdrCg = UHDR_CG_BT_709,
254 uhdr_color_transfer_t hdrTf = UHDR_CT_HLG, int quality = 95,
255 uhdr_color_transfer_t oTf = UHDR_CT_HLG,
256 uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102)
257 : mHdrIntentRawFile(hdrIntentRawFile),
258 mSdrIntentRawFile(sdrIntentRawFile),
259 mSdrIntentCompressedFile(sdrIntentCompressedFile),
260 mGainMapCompressedFile(gainmapCompressedFile),
261 mGainMapMetadataCfgFile(gainmapMetadataCfgFile),
262 mUhdrFile(nullptr),
263 mOutputFile(outputFile),
264 mWidth(width),
265 mHeight(height),
266 mHdrCf(hdrCf),
267 mSdrCf(sdrCf),
268 mHdrCg(hdrCg),
269 mSdrCg(sdrCg),
270 mHdrTf(hdrTf),
271 mQuality(quality),
272 mOTf(oTf),
273 mOfmt(oFmt),
274 mMode(0){};
275
UltraHdrAppInput(const char * uhdrFile,const char * outputFile,uhdr_color_transfer_t oTf=UHDR_CT_HLG,uhdr_img_fmt_t oFmt=UHDR_IMG_FMT_32bppRGBA1010102)276 UltraHdrAppInput(const char* uhdrFile, const char* outputFile,
277 uhdr_color_transfer_t oTf = UHDR_CT_HLG,
278 uhdr_img_fmt_t oFmt = UHDR_IMG_FMT_32bppRGBA1010102)
279 : mHdrIntentRawFile(nullptr),
280 mSdrIntentRawFile(nullptr),
281 mUhdrFile(uhdrFile),
282 mOutputFile(outputFile),
283 mWidth(0),
284 mHeight(0),
285 mHdrCf(UHDR_IMG_FMT_UNSPECIFIED),
286 mSdrCf(UHDR_IMG_FMT_UNSPECIFIED),
287 mHdrCg(UHDR_CG_UNSPECIFIED),
288 mSdrCg(UHDR_CG_UNSPECIFIED),
289 mHdrTf(UHDR_CT_UNSPECIFIED),
290 mQuality(95),
291 mOTf(oTf),
292 mOfmt(oFmt),
293 mMode(1){};
294
~UltraHdrAppInput()295 ~UltraHdrAppInput() {
296 int count = sizeof mRawP010Image.planes / sizeof mRawP010Image.planes[UHDR_PLANE_Y];
297 for (int i = 0; i < count; i++) {
298 if (mRawP010Image.planes[i]) {
299 free(mRawP010Image.planes[i]);
300 mRawP010Image.planes[i] = nullptr;
301 }
302 if (mRawRgba1010102Image.planes[i]) {
303 free(mRawRgba1010102Image.planes[i]);
304 mRawRgba1010102Image.planes[i] = nullptr;
305 }
306 if (mRawYuv420Image.planes[i]) {
307 free(mRawYuv420Image.planes[i]);
308 mRawYuv420Image.planes[i] = nullptr;
309 }
310 if (mRawRgba8888Image.planes[i]) {
311 free(mRawRgba8888Image.planes[i]);
312 mRawRgba8888Image.planes[i] = nullptr;
313 }
314 if (mDecodedUhdrRgbImage.planes[i]) {
315 free(mDecodedUhdrRgbImage.planes[i]);
316 mDecodedUhdrRgbImage.planes[i] = nullptr;
317 }
318 if (mDecodedUhdrYuv444Image.planes[i]) {
319 free(mDecodedUhdrYuv444Image.planes[i]);
320 mDecodedUhdrYuv444Image.planes[i] = nullptr;
321 }
322 }
323 if (mUhdrImage.data) free(mUhdrImage.data);
324 }
325
326 bool fillUhdrImageHandle();
327 bool fillP010ImageHandle();
328 bool fillRGBA1010102ImageHandle();
329 bool convertP010ToRGBImage();
330 bool fillYuv420ImageHandle();
331 bool fillRGBA8888ImageHandle();
332 bool convertYuv420ToRGBImage();
333 bool fillSdrCompressedImageHandle();
334 bool fillGainMapCompressedImageHandle();
335 bool fillGainMapMetadataDescriptor();
336 bool convertRgba8888ToYUV444Image();
337 bool convertRgba1010102ToYUV444Image();
338 bool encode();
339 bool decode();
340 void computeRGBHdrPSNR();
341 void computeRGBSdrPSNR();
342 void computeYUVHdrPSNR();
343 void computeYUVSdrPSNR();
344
345 const char* mHdrIntentRawFile;
346 const char* mSdrIntentRawFile;
347 const char* mSdrIntentCompressedFile;
348 const char* mGainMapCompressedFile;
349 const char* mGainMapMetadataCfgFile;
350 const char* mUhdrFile;
351 const char* mOutputFile;
352 const int mWidth;
353 const int mHeight;
354 const uhdr_img_fmt_t mHdrCf;
355 const uhdr_img_fmt_t mSdrCf;
356 const uhdr_color_gamut_t mHdrCg;
357 const uhdr_color_gamut_t mSdrCg;
358 const uhdr_color_transfer_t mHdrTf;
359 const int mQuality;
360 const uhdr_color_transfer_t mOTf;
361 const uhdr_img_fmt_t mOfmt;
362 const int mMode;
363
364 uhdr_raw_image_t mRawP010Image{};
365 uhdr_raw_image_t mRawRgba1010102Image{};
366 uhdr_raw_image_t mRawYuv420Image{};
367 uhdr_raw_image_t mRawRgba8888Image{};
368 uhdr_compressed_image_t mSdrIntentCompressedImage{};
369 uhdr_compressed_image_t mGainMapCompressedImage{};
370 uhdr_gainmap_metadata mGainMapMetadata{};
371 uhdr_compressed_image_t mUhdrImage{};
372 uhdr_raw_image_t mDecodedUhdrRgbImage{};
373 uhdr_raw_image_t mDecodedUhdrYuv444Image{};
374 double mPsnr[3]{};
375 };
376
fillP010ImageHandle()377 bool UltraHdrAppInput::fillP010ImageHandle() {
378 const int bpp = 2;
379 int p010Size = mWidth * mHeight * bpp * 1.5;
380 mRawP010Image.fmt = UHDR_IMG_FMT_24bppYCbCrP010;
381 mRawP010Image.cg = mHdrCg;
382 mRawP010Image.ct = mHdrTf;
383 mRawP010Image.range = UHDR_CR_LIMITED_RANGE;
384 mRawP010Image.w = mWidth;
385 mRawP010Image.h = mHeight;
386 mRawP010Image.planes[UHDR_PLANE_Y] = malloc(mWidth * mHeight * bpp);
387 mRawP010Image.planes[UHDR_PLANE_UV] = malloc((mWidth / 2) * (mHeight / 2) * bpp * 2);
388 mRawP010Image.planes[UHDR_PLANE_V] = nullptr;
389 mRawP010Image.stride[UHDR_PLANE_Y] = mWidth;
390 mRawP010Image.stride[UHDR_PLANE_UV] = mWidth;
391 mRawP010Image.stride[UHDR_PLANE_V] = 0;
392 return loadFile(mHdrIntentRawFile, &mRawP010Image);
393 }
394
fillYuv420ImageHandle()395 bool UltraHdrAppInput::fillYuv420ImageHandle() {
396 int yuv420Size = mWidth * mHeight * 1.5;
397 mRawYuv420Image.fmt = UHDR_IMG_FMT_12bppYCbCr420;
398 mRawYuv420Image.cg = mSdrCg;
399 mRawYuv420Image.ct = UHDR_CT_SRGB;
400 mRawYuv420Image.range = UHDR_CR_FULL_RANGE;
401 mRawYuv420Image.w = mWidth;
402 mRawYuv420Image.h = mHeight;
403 mRawYuv420Image.planes[UHDR_PLANE_Y] = malloc(mWidth * mHeight);
404 mRawYuv420Image.planes[UHDR_PLANE_U] = malloc((mWidth / 2) * (mHeight / 2));
405 mRawYuv420Image.planes[UHDR_PLANE_V] = malloc((mWidth / 2) * (mHeight / 2));
406 mRawYuv420Image.stride[UHDR_PLANE_Y] = mWidth;
407 mRawYuv420Image.stride[UHDR_PLANE_U] = mWidth / 2;
408 mRawYuv420Image.stride[UHDR_PLANE_V] = mWidth / 2;
409 return loadFile(mSdrIntentRawFile, &mRawYuv420Image);
410 }
411
fillRGBA1010102ImageHandle()412 bool UltraHdrAppInput::fillRGBA1010102ImageHandle() {
413 const int bpp = 4;
414 mRawRgba1010102Image.fmt = UHDR_IMG_FMT_32bppRGBA1010102;
415 mRawRgba1010102Image.cg = mHdrCg;
416 mRawRgba1010102Image.ct = mHdrTf;
417 mRawRgba1010102Image.range = UHDR_CR_FULL_RANGE;
418 mRawRgba1010102Image.w = mWidth;
419 mRawRgba1010102Image.h = mHeight;
420 mRawRgba1010102Image.planes[UHDR_PLANE_PACKED] = malloc(mWidth * mHeight * bpp);
421 mRawRgba1010102Image.planes[UHDR_PLANE_UV] = nullptr;
422 mRawRgba1010102Image.planes[UHDR_PLANE_V] = nullptr;
423 mRawRgba1010102Image.stride[UHDR_PLANE_PACKED] = mWidth;
424 mRawRgba1010102Image.stride[UHDR_PLANE_UV] = 0;
425 mRawRgba1010102Image.stride[UHDR_PLANE_V] = 0;
426 return loadFile(mHdrIntentRawFile, &mRawRgba1010102Image);
427 }
428
fillRGBA8888ImageHandle()429 bool UltraHdrAppInput::fillRGBA8888ImageHandle() {
430 const int bpp = 4;
431 mRawRgba8888Image.fmt = UHDR_IMG_FMT_32bppRGBA8888;
432 mRawRgba8888Image.cg = mSdrCg;
433 mRawRgba8888Image.ct = UHDR_CT_SRGB;
434 mRawRgba8888Image.range = UHDR_CR_FULL_RANGE;
435 mRawRgba8888Image.w = mWidth;
436 mRawRgba8888Image.h = mHeight;
437 mRawRgba8888Image.planes[UHDR_PLANE_PACKED] = malloc(mWidth * mHeight * bpp);
438 mRawRgba8888Image.planes[UHDR_PLANE_U] = nullptr;
439 mRawRgba8888Image.planes[UHDR_PLANE_V] = nullptr;
440 mRawRgba8888Image.stride[UHDR_PLANE_Y] = mWidth;
441 mRawRgba8888Image.stride[UHDR_PLANE_U] = 0;
442 mRawRgba8888Image.stride[UHDR_PLANE_V] = 0;
443 return loadFile(mSdrIntentRawFile, &mRawRgba8888Image);
444 }
445
fillSdrCompressedImageHandle()446 bool UltraHdrAppInput::fillSdrCompressedImageHandle() {
447 std::ifstream ifd(mSdrIntentCompressedFile, std::ios::binary | std::ios::ate);
448 if (ifd.good()) {
449 int size = ifd.tellg();
450 mSdrIntentCompressedImage.capacity = size;
451 mSdrIntentCompressedImage.data_sz = size;
452 mSdrIntentCompressedImage.data = nullptr;
453 mSdrIntentCompressedImage.cg = mSdrCg;
454 mSdrIntentCompressedImage.ct = UHDR_CT_UNSPECIFIED;
455 mSdrIntentCompressedImage.range = UHDR_CR_UNSPECIFIED;
456 ifd.close();
457 return loadFile(mSdrIntentCompressedFile, mSdrIntentCompressedImage.data, size);
458 }
459 return false;
460 }
461
fillGainMapCompressedImageHandle()462 bool UltraHdrAppInput::fillGainMapCompressedImageHandle() {
463 std::ifstream ifd(mGainMapCompressedFile, std::ios::binary | std::ios::ate);
464 if (ifd.good()) {
465 int size = ifd.tellg();
466 mGainMapCompressedImage.capacity = size;
467 mGainMapCompressedImage.data_sz = size;
468 mGainMapCompressedImage.data = nullptr;
469 mGainMapCompressedImage.cg = UHDR_CG_UNSPECIFIED;
470 mGainMapCompressedImage.ct = UHDR_CT_UNSPECIFIED;
471 mGainMapCompressedImage.range = UHDR_CR_UNSPECIFIED;
472 ifd.close();
473 return loadFile(mGainMapCompressedFile, mGainMapCompressedImage.data, size);
474 }
475 return false;
476 }
477
parse_argument(uhdr_gainmap_metadata * metadata,char * argument,float * value)478 void parse_argument(uhdr_gainmap_metadata* metadata, char* argument, float* value) {
479 if (!strcmp(argument, "maxContentBoost"))
480 metadata->max_content_boost = *value;
481 else if (!strcmp(argument, "minContentBoost"))
482 metadata->min_content_boost = *value;
483 else if (!strcmp(argument, "gamma"))
484 metadata->gamma = *value;
485 else if (!strcmp(argument, "offsetSdr"))
486 metadata->offset_sdr = *value;
487 else if (!strcmp(argument, "offsetHdr"))
488 metadata->offset_hdr = *value;
489 else if (!strcmp(argument, "hdrCapacityMin"))
490 metadata->hdr_capacity_min = *value;
491 else if (!strcmp(argument, "hdrCapacityMax"))
492 metadata->hdr_capacity_max = *value;
493 else
494 std::cout << " Ignoring argument " << argument << std::endl;
495 }
496
fillGainMapMetadataDescriptor()497 bool UltraHdrAppInput::fillGainMapMetadataDescriptor() {
498 std::ifstream file(mGainMapMetadataCfgFile);
499 if (!file.is_open()) {
500 return false;
501 }
502 std::string line;
503 char argument[128];
504 float value;
505 while (std::getline(file, line)) {
506 if (sscanf(line.c_str(), "--%s %f", argument, &value) == 2) {
507 parse_argument(&mGainMapMetadata, argument, &value);
508 }
509 }
510 file.close();
511 return true;
512 }
513
fillUhdrImageHandle()514 bool UltraHdrAppInput::fillUhdrImageHandle() {
515 std::ifstream ifd(mUhdrFile, std::ios::binary | std::ios::ate);
516 if (ifd.good()) {
517 int size = ifd.tellg();
518 mUhdrImage.capacity = size;
519 mUhdrImage.data_sz = size;
520 mUhdrImage.data = nullptr;
521 mUhdrImage.cg = UHDR_CG_UNSPECIFIED;
522 mUhdrImage.ct = UHDR_CT_UNSPECIFIED;
523 mUhdrImage.range = UHDR_CR_UNSPECIFIED;
524 ifd.close();
525 return loadFile(mUhdrFile, mUhdrImage.data, size);
526 }
527 return false;
528 }
529
encode()530 bool UltraHdrAppInput::encode() {
531 if (mHdrIntentRawFile != nullptr) {
532 if (mHdrCf == UHDR_IMG_FMT_24bppYCbCrP010) {
533 if (!fillP010ImageHandle()) {
534 std::cerr << " failed to load file " << mHdrIntentRawFile << std::endl;
535 return false;
536 }
537 } else if (mHdrCf == UHDR_IMG_FMT_32bppRGBA1010102) {
538 if (!fillRGBA1010102ImageHandle()) {
539 std::cerr << " failed to load file " << mHdrIntentRawFile << std::endl;
540 return false;
541 }
542 } else {
543 std::cerr << " invalid hdr intent color format " << mHdrCf << std::endl;
544 return false;
545 }
546 }
547 if (mSdrIntentRawFile != nullptr) {
548 if (mSdrCf == UHDR_IMG_FMT_12bppYCbCr420) {
549 if (!fillYuv420ImageHandle()) {
550 std::cerr << " failed to load file " << mSdrIntentRawFile << std::endl;
551 return false;
552 }
553 } else if (mSdrCf == UHDR_IMG_FMT_32bppRGBA8888) {
554 if (!fillRGBA8888ImageHandle()) {
555 std::cerr << " failed to load file " << mSdrIntentRawFile << std::endl;
556 return false;
557 }
558 } else {
559 std::cerr << " invalid sdr intent color format " << mSdrCf << std::endl;
560 return false;
561 }
562 }
563 if (mSdrIntentCompressedFile != nullptr) {
564 if (!fillSdrCompressedImageHandle()) {
565 std::cerr << " failed to load file " << mSdrIntentCompressedFile << std::endl;
566 return false;
567 }
568 }
569 if (mGainMapCompressedFile != nullptr && mGainMapMetadataCfgFile != nullptr) {
570 if (!fillGainMapCompressedImageHandle()) {
571 std::cerr << " failed to load file " << mGainMapCompressedFile << std::endl;
572 return false;
573 }
574 if (!fillGainMapMetadataDescriptor()) {
575 std::cerr << " failed to read config file " << mGainMapMetadataCfgFile << std::endl;
576 return false;
577 }
578 }
579
580 #define RET_IF_ERR(x) \
581 { \
582 uhdr_error_info_t status = (x); \
583 if (status.error_code != UHDR_CODEC_OK) { \
584 if (status.has_detail) { \
585 std::cerr << status.detail << std::endl; \
586 } \
587 uhdr_release_encoder(handle); \
588 return false; \
589 } \
590 }
591 uhdr_codec_private_t* handle = uhdr_create_encoder();
592 if (mHdrIntentRawFile != nullptr) {
593 if (mHdrCf == UHDR_IMG_FMT_24bppYCbCrP010) {
594 RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawP010Image, UHDR_HDR_IMG))
595 } else if (mHdrCf == UHDR_IMG_FMT_32bppRGBA1010102) {
596 RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawRgba1010102Image, UHDR_HDR_IMG))
597 }
598 }
599 if (mSdrIntentRawFile != nullptr) {
600 if (mSdrCf == UHDR_IMG_FMT_12bppYCbCr420) {
601 RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawYuv420Image, UHDR_SDR_IMG))
602 } else if (mSdrCf == UHDR_IMG_FMT_32bppRGBA8888) {
603 RET_IF_ERR(uhdr_enc_set_raw_image(handle, &mRawRgba8888Image, UHDR_SDR_IMG))
604 }
605 }
606 if (mSdrIntentCompressedFile != nullptr) {
607 RET_IF_ERR(uhdr_enc_set_compressed_image(
608 handle, &mSdrIntentCompressedImage,
609 (mGainMapCompressedFile != nullptr && mGainMapMetadataCfgFile != nullptr) ? UHDR_BASE_IMG
610 : UHDR_SDR_IMG))
611 }
612 if (mGainMapCompressedFile != nullptr && mGainMapMetadataCfgFile != nullptr) {
613 RET_IF_ERR(uhdr_enc_set_gainmap_image(handle, &mGainMapCompressedImage, &mGainMapMetadata))
614 }
615 RET_IF_ERR(uhdr_enc_set_quality(handle, mQuality, UHDR_BASE_IMG))
616 #ifdef PROFILE_ENABLE
617 const int profileCount = 10;
618 Profiler profileEncode;
619 profileEncode.timerStart();
620 for (auto i = 0; i < profileCount; i++) {
621 #endif
622 RET_IF_ERR(uhdr_encode(handle))
623 #ifdef PROFILE_ENABLE
624 }
625 profileEncode.timerStop();
626 auto avgEncTime = profileEncode.elapsedTime() / (profileCount * 1000.f);
627 printf("Average encode time for res %d x %d is %f ms \n", mWidth, mHeight, avgEncTime);
628 #endif
629
630 #undef RET_IF_ERR
631
632 auto output = uhdr_get_encoded_stream(handle);
633
634 // for decoding
635 mUhdrImage.data = malloc(output->data_sz);
636 memcpy(mUhdrImage.data, output->data, output->data_sz);
637 mUhdrImage.capacity = mUhdrImage.data_sz = output->data_sz;
638 mUhdrImage.cg = output->cg;
639 mUhdrImage.ct = output->ct;
640 mUhdrImage.range = output->range;
641 writeFile(mOutputFile, output->data, output->data_sz);
642 uhdr_release_encoder(handle);
643
644 return true;
645 }
646
decode()647 bool UltraHdrAppInput::decode() {
648 if (mMode == 1 && !fillUhdrImageHandle()) {
649 std::cerr << " failed to load file " << mUhdrFile << std::endl;
650 return false;
651 }
652
653 #define RET_IF_ERR(x) \
654 { \
655 uhdr_error_info_t status = (x); \
656 if (status.error_code != UHDR_CODEC_OK) { \
657 if (status.has_detail) { \
658 std::cerr << status.detail << std::endl; \
659 } \
660 uhdr_release_decoder(handle); \
661 return false; \
662 } \
663 }
664
665 uhdr_codec_private_t* handle = uhdr_create_decoder();
666 RET_IF_ERR(uhdr_dec_set_image(handle, &mUhdrImage))
667 RET_IF_ERR(uhdr_dec_set_out_color_transfer(handle, mOTf))
668 RET_IF_ERR(uhdr_dec_set_out_img_format(handle, mOfmt))
669
670 #ifdef PROFILE_ENABLE
671 const int profileCount = 10;
672 Profiler profileDecode;
673 profileDecode.timerStart();
674 for (auto i = 0; i < profileCount; i++) {
675 #endif
676 RET_IF_ERR(uhdr_decode(handle))
677 #ifdef PROFILE_ENABLE
678 }
679 profileDecode.timerStop();
680 auto avgDecTime = profileDecode.elapsedTime() / (profileCount * 1000.f);
681 printf("Average decode time for res %ld x %ld is %f ms \n", info.width, info.height, avgDecTime);
682 #endif
683
684 #undef RET_IF_ERR
685
686 uhdr_raw_image_t* output = uhdr_get_decoded_image(handle);
687
688 mDecodedUhdrRgbImage.fmt = output->fmt;
689 mDecodedUhdrRgbImage.cg = output->cg;
690 mDecodedUhdrRgbImage.ct = output->ct;
691 mDecodedUhdrRgbImage.range = output->range;
692 mDecodedUhdrRgbImage.w = output->w;
693 mDecodedUhdrRgbImage.h = output->h;
694 int bpp = (output->fmt == UHDR_IMG_FMT_64bppRGBAHalfFloat) ? 8 : 4;
695 mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED] = malloc(output->w * output->h * bpp);
696 char* inData = static_cast<char*>(output->planes[UHDR_PLANE_PACKED]);
697 char* outData = static_cast<char*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
698 const size_t inStride = output->stride[UHDR_PLANE_PACKED] * bpp;
699 const size_t outStride = output->w * bpp;
700 mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] = output->w;
701 const size_t length = output->w * bpp;
702 for (unsigned i = 0; i < output->h; i++, inData += inStride, outData += outStride) {
703 memcpy(outData, inData, length);
704 }
705 writeFile(mOutputFile, output);
706 uhdr_release_decoder(handle);
707
708 return true;
709 }
710
711 #define CLIP3(x, min, max) ((x) < (min)) ? (min) : ((x) > (max)) ? (max) : (x)
convertP010ToRGBImage()712 bool UltraHdrAppInput::convertP010ToRGBImage() {
713 const float* coeffs = BT2020YUVtoRGBMatrix;
714 if (mHdrCg == UHDR_CG_BT_709) {
715 coeffs = BT709YUVtoRGBMatrix;
716 } else if (mHdrCg == UHDR_CG_BT_2100) {
717 coeffs = BT2020YUVtoRGBMatrix;
718 } else if (mHdrCg == UHDR_CG_DISPLAY_P3) {
719 coeffs = BT601YUVtoRGBMatrix;
720 } else {
721 std::cerr << "color matrix not present for gamut " << mHdrCg << " using BT2020Matrix"
722 << std::endl;
723 }
724
725 mRawRgba1010102Image.fmt = UHDR_IMG_FMT_32bppRGBA1010102;
726 mRawRgba1010102Image.cg = mRawP010Image.cg;
727 mRawRgba1010102Image.ct = mRawP010Image.ct;
728 mRawRgba1010102Image.range = UHDR_CR_FULL_RANGE;
729 mRawRgba1010102Image.w = mRawP010Image.w;
730 mRawRgba1010102Image.h = mRawP010Image.h;
731 mRawRgba1010102Image.planes[UHDR_PLANE_PACKED] = malloc(mRawP010Image.w * mRawP010Image.h * 4);
732 mRawRgba1010102Image.planes[UHDR_PLANE_U] = nullptr;
733 mRawRgba1010102Image.planes[UHDR_PLANE_V] = nullptr;
734 mRawRgba1010102Image.stride[UHDR_PLANE_PACKED] = mWidth;
735 mRawRgba1010102Image.stride[UHDR_PLANE_U] = 0;
736 mRawRgba1010102Image.stride[UHDR_PLANE_V] = 0;
737
738 uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba1010102Image.planes[UHDR_PLANE_PACKED]);
739 uint16_t* y = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_Y]);
740 uint16_t* u = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_UV]);
741 uint16_t* v = u + 1;
742
743 for (size_t i = 0; i < mRawP010Image.h; i++) {
744 for (size_t j = 0; j < mRawP010Image.w; j++) {
745 float y0 = float(y[mRawP010Image.stride[UHDR_PLANE_Y] * i + j] >> 6);
746 float u0 = float(u[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6);
747 float v0 = float(v[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6);
748
749 y0 = CLIP3(y0, 64.0f, 940.0f);
750 u0 = CLIP3(u0, 64.0f, 960.0f);
751 v0 = CLIP3(v0, 64.0f, 960.0f);
752
753 y0 = (y0 - 64.0f) / 876.0f;
754 u0 = (u0 - 512.0f) / 896.0f;
755 v0 = (v0 - 512.0f) / 896.0f;
756
757 float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
758 float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
759 float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
760
761 r = CLIP3(r * 1023.0f + 0.5f, 0.0f, 1023.0f);
762 g = CLIP3(g * 1023.0f + 0.5f, 0.0f, 1023.0f);
763 b = CLIP3(b * 1023.0f + 0.5f, 0.0f, 1023.0f);
764
765 int32_t r0 = int32_t(r);
766 int32_t g0 = int32_t(g);
767 int32_t b0 = int32_t(b);
768 *rgbData = (0x3ff & r0) | ((0x3ff & g0) << 10) | ((0x3ff & b0) << 20) |
769 (0x3 << 30); // Set alpha to 1.0
770
771 rgbData++;
772 }
773 }
774 #ifdef DUMP_DEBUG_DATA
775 writeFile("inRgba1010102.raw", &mRawRgba1010102Image);
776 #endif
777 return true;
778 }
779
convertYuv420ToRGBImage()780 bool UltraHdrAppInput::convertYuv420ToRGBImage() {
781 mRawRgba8888Image.fmt = UHDR_IMG_FMT_32bppRGBA8888;
782 mRawRgba8888Image.cg = mRawYuv420Image.cg;
783 mRawRgba8888Image.ct = mRawYuv420Image.ct;
784 mRawRgba8888Image.range = UHDR_CR_FULL_RANGE;
785 mRawRgba8888Image.w = mRawYuv420Image.w;
786 mRawRgba8888Image.h = mRawYuv420Image.h;
787 mRawRgba8888Image.planes[UHDR_PLANE_PACKED] = malloc(mRawYuv420Image.w * mRawYuv420Image.h * 4);
788 mRawRgba8888Image.planes[UHDR_PLANE_U] = nullptr;
789 mRawRgba8888Image.planes[UHDR_PLANE_V] = nullptr;
790 mRawRgba8888Image.stride[UHDR_PLANE_PACKED] = mWidth;
791 mRawRgba8888Image.stride[UHDR_PLANE_U] = 0;
792 mRawRgba8888Image.stride[UHDR_PLANE_V] = 0;
793
794 uint32_t* rgbData = static_cast<uint32_t*>(mRawRgba8888Image.planes[UHDR_PLANE_PACKED]);
795 uint8_t* y = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_Y]);
796 uint8_t* u = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_U]);
797 uint8_t* v = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_V]);
798
799 const float* coeffs = BT601YUVtoRGBMatrix;
800 if (mSdrCg == UHDR_CG_BT_709) {
801 coeffs = BT709YUVtoRGBMatrix;
802 } else if (mSdrCg == UHDR_CG_BT_2100) {
803 coeffs = BT2020YUVtoRGBMatrix;
804 } else if (mSdrCg == UHDR_CG_DISPLAY_P3) {
805 coeffs = BT601YUVtoRGBMatrix;
806 } else {
807 std::cerr << "color matrix not present for gamut " << mSdrCg << " using BT601Matrix"
808 << std::endl;
809 }
810 for (size_t i = 0; i < mRawYuv420Image.h; i++) {
811 for (size_t j = 0; j < mRawYuv420Image.w; j++) {
812 float y0 = float(y[mRawYuv420Image.stride[UHDR_PLANE_Y] * i + j]);
813 float u0 = float(u[mRawYuv420Image.stride[UHDR_PLANE_U] * (i / 2) + (j / 2)] - 128);
814 float v0 = float(v[mRawYuv420Image.stride[UHDR_PLANE_V] * (i / 2) + (j / 2)] - 128);
815
816 y0 /= 255.0f;
817 u0 /= 255.0f;
818 v0 /= 255.0f;
819
820 float r = coeffs[0] * y0 + coeffs[1] * u0 + coeffs[2] * v0;
821 float g = coeffs[3] * y0 + coeffs[4] * u0 + coeffs[5] * v0;
822 float b = coeffs[6] * y0 + coeffs[7] * u0 + coeffs[8] * v0;
823
824 r = r * 255.0f + 0.5f;
825 g = g * 255.0f + 0.5f;
826 b = b * 255.0f + 0.5f;
827
828 r = CLIP3(r, 0.0f, 255.0f);
829 g = CLIP3(g, 0.0f, 255.0f);
830 b = CLIP3(b, 0.0f, 255.0f);
831
832 int32_t r0 = int32_t(r);
833 int32_t g0 = int32_t(g);
834 int32_t b0 = int32_t(b);
835 *rgbData = r0 | (g0 << 8) | (b0 << 16) | (255 << 24); // Set alpha to 1.0
836
837 rgbData++;
838 }
839 }
840 #ifdef DUMP_DEBUG_DATA
841 writeFile("inRgba8888.raw", &mRawRgba8888Image);
842 #endif
843 return true;
844 }
845
convertRgba8888ToYUV444Image()846 bool UltraHdrAppInput::convertRgba8888ToYUV444Image() {
847 mDecodedUhdrYuv444Image.fmt = static_cast<uhdr_img_fmt_t>(UHDR_IMG_FMT_24bppYCbCr444);
848 mDecodedUhdrYuv444Image.cg = mDecodedUhdrRgbImage.cg;
849 mDecodedUhdrYuv444Image.ct = mDecodedUhdrRgbImage.ct;
850 mDecodedUhdrYuv444Image.range = UHDR_CR_FULL_RANGE;
851 mDecodedUhdrYuv444Image.w = mDecodedUhdrRgbImage.w;
852 mDecodedUhdrYuv444Image.h = mDecodedUhdrRgbImage.h;
853 mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y] =
854 malloc(mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
855 mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U] =
856 malloc(mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
857 mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V] =
858 malloc(mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
859 mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] = mWidth;
860 mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] = mWidth;
861 mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] = mWidth;
862
863 uint32_t* rgbData = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
864
865 uint8_t* yData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
866 uint8_t* uData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
867 uint8_t* vData = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
868
869 const float* coeffs = BT601RGBtoYUVMatrix;
870 if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_709) {
871 coeffs = BT709RGBtoYUVMatrix;
872 } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_2100) {
873 coeffs = BT2020RGBtoYUVMatrix;
874 } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_DISPLAY_P3) {
875 coeffs = BT601RGBtoYUVMatrix;
876 } else {
877 std::cerr << "color matrix not present for gamut " << mDecodedUhdrRgbImage.cg
878 << " using BT601Matrix" << std::endl;
879 }
880
881 for (size_t i = 0; i < mDecodedUhdrRgbImage.h; i++) {
882 for (size_t j = 0; j < mDecodedUhdrRgbImage.w; j++) {
883 float r0 = float(rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] & 0xff);
884 float g0 =
885 float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 8) & 0xff);
886 float b0 =
887 float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 16) & 0xff);
888
889 r0 /= 255.0f;
890 g0 /= 255.0f;
891 b0 /= 255.0f;
892
893 float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
894 float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
895 float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
896
897 y = y * 255.0f + 0.5f;
898 u = u * 255.0f + 0.5f + 128.0f;
899 v = v * 255.0f + 0.5f + 128.0f;
900
901 y = CLIP3(y, 0.0f, 255.0f);
902 u = CLIP3(u, 0.0f, 255.0f);
903 v = CLIP3(v, 0.0f, 255.0f);
904
905 yData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j] = uint8_t(y);
906 uData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j] = uint8_t(u);
907 vData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j] = uint8_t(v);
908 }
909 }
910 #ifdef DUMP_DEBUG_DATA
911 writeFile("outyuv444.yuv", &mDecodedUhdrYuv444Image);
912 #endif
913 return true;
914 }
915
convertRgba1010102ToYUV444Image()916 bool UltraHdrAppInput::convertRgba1010102ToYUV444Image() {
917 const float* coeffs = BT2020RGBtoYUVMatrix;
918 if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_709) {
919 coeffs = BT709RGBtoYUVMatrix;
920 } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_BT_2100) {
921 coeffs = BT2020RGBtoYUVMatrix;
922 } else if (mDecodedUhdrRgbImage.cg == UHDR_CG_DISPLAY_P3) {
923 coeffs = BT601RGBtoYUVMatrix;
924 } else {
925 std::cerr << "color matrix not present for gamut " << mDecodedUhdrRgbImage.cg
926 << " using BT2020Matrix" << std::endl;
927 }
928
929 mDecodedUhdrYuv444Image.fmt = static_cast<uhdr_img_fmt_t>(UHDR_IMG_FMT_48bppYCbCr444);
930 mDecodedUhdrYuv444Image.cg = mDecodedUhdrRgbImage.cg;
931 mDecodedUhdrYuv444Image.ct = mDecodedUhdrRgbImage.ct;
932 mDecodedUhdrYuv444Image.range = UHDR_CR_LIMITED_RANGE;
933 mDecodedUhdrYuv444Image.w = mDecodedUhdrRgbImage.w;
934 mDecodedUhdrYuv444Image.h = mDecodedUhdrRgbImage.h;
935 mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y] =
936 malloc(mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h * 2);
937 mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U] =
938 malloc(mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h * 2);
939 mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V] =
940 malloc(mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h * 2);
941 mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] = mWidth;
942 mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] = mWidth;
943 mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] = mWidth;
944
945 uint32_t* rgbData = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
946
947 uint16_t* yData = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
948 uint16_t* uData = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
949 uint16_t* vData = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
950
951 for (size_t i = 0; i < mDecodedUhdrRgbImage.h; i++) {
952 for (size_t j = 0; j < mDecodedUhdrRgbImage.w; j++) {
953 float r0 = float(rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] & 0x3ff);
954 float g0 =
955 float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 10) & 0x3ff);
956 float b0 =
957 float((rgbData[mDecodedUhdrRgbImage.stride[UHDR_PLANE_PACKED] * i + j] >> 20) & 0x3ff);
958
959 r0 /= 1023.0f;
960 g0 /= 1023.0f;
961 b0 /= 1023.0f;
962
963 float y = coeffs[0] * r0 + coeffs[1] * g0 + coeffs[2] * b0;
964 float u = coeffs[3] * r0 + coeffs[4] * g0 + coeffs[5] * b0;
965 float v = coeffs[6] * r0 + coeffs[7] * g0 + coeffs[8] * b0;
966
967 y = (y * 876.0f) + 64.0f + 0.5f;
968 u = (u * 896.0f) + 512.0f + 0.5f;
969 v = (v * 896.0f) + 512.0f + 0.5f;
970
971 y = CLIP3(y, 64.0f, 940.0f);
972 u = CLIP3(u, 64.0f, 960.0f);
973 v = CLIP3(v, 64.0f, 960.0f);
974
975 yData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j] = uint16_t(y);
976 uData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j] = uint16_t(u);
977 vData[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j] = uint16_t(v);
978 }
979 }
980 #ifdef DUMP_DEBUG_DATA
981 writeFile("outyuv444.yuv", &mDecodedUhdrYuv444Image);
982 #endif
983 return true;
984 }
985
computeRGBHdrPSNR()986 void UltraHdrAppInput::computeRGBHdrPSNR() {
987 if (mOfmt != UHDR_IMG_FMT_32bppRGBA1010102) {
988 std::cout << "psnr not supported for output format " << mOfmt << std::endl;
989 return;
990 }
991 uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba1010102Image.planes[UHDR_PLANE_PACKED]);
992 uint32_t* rgbDataDst = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
993 if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
994 std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
995 return;
996 }
997 if (mOTf != mHdrTf) {
998 std::cout << "input transfer function and output format are not compatible, psnr results "
999 "may be unreliable"
1000 << std::endl;
1001 }
1002 uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
1003 for (size_t i = 0; i < mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h; i++) {
1004 int rSrc = *rgbDataSrc & 0x3ff;
1005 int rDst = *rgbDataDst & 0x3ff;
1006 rSqError += (rSrc - rDst) * (rSrc - rDst);
1007
1008 int gSrc = (*rgbDataSrc >> 10) & 0x3ff;
1009 int gDst = (*rgbDataDst >> 10) & 0x3ff;
1010 gSqError += (gSrc - gDst) * (gSrc - gDst);
1011
1012 int bSrc = (*rgbDataSrc >> 20) & 0x3ff;
1013 int bDst = (*rgbDataDst >> 20) & 0x3ff;
1014 bSqError += (bSrc - bDst) * (bSrc - bDst);
1015
1016 rgbDataSrc++;
1017 rgbDataDst++;
1018 }
1019 double meanSquareError = (double)rSqError / (mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1020 mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1021
1022 meanSquareError = (double)gSqError / (mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1023 mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1024
1025 meanSquareError = (double)bSqError / (mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1026 mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1027
1028 std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
1029 << std::endl;
1030 }
1031
computeRGBSdrPSNR()1032 void UltraHdrAppInput::computeRGBSdrPSNR() {
1033 if (mOfmt != UHDR_IMG_FMT_32bppRGBA8888) {
1034 std::cout << "psnr not supported for output format " << mOfmt << std::endl;
1035 return;
1036 }
1037 uint32_t* rgbDataSrc = static_cast<uint32_t*>(mRawRgba8888Image.planes[UHDR_PLANE_PACKED]);
1038 uint32_t* rgbDataDst = static_cast<uint32_t*>(mDecodedUhdrRgbImage.planes[UHDR_PLANE_PACKED]);
1039 if (rgbDataSrc == nullptr || rgbDataDst == nullptr) {
1040 std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
1041 return;
1042 }
1043
1044 uint64_t rSqError = 0, gSqError = 0, bSqError = 0;
1045 for (size_t i = 0; i < mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h; i++) {
1046 int rSrc = *rgbDataSrc & 0xff;
1047 int rDst = *rgbDataDst & 0xff;
1048 rSqError += (rSrc - rDst) * (rSrc - rDst);
1049
1050 int gSrc = (*rgbDataSrc >> 8) & 0xff;
1051 int gDst = (*rgbDataDst >> 8) & 0xff;
1052 gSqError += (gSrc - gDst) * (gSrc - gDst);
1053
1054 int bSrc = (*rgbDataSrc >> 16) & 0xff;
1055 int bDst = (*rgbDataDst >> 16) & 0xff;
1056 bSqError += (bSrc - bDst) * (bSrc - bDst);
1057
1058 rgbDataSrc++;
1059 rgbDataDst++;
1060 }
1061 double meanSquareError = (double)rSqError / (mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1062 mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1063
1064 meanSquareError = (double)gSqError / (mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1065 mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1066
1067 meanSquareError = (double)bSqError / (mDecodedUhdrRgbImage.w * mDecodedUhdrRgbImage.h);
1068 mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1069
1070 std::cout << "psnr r :: " << mPsnr[0] << " psnr g :: " << mPsnr[1] << " psnr b :: " << mPsnr[2]
1071 << std::endl;
1072 }
1073
computeYUVHdrPSNR()1074 void UltraHdrAppInput::computeYUVHdrPSNR() {
1075 if (mOfmt != UHDR_IMG_FMT_32bppRGBA1010102) {
1076 std::cout << "psnr not supported for output format " << mOfmt << std::endl;
1077 return;
1078 }
1079 uint16_t* yDataSrc = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_Y]);
1080 uint16_t* uDataSrc = static_cast<uint16_t*>(mRawP010Image.planes[UHDR_PLANE_UV]);
1081 uint16_t* vDataSrc = uDataSrc + 1;
1082
1083 uint16_t* yDataDst = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
1084 uint16_t* uDataDst = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
1085 uint16_t* vDataDst = static_cast<uint16_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
1086 if (yDataSrc == nullptr || uDataSrc == nullptr || yDataDst == nullptr || uDataDst == nullptr ||
1087 vDataDst == nullptr) {
1088 std::cerr << "invalid src or dst pointer for psnr computation " << std::endl;
1089 return;
1090 }
1091 if (mOTf != mHdrTf) {
1092 std::cout << "input transfer function and output format are not compatible, psnr results "
1093 "may be unreliable"
1094 << std::endl;
1095 }
1096
1097 uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
1098 for (size_t i = 0; i < mDecodedUhdrYuv444Image.h; i++) {
1099 for (size_t j = 0; j < mDecodedUhdrYuv444Image.w; j++) {
1100 int ySrc = (yDataSrc[mRawP010Image.stride[UHDR_PLANE_Y] * i + j] >> 6) & 0x3ff;
1101 ySrc = CLIP3(ySrc, 64, 940);
1102 int yDst = yDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j] & 0x3ff;
1103 ySqError += (ySrc - yDst) * (ySrc - yDst);
1104
1105 if (i % 2 == 0 && j % 2 == 0) {
1106 int uSrc =
1107 (uDataSrc[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
1108 uSrc = CLIP3(uSrc, 64, 960);
1109 int uDst = uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j] & 0x3ff;
1110 uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j + 1] & 0x3ff;
1111 uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j + 1] & 0x3ff;
1112 uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j + 1] & 0x3ff;
1113 uDst = (uDst + 2) >> 2;
1114 uSqError += (uSrc - uDst) * (uSrc - uDst);
1115
1116 int vSrc =
1117 (vDataSrc[mRawP010Image.stride[UHDR_PLANE_UV] * (i / 2) + (j / 2) * 2] >> 6) & 0x3ff;
1118 vSrc = CLIP3(vSrc, 64, 960);
1119 int vDst = vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j] & 0x3ff;
1120 vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j + 1] & 0x3ff;
1121 vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j + 1] & 0x3ff;
1122 vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j + 1] & 0x3ff;
1123 vDst = (vDst + 2) >> 2;
1124 vSqError += (vSrc - vDst) * (vSrc - vDst);
1125 }
1126 }
1127 }
1128
1129 double meanSquareError =
1130 (double)ySqError / (mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h);
1131 mPsnr[0] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1132
1133 meanSquareError = (double)uSqError / (mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1134 mPsnr[1] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1135
1136 meanSquareError = (double)vSqError / (mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1137 mPsnr[2] = meanSquareError ? 10 * log10((double)1023 * 1023 / meanSquareError) : 100;
1138
1139 std::cout << "psnr y :: " << mPsnr[0] << " psnr u :: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
1140 << std::endl;
1141 }
1142
computeYUVSdrPSNR()1143 void UltraHdrAppInput::computeYUVSdrPSNR() {
1144 if (mOfmt != UHDR_IMG_FMT_32bppRGBA8888) {
1145 std::cout << "psnr not supported for output format " << mOfmt << std::endl;
1146 return;
1147 }
1148
1149 uint8_t* yDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_Y]);
1150 uint8_t* uDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_U]);
1151 uint8_t* vDataSrc = static_cast<uint8_t*>(mRawYuv420Image.planes[UHDR_PLANE_V]);
1152
1153 uint8_t* yDataDst = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_Y]);
1154 uint8_t* uDataDst = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_U]);
1155 uint8_t* vDataDst = static_cast<uint8_t*>(mDecodedUhdrYuv444Image.planes[UHDR_PLANE_V]);
1156
1157 uint64_t ySqError = 0, uSqError = 0, vSqError = 0;
1158 for (size_t i = 0; i < mDecodedUhdrYuv444Image.h; i++) {
1159 for (size_t j = 0; j < mDecodedUhdrYuv444Image.w; j++) {
1160 int ySrc = yDataSrc[mRawYuv420Image.stride[UHDR_PLANE_Y] * i + j];
1161 int yDst = yDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_Y] * i + j];
1162 ySqError += (ySrc - yDst) * (ySrc - yDst);
1163
1164 if (i % 2 == 0 && j % 2 == 0) {
1165 int uSrc = uDataSrc[mRawYuv420Image.stride[UHDR_PLANE_U] * (i / 2) + j / 2];
1166 int uDst = uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j];
1167 uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * i + j + 1];
1168 uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j];
1169 uDst += uDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_U] * (i + 1) + j + 1];
1170 uDst = (uDst + 2) >> 2;
1171 uSqError += (uSrc - uDst) * (uSrc - uDst);
1172
1173 int vSrc = vDataSrc[mRawYuv420Image.stride[UHDR_PLANE_V] * (i / 2) + j / 2];
1174 int vDst = vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j];
1175 vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * i + j + 1];
1176 vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j];
1177 vDst += vDataDst[mDecodedUhdrYuv444Image.stride[UHDR_PLANE_V] * (i + 1) + j + 1];
1178 vDst = (vDst + 2) >> 2;
1179 vSqError += (vSrc - vDst) * (vSrc - vDst);
1180 }
1181 }
1182 }
1183 double meanSquareError =
1184 (double)ySqError / (mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h);
1185 mPsnr[0] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1186
1187 meanSquareError = (double)uSqError / (mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1188 mPsnr[1] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1189
1190 meanSquareError = (double)vSqError / (mDecodedUhdrYuv444Image.w * mDecodedUhdrYuv444Image.h / 4);
1191 mPsnr[2] = meanSquareError ? 10 * log10((double)255 * 255 / meanSquareError) : 100;
1192
1193 std::cout << "psnr y :: " << mPsnr[0] << " psnr u :: " << mPsnr[1] << " psnr v :: " << mPsnr[2]
1194 << std::endl;
1195 }
1196
usage(const char * name)1197 static void usage(const char* name) {
1198 fprintf(stderr, "\n## ultra hdr demo application.\nUsage : %s \n", name);
1199 fprintf(stderr, " -m mode of operation. [0:encode, 1:decode] \n");
1200 fprintf(stderr, "\n## encoder options : \n");
1201 fprintf(stderr,
1202 " -p raw 10 bit input resource, required for encoding scenarios 0, 1, 2, 3. \n");
1203 fprintf(stderr, " -y raw 8 bit input resource, required for encoding scenarios 1, 2. \n");
1204 fprintf(stderr,
1205 " -a raw 10 bit input resource color format, optional. [0:p010, 5:rgba1010102 "
1206 "(default)] \n");
1207 fprintf(stderr,
1208 " -b raw 8 bit input resource color format, optional. [1:yuv420, 3:rgba8888 "
1209 "(default)] \n");
1210 fprintf(stderr,
1211 " -i compressed 8 bit jpeg file path, required for encoding scenarios 2, 3, 4. \n");
1212 fprintf(stderr,
1213 " -g compressed 8 bit gainmap file path, required for encoding scenario 4. \n");
1214 fprintf(stderr, " -f gainmap metadata config file, required for encoding scenario 4. \n");
1215 fprintf(stderr, " -w input file width. \n");
1216 fprintf(stderr, " -h input file height. \n");
1217 fprintf(stderr,
1218 " -C 10 bit input color gamut, optional. [0:bt709, 1:p3 (default), 2:bt2100] \n");
1219 fprintf(stderr,
1220 " -c 8 bit input color gamut, optional. [0:bt709 (default), 1:p3, 2:bt2100] \n");
1221 fprintf(
1222 stderr,
1223 " -t 10 bit input transfer function, optional. [0:linear, 1:hlg (default), 2:pq] \n");
1224 fprintf(stderr,
1225 " -q quality factor to be used while encoding 8 bit image, optional. [0-100], 95 : "
1226 "default.\n"
1227 " gain map image does not use this quality factor. \n"
1228 " for now gain map image quality factor is not configurable. \n");
1229 fprintf(stderr, " -e compute psnr, optional. [0:no (default), 1:yes] \n");
1230 fprintf(stderr, "\n## decoder options : \n");
1231 fprintf(stderr, " -j ultra hdr compressed input resource. \n");
1232 fprintf(
1233 stderr,
1234 " -o output transfer function, optional. [0:linear, 1:hlg (default), 2:pq, 3:srgb] \n");
1235 fprintf(
1236 stderr,
1237 " -O output color format, optional. [3:rgba8888, 4:rgbahalffloat, 5:rgba1010102 "
1238 "(default)] \n"
1239 " It should be noted that not all combinations of output color format and output \n"
1240 " transfer function are supported. \n"
1241 " srgb output color transfer shall be paired with rgba8888 only. \n"
1242 " hlg, pq shall be paired with rgba1010102. \n"
1243 " linear shall be paired with rgbahalffloat. \n");
1244 fprintf(stderr, "\n## common options : \n");
1245 fprintf(stderr,
1246 " -z output filename, optional. \n"
1247 " in encoding mode, default output filename 'out.jpeg'. \n"
1248 " in decoding mode, default output filename 'outrgb.raw'. \n");
1249 fprintf(stderr, "\n## examples of usage :\n");
1250 fprintf(stderr, "\n## encode scenario 0 :\n");
1251 fprintf(stderr,
1252 " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -a 0\n");
1253 fprintf(stderr,
1254 " ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw -w 1920 -h 1080 -q 97 -a 5\n");
1255 fprintf(
1256 stderr,
1257 " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -w 1920 -h 1080 -q 97 -C 1 -t 2 -a 0\n");
1258 fprintf(stderr,
1259 " ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw -w 1920 -h 1080 -q 97 -C 1 "
1260 "-t 2 -a 5\n");
1261 fprintf(stderr, "\n## encode scenario 1 :\n");
1262 fprintf(stderr,
1263 " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
1264 "-h 1080 -q 97 -a 0 -b 1\n");
1265 fprintf(stderr,
1266 " ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw "
1267 "-y cosmat_1920x1080_rgba8888.raw -w 1920 -h 1080 -q 97 -a 5 -b 3\n");
1268 fprintf(stderr,
1269 " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
1270 "-h 1080 -q 97 -C 2 -c 1 -t 1 -a 0 -b 1\n");
1271 fprintf(stderr,
1272 " ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw "
1273 "-y cosmat_1920x1080_rgba8888.raw -w 1920 -h 1080 -q 97 -C 2 -c 1 -t 1 -a 5 -b 3\n");
1274 fprintf(stderr,
1275 " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -w 1920 "
1276 "-h 1080 -q 97 -C 2 -c 1 -t 1 -e 1 -a 0 -b 1\n");
1277 fprintf(stderr, "\n## encode scenario 2 :\n");
1278 fprintf(stderr,
1279 " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -y cosmat_1920x1080_420.yuv -i "
1280 "cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -O 3 -e 1 -a 0 -b 1\n");
1281 fprintf(stderr,
1282 " ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw -y cosmat_1920x1080_420.yuv "
1283 "-i cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 3 -O 3 -e 1 -a 5 -b 1\n");
1284 fprintf(stderr, "\n## encode scenario 3 :\n");
1285 fprintf(stderr,
1286 " ultrahdr_app -m 0 -p cosmat_1920x1080_p010.yuv -i cosmat_1920x1080_420_8bit.jpg -w "
1287 "1920 -h 1080 -t 1 -o 1 -O 5 -e 1 -a 0\n");
1288 fprintf(stderr,
1289 " ultrahdr_app -m 0 -p cosmat_1920x1080_rgba1010102.raw "
1290 "-i cosmat_1920x1080_420_8bit.jpg -w 1920 -h 1080 -t 1 -o 1 -O 5 -e 1 -a 5\n");
1291 fprintf(stderr, "\n## encode scenario 4 :\n");
1292 fprintf(stderr,
1293 " ultrahdr_app -m 0 -i cosmat_1920x1080_420_8bit.jpg -g cosmat_1920x1080_420_8bit.jpg "
1294 "-f metadata.cfg\n");
1295 fprintf(stderr, "\n## decode api :\n");
1296 fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg \n");
1297 fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 3 -O 3\n");
1298 fprintf(stderr, " ultrahdr_app -m 1 -j cosmat_1920x1080_hdr.jpg -o 1 -O 5\n");
1299 fprintf(stderr, "\n");
1300 }
1301
main(int argc,char * argv[])1302 int main(int argc, char* argv[]) {
1303 char opt_string[] = "p:y:i:g:f:w:h:C:c:t:q:o:O:m:j:e:a:b:z:";
1304 char *hdr_intent_raw_file = nullptr, *sdr_intent_raw_file = nullptr, *uhdr_file = nullptr,
1305 *sdr_intent_compressed_file = nullptr, *gainmap_compressed_file = nullptr,
1306 *gainmap_metadata_cfg_file = nullptr, *output_file = nullptr;
1307 int width = 0, height = 0;
1308 uhdr_color_gamut_t hdr_cg = UHDR_CG_DISPLAY_P3;
1309 uhdr_color_gamut_t sdr_cg = UHDR_CG_BT_709;
1310 uhdr_img_fmt_t hdr_cf = UHDR_IMG_FMT_32bppRGBA1010102;
1311 uhdr_img_fmt_t sdr_cf = UHDR_IMG_FMT_32bppRGBA8888;
1312 uhdr_color_transfer_t hdr_tf = UHDR_CT_HLG;
1313 int quality = 95;
1314 uhdr_color_transfer_t out_tf = UHDR_CT_HLG;
1315 uhdr_img_fmt_t out_cf = UHDR_IMG_FMT_32bppRGBA1010102;
1316 int mode = -1;
1317 int compute_psnr = 0;
1318 int ch;
1319 while ((ch = getopt_s(argc, argv, opt_string)) != -1) {
1320 switch (ch) {
1321 case 'a':
1322 hdr_cf = static_cast<uhdr_img_fmt_t>(atoi(optarg_s));
1323 break;
1324 case 'b':
1325 sdr_cf = static_cast<uhdr_img_fmt_t>(atoi(optarg_s));
1326 break;
1327 case 'p':
1328 hdr_intent_raw_file = optarg_s;
1329 break;
1330 case 'y':
1331 sdr_intent_raw_file = optarg_s;
1332 break;
1333 case 'i':
1334 sdr_intent_compressed_file = optarg_s;
1335 break;
1336 case 'g':
1337 gainmap_compressed_file = optarg_s;
1338 break;
1339 case 'f':
1340 gainmap_metadata_cfg_file = optarg_s;
1341 break;
1342 case 'w':
1343 width = atoi(optarg_s);
1344 break;
1345 case 'h':
1346 height = atoi(optarg_s);
1347 break;
1348 case 'C':
1349 hdr_cg = static_cast<uhdr_color_gamut_t>(atoi(optarg_s));
1350 break;
1351 case 'c':
1352 sdr_cg = static_cast<uhdr_color_gamut_t>(atoi(optarg_s));
1353 break;
1354 case 't':
1355 hdr_tf = static_cast<uhdr_color_transfer_t>(atoi(optarg_s));
1356 break;
1357 case 'q':
1358 quality = atoi(optarg_s);
1359 break;
1360 case 'O':
1361 out_cf = static_cast<uhdr_img_fmt_t>(atoi(optarg_s));
1362 break;
1363 case 'o':
1364 out_tf = static_cast<uhdr_color_transfer_t>(atoi(optarg_s));
1365 break;
1366 case 'm':
1367 mode = atoi(optarg_s);
1368 break;
1369 case 'j':
1370 uhdr_file = optarg_s;
1371 break;
1372 case 'e':
1373 compute_psnr = atoi(optarg_s);
1374 break;
1375 case 'z':
1376 output_file = optarg_s;
1377 break;
1378 default:
1379 usage(argv[0]);
1380 return -1;
1381 }
1382 }
1383 if (mode == 0) {
1384 if (width <= 0) {
1385 std::cerr << "did not receive valid image width for encoding. width : " << width
1386 << std::endl;
1387 return -1;
1388 }
1389 if (height <= 0) {
1390 std::cerr << "did not receive valid image height for encoding. height : " << height
1391 << std::endl;
1392 return -1;
1393 }
1394 if (hdr_intent_raw_file == nullptr &&
1395 (sdr_intent_compressed_file == nullptr || gainmap_compressed_file == nullptr ||
1396 gainmap_metadata_cfg_file == nullptr)) {
1397 std::cerr << "did not receive raw resources for encoding." << std::endl;
1398 return -1;
1399 }
1400 UltraHdrAppInput appInput(hdr_intent_raw_file, sdr_intent_raw_file, sdr_intent_compressed_file,
1401 gainmap_compressed_file, gainmap_metadata_cfg_file,
1402 output_file ? output_file : "out.jpeg", width, height, hdr_cf, sdr_cf,
1403 hdr_cg, sdr_cg, hdr_tf, quality, out_tf, out_cf);
1404 if (!appInput.encode()) return -1;
1405 if (compute_psnr == 1) {
1406 if (!appInput.decode()) return -1;
1407 if (out_cf == UHDR_IMG_FMT_32bppRGBA8888 && sdr_intent_raw_file != nullptr) {
1408 if (sdr_cf == UHDR_IMG_FMT_12bppYCbCr420) {
1409 appInput.convertYuv420ToRGBImage();
1410 }
1411 appInput.computeRGBSdrPSNR();
1412 if (sdr_cf == UHDR_IMG_FMT_12bppYCbCr420) {
1413 appInput.convertRgba8888ToYUV444Image();
1414 appInput.computeYUVSdrPSNR();
1415 }
1416 } else if (out_cf == UHDR_IMG_FMT_32bppRGBA1010102 && hdr_intent_raw_file != nullptr) {
1417 if (hdr_cf == UHDR_IMG_FMT_24bppYCbCrP010) {
1418 appInput.convertP010ToRGBImage();
1419 }
1420 appInput.computeRGBHdrPSNR();
1421 if (hdr_cf == UHDR_IMG_FMT_24bppYCbCrP010) {
1422 appInput.convertRgba1010102ToYUV444Image();
1423 appInput.computeYUVHdrPSNR();
1424 }
1425 } else {
1426 std::cerr << "failed to compute psnr " << std::endl;
1427 }
1428 }
1429 } else if (mode == 1) {
1430 if (uhdr_file == nullptr) {
1431 std::cerr << "did not receive resources for decoding " << std::endl;
1432 return -1;
1433 }
1434 UltraHdrAppInput appInput(uhdr_file, output_file ? output_file : "outrgb.raw", out_tf, out_cf);
1435 if (!appInput.decode()) return -1;
1436 } else {
1437 std::cerr << "unrecognized input mode " << mode << std::endl;
1438 usage(argv[0]);
1439 return -1;
1440 }
1441
1442 return 0;
1443 }
1444