Merge pull request #22064 from Kumataro:3.4-fix22052

* imgcodecs: jpeg: add IMWRITE_JPEG_SAMPLING_FACTOR parameter

* fix compile error

* imgcodecs: jpeg: add CV_LOG_WARNING() and fix how to initilize Mat

* imgcodecs: jpeg: fix for C++98 mode.

* samples: imgcodec_jpeg: Remove license
This commit is contained in:
Kumataro 2022-06-21 03:42:50 +09:00 committed by GitHub
parent 9d06e58c3c
commit dd7b9000ad
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 217 additions and 0 deletions

View File

@ -89,6 +89,7 @@ enum ImwriteFlags {
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.
@ -102,12 +103,22 @@ enum ImwriteFlags {
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.

View File

@ -44,6 +44,8 @@
#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)
@ -640,6 +642,7 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
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 )
{
@ -687,6 +690,27 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
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 );
@ -699,6 +723,14 @@ bool JpegEncoder::write( const Mat& img, const std::vector<int>& params )
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)
{

View File

@ -178,6 +178,98 @@ TEST(Imgcodecs_Jpeg, encode_decode_rst_jpeg)
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

View File

@ -0,0 +1,82 @@
#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;
}