1
0
mirror of https://github.com/opencv/opencv.git synced 2026-07-30 15:53:03 +04:00

Merge pull request #28785 from chacha21:tiff_32F_compression

Allow TIFF compression schemes for 32F#28785

Related to [https://github.com/opencv/opencv/issues/28775](https://github.com/opencv/opencv/issues/28775)

Previously, only 32FC3+SGILOG could be specified for TIFF encoding. But when floats use quantization, compression schemes can be efficient even on 32F data. This PR will allow them.

### Pull Request Readiness Checklist

See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request

- [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
- [X] 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.
- [X] The feature is well documented and sample code can be built with the project CMake

I just don't know what kind of accuracy/performance tests should be added.
This commit is contained in:
Pierre Chatelier
2026-04-10 14:16:29 +02:00
committed by GitHub
parent 1acf28507f
commit 723670c33d
4 changed files with 121 additions and 15 deletions
@@ -109,7 +109,7 @@ enum ImwriteFlags {
IMWRITE_TIFF_RESUNIT = 256,//!< For TIFF, use to specify which DPI resolution unit to set. See ImwriteTiffResolutionUnitFlags. Default is IMWRITE_TIFF_RESOLUTION_UNIT_INCH.
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. 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.
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_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.
@@ -540,8 +540,11 @@ can be saved using this function, with these exceptions:
32-bit signed (CV_32S),
32-bit float (CV_32F) and 64-bit float (CV_64F) images can be saved.
- Multiple images (vector of Mat) can be saved in TIFF format (see the code sample below).
- 32-bit float 3-channel (CV_32FC3) TIFF images will be saved
using the LogLuv high dynamic range encoding (4 bytes per pixel)
- 32-bit float 3-channel (CV_32FC3) TIFF images can be saved
using the LogLuv high dynamic range encoding (4 bytes per pixel) through TIFF_COMPRESSION_SGILOG or
(3 bytes per pixel) through TIFF_COMPRESSION_SGILOG24.
- Other compression schemes (LZW...) are supported as well for 32F depth, but the efficiency might not
be very good for the floating-point representation bit patterns.
- With GIF encoder, 8-bit unsigned (CV_8U) images can be saved.
- GIF images with an alpha channel can be saved using this function.
To achieve this, create an 8-bit 4-channel (CV_8UC4) BGRA image, ensuring the alpha channel is the last component.
+72 -11
View File
@@ -1312,8 +1312,12 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
cv::Ptr<void> tif_cleanup(tif, cv_tiffCloseHandle);
//Settings that matter to all images
int compression = IMWRITE_TIFF_COMPRESSION_LZW;
int predictor = IMWRITE_TIFF_PREDICTOR_HORIZONTAL;
const int compression_default_32F = IMWRITE_TIFF_COMPRESSION_NONE;
const int compression_default = IMWRITE_TIFF_COMPRESSION_LZW;
const int predictor_default_32F = IMWRITE_TIFF_PREDICTOR_FLOATINGPOINT;
const int predictor_default = IMWRITE_TIFF_PREDICTOR_HORIZONTAL;
int compression = -1;
int predictor = -1;
int resUnit = -1, dpiX = -1, dpiY = -1;
if(readParam(params, IMWRITE_TIFF_COMPRESSION, compression))
@@ -1420,14 +1424,37 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PAGENUMBER, page, img_vec.size()));
}
if (type == CV_32FC3 && compression == COMPRESSION_SGILOG)
{
if (!write_32FC3_SGILOG(img, tif))
return false;
continue;
}
const bool is32F = (depth == CV_32F);
int page_compression =
(compression < 0) ?
(is32F ? compression_default_32F : compression_default) :
compression;
int page_predictor =
(predictor < 0) ?
(is32F ? predictor_default_32F : predictor_default) :
predictor;
int page_compression = compression;
if ((page_compression == COMPRESSION_SGILOG) || (page_compression == COMPRESSION_SGILOG24))
{
if (depth != CV_32F)
CV_Error(cv::Error::StsError, "SGILOG requires 32F");
else if ((page_compression == COMPRESSION_SGILOG24) && (type != CV_32FC3))
CV_Error(cv::Error::StsError, "SGILOG24 requires 32FC3");
else if ((page_compression == COMPRESSION_SGILOG) && (type != CV_32FC1) && (type != CV_32FC3))
CV_Error(cv::Error::StsError, "SGILOG requires 32FC1 or 32FC3");
else if ((page_compression == COMPRESSION_SGILOG) && (type == CV_32FC3))
{
if (!write_32FC3_SGILOG(img, tif))
return false;
continue;
}
else
{
if (!write_32F_SGILOG(img, tif, page_compression))
return false;
continue;
}
}
int bitsPerChannel = -1;
uint16_t sample_format = SAMPLEFORMAT_INT;
@@ -1460,7 +1487,6 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
case CV_32F:
{
bitsPerChannel = 32;
page_compression = COMPRESSION_NONE;
sample_format = SAMPLEFORMAT_IEEEFP;
break;
}
@@ -1498,7 +1524,7 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
if (page_compression == COMPRESSION_LZW || page_compression == COMPRESSION_ADOBE_DEFLATE || page_compression == COMPRESSION_DEFLATE)
{
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PREDICTOR, page_predictor));
}
if (resUnit >= RESUNIT_NONE && resUnit <= RESUNIT_CENTIMETER)
@@ -1583,6 +1609,41 @@ bool TiffEncoder::write_32FC3_SGILOG(const Mat& _img, void* tif_)
return true;
}
bool TiffEncoder::write_32F_SGILOG(const Mat& _img, void* tif_, int compression)
{
TIFF* tif = (TIFF*)tif_;
CV_Assert(tif);
const int nChannels = _img.channels();
CV_Assert(
((compression == COMPRESSION_SGILOG) && ((nChannels == 1) || (nChannels == 3))) ||
((compression == COMPRESSION_SGILOG24) && (nChannels == 3))
);
Mat img;
if (nChannels == 1)
img = _img;
else
cvtColor(_img, img, COLOR_BGR2XYZ);
//done by caller: CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, img.cols));
//done by caller: CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_IMAGELENGTH, img.rows));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, nChannels));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_COMPRESSION, compression));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PHOTOMETRIC,
(nChannels == 1) ? PHOTOMETRIC_LOGL : PHOTOMETRIC_LOGLUV));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT));
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1));
const int strip_size = nChannels * img.cols;
for (int i = 0; i < img.rows; i++)
{
CV_TIFF_CHECK_CALL(TIFFWriteEncodedStrip(tif, i, (tdata_t)img.ptr<float>(i), strip_size * sizeof(float)) != (tsize_t)-1);
}
CV_TIFF_CHECK_CALL(TIFFWriteDirectory(tif));
return true;
}
bool TiffEncoder::writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params)
{
return writeLibTiff(img_vec, params);
+1
View File
@@ -134,6 +134,7 @@ public:
protected:
bool writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params );
bool write_32FC3_SGILOG(const Mat& img, void* tif);
bool write_32F_SGILOG(const Mat& img, void* tif, int compression);
private:
TiffEncoder(const TiffEncoder &); // copy disabled
+42 -1
View File
@@ -934,7 +934,7 @@ Imgcodes_Tiff_TypeAndComp all_types[] = {
{ CV_16UC1, true }, { CV_16UC3, true }, { CV_16UC4, true },
{ CV_16SC1, true }, { CV_16SC3, true }, { CV_16SC4, true },
{ CV_32SC1, true }, { CV_32SC3, true }, { CV_32SC4, true },
{ CV_32FC1, false }, { CV_32FC3, false }, { CV_32FC4, false }, // No compression
{ CV_32FC1, true }, { CV_32FC3, true }, { CV_32FC4, true },
{ CV_64FC1, false }, { CV_64FC3, false }, { CV_64FC4, false } // No compression
};
@@ -1293,6 +1293,47 @@ TEST(Imgcodecs_Tiff, read_junk) {
ASSERT_TRUE(img.empty());
}
typedef int Imgcodecs_Tiff_32F_Compressions_32F_Values;
typedef testing::TestWithParam<Imgcodecs_Tiff_32F_Compressions_32F_Values> Imgcodecs_Tiff_32F_Compressions_32F;
TEST_P(Imgcodecs_Tiff_32F_Compressions_32F, compressions_32F)
{
const int compression = GetParam();
const Size size(64, 64);
Mat src = Mat(size, CV_32FC1);
cv::randu(src, cv::Scalar::all(0.), cv::Scalar::all(1.));
std::vector<int> params;
if (compression > 0)
{
params.push_back(IMWRITE_TIFF_COMPRESSION);
params.push_back(compression);
}
std::vector<unsigned char> encoded_data;
imencode(".tiff", src, encoded_data, params);
Mat dst;
imdecode(encoded_data, IMREAD_UNCHANGED, &dst);
EXPECT_LE(cvtest::norm(src, dst, NORM_INF), 1e-6);
}
const int Imgcodecs_Tiff_32F_Compressions_32F_All_Values[] =
{
-1,//will mean "default"
IMWRITE_TIFF_COMPRESSION_NONE,
IMWRITE_TIFF_COMPRESSION_LZW,
//IMWRITE_TIFF_COMPRESSION_LZMA,//might not be configured
//IMWRITE_TIFF_COMPRESSION_ZSTD,//might not be configured
//IMWRITE_TIFF_COMPRESSION_DEFLATE,//deprecated
IMWRITE_TIFF_COMPRESSION_ADOBE_DEFLATE,
};
INSTANTIATE_TEST_CASE_P(compressions_32F, Imgcodecs_Tiff_32F_Compressions_32F, testing::ValuesIn(Imgcodecs_Tiff_32F_Compressions_32F_All_Values));
#endif
}} // namespace