test_jpegxl.cpp 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364
  1. // This file is part of OpenCV project.
  2. // It is subject to the license terms in the LICENSE file found in the top-level
  3. // directory of this distribution and at http://opencv.org/license.html
  4. #include "test_precomp.hpp"
  5. namespace opencv_test { namespace {
  6. #ifdef HAVE_JPEGXL
  7. #include <jxl/version.h> // For JPEGXL_MAJOR_VERSION and JPEGXL_MINOR_VERSION
  8. typedef tuple<perf::MatType, int> MatType_and_Distance;
  9. typedef testing::TestWithParam<MatType_and_Distance> Imgcodecs_JpegXL_MatType;
  10. TEST_P(Imgcodecs_JpegXL_MatType, write_read)
  11. {
  12. const int matType = get<0>(GetParam());
  13. const int distanceParam = get<1>(GetParam());
  14. cv::Scalar col;
  15. // Jpeg XL supports lossy and lossless compressions.
  16. // Lossy compression may be small differences in decoding results by environments.
  17. double th;
  18. switch( CV_MAT_DEPTH(matType) )
  19. {
  20. case CV_16U:
  21. col = cv::Scalar(124 * 256, 76 * 256, 42 * 256, 192 * 256 );
  22. th = 656; // = 65535 / 100;
  23. break;
  24. case CV_32F:
  25. col = cv::Scalar(0.486, 0.298, 0.165, 0.75);
  26. th = 1.0 / 100.0;
  27. break;
  28. default:
  29. case CV_8U:
  30. col = cv::Scalar(124, 76, 42, 192);
  31. th = 3; // = 255 / 100 (1%);
  32. break;
  33. }
  34. // If increasing distanceParam, threshold should be increased.
  35. th *= (distanceParam >= 25) ? 5 : (distanceParam > 2) ? 3 : distanceParam;
  36. bool ret = false;
  37. string tmp_fname = cv::tempfile(".jxl");
  38. Mat img_org(320, 480, matType, col);
  39. vector<int> param;
  40. param.push_back(IMWRITE_JPEGXL_DISTANCE);
  41. param.push_back(distanceParam);
  42. EXPECT_NO_THROW(ret = imwrite(tmp_fname, img_org, param));
  43. EXPECT_TRUE(ret);
  44. Mat img_decoded;
  45. EXPECT_NO_THROW(img_decoded = imread(tmp_fname, IMREAD_UNCHANGED));
  46. EXPECT_FALSE(img_decoded.empty());
  47. EXPECT_LE(cvtest::norm(img_org, img_decoded, NORM_INF), th);
  48. EXPECT_EQ(0, remove(tmp_fname.c_str()));
  49. }
  50. TEST_P(Imgcodecs_JpegXL_MatType, encode_decode)
  51. {
  52. const int matType = get<0>(GetParam());
  53. const int distanceParam = get<1>(GetParam());
  54. cv::Scalar col;
  55. // Jpeg XL supports lossy and lossless compressions.
  56. // Lossy compression may be small differences in decoding results by environments.
  57. double th;
  58. // If alpha=0, libjxl modify color channels(BGR). So do not set it.
  59. switch( CV_MAT_DEPTH(matType) )
  60. {
  61. case CV_16U:
  62. col = cv::Scalar(124 * 256, 76 * 256, 42 * 256, 192 * 256 );
  63. th = 656; // = 65535 / 100;
  64. break;
  65. case CV_32F:
  66. col = cv::Scalar(0.486, 0.298, 0.165, 0.75);
  67. th = 1.0 / 100.0;
  68. break;
  69. default:
  70. case CV_8U:
  71. col = cv::Scalar(124, 76, 42, 192);
  72. th = 3; // = 255 / 100 (1%);
  73. break;
  74. }
  75. // If increasing distanceParam, threshold should be increased.
  76. th *= (distanceParam >= 25) ? 5 : (distanceParam > 2) ? 3 : distanceParam;
  77. bool ret = false;
  78. vector<uchar> buff;
  79. Mat img_org(320, 480, matType, col);
  80. vector<int> param;
  81. param.push_back(IMWRITE_JPEGXL_DISTANCE);
  82. param.push_back(distanceParam);
  83. EXPECT_NO_THROW(ret = imencode(".jxl", img_org, buff, param));
  84. EXPECT_TRUE(ret);
  85. Mat img_decoded;
  86. EXPECT_NO_THROW(img_decoded = imdecode(buff, IMREAD_UNCHANGED));
  87. EXPECT_FALSE(img_decoded.empty());
  88. EXPECT_LE(cvtest::norm(img_org, img_decoded, NORM_INF), th);
  89. }
  90. INSTANTIATE_TEST_CASE_P(
  91. /**/,
  92. Imgcodecs_JpegXL_MatType,
  93. testing::Combine(
  94. testing::Values(
  95. CV_8UC1, CV_8UC3, CV_8UC4,
  96. CV_16UC1, CV_16UC3, CV_16UC4,
  97. CV_32FC1, CV_32FC3, CV_32FC4
  98. ),
  99. testing::Values( // Distance
  100. 0, // Lossless
  101. 1, // Default
  102. 3, // Recomended Lossy Max
  103. 25 // Specification Max
  104. )
  105. ) );
  106. typedef tuple<int, int> Effort_and_Decoding_speed;
  107. typedef testing::TestWithParam<Effort_and_Decoding_speed> Imgcodecs_JpegXL_Effort_DecodingSpeed;
  108. TEST_P(Imgcodecs_JpegXL_Effort_DecodingSpeed, encode_decode)
  109. {
  110. const int effort = get<0>(GetParam());
  111. const int speed = get<1>(GetParam());
  112. cv::Scalar col = cv::Scalar(124,76,42);
  113. // Jpeg XL supports lossy and lossless compression.
  114. // Lossy compression may be small differences in decoding results by environments.
  115. double th = 3; // = 255 / 100 (1%);
  116. bool ret = false;
  117. vector<uchar> buff;
  118. Mat img_org(320, 480, CV_8UC3, col);
  119. vector<int> param;
  120. param.push_back(IMWRITE_JPEGXL_EFFORT);
  121. param.push_back(effort);
  122. param.push_back(IMWRITE_JPEGXL_DECODING_SPEED);
  123. param.push_back(speed);
  124. EXPECT_NO_THROW(ret = imencode(".jxl", img_org, buff, param));
  125. EXPECT_TRUE(ret);
  126. Mat img_decoded;
  127. EXPECT_NO_THROW(img_decoded = imdecode(buff, IMREAD_UNCHANGED));
  128. EXPECT_FALSE(img_decoded.empty());
  129. EXPECT_LE(cvtest::norm(img_org, img_decoded, NORM_INF), th);
  130. }
  131. INSTANTIATE_TEST_CASE_P(
  132. /**/,
  133. Imgcodecs_JpegXL_Effort_DecodingSpeed,
  134. testing::Combine(
  135. testing::Values( // Effort
  136. 1, // fastest
  137. 7, // default
  138. 9 // slowest
  139. ),
  140. testing::Values( // Decoding Speed
  141. 0, // default, slowest, and best quality/density
  142. 2,
  143. 4 // fastest, at the cost of some qulity/density
  144. )
  145. ) );
  146. TEST(Imgcodecs_JpegXL, encode_from_uncontinued_image)
  147. {
  148. cv::Mat src(100, 100, CV_8UC1, Scalar(40,50,10));
  149. cv::Mat roi = src(cv::Rect(10,20,30,50));
  150. EXPECT_FALSE(roi.isContinuous()); // uncontinued image
  151. vector<uint8_t> buff;
  152. vector<int> param;
  153. bool ret = false;
  154. EXPECT_NO_THROW(ret = cv::imencode(".jxl", roi, buff, param));
  155. EXPECT_TRUE(ret);
  156. }
  157. // See https://github.com/opencv/opencv/issues/26767
  158. typedef tuple<perf::MatType, ImreadModes> MatType_and_ImreadFlag;
  159. typedef testing::TestWithParam<MatType_and_ImreadFlag> Imgcodecs_JpegXL_MatType_ImreadFlag;
  160. TEST_P(Imgcodecs_JpegXL_MatType_ImreadFlag, all_imreadFlags)
  161. {
  162. string tmp_fname = cv::tempfile(".jxl");
  163. const int matType = get<0>(GetParam());
  164. const int imreadFlag = get<1>(GetParam());
  165. Mat img(240, 320, matType);
  166. randu(img, Scalar(0, 0, 0, 255), Scalar(255, 255, 255, 255));
  167. vector<int> param;
  168. param.push_back(IMWRITE_JPEGXL_DISTANCE);
  169. param.push_back(0 /* Lossless */);
  170. EXPECT_NO_THROW(imwrite(tmp_fname, img, param));
  171. Mat img_decoded;
  172. EXPECT_NO_THROW(img_decoded = imread(tmp_fname, imreadFlag));
  173. EXPECT_FALSE(img_decoded.empty());
  174. switch( imreadFlag )
  175. {
  176. case IMREAD_UNCHANGED:
  177. EXPECT_EQ( img.type(), img_decoded.type() );
  178. break;
  179. case IMREAD_GRAYSCALE:
  180. EXPECT_EQ( img_decoded.depth(), CV_8U );
  181. EXPECT_EQ( img_decoded.channels(), 1 );
  182. break;
  183. case IMREAD_COLOR:
  184. case IMREAD_COLOR_RGB:
  185. EXPECT_EQ( img_decoded.depth(), CV_8U );
  186. EXPECT_EQ( img_decoded.channels(), 3 );
  187. break;
  188. case IMREAD_ANYDEPTH:
  189. EXPECT_EQ( img_decoded.depth(), img.depth() );
  190. EXPECT_EQ( img_decoded.channels(), 1 );
  191. break;
  192. case IMREAD_ANYCOLOR:
  193. EXPECT_EQ( img_decoded.depth(), CV_8U ) ;
  194. EXPECT_EQ( img_decoded.channels(), img.channels() == 1 ? 1 : 3 ); // Alpha channel will be dropped.
  195. break;
  196. }
  197. remove(tmp_fname.c_str());
  198. }
  199. INSTANTIATE_TEST_CASE_P(
  200. /**/,
  201. Imgcodecs_JpegXL_MatType_ImreadFlag,
  202. testing::Combine(
  203. testing::Values(
  204. CV_8UC1, CV_8UC3, CV_8UC4,
  205. CV_16UC1, CV_16UC3, CV_16UC4,
  206. CV_32FC1, CV_32FC3, CV_32FC4
  207. ),
  208. testing::Values(
  209. IMREAD_UNCHANGED,
  210. IMREAD_GRAYSCALE,
  211. IMREAD_COLOR,
  212. IMREAD_COLOR_RGB,
  213. IMREAD_ANYDEPTH,
  214. IMREAD_ANYCOLOR
  215. )
  216. ) );
  217. TEST(Imgcodecs_JpegXL, imdecode_truncated_stream)
  218. {
  219. cv::Mat src(100, 100, CV_8UC1, Scalar(40,50,10));
  220. vector<uint8_t> buff;
  221. vector<int> param;
  222. bool ret = false;
  223. EXPECT_NO_THROW(ret = cv::imencode(".jxl", src, buff, param));
  224. EXPECT_TRUE(ret);
  225. // Try to decode non-truncated image.
  226. cv::Mat decoded;
  227. EXPECT_NO_THROW(decoded = cv::imdecode(buff, cv::IMREAD_COLOR));
  228. EXPECT_FALSE(decoded.empty());
  229. // Try to decode truncated image.
  230. buff.resize(buff.size() - 1 );
  231. EXPECT_NO_THROW(decoded = cv::imdecode(buff, cv::IMREAD_COLOR));
  232. EXPECT_TRUE(decoded.empty());
  233. }
  234. TEST(Imgcodecs_JpegXL, imread_truncated_stream)
  235. {
  236. string tmp_fname = cv::tempfile(".jxl");
  237. cv::Mat src(100, 100, CV_8UC1, Scalar(40,50,10));
  238. vector<uint8_t> buff;
  239. vector<int> param;
  240. bool ret = false;
  241. EXPECT_NO_THROW(ret = cv::imencode(".jxl", src, buff, param));
  242. EXPECT_TRUE(ret);
  243. // Try to decode non-truncated image.
  244. FILE *fp = nullptr;
  245. fp = fopen(tmp_fname.c_str(), "wb");
  246. EXPECT_TRUE(fp != nullptr);
  247. fwrite(&buff[0], sizeof(uint8_t), buff.size(), fp);
  248. fclose(fp);
  249. cv::Mat decoded;
  250. EXPECT_NO_THROW(decoded = cv::imread(tmp_fname, cv::IMREAD_COLOR));
  251. EXPECT_FALSE(decoded.empty());
  252. // Try to decode truncated image.
  253. fp = fopen(tmp_fname.c_str(), "wb");
  254. EXPECT_TRUE(fp != nullptr);
  255. fwrite(&buff[0], sizeof(uint8_t), buff.size() - 1, fp);
  256. fclose(fp);
  257. EXPECT_NO_THROW(decoded = cv::imread(tmp_fname, cv::IMREAD_COLOR));
  258. EXPECT_TRUE(decoded.empty());
  259. // Delete temporary file
  260. remove(tmp_fname.c_str());
  261. }
  262. // See https://github.com/opencv/opencv/issues/27382
  263. TEST(Imgcodecs_JpegXL, imencode_regression27382)
  264. {
  265. cv::Mat image(1024, 1024, CV_16U);
  266. cv::RNG rng(1024);
  267. rng.fill(image, cv::RNG::NORMAL, 0, 65535);
  268. std::vector<unsigned char> buffer;
  269. std::vector<int> params = {cv::IMWRITE_JPEGXL_DISTANCE, 0}; // lossless
  270. EXPECT_NO_THROW(cv::imencode(".jxl", image, buffer, params));
  271. cv::Mat decoded;
  272. EXPECT_NO_THROW(decoded = cv::imdecode(buffer, cv::IMREAD_UNCHANGED));
  273. EXPECT_FALSE(decoded.empty());
  274. cv::Mat diff;
  275. cv::absdiff(image, decoded, diff);
  276. double max_diff = 0.0;
  277. cv::minMaxLoc(diff, nullptr, &max_diff);
  278. EXPECT_EQ(max_diff, 0 );
  279. }
  280. TEST(Imgcodecs_JpegXL, imencode_regression27382_2)
  281. {
  282. cv::Mat image(1024, 1024, CV_16U);
  283. cv::RNG rng(1024);
  284. rng.fill(image, cv::RNG::NORMAL, 0, 65535);
  285. std::vector<unsigned char> buffer;
  286. std::vector<int> params = {cv::IMWRITE_JPEGXL_QUALITY, 100}; // lossless
  287. EXPECT_NO_THROW(cv::imencode(".jxl", image, buffer, params));
  288. cv::Mat decoded;
  289. EXPECT_NO_THROW(decoded = cv::imdecode(buffer, cv::IMREAD_UNCHANGED));
  290. EXPECT_FALSE(decoded.empty());
  291. cv::Mat diff;
  292. cv::absdiff(image, decoded, diff);
  293. double max_diff = 0.0;
  294. cv::minMaxLoc(diff, nullptr, &max_diff);
  295. #if JPEGXL_MAJOR_VERSION > 0 || JPEGXL_MINOR_VERSION >= 10
  296. // Quality parameter is supported with libjxl v0.10.0 or later
  297. EXPECT_EQ(max_diff, 0); // Lossless
  298. #else
  299. EXPECT_NE(max_diff, 0); // Lossy
  300. #endif
  301. }
  302. #endif // HAVE_JPEGXL
  303. } // namespace
  304. } // namespace opencv_test