diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 81f8a45f65..94948525c9 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -56,14 +56,17 @@ enum { IMREAD_UNCHANGED = -1, // 8bit, color or not IMREAD_ANYCOLOR = 4 // ?, any color }; -enum { IMWRITE_JPEG_QUALITY = 1, - IMWRITE_JPEG_PROGRESSIVE = 2, - IMWRITE_JPEG_OPTIMIZE = 3, - IMWRITE_PNG_COMPRESSION = 16, - IMWRITE_PNG_STRATEGY = 17, - IMWRITE_PNG_BILEVEL = 18, - IMWRITE_PXM_BINARY = 32, - IMWRITE_WEBP_QUALITY = 64 +enum { IMWRITE_JPEG_QUALITY = 1, + IMWRITE_JPEG_PROGRESSIVE = 2, + IMWRITE_JPEG_OPTIMIZE = 3, + IMWRITE_JPEG_RST_INTERVAL = 4, + IMWRITE_JPEG_LUM_QUALITY = 5, + IMWRITE_JPEG_CHROM_QUALITY = 6, + IMWRITE_PNG_COMPRESSION = 16, + IMWRITE_PNG_STRATEGY = 17, + IMWRITE_PNG_BILEVEL = 18, + IMWRITE_PXM_BINARY = 32, + IMWRITE_WEBP_QUALITY = 64 }; enum { IMWRITE_PNG_STRATEGY_DEFAULT = 0, diff --git a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h index f0c2ae13fe..a36b454058 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h +++ b/modules/imgcodecs/include/opencv2/imgcodecs/imgcodecs_c.h @@ -76,6 +76,9 @@ enum CV_IMWRITE_JPEG_QUALITY =1, CV_IMWRITE_JPEG_PROGRESSIVE =2, CV_IMWRITE_JPEG_OPTIMIZE =3, + CV_IMWRITE_JPEG_RST_INTERVAL =4, + CV_IMWRITE_JPEG_LUM_QUALITY =5, + CV_IMWRITE_JPEG_CHROM_QUALITY =6, CV_IMWRITE_PNG_COMPRESSION =16, CV_IMWRITE_PNG_STRATEGY =17, CV_IMWRITE_PNG_BILEVEL =18, diff --git a/modules/imgcodecs/src/grfmt_jpeg.cpp b/modules/imgcodecs/src/grfmt_jpeg.cpp index 147f185e4c..3157ebfcb1 100644 --- a/modules/imgcodecs/src/grfmt_jpeg.cpp +++ b/modules/imgcodecs/src/grfmt_jpeg.cpp @@ -600,6 +600,9 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) int quality = 95; int progressive = 0; int optimize = 0; + int rst_interval = 0; + int lum_quality = 100; + int chrom_quality = 100; for( size_t i = 0; i < params.size(); i += 2 ) { @@ -618,15 +621,49 @@ bool JpegEncoder::write( const Mat& img, const std::vector& params ) { optimize = params[i+1]; } + + if( params[i] == CV_IMWRITE_JPEG_LUM_QUALITY ) + { + lum_quality = params[i+1]; + lum_quality = MIN(MAX(lum_quality, 0), 100); + } + + if( params[i] == CV_IMWRITE_JPEG_CHROM_QUALITY ) + { + chrom_quality = params[i+1]; + chrom_quality = MIN(MAX(chrom_quality, 0), 100); + } + + if( params[i] == CV_IMWRITE_JPEG_RST_INTERVAL ) + { + rst_interval = params[i+1]; + rst_interval = MIN(MAX(rst_interval, 0), 65535L); + } } jpeg_set_defaults( &cinfo ); + cinfo.restart_interval = rst_interval; jpeg_set_quality( &cinfo, quality, TRUE /* limit to baseline-JPEG values */ ); if( progressive ) jpeg_simple_progression( &cinfo ); if( optimize ) cinfo.optimize_coding = TRUE; + +#if JPEG_LIB_VERSION >= 70 + cinfo.q_scale_factor[0] = jpeg_quality_scaling(lum_quality); + cinfo.q_scale_factor[1] = jpeg_quality_scaling(chrom_quality); + if ( lum_quality != chrom_quality ) + { + /* disable subsampling - ref. Libjpeg.txt */ + cinfo.comp_info[0].v_samp_factor = 1; + cinfo.comp_info[0].h_samp_factor = 1; + cinfo.comp_info[1].v_samp_factor = 1; + cinfo.comp_info[1].h_samp_factor = 1; + } + jpeg_default_qtables( &cinfo, TRUE ); +#endif // #if JPEG_LIB_VERSION >= 70 + jpeg_start_compress( &cinfo, TRUE ); if( channels > 1 ) diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index 9b06c5744c..05bd1227ff 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -433,6 +433,31 @@ TEST(Imgcodecs_Jpeg, encode_decode_optimize_jpeg) remove(output_optimized.c_str()); } + +TEST(Imgcodecs_Jpeg, encode_decode_rst_jpeg) +{ + cvtest::TS& ts = *cvtest::TS::ptr(); + string input = string(ts.get_data_path()) + "../cv/shared/lena.png"; + cv::Mat img = cv::imread(input); + ASSERT_FALSE(img.empty()); + + std::vector params; + params.push_back(IMWRITE_JPEG_RST_INTERVAL); + params.push_back(1); + + string output_rst = cv::tempfile(".jpg"); + EXPECT_NO_THROW(cv::imwrite(output_rst, img, params)); + cv::Mat img_jpg_rst = cv::imread(output_rst); + + string output_normal = cv::tempfile(".jpg"); + EXPECT_NO_THROW(cv::imwrite(output_normal, img)); + cv::Mat img_jpg_normal = cv::imread(output_normal); + + EXPECT_EQ(0, cvtest::norm(img_jpg_rst, img_jpg_normal, NORM_INF)); + + remove(output_optimized.c_str()); +} + #endif