mirror of
https://github.com/opencv/opencv.git
synced 2026-11-09 18:04:29 -06:00
Merge pull request #30165 from chacha21:tiff_compression_level
Adding IMWRITE_TIFF_COMPRESSION_LEVEL - #30165 For some compression schemes (DEFLATE, ZSTD), libtiff supports a TIFFTAG_ZIPQUALITY. This PR adds an IMWRITE_TIFF_COMPRESSION_LEVEL param to set it. - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [X] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake I don't know what kind of automatic tests could be relevant. I just checked that 1) the param is honoured 2) for some basic mats, level 1 gives lower compression than level 9 3) level -1 (default) gives a compression ratio between levels 1 and 9 (actually seems to be 6, as expected) But I am not sure that 2) and 3) are generally guaranteed by the compression scheme for any data, so is it relevant to make a test assert on that ?
This commit is contained in:
@@ -110,6 +110,7 @@ enum ImwriteFlags {
|
||||
IMWRITE_TIFF_XDPI = 257,//!< For TIFF, use to specify the X direction DPI
|
||||
IMWRITE_TIFF_YDPI = 258,//!< For TIFF, use to specify the Y direction DPI
|
||||
IMWRITE_TIFF_COMPRESSION = 259,//!< For TIFF, use to specify the image compression scheme. See cv::ImwriteTiffCompressionFlags. The compression scheme can be specified by this flag; the default is LZW compression, except for 32F depth where it is NONE
|
||||
IMWRITE_TIFF_COMPRESSION_LEVEL = 260,//!< For TIFF, if IMWRITE_TIFF_COMPRESSION was used and supports it, sets a compression level. Valid range and meaning depends on the compression scheme. It is currently 1-9 for DEFLATE, 0-9 for LZMA, 1-22 for ZSTD, 1-100 for WEBP. -1 can be used as default.
|
||||
IMWRITE_TIFF_ROWSPERSTRIP = 278,//!< For TIFF, use to specify the number of rows per strip.
|
||||
IMWRITE_TIFF_PREDICTOR = 317,//!< For TIFF, use to specify predictor. See cv::ImwriteTiffPredictorFlags. Default is IMWRITE_TIFF_PREDICTOR_HORIZONTAL .
|
||||
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
|
||||
|
||||
@@ -1336,7 +1336,7 @@ TiffEncoder::TiffEncoder()
|
||||
{
|
||||
m_description = "TIFF Files (*.tiff;*.tif)";
|
||||
m_buf_supported = true;
|
||||
m_supported_encode_key = {IMWRITE_TIFF_RESUNIT, IMWRITE_TIFF_XDPI, IMWRITE_TIFF_YDPI, IMWRITE_TIFF_COMPRESSION, IMWRITE_TIFF_ROWSPERSTRIP, IMWRITE_TIFF_PREDICTOR};
|
||||
m_supported_encode_key = {IMWRITE_TIFF_RESUNIT, IMWRITE_TIFF_XDPI, IMWRITE_TIFF_YDPI, IMWRITE_TIFF_COMPRESSION, IMWRITE_TIFF_ROWSPERSTRIP, IMWRITE_TIFF_PREDICTOR, IMWRITE_TIFF_COMPRESSION_LEVEL};
|
||||
}
|
||||
|
||||
TiffEncoder::~TiffEncoder()
|
||||
@@ -1477,6 +1477,8 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
|
||||
const int predictor_default_32F = IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT;
|
||||
const int predictor_default = IMWRITE_TIFF_PREDICTOR_HORIZONTAL;
|
||||
int compression = -1;
|
||||
int compressionLevel = -1;
|
||||
std::pair<int, int> compressionLevelValidRange = {-1, -1};
|
||||
int predictor = -1;
|
||||
int resUnit = -1, dpiX = -1, dpiY = -1;
|
||||
|
||||
@@ -1521,6 +1523,45 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (compression) {
|
||||
case IMWRITE_TIFF_COMPRESSION_DEFLATE:
|
||||
case IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE:
|
||||
compressionLevelValidRange = std::make_pair(1, 9);
|
||||
break;
|
||||
case IMWRITE_TIFF_COMPRESSION_LZMA:
|
||||
compressionLevelValidRange = std::make_pair(0, 9);
|
||||
break;
|
||||
case IMWRITE_TIFF_COMPRESSION_ZSTD:
|
||||
compressionLevelValidRange = std::make_pair(1, 22);
|
||||
break;
|
||||
case IMWRITE_TIFF_COMPRESSION_WEBP:
|
||||
compressionLevelValidRange = std::make_pair(1, 100);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
if (readParam(params, IMWRITE_TIFF_COMPRESSION_LEVEL, compressionLevel))
|
||||
{
|
||||
const bool isValidCompression =
|
||||
(compression == IMWRITE_TIFF_COMPRESSION_DEFLATE) ||
|
||||
(compression == IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE) ||
|
||||
(compression == IMWRITE_TIFF_COMPRESSION_LZMA) ||
|
||||
(compression == IMWRITE_TIFF_COMPRESSION_ZSTD) ||
|
||||
(compression == IMWRITE_TIFF_COMPRESSION_WEBP);
|
||||
const bool isValidValue = (compressionLevel == -1) ||
|
||||
((compressionLevel >= compressionLevelValidRange.first) && (compressionLevel <= compressionLevelValidRange.second));
|
||||
if (!isValidCompression) {
|
||||
CV_LOG_WARNING(nullptr, cv::format("IMWRITE_TIFF_COMPRESSION_LEVEL can only be used for DEFLATE, LZMA, ZSTD or WEBP compression. It is ignored."));
|
||||
compressionLevel = -1;
|
||||
}
|
||||
else if (!isValidValue) {
|
||||
CV_LOG_WARNING(nullptr, cv::format("The value(%d) for IMWRITE_TIFF_COMPRESSION_LEVEL=%d must be in the range %d-%d (or -1 for default). It is set to default.",
|
||||
compressionLevel, compression,
|
||||
compressionLevelValidRange.first, compressionLevelValidRange.second));
|
||||
compressionLevel = -1;
|
||||
}
|
||||
}
|
||||
if(readParam(params, IMWRITE_TIFF_PREDICTOR, predictor))
|
||||
{
|
||||
switch(predictor) {
|
||||
@@ -1675,6 +1716,40 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
|
||||
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitsPerChannel));
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_COMPRESSION, page_compression));
|
||||
if (compressionLevel >= 0)
|
||||
{
|
||||
switch(page_compression)
|
||||
{
|
||||
case IMWRITE_TIFF_COMPRESSION_DEFLATE:
|
||||
case IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE:
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_ZIPQUALITY, compressionLevel));
|
||||
break;
|
||||
case IMWRITE_TIFF_COMPRESSION_LZMA:
|
||||
#ifdef TIFFTAG_LZMAPRESET // libtiff 4.0.0+
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_LZMAPRESET, compressionLevel));
|
||||
#else
|
||||
CV_LOG_WARNING(nullptr, cv::format("The LZMA compression(%d) is not supported in that build of the TIFF library. It is ignored.", page_compression));
|
||||
#endif
|
||||
break;
|
||||
case IMWRITE_TIFF_COMPRESSION_ZSTD:
|
||||
#ifdef TIFFTAG_ZSTD_LEVEL // libtiff 4.0.10+
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_ZSTD_LEVEL, compressionLevel));
|
||||
#else
|
||||
CV_LOG_WARNING(nullptr, cv::format("The ZSTD compression(%d) is not supported in that build of the TIFF library. It is ignored.", page_compression));
|
||||
#endif
|
||||
break;
|
||||
case IMWRITE_TIFF_COMPRESSION_WEBP:
|
||||
#ifdef TIFFTAG_WEBP_LEVEL // libtiff 4.0.10+
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_WEBP_LEVEL, compressionLevel));
|
||||
#else
|
||||
CV_LOG_WARNING(nullptr, cv::format("The WEBP compression(%d) is not supported in that build of the TIFF library. It is ignored.", page_compression));
|
||||
#endif
|
||||
break;
|
||||
default:
|
||||
CV_LOG_WARNING(nullptr, cv::format("Unexpected page_compression %d", page_compression));
|
||||
break;
|
||||
}//end switch(page_compression)
|
||||
}//end if (compressionLevel >= 0)
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, colorspace));
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, channels));
|
||||
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG));
|
||||
|
||||
@@ -1830,6 +1830,63 @@ TEST(Imgcodecs_Tiff, decode_planar_separate_multipage)
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), page1, pages[1]);
|
||||
}
|
||||
|
||||
// https://github.com/opencv/opencv/pull/30165
|
||||
|
||||
typedef tuple< int, int, int > Method_minLevel_maxLevel;
|
||||
typedef testing::TestWithParam<Method_minLevel_maxLevel> Imgcodecs_Tiff_compress_level;
|
||||
|
||||
TEST_P(Imgcodecs_Tiff_compress_level, normal)
|
||||
{
|
||||
// Get test parameters
|
||||
const int compressMethod = get<0>(GetParam());
|
||||
const int minLevel = get<1>(GetParam());
|
||||
const int maxLevel = get<2>(GetParam());
|
||||
|
||||
//build a dummy image that can be heavily compressed
|
||||
cv::Mat src = cv::Mat(cv::Size(640, 480), CV_8UC3);
|
||||
src.forEach<cv::Vec3b>([&](cv::Vec3b& pixel, const int* pos) {
|
||||
const int y = pos[0];
|
||||
const int x = pos[1];
|
||||
const uchar value = static_cast<uchar>(y*src.cols+x);
|
||||
pixel = cv::Vec3b(value, value, value);
|
||||
});
|
||||
|
||||
std::vector<uint8_t> bufNone;
|
||||
std::vector<int> paramsNone = { IMWRITE_TIFF_COMPRESSION, IMWRITE_TIFF_COMPRESSION_NONE};
|
||||
ASSERT_NO_THROW(ASSERT_TRUE(imencode(".tiff", src, bufNone, paramsNone)));
|
||||
|
||||
std::vector<uint8_t> bufDef;
|
||||
std::vector<int> paramsDef = {IMWRITE_TIFF_COMPRESSION, compressMethod};
|
||||
ASSERT_NO_THROW(ASSERT_TRUE(imencode(".tiff", src, bufDef, paramsDef)));
|
||||
|
||||
std::vector<uint8_t> bufMin;
|
||||
std::vector<int> paramsMin = {IMWRITE_TIFF_COMPRESSION, compressMethod, IMWRITE_TIFF_COMPRESSION_LEVEL, minLevel};
|
||||
ASSERT_NO_THROW(ASSERT_TRUE(imencode(".tiff", src, bufMin, paramsMin)));
|
||||
|
||||
std::vector<uint8_t> bufMax;
|
||||
std::vector<int> paramsMax = {IMWRITE_TIFF_COMPRESSION, compressMethod, IMWRITE_TIFF_COMPRESSION_LEVEL, maxLevel};
|
||||
ASSERT_NO_THROW(ASSERT_TRUE(imencode(".tiff", src, bufMax, paramsMax)));
|
||||
|
||||
ASSERT_LT(bufDef.size(), bufNone.size());
|
||||
ASSERT_LE(bufDef.size(), bufMin.size());
|
||||
ASSERT_LE(bufMax.size(), bufDef.size());
|
||||
ASSERT_NE(bufMax.size(), bufMin.size());
|
||||
}
|
||||
|
||||
const Method_minLevel_maxLevel supportedMethod_minLevel_maxLevel[] =
|
||||
{
|
||||
make_tuple<int, int, int>(IMWRITE_TIFF_COMPRESSION_DEFLATE, 1, 9),
|
||||
make_tuple<int, int, int>(IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE, 1, 9),
|
||||
// make_tuple<int, int, int>(IMWRITE_TIFF_COMPRESSION_LZMA, 0, 9),
|
||||
// make_tuple<int, int, int>(IMWRITE_TIFF_COMPRESSION_ZSTD, 1, 22),
|
||||
// make_tuple<int, int, int>(IMWRITE_TIFF_COMPRESSION_WEBP, 1, 100),
|
||||
};
|
||||
|
||||
INSTANTIATE_TEST_CASE_P(Imgcodecs_Tiff, Imgcodecs_Tiff_compress_level,
|
||||
testing::ValuesIn(supportedMethod_minLevel_maxLevel)
|
||||
);
|
||||
|
||||
|
||||
#endif
|
||||
|
||||
}} // namespace
|
||||
|
||||
Reference in New Issue
Block a user