test_jpeg.cpp 9.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294
  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 directory
  3. // of this distribution and at http://opencv.org/license.html
  4. #include "test_precomp.hpp"
  5. namespace opencv_test { namespace {
  6. #ifdef HAVE_JPEG
  7. extern "C" {
  8. #include "jpeglib.h"
  9. }
  10. TEST(Imgcodecs_Jpeg, encode_empty)
  11. {
  12. cv::Mat img;
  13. std::vector<uchar> jpegImg;
  14. ASSERT_THROW(cv::imencode(".jpg", img, jpegImg), cv::Exception);
  15. }
  16. TEST(Imgcodecs_Jpeg, encode_decode_progressive_jpeg)
  17. {
  18. cvtest::TS& ts = *cvtest::TS::ptr();
  19. string input = string(ts.get_data_path()) + "../cv/shared/lena.png";
  20. cv::Mat img = cv::imread(input);
  21. ASSERT_FALSE(img.empty());
  22. std::vector<int> params;
  23. params.push_back(IMWRITE_JPEG_PROGRESSIVE);
  24. params.push_back(1);
  25. string output_progressive = cv::tempfile(".jpg");
  26. EXPECT_NO_THROW(cv::imwrite(output_progressive, img, params));
  27. cv::Mat img_jpg_progressive = cv::imread(output_progressive);
  28. string output_normal = cv::tempfile(".jpg");
  29. EXPECT_NO_THROW(cv::imwrite(output_normal, img));
  30. cv::Mat img_jpg_normal = cv::imread(output_normal);
  31. EXPECT_EQ(0, cvtest::norm(img_jpg_progressive, img_jpg_normal, NORM_INF));
  32. EXPECT_EQ(0, remove(output_progressive.c_str()));
  33. EXPECT_EQ(0, remove(output_normal.c_str()));
  34. }
  35. TEST(Imgcodecs_Jpeg, encode_decode_optimize_jpeg)
  36. {
  37. cvtest::TS& ts = *cvtest::TS::ptr();
  38. string input = string(ts.get_data_path()) + "../cv/shared/lena.png";
  39. cv::Mat img = cv::imread(input);
  40. ASSERT_FALSE(img.empty());
  41. std::vector<int> params;
  42. params.push_back(IMWRITE_JPEG_OPTIMIZE);
  43. params.push_back(1);
  44. string output_optimized = cv::tempfile(".jpg");
  45. EXPECT_NO_THROW(cv::imwrite(output_optimized, img, params));
  46. cv::Mat img_jpg_optimized = cv::imread(output_optimized);
  47. string output_normal = cv::tempfile(".jpg");
  48. EXPECT_NO_THROW(cv::imwrite(output_normal, img));
  49. cv::Mat img_jpg_normal = cv::imread(output_normal);
  50. EXPECT_EQ(0, cvtest::norm(img_jpg_optimized, img_jpg_normal, NORM_INF));
  51. EXPECT_EQ(0, remove(output_optimized.c_str()));
  52. EXPECT_EQ(0, remove(output_normal.c_str()));
  53. }
  54. TEST(Imgcodecs_Jpeg, encode_decode_rst_jpeg)
  55. {
  56. cvtest::TS& ts = *cvtest::TS::ptr();
  57. string input = string(ts.get_data_path()) + "../cv/shared/lena.png";
  58. cv::Mat img = cv::imread(input);
  59. ASSERT_FALSE(img.empty());
  60. std::vector<int> params;
  61. params.push_back(IMWRITE_JPEG_RST_INTERVAL);
  62. params.push_back(1);
  63. string output_rst = cv::tempfile(".jpg");
  64. EXPECT_NO_THROW(cv::imwrite(output_rst, img, params));
  65. cv::Mat img_jpg_rst = cv::imread(output_rst);
  66. string output_normal = cv::tempfile(".jpg");
  67. EXPECT_NO_THROW(cv::imwrite(output_normal, img));
  68. cv::Mat img_jpg_normal = cv::imread(output_normal);
  69. EXPECT_EQ(0, cvtest::norm(img_jpg_rst, img_jpg_normal, NORM_INF));
  70. EXPECT_EQ(0, remove(output_rst.c_str()));
  71. EXPECT_EQ(0, remove(output_normal.c_str()));
  72. }
  73. // See https://github.com/opencv/opencv/issues/25274
  74. typedef testing::TestWithParam<int> Imgcodecs_Jpeg_decode_cmyk;
  75. TEST_P(Imgcodecs_Jpeg_decode_cmyk, regression25274)
  76. {
  77. const int imread_flag = GetParam();
  78. /*
  79. * "test_1_c4.jpg" is CMYK-JPEG.
  80. * $ convert test_1_c3.jpg -colorspace CMYK test_1_c4.jpg
  81. * $ identify test_1_c4.jpg
  82. * test_1_c4.jpg JPEG 480x640 480x640+0+0 8-bit CMYK 11240B 0.000u 0:00.000
  83. */
  84. cvtest::TS& ts = *cvtest::TS::ptr();
  85. string rgb_filename = string(ts.get_data_path()) + "readwrite/test_1_c3.jpg";
  86. cv::Mat rgb_img = cv::imread(rgb_filename, imread_flag);
  87. ASSERT_FALSE(rgb_img.empty());
  88. string cmyk_filename = string(ts.get_data_path()) + "readwrite/test_1_c4.jpg";
  89. cv::Mat cmyk_img = cv::imread(cmyk_filename, imread_flag);
  90. ASSERT_FALSE(cmyk_img.empty());
  91. EXPECT_EQ(rgb_img.size(), cmyk_img.size());
  92. EXPECT_EQ(rgb_img.type(), cmyk_img.type());
  93. // Jpeg is lossy compression.
  94. // There may be small differences in decoding results by environments.
  95. // -> 255 * 1% = 2.55 .
  96. EXPECT_LE(cvtest::norm(rgb_img, cmyk_img, NORM_INF), 3); // norm() <= 3
  97. }
  98. INSTANTIATE_TEST_CASE_P( /* nothing */,
  99. Imgcodecs_Jpeg_decode_cmyk,
  100. testing::Values(cv::IMREAD_COLOR,
  101. cv::IMREAD_COLOR_RGB,
  102. cv::IMREAD_GRAYSCALE,
  103. cv::IMREAD_ANYCOLOR));
  104. //==================================================================================================
  105. static const uint32_t default_sampling_factor = static_cast<uint32_t>(0x221111);
  106. static uint32_t test_jpeg_subsampling( const Mat src, const vector<int> param )
  107. {
  108. vector<uint8_t> jpeg;
  109. if ( cv::imencode(".jpg", src, jpeg, param ) == false )
  110. {
  111. return 0;
  112. }
  113. if ( src.channels() != 3 )
  114. {
  115. return 0;
  116. }
  117. // Find SOF Marker(FFC0)
  118. int sof_offset = 0; // not found.
  119. int jpeg_size = static_cast<int>( jpeg.size() );
  120. for ( int i = 0 ; i < jpeg_size - 1; i++ )
  121. {
  122. if ( (jpeg[i] == 0xff ) && ( jpeg[i+1] == 0xC0 ) )
  123. {
  124. sof_offset = i;
  125. break;
  126. }
  127. }
  128. if ( sof_offset == 0 )
  129. {
  130. return 0;
  131. }
  132. // Extract Subsampling Factor from SOF.
  133. return ( jpeg[sof_offset + 0x0A + 3 * 0 + 1] << 16 ) +
  134. ( jpeg[sof_offset + 0x0A + 3 * 1 + 1] << 8 ) +
  135. ( jpeg[sof_offset + 0x0A + 3 * 2 + 1] ) ;
  136. }
  137. TEST(Imgcodecs_Jpeg, encode_subsamplingfactor_default)
  138. {
  139. vector<int> param;
  140. Mat src( 48, 64, CV_8UC3, cv::Scalar::all(0) );
  141. EXPECT_EQ( default_sampling_factor, test_jpeg_subsampling(src, param) );
  142. }
  143. TEST(Imgcodecs_Jpeg, encode_subsamplingfactor_usersetting_valid)
  144. {
  145. Mat src( 48, 64, CV_8UC3, cv::Scalar::all(0) );
  146. const uint32_t sampling_factor_list[] = {
  147. IMWRITE_JPEG_SAMPLING_FACTOR_411,
  148. IMWRITE_JPEG_SAMPLING_FACTOR_420,
  149. IMWRITE_JPEG_SAMPLING_FACTOR_422,
  150. IMWRITE_JPEG_SAMPLING_FACTOR_440,
  151. IMWRITE_JPEG_SAMPLING_FACTOR_444,
  152. };
  153. const int sampling_factor_list_num = 5;
  154. for ( int i = 0 ; i < sampling_factor_list_num; i ++ )
  155. {
  156. vector<int> param;
  157. param.push_back( IMWRITE_JPEG_SAMPLING_FACTOR );
  158. param.push_back( sampling_factor_list[i] );
  159. EXPECT_EQ( sampling_factor_list[i], test_jpeg_subsampling(src, param) );
  160. }
  161. }
  162. TEST(Imgcodecs_Jpeg, encode_subsamplingfactor_usersetting_invalid)
  163. {
  164. Mat src( 48, 64, CV_8UC3, cv::Scalar::all(0) );
  165. const uint32_t sampling_factor_list[] = { // Invalid list
  166. 0x111112,
  167. 0x000000,
  168. 0x001111,
  169. 0xFF1111,
  170. 0x141111, // 1x4,1x1,1x1 - unknown
  171. 0x241111, // 2x4,1x1,1x1 - unknown
  172. 0x421111, // 4x2,1x1,1x1 - unknown
  173. 0x441111, // 4x4,1x1,1x1 - 410(libjpeg cannot handle it)
  174. };
  175. const int sampling_factor_list_num = 8;
  176. for ( int i = 0 ; i < sampling_factor_list_num; i ++ )
  177. {
  178. vector<int> param;
  179. param.push_back( IMWRITE_JPEG_SAMPLING_FACTOR );
  180. param.push_back( sampling_factor_list[i] );
  181. EXPECT_EQ( default_sampling_factor, test_jpeg_subsampling(src, param) );
  182. }
  183. }
  184. //==================================================================================================
  185. // See https://github.com/opencv/opencv/issues/25646
  186. typedef testing::TestWithParam<std::tuple<int, int>> Imgcodecs_Jpeg_encode_withLumaChromaQuality;
  187. TEST_P(Imgcodecs_Jpeg_encode_withLumaChromaQuality, basic)
  188. {
  189. const int luma = get<0>(GetParam());
  190. const int chroma = get<1>(GetParam());
  191. cvtest::TS& ts = *cvtest::TS::ptr();
  192. string fname = string(ts.get_data_path()) + "../cv/shared/lena.png";
  193. cv::Mat src = imread(fname, cv::IMREAD_COLOR);
  194. ASSERT_FALSE(src.empty());
  195. // Add imread RGB test
  196. cv::Mat src_rgb = imread(fname, cv::IMREAD_COLOR_RGB);
  197. ASSERT_FALSE(src_rgb.empty());
  198. cvtColor(src_rgb, src_rgb, COLOR_RGB2BGR);
  199. EXPECT_TRUE(cvtest::norm(src, src_rgb, NORM_INF) == 0);
  200. std::vector<uint8_t> jpegNormal;
  201. ASSERT_NO_THROW(cv::imencode(".jpg", src, jpegNormal));
  202. std::vector<int> param;
  203. param.push_back(IMWRITE_JPEG_LUMA_QUALITY);
  204. param.push_back(luma);
  205. param.push_back(IMWRITE_JPEG_CHROMA_QUALITY);
  206. param.push_back(chroma);
  207. std::vector<uint8_t> jpegCustom;
  208. ASSERT_NO_THROW(cv::imencode(".jpg", src, jpegCustom, param));
  209. #if JPEG_LIB_VERSION >= 70
  210. // For jpeg7+, we can support IMWRITE_JPEG_LUMA_QUALITY and IMWRITE_JPEG_CHROMA_QUALITY.
  211. if( (luma == 95 /* Default Luma Quality */ ) && ( chroma == 95 /* Default Chroma Quality */))
  212. {
  213. EXPECT_EQ(jpegNormal, jpegCustom);
  214. }
  215. else
  216. {
  217. EXPECT_NE(jpegNormal, jpegCustom);
  218. }
  219. #else
  220. // For jpeg6-, we cannot support IMWRITE_JPEG_LUMA/CHROMA_QUALITY because jpeg_default_qtables() is missing.
  221. // - IMWRITE_JPEG_LUMA_QUALITY updates internal parameter of IMWRITE_JPEG_QUALITY.
  222. // - IMWRITE_JPEG_CHROMA_QUALITY updates nothing.
  223. if( luma == 95 /* Default Jpeg Quality */ )
  224. {
  225. EXPECT_EQ(jpegNormal, jpegCustom);
  226. }
  227. else
  228. {
  229. EXPECT_NE(jpegNormal, jpegCustom);
  230. }
  231. #endif
  232. }
  233. INSTANTIATE_TEST_CASE_P( /* nothing */,
  234. Imgcodecs_Jpeg_encode_withLumaChromaQuality,
  235. testing::Combine(
  236. testing::Values(70, 95, 100), // IMWRITE_JPEG_LUMA_QUALITY
  237. testing::Values(70, 95, 100) )); // IMWRITE_JPEG_CHROMA_QUALITY
  238. #endif // HAVE_JPEG
  239. }} // namespace