IMWRITE_JPEG_RST_INTERVAL = 4, //!< JPEG restart interval, 0 - 65535, default is 0 - no restart.
IMWRITE_JPEG_LUMA_QUALITY = 5, //!< Separate luma quality level, 0 - 100, default is -1 - don't use.
IMWRITE_JPEG_CHROMA_QUALITY = 6, //!< Separate chroma quality level, 0 - 100, default is -1 - don't use.
+ IMWRITE_JPEG_SAMPLING_FACTOR = 7, //!< For JPEG, set sampling factor. See cv::ImwriteJPEGSamplingFactorParams.
IMWRITE_PNG_COMPRESSION = 16, //!< For PNG, it can be the compression level from 0 to 9. A higher value means a smaller size and longer compression time. If specified, strategy is changed to IMWRITE_PNG_STRATEGY_DEFAULT (Z_DEFAULT_STRATEGY). Default value is 1 (best speed setting).
IMWRITE_PNG_STRATEGY = 17, //!< One of cv::ImwritePNGFlags, default is IMWRITE_PNG_STRATEGY_RLE.
IMWRITE_PNG_BILEVEL = 18, //!< Binary level PNG, 0 or 1, default is 0.
IMWRITE_TIFF_COMPRESSION = 259 //!< For TIFF, use to specify the image compression scheme. See libtiff for integer constants corresponding to compression formats. Note, for images whose depth is CV_32F, only libtiff's SGILOG compression scheme is used. For other supported depths, the compression scheme can be specified by this flag; LZW compression is the default.
};
+enum ImwriteJPEGSamplingFactorParams {
+ IMWRITE_JPEG_SAMPLING_FACTOR_411 = 0x411111, //!< 4x1,1x1,1x1
+ IMWRITE_JPEG_SAMPLING_FACTOR_420 = 0x221111, //!< 2x2,1x1,1x1(Default)
+ IMWRITE_JPEG_SAMPLING_FACTOR_422 = 0x211111, //!< 2x1,1x1,1x1
+ IMWRITE_JPEG_SAMPLING_FACTOR_440 = 0x121111, //!< 1x2,1x1,1x1
+ IMWRITE_JPEG_SAMPLING_FACTOR_444 = 0x111111 //!< 1x1,1x1,1x1(No subsampling)
+ };
+
+
enum ImwriteEXRTypeFlags {
/*IMWRITE_EXR_TYPE_UNIT = 0, //!< not supported */
IMWRITE_EXR_TYPE_HALF = 1, //!< store as HALF (FP16)
IMWRITE_EXR_TYPE_FLOAT = 2 //!< store as FP32 (default)
};
+
//! Imwrite PNG specific flags used to tune the compression algorithm.
/** These flags will be modify the way of PNG image compression and will be passed to the underlying zlib processing stage.
#ifdef HAVE_JPEG
+#include <opencv2/core/utils/logger.hpp>
+
#ifdef _MSC_VER
//interaction between '_setjmp' and C++ object destruction is non-portable
#pragma warning(disable: 4611)
int rst_interval = 0;
int luma_quality = -1;
int chroma_quality = -1;
+ uint32_t sampling_factor = 0; // same as 0x221111
for( size_t i = 0; i < params.size(); i += 2 )
{
rst_interval = params[i+1];
rst_interval = MIN(MAX(rst_interval, 0), 65535L);
}
+
+ if( params[i] == IMWRITE_JPEG_SAMPLING_FACTOR )
+ {
+ sampling_factor = static_cast<uint32_t>(params[i+1]);
+
+ switch ( sampling_factor )
+ {
+ case IMWRITE_JPEG_SAMPLING_FACTOR_411:
+ case IMWRITE_JPEG_SAMPLING_FACTOR_420:
+ case IMWRITE_JPEG_SAMPLING_FACTOR_422:
+ case IMWRITE_JPEG_SAMPLING_FACTOR_440:
+ case IMWRITE_JPEG_SAMPLING_FACTOR_444:
+ // OK.
+ break;
+
+ default:
+ CV_LOG_WARNING(NULL, cv::format("Unknown value for IMWRITE_JPEG_SAMPLING_FACTOR: 0x%06x", sampling_factor ) );
+ sampling_factor = 0;
+ break;
+ }
+ }
}
jpeg_set_defaults( &cinfo );
if( optimize )
cinfo.optimize_coding = TRUE;
+ if( (channels > 1) && ( sampling_factor != 0 ) )
+ {
+ cinfo.comp_info[0].v_samp_factor = (sampling_factor >> 16 ) & 0xF;
+ cinfo.comp_info[0].h_samp_factor = (sampling_factor >> 20 ) & 0xF;
+ cinfo.comp_info[1].v_samp_factor = 1;
+ cinfo.comp_info[1].h_samp_factor = 1;
+ }
+
#if JPEG_LIB_VERSION >= 70
if (luma_quality >= 0 && chroma_quality >= 0)
{
EXPECT_EQ(0, remove(output_normal.c_str()));
}
+//==================================================================================================
+
+static const uint32_t default_sampling_factor = static_cast<uint32_t>(0x221111);
+
+static uint32_t test_jpeg_subsampling( const Mat src, const vector<int> param )
+{
+ vector<uint8_t> jpeg;
+
+ if ( cv::imencode(".jpg", src, jpeg, param ) == false )
+ {
+ return 0;
+ }
+
+ if ( src.channels() != 3 )
+ {
+ return 0;
+ }
+
+ // Find SOF Marker(FFC0)
+ int sof_offset = 0; // not found.
+ int jpeg_size = static_cast<int>( jpeg.size() );
+ for ( int i = 0 ; i < jpeg_size - 1; i++ )
+ {
+ if ( (jpeg[i] == 0xff ) && ( jpeg[i+1] == 0xC0 ) )
+ {
+ sof_offset = i;
+ break;
+ }
+ }
+ if ( sof_offset == 0 )
+ {
+ return 0;
+ }
+
+ // Extract Subsampling Factor from SOF.
+ return ( jpeg[sof_offset + 0x0A + 3 * 0 + 1] << 16 ) +
+ ( jpeg[sof_offset + 0x0A + 3 * 1 + 1] << 8 ) +
+ ( jpeg[sof_offset + 0x0A + 3 * 2 + 1] ) ;
+}
+
+TEST(Imgcodecs_Jpeg, encode_subsamplingfactor_default)
+{
+ vector<int> param;
+ Mat src( 48, 64, CV_8UC3, cv::Scalar::all(0) );
+ EXPECT_EQ( default_sampling_factor, test_jpeg_subsampling(src, param) );
+}
+
+TEST(Imgcodecs_Jpeg, encode_subsamplingfactor_usersetting_valid)
+{
+ Mat src( 48, 64, CV_8UC3, cv::Scalar::all(0) );
+ const uint32_t sampling_factor_list[] = {
+ IMWRITE_JPEG_SAMPLING_FACTOR_411,
+ IMWRITE_JPEG_SAMPLING_FACTOR_420,
+ IMWRITE_JPEG_SAMPLING_FACTOR_422,
+ IMWRITE_JPEG_SAMPLING_FACTOR_440,
+ IMWRITE_JPEG_SAMPLING_FACTOR_444,
+ };
+ const int sampling_factor_list_num = 5;
+
+ for ( int i = 0 ; i < sampling_factor_list_num; i ++ )
+ {
+ vector<int> param;
+ param.push_back( IMWRITE_JPEG_SAMPLING_FACTOR );
+ param.push_back( sampling_factor_list[i] );
+ EXPECT_EQ( sampling_factor_list[i], test_jpeg_subsampling(src, param) );
+ }
+}
+
+TEST(Imgcodecs_Jpeg, encode_subsamplingfactor_usersetting_invalid)
+{
+ Mat src( 48, 64, CV_8UC3, cv::Scalar::all(0) );
+ const uint32_t sampling_factor_list[] = { // Invalid list
+ 0x111112,
+ 0x000000,
+ 0x001111,
+ 0xFF1111,
+ 0x141111, // 1x4,1x1,1x1 - unknown
+ 0x241111, // 2x4,1x1,1x1 - unknown
+ 0x421111, // 4x2,1x1,1x1 - unknown
+ 0x441111, // 4x4,1x1,1x1 - 410(libjpeg cannot handle it)
+ };
+ const int sampling_factor_list_num = 8;
+
+ for ( int i = 0 ; i < sampling_factor_list_num; i ++ )
+ {
+ vector<int> param;
+ param.push_back( IMWRITE_JPEG_SAMPLING_FACTOR );
+ param.push_back( sampling_factor_list[i] );
+ EXPECT_EQ( default_sampling_factor, test_jpeg_subsampling(src, param) );
+ }
+}
+
#endif // HAVE_JPEG
}} // namespace
--- /dev/null
+#include <opencv2/core.hpp>
+#include <opencv2/imgproc.hpp>
+#include <opencv2/imgcodecs.hpp>
+#include <iostream>
+#include <vector>
+
+using namespace std;
+using namespace cv;
+
+int main(int /*argc*/, const char** /* argv */ )
+{
+ Mat framebuffer( 160 * 2, 160 * 5, CV_8UC3, cv::Scalar::all(255) );
+
+ Mat img( 160, 160, CV_8UC3, cv::Scalar::all(255) );
+
+ // Create test image.
+ {
+ const Point center( img.rows / 2 , img.cols /2 );
+
+ for( int radius = 5; radius < img.rows ; radius += 3.5 )
+ {
+ cv::circle( img, center, radius, Scalar(255,0,255) );
+ }
+ cv::rectangle( img, Point(0,0), Point(img.rows-1, img.cols-1), Scalar::all(0), 2 );
+ }
+
+ // Draw original image(s).
+ int top = 0; // Upper images
+ {
+ for( int left = 0 ; left < img.rows * 5 ; left += img.rows ){
+ Mat roi = framebuffer( Rect( left, top, img.rows, img.cols ) );
+ img.copyTo(roi);
+
+ cv::putText( roi, "original", Point(5,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar::all(0), 2, 4, false );
+ }
+ }
+
+ // Draw lossy images
+ top += img.cols; // Lower images
+ {
+ struct test_config{
+ string comment;
+ uint32_t sampling_factor;
+ } config [] = {
+ { "411", IMWRITE_JPEG_SAMPLING_FACTOR_411 },
+ { "420", IMWRITE_JPEG_SAMPLING_FACTOR_420 },
+ { "422", IMWRITE_JPEG_SAMPLING_FACTOR_422 },
+ { "440", IMWRITE_JPEG_SAMPLING_FACTOR_440 },
+ { "444", IMWRITE_JPEG_SAMPLING_FACTOR_444 },
+ };
+
+ const int config_num = 5;
+
+ int left = 0;
+
+ for ( int i = 0 ; i < config_num; i++ )
+ {
+ // Compress images with sampling factor parameter.
+ vector<int> param;
+ param.push_back( IMWRITE_JPEG_SAMPLING_FACTOR );
+ param.push_back( config[i].sampling_factor );
+ vector<uint8_t> jpeg;
+ (void) imencode(".jpg", img, jpeg, param );
+
+ // Decompress it.
+ Mat jpegMat(jpeg);
+ Mat lossy_img = imdecode(jpegMat, -1);
+
+ // Copy into framebuffer and comment
+ Mat roi = framebuffer( Rect( left, top, lossy_img.rows, lossy_img.cols ) );
+ lossy_img.copyTo(roi);
+ cv::putText( roi, config[i].comment, Point(5,155), FONT_HERSHEY_SIMPLEX, 0.5, Scalar::all(0), 2, 4, false );
+
+ left += lossy_img.rows;
+ }
+ }
+
+ // Output framebuffer(as lossless).
+ imwrite( "imgcodecs_jpeg_samplingfactor_result.png", framebuffer );
+
+ return 0;
+}