71#include "gtest/gtest.h"
76#define REF_FILE_DIR ".\\jp2k_test_codestreams\\openjph\\references\\"
77#define OUT_FILE_DIR ".\\"
79#define REF_FILE_DIR "./jp2k_test_codestreams/openjph/references/"
80#define OUT_FILE_DIR "./"
85#define NLT_MARKER_HIGH 0xFF
86#define NLT_MARKER_LOW 0x76
97 const ui32 pfmLutNumPoints = 152;
98 const ui32 pfmLutDmin = 67109888;
99 const ui32 pfmLutDmax = 4227857408;
100 const ui32 pfmLutPoints[pfmLutNumPoints] = {
101 67109888, 88737098, 106890847, 122160968, 135661590, 157092189,
102 175049327, 190122837, 204213292, 225447280, 243207807, 258084706,
103 272764994, 293736834, 311300750, 326046575, 341316696, 362026388,
104 379393693, 394008444, 409802861, 430315942, 447486636, 461904776,
105 478289026, 498605496, 515579579, 529866645, 546775191, 566829513,
106 583672522, 597762977, 615261356, 635053530, 651699928, 665659309,
107 683681984, 703343084, 719792871, 733555641, 752102612, 771501564,
108 787820277, 801451973, 820523240, 839725581, 855847683, 869348305,
109 888943868, 907949598, 923875089, 937179100, 957364496, 976108078,
110 991836958, 1005075432, 1025719587, 1044266558, 1059864364, 1072906227,
111 1094074678, 1112425038, 1127891770, 1140802559, 1163281750, 1183532683,
112 1201620895, 1227180325, 1254836939, 1281838183, 1321029309, 1362907452,
113 1412912183, 1485985938, 1596677931, 1830579484, 2464387812, 2698289365,
114 2808981358, 2882055113, 2932059844, 2973937987, 3013129113, 3040130357,
115 3067786971, 3093346401, 3111434613, 3131685546, 3154164737, 3167075526,
116 3182542258, 3200892618, 3222061069, 3235102932, 3250700738, 3269247709,
117 3289891864, 3303130338, 3318859218, 3337602800, 3357788196, 3371092207,
118 3387017698, 3406023428, 3425618991, 3439119613, 3455241715, 3474444056,
119 3493515323, 3507147019, 3523465732, 3542864684, 3561411655, 3575174425,
120 3591624212, 3611285312, 3629307987, 3643267368, 3659913766, 3679705940,
121 3697204319, 3711294774, 3728137783, 3748192105, 3765100651, 3779387717,
122 3796361800, 3816678270, 3833062520, 3847480660, 3864651354, 3885164435,
123 3900958852, 3915573603, 3932940908, 3953650600, 3968920721, 3983666546,
124 4001230462, 4022202302, 4036882590, 4051759489, 4069520016, 4090754004,
125 4104844459, 4119917969, 4137875107, 4159305706, 4172806328, 4188076449,
126 4206230198, 4227857408};
134 const ui32 identityLutNumPoints = 129;
135 ui32 identityLutPoints[identityLutNumPoints];
136 bool identityLutReady =
false;
138 void prepare_identity_lut()
140 if (identityLutReady)
142 for (
ui32 i = 0; i < identityLutNumPoints; ++i)
143 identityLutPoints[i] =
144 (
ui32)(((double)i / (identityLutNumPoints - 1)) * 4294967295.0);
145 identityLutReady =
true;
156 const ui32 narrowLutNumPoints = 129;
157 ui32 narrowLutPoints[narrowLutNumPoints];
158 bool narrowLutReady =
false;
160 void prepare_narrow_lut()
164 for (
ui32 i = 0; i < narrowLutNumPoints; ++i)
165 narrowLutPoints[i] = (
ui32)(1073741824.0 +
166 ((double)i / (narrowLutNumPoints - 1)) * 2147483648.0);
167 narrowLutReady =
true;
179 const ui32 bentLutNumPoints = 3;
180 const ui8 bentLutPoints8[bentLutNumPoints] =
181 { 0x40u, 0x50u, 0xC0u };
182 const ui16 bentLutPoints16[bentLutNumPoints] =
183 { 0x4000u, 0x5000u, 0xC000u };
184 const ui32 bentLutPoints[bentLutNumPoints] =
185 { 0x40000000u, 0x50000000u, 0xC0000000u };
205 const ui32 clusteredLutNumPoints = 1024;
206 ui32 clusteredLutPoints[clusteredLutNumPoints];
207 bool clusteredLutReady =
false;
209 void prepare_clustered_lut()
211 if (clusteredLutReady)
213 const ui32 first = 0x10000000u, last = 0xF0000000u;
214 const ui32 start = 128, num_small = 512;
215 const ui32 small_gap = 32768;
216 const ui32 large_gap = 8 * small_gap;
218 for (
ui32 i = 0; i < num_small; ++i)
219 run += (i & 1) ? large_gap : small_gap;
220 ui32 big_gap = ((last - first) - run) /
221 (clusteredLutNumPoints - 1 - num_small);
222 clusteredLutPoints[0] = first;
223 for (
ui32 i = 1; i < clusteredLutNumPoints; ++i)
224 clusteredLutPoints[i] = clusteredLutPoints[i - 1] +
225 ((i >= start && i < start + num_small)
226 ? (((i - start) & 1) ? large_gap : small_gap) : big_gap);
227 clusteredLutReady =
true;
237 ui32 width = 0, height = 0, num_comps = 0;
238 std::vector<std::vector<si32> > samples;
240 size_t num_samples()
const
241 {
return (
size_t)width * height * num_comps; }
249 long long max_abs_error = 0;
250 double mean_abs_error = 0.0;
251 double max_rel_error = 0.0;
252 double mean_rel_error = 0.0;
253 size_t num_bit_exact = 0;
254 size_t num_samples = 0;
256 bool is_bit_exact()
const {
return num_bit_exact == num_samples; }
264 bool load_pfm(
const std::string& filename, test_image& img)
266 FILE* f = fopen(filename.c_str(),
"rb");
270 char magic[3] = { 0, 0, 0 };
271 if (fscanf(f,
"%2s", magic) != 1 ||
272 (magic[1] !=
'f' && magic[1] !=
'F'))
280 if (fscanf(f,
"%d %d %f", &w, &h, &scale) != 3 || w <= 0 || h <= 0)
292 const bool file_little_endian = scale < 0.0f;
296 img.height = (
ui32)h;
297 img.num_comps = magic[1] ==
'f' ? 1 : 3;
298 img.samples.assign(img.num_comps,
299 std::vector<si32>((
size_t)w * (
size_t)h, 0));
301 std::vector<ui32> line((
size_t)w * img.num_comps, 0);
302 for (
int y = h - 1; y >= 0; --y)
304 if (fread(&line[0],
sizeof(
ui32), (
size_t)w * img.num_comps, f)
305 != (
size_t)w * img.num_comps)
310 for (
int x = 0; x < w; ++x)
311 for (
ui32 c = 0; c < img.num_comps; ++c)
313 ui32 u = line[(size_t)x * img.num_comps + c];
315 u = (u >> 24) | ((u >> 8) & 0x0000FF00u) |
316 ((u << 8) & 0x00FF0000u) | (u << 24);
317 img.samples[c][(size_t)y * (
size_t)w + (size_t)x] = (
si32)u;
332 std::string find_test_image(
const std::string& name)
335 for (
size_t i = 0; i <
sizeof(dirs) /
sizeof(dirs[0]); ++i)
337 std::string path = std::string(dirs[i]) + name;
338 FILE* f = fopen(path.c_str(),
"rb");
345 return std::string();
353 const char*
const image_names[2] = {
"random.pfm",
"rgb.pfm" };
354 const size_t num_images =
sizeof(image_names) /
sizeof(image_names[0]);
359 std::string load_test_images(test_image images[num_images])
362 for (
size_t i = 0; i < num_images; ++i)
364 std::string path = find_test_image(image_names[i]);
365 if (path.empty() || !load_pfm(path, images[i]))
367 if (!missing.empty())
369 missing += image_names[i];
392 void make_synthetic_image(test_image& img,
ui32 width,
ui32 height)
397 img.samples.assign(1, std::vector<si32>((
size_t)width * height, 0));
401 const si64 lowest = -2070000000LL;
402 const si64 highest = 2070000000LL;
403 const si64 gap = 1LL << 20;
404 const si64 half = (
si64)((
size_t)width * height / 2);
405 std::vector<si32>& s = img.samples[0];
406 for (
size_t i = 0; i < s.size(); ++i)
408 if (i < (
size_t)half)
409 s[i] = (
si32)(gap + (highest - gap) * (
si64)i / (half - 1));
412 (lowest + gap) * (
si64)(i - (
size_t)half) / (half - 1));
424 bool use_narrow_lut =
false;
425 bool use_bent_lut =
false;
426 bool use_clustered_lut =
false;
427 bool use_approx_inverse =
false;
438 float default_qstep() {
return 1.0f / 16384.0f; }
443 void apply_nlt(
codestream& cs,
const nlt_setting& setting)
454 ui32 d_min, d_max, num_points;
456 if (setting.use_bent_lut)
460 num_points = bentLutNumPoints;
461 if (setting.pt_val <= 8)
462 points = (
void*)bentLutPoints8;
463 else if (setting.pt_val <= 16)
464 points = (
void*)bentLutPoints16;
466 points = (
void*)bentLutPoints;
468 else if (setting.use_clustered_lut)
471 prepare_clustered_lut();
474 num_points = clusteredLutNumPoints;
475 points = (
void*)clusteredLutPoints;
477 else if (setting.use_narrow_lut)
480 prepare_narrow_lut();
483 num_points = narrowLutNumPoints;
484 points = (
void*)narrowLutPoints;
486 else if (setting.use_pfm_lut)
490 num_points = pfmLutNumPoints;
491 points = (
void*)pfmLutPoints;
495 prepare_identity_lut();
498 num_points = identityLutNumPoints;
499 points = (
void*)identityLutPoints;
502 d_min, d_max, setting.pt_val, (
ui16)num_points, points, setting.type,
503 setting.use_approx_inverse, 0);
510 void encode_image(
const std::string& filename,
const test_image& img,
511 const nlt_setting& setting)
518 for (
ui32 c = 0; c < img.num_comps; ++c)
529 if (!setting.reversible)
532 apply_nlt(cs, setting);
537 file.
open(filename.c_str());
542 ASSERT_TRUE(cur_line != NULL) << filename;
543 for (
ui32 c = 0; c < img.num_comps; ++c)
544 for (
ui32 y = 0; y < img.height; ++y)
546 ASSERT_EQ(next_comp, c) << filename;
547 for (
ui32 x = 0; x < img.width; ++x)
548 cur_line->i32[x] = img.samples[c][(
size_t)y * img.width + x];
549 cur_line = cs.
exchange(cur_line, next_comp);
559 void decode_image(
const std::string& filename, test_image& img,
560 bool* has_nlt,
ui8* nlt_type,
ui8* nlt_bit_depth,
565 file.
open(filename.c_str());
568 if (has_nlt != NULL || nlt_type != NULL || nlt_bit_depth != NULL ||
569 nlt_is_signed != NULL)
571 ui8 bit_depth = 0, type = 0;
572 bool is_signed =
false;
575 if (has_nlt != NULL) *has_nlt = found;
576 if (nlt_type != NULL) *nlt_type = type;
577 if (nlt_bit_depth != NULL) *nlt_bit_depth = bit_depth;
578 if (nlt_is_signed != NULL) *nlt_is_signed = is_signed;
585 img.samples.assign(img.num_comps,
586 std::vector<si32>((
size_t)img.width * img.height, 0));
592 for (
ui32 c = 0; c < img.num_comps; ++c)
593 for (
ui32 y = 0; y < img.height; ++y)
597 ASSERT_TRUE(line != NULL) << filename;
598 ASSERT_EQ(comp_num, c) << filename;
599 for (
ui32 x = 0; x < img.width; ++x)
600 img.samples[c][(
size_t)y * img.width + x] = line->i32[x];
611 comparison compare_images(
const test_image& before,
const test_image& after)
616 for (
ui32 c = 0; c < before.num_comps; ++c)
617 for (
size_t i = 0; i < before.samples[c].size(); ++i)
620 memcpy(&v, &before.samples[c][i],
sizeof(
float));
621 peak = std::max(peak, (
double)fabs(v));
625 const double floor = std::max(peak * 1e-5, 1e-30);
627 double sum_abs = 0.0, sum_rel = 0.0;
628 for (
ui32 c = 0; c < before.num_comps; ++c)
629 for (
size_t i = 0; i < before.samples[c].size(); ++i)
631 si32 a = before.samples[c][i], b = after.samples[c][i];
635 result.max_abs_error = std::max(result.max_abs_error, (
long long)err);
636 sum_abs += (double)err;
639 memcpy(&av, &a,
sizeof(
float));
640 memcpy(&bv, &b,
sizeof(
float));
641 double rel = fabs((
double)av - (
double)bv) /
642 std::max((
double)fabs(av), floor);
643 result.max_rel_error = std::max(result.max_rel_error, rel);
647 ++result.num_bit_exact;
648 ++result.num_samples;
651 result.mean_abs_error = sum_abs / (double)result.num_samples;
652 result.mean_rel_error = sum_rel / (double)result.num_samples;
660 comparison round_trip(
const std::string& tag,
const test_image& img,
661 const nlt_setting& setting)
663 const std::string filename = std::string(
OUT_FILE_DIR) + tag +
".j2c";
664 encode_image(filename, img, setting);
667 decode_image(filename, decoded, NULL, NULL, NULL, NULL);
669 EXPECT_EQ(decoded.width, img.width) << tag;
670 EXPECT_EQ(decoded.height, img.height) << tag;
671 EXPECT_EQ(decoded.num_comps, img.num_comps) << tag;
673 return compare_images(img, decoded);
679 bool has_nlt_marker(
const std::string& filename)
681 FILE* f = fopen(filename.c_str(),
"rb");
687 while ((cur = fgetc(f)) != EOF)
706 int num_messages = 0;
709 virtual void operator() (
int warn_code,
const char* file_name,
710 int line_num,
const char* fmt, ...)
716 last_code = warn_code;
722 int num_messages = 0;
725 virtual void operator() (
int error_code,
const char* file_name,
726 int line_num,
const char* fmt, ...)
732 last_code = error_code;
733 throw std::runtime_error(
"ojph error");
737 struct message_capture
739 warning_collector warnings;
740 error_collector errors;
769 short_read_infile(
const std::vector<ui8>& data,
size_t readable_size)
770 : readable_size(readable_size), short_read(
false)
772 EXPECT_FALSE(data.empty());
773 EXPECT_LE(readable_size, data.size());
774 file.open(&data[0], data.size());
777 size_t read(
void* ptr,
size_t size)
override
779 si64 start = file.tell();
780 size_t bytes_read = file.read(ptr,
size);
781 size_t bytes_to_report = 0;
782 if (start >= 0 && (
size_t)start < readable_size)
783 bytes_to_report = std::min(
size, readable_size - (
size_t)start);
784 bytes_to_report = std::min(bytes_to_report, bytes_read);
785 if (bytes_to_report !=
size)
788 return bytes_to_report;
792 {
return file.seek(offset, origin); }
794 si64 tell()
override {
return file.tell(); }
795 bool eof()
override {
return file.eof(); }
797 bool saw_short_read()
const {
return short_read; }
801 size_t readable_size;
809 bool load_codestream(
const std::string& filename, std::vector<ui8>& data)
811 FILE* f = fopen(filename.c_str(),
"rb");
817 while ((num_read = fread(buffer, 1,
sizeof(buffer), f)) != 0)
818 data.insert(data.end(), buffer, buffer + num_read);
819 bool ok = ferror(f) == 0;
827 size_t find_nlt_marker(
const std::vector<ui8>& data)
829 for (
size_t i = 0; i + 1 < data.size(); ++i)
841 bool insert_lut_nlt_marker(
const std::string& filename,
ui8 nlt_type)
843 FILE* f = fopen(filename.c_str(),
"rb");
847 std::vector<ui8> data;
850 while ((num_read = fread(buffer, 1,
sizeof(buffer), f)) > 0)
851 data.insert(data.end(), buffer, buffer + num_read);
855 size_t sot = data.size();
856 for (
size_t i = 0; i + 1 < data.size(); ++i)
857 if (data[i] == 0xFF && data[i + 1] == 0x90)
862 if (sot == data.size())
865 const ui16 num_points = 17;
866 const ui32 d_min = 0, d_max = 0xFFFFFFFFu;
867 const ui32 length = 17u + 4u * (
ui32)num_points;
869 std::vector<ui8> marker;
870 marker.push_back(0xFF);
871 marker.push_back(0x76);
872 marker.push_back((
ui8)(length >> 8));
873 marker.push_back((
ui8)(length & 0xFF));
874 marker.push_back(0xFF);
875 marker.push_back(0xFF);
876 marker.push_back(0x9F);
877 marker.push_back(nlt_type);
878 marker.push_back((
ui8)((num_points - 1) >> 8));
879 marker.push_back((
ui8)((num_points - 1) & 0xFF));
880 for (
int i = 3; i >= 0; --i)
881 marker.push_back((
ui8)(d_min >> (8 * i)));
882 for (
int i = 3; i >= 0; --i)
883 marker.push_back((
ui8)(d_max >> (8 * i)));
884 marker.push_back(32);
885 for (
ui32 i = 0; i < (
ui32)num_points; ++i)
888 (
ui32)(((
double)i / (num_points - 1)) * 4294967295.0);
889 for (
int b = 3; b >= 0; --b)
890 marker.push_back((
ui8)(
point >> (8 * b)));
893 data.insert(data.begin() + (
long)sot, marker.begin(), marker.end());
895 f = fopen(filename.c_str(),
"wb");
898 bool ok = fwrite(&data[0], 1, data.size(), f) == data.size();
909 void encode_two_component_image(
const std::string& filename,
910 const test_image& img,
ui32 nlt_comp)
917 for (
ui32 c = 0; c < 2; ++c)
932 pfmLutDmin, pfmLutDmax, 32, (
ui16)pfmLutNumPoints, (
void*)pfmLutPoints,
938 file.
open(filename.c_str());
943 ASSERT_TRUE(cur_line != NULL) << filename;
944 for (
ui32 c = 0; c < 2; ++c)
945 for (
ui32 y = 0; y < img.height; ++y)
947 ASSERT_EQ(next_comp, c) << filename;
948 for (
ui32 x = 0; x < img.width; ++x)
949 cur_line->i32[x] = img.samples[0][(
size_t)y * img.width + x];
950 cur_line = cs.
exchange(cur_line, next_comp);
964TEST(NltTest, MarkerSegmentRoundTrip)
967 make_synthetic_image(img, 64, 64);
972 for (
size_t i = 0; i < 3; ++i)
975 setting.type = types[i];
976 setting.use_pfm_lut =
true;
977 setting.use_approx_inverse =
true;
978 setting.reversible =
false;
979 setting.qstep = default_qstep();
981 const std::string filename =
OUT_FILE_DIR "nlt_marker.j2c";
982 encode_image(filename, img, setting);
984 bool has_nlt =
false, is_signed =
false;
985 ui8 type = 0, bit_depth = 0;
987 decode_image(filename, decoded, &has_nlt, &type, &bit_depth, &is_signed);
989 EXPECT_TRUE(has_nlt) <<
"nonlinearity " << (int)types[i];
990 EXPECT_EQ((
int)type, (
int)types[i]) <<
"nonlinearity " << (int)types[i];
991 EXPECT_EQ((
int)bit_depth, 32) <<
"nonlinearity " << (int)types[i];
992 EXPECT_TRUE(is_signed) <<
"nonlinearity " << (int)types[i];
993 EXPECT_TRUE(has_nlt_marker(filename)) <<
"nonlinearity " << (int)types[i];
1003TEST(NltTest, TruncatedMarkerSegmentIsRejected)
1006 make_synthetic_image(img, 64, 64);
1008 nlt_setting settings[4];
1010 settings[0].use_pfm_lut =
false;
1011 settings[0].reversible =
false;
1012 settings[0].qstep = default_qstep();
1014 const ui8 precisions[3] = { 8, 16, 32 };
1015 for (
size_t i = 1; i < 4; ++i)
1018 settings[i].use_pfm_lut =
false;
1019 settings[i].use_bent_lut =
true;
1020 settings[i].use_approx_inverse =
true;
1021 settings[i].pt_val = precisions[i - 1];
1022 settings[i].reversible =
false;
1023 settings[i].qstep = default_qstep();
1026 const char* tags[4] = {
"nlt_truncated_fixed",
"nlt_truncated_lut8",
1027 "nlt_truncated_lut16",
"nlt_truncated_lut32" };
1028 for (
size_t i = 0; i < 4; ++i)
1030 const std::string filename =
1032 encode_image(filename, img, settings[i]);
1034 std::vector<ui8> data;
1035 ASSERT_TRUE(load_codestream(filename, data));
1036 remove(filename.c_str());
1038 size_t marker = find_nlt_marker(data);
1039 ASSERT_LT(marker + 3, data.size());
1040 size_t marker_length = ((size_t)data[marker + 2] << 8) |
1041 (
size_t)data[marker + 3];
1042 ASSERT_GE(marker_length, (
size_t)6);
1043 ASSERT_LE(marker + 2 + marker_length, data.size());
1045 for (
size_t available = 0; available < marker_length; ++available)
1047 SCOPED_TRACE(testing::Message() << tags[i]
1048 <<
", bytes available in marker segment: " << available);
1050 message_capture messages;
1051 short_read_infile file(data, marker + 2 + available);
1053 EXPECT_THROW(cs.
read_headers(&file), std::runtime_error);
1054 EXPECT_TRUE(file.saw_short_read());
1055 EXPECT_EQ(messages.errors.num_messages, 1);
1059 EXPECT_TRUE(messages.errors.last_code == 0x000501E1 ||
1060 messages.errors.last_code == 0x000501E2 ||
1061 messages.errors.last_code == 0x000501E3)
1062 << std::hex <<
"error code " << messages.errors.last_code;
1072TEST(NltTest, NoMarkerSegmentWithoutNlt)
1075 make_synthetic_image(img, 64, 64);
1077 nlt_setting setting;
1079 setting.use_pfm_lut =
true;
1080 setting.use_approx_inverse =
true;
1081 setting.reversible =
false;
1082 setting.qstep = default_qstep();
1084 const std::string filename =
OUT_FILE_DIR "nlt_none.j2c";
1085 encode_image(filename, img, setting);
1087 bool has_nlt =
true;
1089 decode_image(filename, decoded, &has_nlt, NULL, NULL, NULL);
1091 EXPECT_FALSE(has_nlt);
1092 EXPECT_FALSE(has_nlt_marker(filename));
1103TEST(NltTest, LutStyleWithIdentityLutKeepsSamples)
1106 make_synthetic_image(img, 128, 128);
1108 nlt_setting baseline;
1110 baseline.use_pfm_lut =
false;
1111 baseline.use_approx_inverse =
true;
1112 baseline.reversible =
false;
1113 baseline.qstep = 1e-5f;
1114 comparison plain = round_trip(
"nlt_identity_plain", img, baseline);
1118 for (
size_t i = 0; i < 2; ++i)
1120 nlt_setting setting = baseline;
1121 setting.type = types[i];
1122 const std::string tag =
"nlt_identity_" + std::to_string((
int)types[i]);
1123 comparison nlt = round_trip(tag, img, setting);
1128 EXPECT_LE(nlt.mean_abs_error, 3.0 * plain.mean_abs_error + 16.0)
1129 <<
"nonlinearity " << (int)types[i] <<
": mean absolute error "
1130 << nlt.mean_abs_error <<
" against " << plain.mean_abs_error
1131 <<
" for a round trip without a nonlinearity";
1132 EXPECT_LT(nlt.max_rel_error, 0.01)
1133 <<
"nonlinearity " << (int)types[i] <<
" with an identity table";
1146TEST(NltTest, LutWithNarrowRangeOfEntriesKeepsSamples)
1148 const ui32 width = 128, height = 128;
1149 const size_t count = (size_t)width * height;
1155 const si64 bias = ((
si64)1 << 31) + 1;
1158 for (
size_t t = 0; t < 2; ++t)
1165 img.height = height;
1167 img.samples.assign(1, std::vector<si32>(count, 0));
1168 for (
size_t i = 0; i < count; ++i)
1171 img.samples[0][i] = (
si32)((smag && u < 0) ? -u - bias : u);
1174 nlt_setting setting;
1175 setting.type = types[t];
1176 setting.use_pfm_lut =
false;
1177 setting.use_narrow_lut =
true;
1178 setting.use_approx_inverse =
true;
1179 setting.reversible =
false;
1180 setting.qstep = 1e-5f;
1182 const std::string tag =
"nlt_narrow_lut_" + std::to_string((
int)types[t]);
1183 const std::string filename = std::string(
OUT_FILE_DIR) + tag +
".j2c";
1184 encode_image(filename, img, setting);
1186 decode_image(filename, decoded, NULL, NULL, NULL, NULL);
1187 ASSERT_EQ(decoded.samples.size(), 1u) << tag;
1188 ASSERT_EQ(decoded.samples[0].size(), count) << tag;
1196 for (
size_t i = 0; i < count; ++i)
1198 si64 a = img.samples[0][i], b = decoded.samples[0][i];
1199 if (smag && a < 0) a = -a - bias;
1200 if (smag && b < 0) b = -b - bias;
1201 const si64 err = a > b ? a - b : b - a;
1202 if (err > max_error)
1208 EXPECT_LT(max_error, (
si64)1 << 20)
1209 <<
"nonlinearity " << (int)types[t] <<
": sample " << worst
1210 <<
" of the ramp came back with an error of " << max_error;
1218TEST(NltTest, BinaryComplementIsLossless)
1220 test_image images[num_images];
1221 std::string missing = load_test_images(images);
1223 for (
size_t i = 0; i < num_images; ++i)
1225 nlt_setting setting;
1227 setting.use_pfm_lut =
false;
1228 setting.use_approx_inverse =
true;
1229 setting.reversible =
true;
1230 setting.qstep = default_qstep();
1232 const std::string tag = std::string(
"nlt_lossless_") + image_names[i];
1233 comparison result = round_trip(tag, images[i], setting);
1235 EXPECT_TRUE(result.is_bit_exact()) << image_names[i] <<
": "
1236 << (result.num_samples - result.num_bit_exact) <<
" of "
1237 << result.num_samples <<
" samples changed; the largest change is "
1238 << result.max_abs_error <<
" in a 32 bit pattern";
1250TEST(NltTest, RoundTripOfTestImages)
1252 test_image images[num_images];
1253 std::string missing = load_test_images(images);
1263 for (
int precise = 0; precise < 2; ++precise)
1265 nlt_setting baseline;
1266 baseline.use_approx_inverse = (precise == 0);
1267 for (
size_t n = 0; n < num_images; ++n)
1270 baseline.use_pfm_lut =
true;
1271 baseline.reversible =
false;
1272 baseline.qstep = default_qstep();
1273 comparison plain = round_trip(std::string(
"nlt_plain_")
1274 + (precise == 0 ?
"approx_" :
"precise_") + image_names[n],
1275 images[n], baseline);
1277 for (
size_t t = 0; t < 3; ++t)
1279 nlt_setting setting = baseline;
1280 setting.type = types[t];
1281 const std::string tag = std::string(
"nlt_") +
1282 std::to_string((
int)types[t]) +
"_" + image_names[n];
1283 comparison nlt = round_trip(tag, images[n], setting);
1285 std::cout << image_names[n] <<
" with nonlinearity " << (int)types[t]
1286 <<
": mean absolute error " << nlt.mean_abs_error
1287 <<
", mean relative error " << nlt.mean_rel_error
1288 <<
", largest relative error " << nlt.max_rel_error
1289 <<
" (without a nonlinearity the mean absolute error is "
1290 << plain.mean_abs_error <<
")" << std::endl;
1294 const double factor =
1297 EXPECT_LE(nlt.mean_abs_error, factor * plain.mean_abs_error + 1024.0)
1298 << image_names[n] <<
" with nonlinearity " << (int)types[t]
1299 <<
": mean absolute error " << nlt.mean_abs_error <<
" against "
1300 << plain.mean_abs_error <<
" for a round trip without a nonlinearity";
1301 EXPECT_LT(nlt.mean_rel_error, 0.05)
1302 << image_names[n] <<
" with nonlinearity " << (int)types[t];
1303 EXPECT_LT(nlt.max_rel_error, 0.30)
1304 << image_names[n] <<
" with nonlinearity " << (int)types[t];
1320TEST(NltTest, ExactInverseOfLutWithBentCurveKeepsSamples)
1322 const ui32 width = 128, height = 128;
1323 const size_t count = (size_t)width * height;
1327 const si64 bias = ((
si64)1 << 31) + 1;
1330 for (
size_t t = 0; t < 2; ++t)
1337 img.height = height;
1339 img.samples.assign(1, std::vector<si32>(count, 0));
1340 for (
size_t i = 0; i < count; ++i)
1343 img.samples[0][i] = (
si32)((smag && u < 0) ? -u - bias : u);
1347 for (
size_t e = 0; e < 2; ++e)
1349 nlt_setting setting;
1350 setting.type = types[t];
1351 setting.use_pfm_lut =
false;
1352 setting.use_bent_lut =
true;
1353 setting.use_approx_inverse = e == 0;
1354 setting.reversible =
false;
1355 setting.qstep = 1e-5f;
1357 const std::string tag =
"nlt_bent_lut_" +
1358 std::to_string((
int)types[t]) +
"_" + std::to_string(e);
1359 const std::string filename = std::string(
OUT_FILE_DIR) + tag +
".j2c";
1360 encode_image(filename, img, setting);
1362 decode_image(filename, decoded, NULL, NULL, NULL, NULL);
1363 ASSERT_EQ(decoded.samples.size(), 1u) << tag;
1364 ASSERT_EQ(decoded.samples[0].size(), count) << tag;
1367 for (
size_t i = 0; i < count; ++i)
1369 si64 a = img.samples[0][i], b = decoded.samples[0][i];
1378 const si64 err = a > b ? a - b : b - a;
1390 <<
"nonlinearity " << (int)types[t]
1391 <<
": with the exact inverse the largest error of the ramp is "
1394 <<
"nonlinearity " << (int)types[t]
1395 <<
": the uniformly sampled inverse is not less accurate than the exact "
1396 <<
"inverse; errors are " <<
error[0] <<
" and " <<
error[1];
1414TEST(NltTest, ExactInverseOfLutWithFlatSegmentsKeepsSamples)
1416 const ui32 width = 128, height = 128;
1417 const size_t count = (size_t)width * height;
1421 const si64 bias = ((
si64)1 << 31) + 1;
1424 for (
size_t t = 0; t < 2; ++t)
1432 img.height = height;
1434 img.samples.assign(1, std::vector<si32>(count, 0));
1435 for (
size_t i = 0; i < count; ++i)
1438 img.samples[0][i] = (
si32)((smag && u < 0) ? -u - bias : u);
1442 for (
size_t e = 0; e < 2; ++e)
1444 nlt_setting setting;
1445 setting.type = types[t];
1446 setting.use_clustered_lut =
true;
1447 setting.use_approx_inverse = e == 0;
1448 setting.reversible =
false;
1449 setting.qstep = 1e-6f;
1451 const std::string tag =
"nlt_clustered_lut_" +
1452 std::to_string((
int)types[t]) +
"_" + std::to_string(e);
1453 const std::string filename = std::string(
OUT_FILE_DIR) + tag +
".j2c";
1454 encode_image(filename, img, setting);
1456 decode_image(filename, decoded, NULL, NULL, NULL, NULL);
1457 ASSERT_EQ(decoded.samples.size(), 1u) << tag;
1458 ASSERT_EQ(decoded.samples[0].size(), count) << tag;
1461 for (
size_t i = 0; i < count; ++i)
1463 si64 a = img.samples[0][i], b = decoded.samples[0][i];
1472 const si64 err = a > b ? a - b : b - a;
1485 const si64 entry = ((
si64)0xF0000000 - 0x10000000) /
1486 (clusteredLutNumPoints - 1);
1487 EXPECT_LT(
error[1], entry / 8)
1488 <<
"nonlinearity " << (int)types[t]
1489 <<
": with the exact inverse the largest error of the ramp is "
1490 <<
error[1] <<
", and one entry of the table is " << entry;
1492 <<
"nonlinearity " << (int)types[t]
1493 <<
": the uniformly sampled inverse is not less accurate than the exact "
1494 <<
"inverse; errors are " <<
error[0] <<
" and " <<
error[1];
1504TEST(NltTest, LutStyleIsRejectedWithReversibleWavelet)
1507 make_synthetic_image(img, 64, 64);
1511 for (
size_t i = 0; i < 2; ++i)
1513 nlt_setting setting;
1514 setting.type = types[i];
1515 setting.use_pfm_lut =
true;
1516 setting.use_approx_inverse =
true;
1517 setting.reversible =
true;
1518 setting.qstep = default_qstep();
1520 const std::string filename =
OUT_FILE_DIR "nlt_rejected.j2c";
1521 message_capture capture;
1522 EXPECT_THROW(encode_image(filename, img, setting), std::runtime_error)
1523 <<
"nonlinearity " << (int)types[i] <<
" with the reversible wavelet";
1524 EXPECT_EQ(capture.errors.last_code, 0x000501B1)
1525 <<
"nonlinearity " << (int)types[i];
1532 nlt_setting accepted;
1534 accepted.use_pfm_lut =
true;
1535 accepted.reversible =
false;
1536 accepted.qstep = default_qstep();
1537 EXPECT_NO_THROW(encode_image(
OUT_FILE_DIR "nlt_accepted.j2c", img, accepted));
1544TEST(NltTest, LutStyleIsRejectedPerComponent)
1547 make_synthetic_image(img, 64, 64);
1552 const std::string filename =
OUT_FILE_DIR "nlt_component_accepted.j2c";
1553 EXPECT_NO_THROW(encode_two_component_image(filename, img, 1));
1555 decode_image(filename, decoded, NULL, NULL, NULL, NULL);
1556 EXPECT_EQ(decoded.num_comps, 2u);
1562 const std::string filename =
OUT_FILE_DIR "nlt_component_rejected.j2c";
1563 message_capture capture;
1564 EXPECT_THROW(encode_two_component_image(filename, img, 0),
1565 std::runtime_error);
1566 EXPECT_EQ(capture.errors.last_code, 0x000501B1);
1576TEST(NltTest, LutStyleWithReversibleWaveletIsReportedWhenRead)
1579 make_synthetic_image(img, 64, 64);
1583 nlt_setting setting;
1585 setting.use_pfm_lut =
true;
1586 setting.use_approx_inverse =
true;
1587 setting.reversible =
true;
1588 setting.qstep = default_qstep();
1590 const std::string filename =
OUT_FILE_DIR "nlt_foreign.j2c";
1591 encode_image(filename, img, setting);
1592 ASSERT_TRUE(insert_lut_nlt_marker(filename,
1595 message_capture capture;
1598 file.
open(filename.c_str());
1599 EXPECT_THROW(cs.
read_headers(&file), std::runtime_error);
1600 EXPECT_GE(capture.errors.num_messages, 1);
1601 EXPECT_EQ(capture.errors.last_code, 0x00030059);
1603 ui8 bit_depth = 0, type = 0;
1604 bool is_signed =
false;
1606 bit_depth, is_signed, type));
The object represent a codestream.
param_siz access_siz()
Returns the underlying SIZ marker segment object.
param_cod access_cod()
Returns the underlying COD marker segment object.
void restrict_input_resolution(ui32 skipped_res_for_data, ui32 skipped_res_for_recon)
This function restricts resolution decoding for a codestream. It is for a reading (decoding) codestre...
void close()
Call this function to close the underlying file; works for both encoding and decoding codestreams.
void set_planar(bool planar)
Sets the sequence of pushing or pull rows from the machinery.
line_buf * exchange(line_buf *line, ui32 &next_component)
This call is used to send image data rows to the library. We expect to send one row from a single com...
param_qcd access_qcd()
Returns the underlying QCD marker segment object.
void read_headers(infile_base *file)
This call reads the headers of a codestream. It is for a reading (or decoding) codestream,...
void write_headers(outfile_base *file, const comment_exchange *comments=NULL, ui32 num_comments=0)
Writes codestream headers when the codestream is used for writing. This function should be called aft...
void create()
This call is for a decoding (or reading) codestream. Call this function after calling restrict_input_...
param_nlt access_nlt()
Returns the underlying NLT marker segment object.
void flush()
This is the last call to a writing (encoding) codestream. This will write encoded bitstream data to t...
line_buf * pull(ui32 &comp_num)
This call is to pull one row from the codestream, being decoded. The returned line_buf object holds o...
void open(const char *filename)
void open(const char *filename)
Derived from message_base to handle error messages.
Derived from message_base to handle warning messages.
void set_num_decomposition(ui32 num_decompositions)
void set_block_dims(ui32 width, ui32 height)
void set_color_transform(bool color_transform)
void set_reversible(bool reversible)
non-linearity point transformation object (implements NLT marker segment)
@ OJPH_NLT_BINARY_COMPLEMENT_NLT
@ OJPH_NLT_BINARY_COMPLEMENT_PLUS_LUT
bool get_nonlinear_transform(ui32 comp_num, ui8 &decoded_bit_depth, bool &decoded_signedness, ui8 &nl_type) const
void set_nonlinear_transform(ui32 comp_num, ui8 nl_type)
void set_irrev_quant(float delta)
Set the irreversible quantization base delta.
void set_tile_size(size s)
void set_component(ui32 comp_num, const point &downsampling, ui32 bit_depth, bool is_signed)
void set_num_components(ui32 num_comps)
void set_tile_offset(point offset)
void set_image_offset(point offset)
ui32 get_recon_height(ui32 comp_num) const
void set_image_extent(point extent)
ui32 get_recon_width(ui32 comp_num) const
ui32 get_num_components() const
OJPH_EXPORT message_warning * get_warning()
Get the warning message object, whose operator() member class is called for warning messages – See th...
OJPH_EXPORT void configure_error(message_error *error)
This overrides the default behaviour of handling error messages.
const bool is_machine_little_endian
OJPH_EXPORT message_error * get_error()
Get the error message object, whose operator() member class is called for error messages – See the ma...
OJPH_EXPORT void configure_warning(message_warning *warn)
This overrides the default behaviour of handling warning messages.
point(ui32 x=0, ui32 y=0)
TEST(TestExecutables, OpenJPHCompressNoArguments)