diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index 6e317f585b..6cc785e07d 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -332,7 +332,10 @@ can be saved using this function, with these exceptions: 8-bit (or 16-bit) 4-channel image BGRA, where the alpha channel goes last. Fully transparent pixels should have alpha set to 0, fully opaque pixels should have alpha set to 255/65535 (see the code sample below). - With PGM/PPM encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved. -- With TIFF encoder, 8-bit unsigned (CV_8U), 16-bit unsigned (CV_16U), +- With TIFF encoder, 8-bit unsigned (CV_8U), 8-bit signed (CV_8S), + 16-bit unsigned (CV_16U), 16-bit signed (CV_16S), + 32-bit unsigned (CV_32U), 32-bit signed (CV_32S), + 64-bit unsigned (CV_64U), 64-bit signed (CV_64S), 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 diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index ccc6579a01..99f1840fbf 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -55,6 +55,7 @@ #include "tiff.h" #include "tiffio.h" +#include "tiffvers.h" // For TIFFLIB_VERSION namespace cv { @@ -344,17 +345,20 @@ bool TiffDecoder::readHeader() } case 32: { - CV_Check((int)sample_format, sample_format == SAMPLEFORMAT_IEEEFP || sample_format == SAMPLEFORMAT_INT, ""); - int depth = sample_format == SAMPLEFORMAT_IEEEFP ? CV_32F : CV_32S; + CV_Check((int)sample_format, sample_format == SAMPLEFORMAT_IEEEFP || sample_format == SAMPLEFORMAT_UINT || sample_format == SAMPLEFORMAT_INT, ""); + int depth = sample_format == SAMPLEFORMAT_IEEEFP ? CV_32F : sample_format == SAMPLEFORMAT_INT ? CV_32S : CV_32U; m_type = CV_MAKETYPE(depth, wanted_channels); result = true; break; } case 64: - CV_CheckEQ((int)sample_format, SAMPLEFORMAT_IEEEFP, ""); - m_type = CV_MAKETYPE(CV_64F, wanted_channels); + { + CV_Check((int)sample_format, sample_format == SAMPLEFORMAT_IEEEFP || sample_format == SAMPLEFORMAT_UINT || sample_format == SAMPLEFORMAT_INT, ""); + int depth = sample_format == SAMPLEFORMAT_IEEEFP ? CV_64F : sample_format == SAMPLEFORMAT_INT ? CV_64S : CV_64U; + m_type = CV_MAKETYPE(depth, wanted_channels); result = true; break; + } default: CV_Error(cv::Error::StsError, "Invalid bitsperpixel value read from TIFF header! Must be 1, 8, 10, 12, 14, 16, 32 or 64."); } @@ -589,21 +593,24 @@ bool TiffDecoder::readData( Mat& img ) int type = img.type(); int depth = CV_MAT_DEPTH(type); + CV_CheckDepth(type, ( depth == CV_8U || depth == CV_8S || + depth == CV_16U || depth == CV_16S || + depth == CV_32U || depth == CV_32S || + depth == CV_64U || depth == CV_64S || + depth == CV_32F || depth == CV_64F), "Unsupported depth of image"); + CV_Assert(!m_tif.empty()); TIFF* tif = (TIFF*)m_tif.get(); uint16_t photometric = (uint16_t)-1; CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)); - if (m_hdr && depth >= CV_32F) + if (m_hdr && ((depth == CV_32F) || (depth == CV_64F)) ) { CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT)); } bool color = img.channels() > 1; - - CV_CheckType(type, depth == CV_8U || depth == CV_8S || depth == CV_16U || depth == CV_16S || depth == CV_32S || depth == CV_32F || depth == CV_64F, ""); - if (m_width && m_height) { int is_tiled = TIFFIsTiled(tif) != 0; @@ -755,7 +762,7 @@ bool TiffDecoder::readData( Mat& img ) } } } - else if (dst_bpp == 32 || dst_bpp == 64) + else if (depth == CV_32F || depth == CV_64F) { CV_Assert(ncn == img.channels()); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)); @@ -1038,7 +1045,7 @@ bool TiffDecoder::readData( Mat& img ) CV_TIFF_CHECK_CALL((int)TIFFReadEncodedTile(tif, tileidx, src_buffer, src_buffer_size) >= 0); } - Mat m_tile(Size(tile_width0, tile_height0), CV_MAKETYPE((dst_bpp == 32) ? (depth == CV_32S ? CV_32S : CV_32F) : CV_64F, ncn), src_buffer); + Mat m_tile(Size(tile_width0, tile_height0), CV_MAKETYPE(depth, ncn), src_buffer); Rect roi_tile(0, 0, tile_width, tile_height); Rect roi_img(x, img_y, tile_width, tile_height); if (!m_hdr && ncn == 3 && !m_use_rgb) @@ -1066,7 +1073,7 @@ bool TiffDecoder::readData( Mat& img ) ( ( dst_bpp != 8 ) && ( !doReadScanline ) ) ); } - if (m_hdr && depth >= CV_32F) + if (m_hdr && ((depth == CV_32F) || (depth == CV_64F)) ) { CV_Assert(photometric == PHOTOMETRIC_LOGLUV); if (m_use_rgb) @@ -1097,7 +1104,11 @@ ImageEncoder TiffEncoder::newEncoder() const bool TiffEncoder::isFormatSupported( int depth ) const { - return depth == CV_8U || depth == CV_8S || depth == CV_16U || depth == CV_16S || depth == CV_32S || depth == CV_32F || depth == CV_64F; + return depth == CV_8U || depth == CV_8S || + depth == CV_16U || depth == CV_16S || + depth == CV_32U || depth == CV_32S || + depth == CV_64U || depth == CV_64S || + depth == CV_32F || depth == CV_64F; } class TiffEncoderBufHelper @@ -1237,7 +1248,7 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect int width = img.cols, height = img.rows; int type = img.type(); int depth = CV_MAT_DEPTH(type); - CV_CheckType(type, depth == CV_8U || depth == CV_8S || depth == CV_16U || depth == CV_16S || depth == CV_32S || depth == CV_32F || depth == CV_64F, ""); + CV_CheckDepth(type, isFormatSupported(depth), "Unsupported depth of input image"); CV_CheckType(type, channels >= 1 && channels <= 4, ""); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width)); @@ -1281,12 +1292,34 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect break; } + case CV_32U: + { + bitsPerChannel = 32; + sample_format = SAMPLEFORMAT_UINT; + break; + } + case CV_32S: { bitsPerChannel = 32; sample_format = SAMPLEFORMAT_INT; break; } + + case CV_64U: + { + bitsPerChannel = 64; + sample_format = SAMPLEFORMAT_UINT; + break; + } + + case CV_64S: + { + bitsPerChannel = 64; + sample_format = SAMPLEFORMAT_INT; + break; + } + case CV_32F: { bitsPerChannel = 32; @@ -1307,6 +1340,16 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect } } +// Predictor 2 for 64-bit is supported at v4.4.0 or later. +// See https://libtiff.gitlab.io/libtiff/releases/v4.4.0.html +#if TIFFLIB_VERSION < 20220520 /* Magic number of libtiff v4.4.0 */ + if ( (bitsPerChannel == 64) && (predictor == PREDICTOR_HORIZONTAL /* 2 */) ) + { + CV_LOG_ONCE_WARNING(NULL, "Predictor 2(HORIZONTAL) for 64-bit is supported at v4.4.0 or later, so it is fallbacked to 0(NONE)"); + predictor = PREDICTOR_NONE; + } +#endif + const int bitsPerByte = 8; size_t fileStep = (width * channels * bitsPerChannel) / bitsPerByte; CV_Assert(fileStep > 0); @@ -1423,7 +1466,7 @@ bool TiffEncoder::write( const Mat& img, const std::vector& params) int type = img.type(); int depth = CV_MAT_DEPTH(type); - CV_CheckType(type, depth == CV_8U || depth == CV_8S || depth == CV_16U || depth == CV_16S || depth == CV_32S || depth == CV_32F || depth == CV_64F, ""); + CV_CheckDepth(type, isFormatSupported(depth), "Unsupported depth of input image"); std::vector img_vec; img_vec.push_back(img); @@ -1434,24 +1477,16 @@ static void extend_cvtColor( InputArray _src, OutputArray _dst, int code ) { CV_Assert( !_src.empty() ); CV_Assert( _src.dims() == 2 ); + CV_Assert( (code == COLOR_BGR2RGB) || (code == COLOR_BGRA2RGBA) ); // This function extend_cvtColor reorders the src channels with only thg limited condition. // Otherwise, it calls cvtColor. const int stype = _src.type(); - if(! - ( - ( - ( stype == CV_8SC3 ) || ( stype == CV_8SC4 ) || - ( stype == CV_16SC3 ) || ( stype == CV_16SC4 ) || - ( stype == CV_32SC3 ) || ( stype == CV_32SC4 ) || - ( stype == CV_64FC3 ) || ( stype == CV_64FC4 ) - ) - && - ( - ( code == COLOR_BGR2RGB ) || ( code == COLOR_BGRA2RGBA ) - ) - ) + if( + ( stype == CV_8UC3 ) || ( stype == CV_8UC4 ) || + ( stype == CV_16UC3 ) || ( stype == CV_16UC4 ) || + ( stype == CV_32FC3 ) || ( stype == CV_32FC4 ) ) { cvtColor( _src, _dst, code ); diff --git a/modules/imgcodecs/test/test_tiff.cpp b/modules/imgcodecs/test/test_tiff.cpp index f9b4edaa10..81832d0170 100644 --- a/modules/imgcodecs/test/test_tiff.cpp +++ b/modules/imgcodecs/test/test_tiff.cpp @@ -951,7 +951,10 @@ Imgcodes_Tiff_TypeAndComp all_types[] = { { CV_8SC1, true }, { CV_8SC3, true }, { CV_8SC4, true }, { CV_16UC1, true }, { CV_16UC3, true }, { CV_16UC4, true }, { CV_16SC1, true }, { CV_16SC3, true }, { CV_16SC4, true }, + { CV_32UC1, true }, { CV_32UC3, true }, { CV_32UC4, true }, { CV_32SC1, true }, { CV_32SC3, true }, { CV_32SC4, true }, + { CV_64UC1, true }, { CV_64UC3, true }, { CV_64UC4, true }, + { CV_64SC1, true }, { CV_64SC3, true }, { CV_64SC4, true }, { CV_32FC1, false }, { CV_32FC3, false }, { CV_32FC4, false }, // No compression { CV_64FC1, false }, { CV_64FC3, false }, { CV_64FC4, false } // No compression };