From ba5ddd649957e42e61f3ebba620bf503e55e9c60 Mon Sep 17 00:00:00 2001 From: Alexander Alekhin Date: Thu, 4 Apr 2019 00:35:08 +0000 Subject: [PATCH] imgcodecs(tiff): sanitize tiff decoder - more checks - drop separate branches for 32FC1/32FC3(read) handling - added for 32F/64F non-compressed - added tests for 32FC3 (RAW + hdr SGILOG compression) - added test 64FC1 - dump tiff errors on stderr --- modules/imgcodecs/src/grfmt_tiff.cpp | 666 ++++++++++++--------------- modules/imgcodecs/src/grfmt_tiff.hpp | 7 +- modules/imgcodecs/src/utils.cpp | 43 +- modules/imgcodecs/src/utils.hpp | 38 +- modules/imgcodecs/test/test_tiff.cpp | 62 ++- 5 files changed, 396 insertions(+), 420 deletions(-) diff --git a/modules/imgcodecs/src/grfmt_tiff.cpp b/modules/imgcodecs/src/grfmt_tiff.cpp index 7fa02562ad..b1f3d8192c 100644 --- a/modules/imgcodecs/src/grfmt_tiff.cpp +++ b/modules/imgcodecs/src/grfmt_tiff.cpp @@ -48,6 +48,8 @@ #include "precomp.hpp" #ifdef HAVE_TIFF +#include + #include "grfmt_tiff.hpp" #include @@ -61,23 +63,58 @@ using namespace tiff_dummy_namespace; namespace cv { +#define CV_TIFF_CHECK_CALL(call) \ + if (0 == (call)) { \ + CV_LOG_WARNING(NULL, "OpenCV TIFF(line " << __LINE__ << "): failed " #call); \ + CV_Error(Error::StsError, "OpenCV TIFF: failed " #call); \ + } + +#define CV_TIFF_CHECK_CALL_INFO(call) \ + if (0 == (call)) { \ + CV_LOG_INFO(NULL, "OpenCV TIFF(line " << __LINE__ << "): failed optional call: " #call ", ignoring"); \ + } + +#define CV_TIFF_CHECK_CALL_DEBUG(call) \ + if (0 == (call)) { \ + CV_LOG_DEBUG(NULL, "OpenCV TIFF(line " << __LINE__ << "): failed optional call: " #call ", ignoring"); \ + } + +static void cv_tiffCloseHandle(void* handle) +{ + TIFFClose((TIFF*)handle); +} + +static void cv_tiffErrorHandler(const char* module, const char* fmt, va_list ap) +{ + if (cv::utils::logging::getLogLevel() < cv::utils::logging::LOG_LEVEL_DEBUG) + return; + // TODO cv::vformat() with va_list parameter + fprintf(stderr, "OpenCV TIFF: "); + if (module != NULL) + fprintf(stderr, "%s: ", module); + fprintf(stderr, "Warning, "); + vfprintf(stderr, fmt, ap); + fprintf(stderr, ".\n"); +} + +static bool cv_tiffSetErrorHandler_() +{ + TIFFSetErrorHandler(cv_tiffErrorHandler); + TIFFSetWarningHandler(cv_tiffErrorHandler); + return true; +} + +static bool cv_tiffSetErrorHandler() +{ + static bool v = cv_tiffSetErrorHandler_(); + return v; +} static const char fmtSignTiffII[] = "II\x2a\x00"; static const char fmtSignTiffMM[] = "MM\x00\x2a"; -static int grfmt_tiff_err_handler_init = 0; -static void GrFmtSilentTIFFErrorHandler( const char*, const char*, va_list ) {} - TiffDecoder::TiffDecoder() { - m_tif = 0; - if( !grfmt_tiff_err_handler_init ) - { - grfmt_tiff_err_handler_init = 1; - - TIFFSetErrorHandler( GrFmtSilentTIFFErrorHandler ); - TIFFSetWarningHandler( GrFmtSilentTIFFErrorHandler ); - } m_hdr = false; m_buf_supported = true; m_buf_pos = 0; @@ -86,12 +123,7 @@ TiffDecoder::TiffDecoder() void TiffDecoder::close() { - if( m_tif ) - { - TIFF* tif = (TIFF*)m_tif; - TIFFClose( tif ); - m_tif = 0; - } + m_tif.release(); } TiffDecoder::~TiffDecoder() @@ -113,11 +145,13 @@ bool TiffDecoder::checkSignature( const String& signature ) const int TiffDecoder::normalizeChannelsNumber(int channels) const { + CV_Assert(channels <= 4); return channels > 4 ? 4 : channels; } ImageDecoder TiffDecoder::newDecoder() const { + cv_tiffSetErrorHandler(); return makePtr(); } @@ -201,8 +235,8 @@ bool TiffDecoder::readHeader() { bool result = false; - TIFF* tif = static_cast(m_tif); - if (!m_tif) + TIFF* tif = static_cast(m_tif.get()); + if (!tif) { // TIFFOpen() mode flags are different to fopen(). A 'b' in mode "rb" has no effect when reading. // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html @@ -221,25 +255,30 @@ bool TiffDecoder::readHeader() { tif = TIFFOpen(m_filename.c_str(), "r"); } + if (tif) + m_tif.reset(tif, cv_tiffCloseHandle); + else + m_tif.release(); } - if( tif ) + if (tif) { uint32 wdth = 0, hght = 0; uint16 photometric = 0; - m_tif = tif; - if( TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &wdth ) && - TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &hght ) && - TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric )) + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &wdth)); + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &hght)); + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)); + { - uint16 bpp=8, ncn = photometric > 1 ? 3 : 1; - TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); - TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); + bool isGrayScale = photometric == PHOTOMETRIC_MINISWHITE || photometric == PHOTOMETRIC_MINISBLACK; + uint16 bpp = 8, ncn = isGrayScale ? 1 : 3; + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bpp)); + CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn)); m_width = wdth; m_height = hght; - if((bpp == 32 && ncn == 3) || photometric == PHOTOMETRIC_LOGLUV) + if (ncn == 3 && photometric == PHOTOMETRIC_LOGLUV) { m_type = CV_32FC3; m_hdr = true; @@ -256,23 +295,23 @@ bool TiffDecoder::readHeader() switch(bpp) { case 1: - m_type = CV_MAKETYPE(CV_8U, photometric > 1 ? wanted_channels : 1); + m_type = CV_MAKETYPE(CV_8U, !isGrayScale ? wanted_channels : 1); result = true; break; case 8: - m_type = CV_MAKETYPE(CV_8U, photometric > 1 ? wanted_channels : 1); + m_type = CV_MAKETYPE(CV_8U, !isGrayScale ? wanted_channels : 1); result = true; break; case 16: - m_type = CV_MAKETYPE(CV_16U, photometric > 1 ? wanted_channels : 1); + m_type = CV_MAKETYPE(CV_16U, !isGrayScale ? wanted_channels : 1); result = true; break; case 32: - m_type = CV_MAKETYPE(CV_32F, photometric > 1 ? 3 : 1); + m_type = CV_MAKETYPE(CV_32F, wanted_channels); result = true; break; case 64: - m_type = CV_MAKETYPE(CV_64F, photometric > 1 ? 3 : 1); + m_type = CV_MAKETYPE(CV_64F, wanted_channels); result = true; break; default: @@ -290,206 +329,210 @@ bool TiffDecoder::readHeader() bool TiffDecoder::nextPage() { // Prepare the next page, if any. - return m_tif && - TIFFReadDirectory(static_cast(m_tif)) && + return !m_tif.empty() && + TIFFReadDirectory(static_cast(m_tif.get())) && readHeader(); } bool TiffDecoder::readData( Mat& img ) { - if(m_hdr && img.type() == CV_32FC3) + int type_ = img.type(); + int depth = CV_MAT_DEPTH(type_); + + CV_Assert(!m_tif.empty()); + TIFF* tif = (TIFF*)m_tif.get(); + + uint16 photometric = (uint16)-1; + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric)); + + if (m_hdr && depth >= CV_32F) { - return readData_32FC3(img); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT)); } - if(img.type() == CV_32FC1) - { - return readData_32FC1(img); - } - bool result = false; + bool color = img.channels() > 1; - if( img.depth() != CV_8U && img.depth() != CV_16U && img.depth() != CV_32F && img.depth() != CV_64F ) - return false; + CV_CheckType(type_, depth == CV_8U || depth == CV_16U || depth == CV_32F || depth == CV_64F, ""); - if( m_tif && m_width && m_height ) + if (m_width && m_height) { - TIFF* tif = (TIFF*)m_tif; - uint32 tile_width0 = m_width, tile_height0 = 0; - int x, y, i; - int is_tiled = TIFFIsTiled(tif); - uint16 photometric; - TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ); - uint16 bpp = 8, ncn = photometric > 1 ? 3 : 1; - TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); - TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); + int is_tiled = TIFFIsTiled(tif) != 0; + bool isGrayScale = photometric == PHOTOMETRIC_MINISWHITE || photometric == PHOTOMETRIC_MINISBLACK; + uint16 bpp = 8, ncn = isGrayScale ? 1 : 3; + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bpp)); + CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn)); uint16 img_orientation = ORIENTATION_TOPLEFT; - TIFFGetField( tif, TIFFTAG_ORIENTATION, &img_orientation); + CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_ORIENTATION, &img_orientation)); bool vert_flip = (img_orientation == ORIENTATION_BOTRIGHT) || (img_orientation == ORIENTATION_RIGHTBOT) || (img_orientation == ORIENTATION_BOTLEFT) || (img_orientation == ORIENTATION_LEFTBOT); const int bitsPerByte = 8; int dst_bpp = (int)(img.elemSize1() * bitsPerByte); int wanted_channels = normalizeChannelsNumber(img.channels()); - if(dst_bpp == 8) + if (dst_bpp == 8) { char errmsg[1024]; - if(!TIFFRGBAImageOK( tif, errmsg )) + if (!TIFFRGBAImageOK(tif, errmsg)) { + CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg); close(); return false; } } - if( (!is_tiled) || - (is_tiled && - TIFFGetField( tif, TIFFTAG_TILEWIDTH, &tile_width0 ) && - TIFFGetField( tif, TIFFTAG_TILELENGTH, &tile_height0 ))) - { - if(!is_tiled) - TIFFGetField( tif, TIFFTAG_ROWSPERSTRIP, &tile_height0 ); + uint32 tile_width0 = m_width, tile_height0 = 0; - if( tile_width0 <= 0 ) + if (is_tiled) + { + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width0)); + CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height0)); + } + else + { + // optional + CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &tile_height0)); + } + + { + if (tile_width0 == 0) tile_width0 = m_width; - if( tile_height0 <= 0 || - (!is_tiled && tile_height0 == std::numeric_limits::max()) ) + if (tile_height0 == 0 || + (!is_tiled && tile_height0 == std::numeric_limits::max()) ) tile_height0 = m_height; - if(dst_bpp == 8) { + if (dst_bpp == 8) + { // we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA bpp = 8; ncn = 4; } - const size_t buffer_size = (bpp/bitsPerByte) * ncn * tile_height0 * tile_width0; - AutoBuffer _buffer( buffer_size ); + else if (dst_bpp == 32 || dst_bpp == 64) + { + CV_Assert(ncn == img.channels()); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP)); + } + const size_t buffer_size = (bpp / bitsPerByte) * ncn * tile_height0 * tile_width0; + AutoBuffer _buffer(buffer_size); uchar* buffer = _buffer.data(); ushort* buffer16 = (ushort*)buffer; - float* buffer32 = (float*)buffer; - double* buffer64 = (double*)buffer; int tileidx = 0; - for( y = 0; y < m_height; y += tile_height0 ) + for (int y = 0; y < m_height; y += (int)tile_height0) { - int tile_height = tile_height0; + int tile_height = std::min((int)tile_height0, m_height - y); - if( y + tile_height > m_height ) - tile_height = m_height - y; + const int img_y = vert_flip ? m_height - y - tile_height : y; - uchar* data = img.ptr(vert_flip ? m_height - y - tile_height : y); - - for( x = 0; x < m_width; x += tile_width0, tileidx++ ) + for(int x = 0; x < m_width; x += (int)tile_width0, tileidx++) { - int tile_width = tile_width0, ok; + int tile_width = std::min((int)tile_width0, m_width - x); - if( x + tile_width > m_width ) - tile_width = m_width - x; - - switch(dst_bpp) + switch (dst_bpp) { case 8: { - uchar * bstart = buffer; - if( !is_tiled ) - ok = TIFFReadRGBAStrip( tif, y, (uint32*)buffer ); + uchar* bstart = buffer; + if (!is_tiled) + { + CV_TIFF_CHECK_CALL(TIFFReadRGBAStrip(tif, y, (uint32*)buffer)); + } else { - ok = TIFFReadRGBATile( tif, x, y, (uint32*)buffer ); - //Tiles fill the buffer from the bottom up + CV_TIFF_CHECK_CALL(TIFFReadRGBATile(tif, x, y, (uint32*)buffer)); + // Tiles fill the buffer from the bottom up bstart += (tile_height0 - tile_height) * tile_width0 * 4; } - if( !ok ) - { - close(); - return false; - } - for( i = 0; i < tile_height; i++ ) - if( color ) + for (int i = 0; i < tile_height; i++) + { + if (color) { if (wanted_channels == 4) { - icvCvt_BGRA2RGBA_8u_C4R( bstart + i*tile_width0*4, 0, - data + x*4 + img.step*(tile_height - i - 1), 0, - cvSize(tile_width,1) ); + icvCvt_BGRA2RGBA_8u_C4R(bstart + i*tile_width0*4, 0, + img.ptr(img_y + tile_height - i - 1, x), 0, + Size(tile_width, 1) ); } else { - icvCvt_BGRA2BGR_8u_C4C3R( bstart + i*tile_width0*4, 0, - data + x*3 + img.step*(tile_height - i - 1), 0, - cvSize(tile_width,1), 2 ); + icvCvt_BGRA2BGR_8u_C4C3R(bstart + i*tile_width0*4, 0, + img.ptr(img_y + tile_height - i - 1, x), 0, + Size(tile_width, 1), 2); } } else + { icvCvt_BGRA2Gray_8u_C4C1R( bstart + i*tile_width0*4, 0, - data + x + img.step*(tile_height - i - 1), 0, - cvSize(tile_width,1), 2 ); + img.ptr(img_y + tile_height - i - 1, x), 0, + Size(tile_width, 1), 2); + } + } break; } case 16: { - if( !is_tiled ) - ok = (int)TIFFReadEncodedStrip( tif, tileidx, (uint32*)buffer, buffer_size ) >= 0; - else - ok = (int)TIFFReadEncodedTile( tif, tileidx, (uint32*)buffer, buffer_size ) >= 0; - - if( !ok ) + if (!is_tiled) { - close(); - return false; + CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, (uint32*)buffer, buffer_size) >= 0); + } + else + { + CV_TIFF_CHECK_CALL((int)TIFFReadEncodedTile(tif, tileidx, (uint32*)buffer, buffer_size) >= 0); } - for( i = 0; i < tile_height; i++ ) + for (int i = 0; i < tile_height; i++) { - if( color ) + if (color) { - if( ncn == 1 ) + if (ncn == 1) { icvCvt_Gray2BGR_16u_C1C3R(buffer16 + i*tile_width0*ncn, 0, - (ushort*)(data + img.step*i) + x*3, 0, - cvSize(tile_width,1) ); + img.ptr(img_y + i, x), 0, + Size(tile_width, 1)); } - else if( ncn == 3 ) + else if (ncn == 3) { icvCvt_RGB2BGR_16u_C3R(buffer16 + i*tile_width0*ncn, 0, - (ushort*)(data + img.step*i) + x*3, 0, - cvSize(tile_width,1) ); + img.ptr(img_y + i, x), 0, + Size(tile_width, 1)); } else if (ncn == 4) { if (wanted_channels == 4) { icvCvt_BGRA2RGBA_16u_C4R(buffer16 + i*tile_width0*ncn, 0, - (ushort*)(data + img.step*i) + x * 4, 0, - cvSize(tile_width, 1)); + img.ptr(img_y + i, x), 0, + Size(tile_width, 1)); } else { icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0, - (ushort*)(data + img.step*i) + x * 3, 0, - cvSize(tile_width, 1), 2); + img.ptr(img_y + i, x), 0, + Size(tile_width, 1), 2); } } else { icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0, - (ushort*)(data + img.step*i) + x*3, 0, - cvSize(tile_width,1), 2 ); + img.ptr(img_y + i, x), 0, + Size(tile_width, 1), 2); } } else { if( ncn == 1 ) { - memcpy((ushort*)(data + img.step*i)+x, + memcpy(img.ptr(img_y + i, x), buffer16 + i*tile_width0*ncn, - tile_width*sizeof(buffer16[0])); + tile_width*sizeof(ushort)); } else { icvCvt_BGRA2Gray_16u_CnC1R(buffer16 + i*tile_width0*ncn, 0, - (ushort*)(data + img.step*i) + x, 0, - cvSize(tile_width,1), ncn, 2 ); + img.ptr(img_y + i, x), 0, + Size(tile_width, 1), ncn, 2); } } } @@ -500,120 +543,43 @@ bool TiffDecoder::readData( Mat& img ) case 64: { if( !is_tiled ) - ok = (int)TIFFReadEncodedStrip( tif, tileidx, buffer, buffer_size ) >= 0; + { + CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, buffer, buffer_size) >= 0); + } else - ok = (int)TIFFReadEncodedTile( tif, tileidx, buffer, buffer_size ) >= 0; - - if( !ok || ncn != 1 ) { - close(); - return false; - } - - for( i = 0; i < tile_height; i++ ) - { - if(dst_bpp == 32) - { - memcpy((float*)(data + img.step*i)+x, - buffer32 + i*tile_width0*ncn, - tile_width*sizeof(buffer32[0])); - } - else - { - memcpy((double*)(data + img.step*i)+x, - buffer64 + i*tile_width0*ncn, - tile_width*sizeof(buffer64[0])); - } + CV_TIFF_CHECK_CALL((int)TIFFReadEncodedTile(tif, tileidx, buffer, buffer_size) >= 0); } + Mat m_tile(Size(tile_width0, tile_height0), CV_MAKETYPE((dst_bpp == 32) ? CV_32F : CV_64F, ncn), 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) + cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGB2BGR); + else if (!m_hdr && ncn == 4) + cvtColor(m_tile(roi_tile), img(roi_img), COLOR_RGBA2BGRA); + else + m_tile(roi_tile).copyTo(img(roi_img)); break; } default: { - close(); - return false; + CV_Assert(0 && "OpenCV TIFF: unsupported depth"); } - } - } - } - - result = true; + } // switch (dst_bpp) + } // for x + } // for y } } - return result; -} - -bool TiffDecoder::readData_32FC3(Mat& img) -{ - int rows_per_strip = 0, photometric = 0; - if(!m_tif) - { - return false; - } - TIFF *tif = static_cast(m_tif); - TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &rows_per_strip); - TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric ); - TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT); - int size = 3 * m_width * m_height * sizeof (float); - tstrip_t strip_size = 3 * m_width * rows_per_strip; - float *ptr = img.ptr(); - for (tstrip_t i = 0; i < TIFFNumberOfStrips(tif); i++, ptr += strip_size) - { - TIFFReadEncodedStrip(tif, i, ptr, size); - size -= strip_size * sizeof(float); - } - close(); - if(photometric == PHOTOMETRIC_LOGLUV) + if (m_hdr && depth >= CV_32F) { + CV_Assert(photometric == PHOTOMETRIC_LOGLUV); cvtColor(img, img, COLOR_XYZ2BGR); } - else - { - cvtColor(img, img, COLOR_RGB2BGR); - } return true; } -bool TiffDecoder::readData_32FC1(Mat& img) -{ - if(!m_tif) - { - return false; - } - TIFF *tif = static_cast(m_tif); - - uint32 img_width, img_height; - TIFFGetField(tif,TIFFTAG_IMAGEWIDTH, &img_width); - TIFFGetField(tif,TIFFTAG_IMAGELENGTH, &img_height); - if(img.size() != Size(img_width,img_height)) - { - close(); - return false; - } - tsize_t scanlength = TIFFScanlineSize(tif); - tdata_t buf = _TIFFmalloc(scanlength); - float* data; - bool result = true; - for (uint32 row = 0; row < img_height; row++) - { - if (TIFFReadScanline(tif, buf, row) != 1) - { - result = false; - break; - } - data=(float*)buf; - for (uint32 i=0; i(row,i) = data[i]; - } - } - _TIFFfree(buf); - close(); - - return result; -} - ////////////////////////////////////////////////////////////////////////////////////////// TiffEncoder::TiffEncoder() @@ -633,7 +599,7 @@ ImageEncoder TiffEncoder::newEncoder() const bool TiffEncoder::isFormatSupported( int depth ) const { - return depth == CV_8U || depth == CV_16U || depth == CV_32F; + return depth == CV_8U || depth == CV_16U || depth == CV_32F || depth == CV_64F; } void TiffEncoder::writeTag( WLByteStream& strm, TiffTag tag, @@ -656,6 +622,8 @@ public: TIFF* open () { + // do NOT put "wb" as the mode, because the b means "big endian" mode, not "binary" mode. + // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html return TIFFClientOpen( "", "w", reinterpret_cast(this), &TiffEncoderBufHelper::read, &TiffEncoderBufHelper::write, &TiffEncoderBufHelper::seek, &TiffEncoderBufHelper::close, &TiffEncoderBufHelper::size, @@ -721,35 +689,39 @@ private: toff_t m_buf_pos; }; -static void readParam(const std::vector& params, int key, int& value) +static bool readParam(const std::vector& params, int key, int& value) { - for(size_t i = 0; i + 1 < params.size(); i += 2) - if(params[i] == key) + for (size_t i = 0; i + 1 < params.size(); i += 2) + { + if (params[i] == key) { - value = params[i+1]; - break; + value = params[i + 1]; + return true; } + } + return false; } bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vector& params) { // do NOT put "wb" as the mode, because the b means "big endian" mode, not "binary" mode. // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html - TIFF* pTiffHandle; + TIFF* tif = NULL; TiffEncoderBufHelper buf_helper(m_buf); if ( m_buf ) { - pTiffHandle = buf_helper.open(); + tif = buf_helper.open(); } else { - pTiffHandle = TIFFOpen(m_filename.c_str(), "w"); + tif = TIFFOpen(m_filename.c_str(), "w"); } - if (!pTiffHandle) + if (!tif) { return false; } + cv::Ptr tif_cleanup(tif, cv_tiffCloseHandle); //Settings that matter to all images int compression = COMPRESSION_LZW; @@ -768,7 +740,29 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect const Mat& img = img_vec[page]; int channels = img.channels(); int width = img.cols, height = img.rows; - int depth = img.depth(); + int type = img.type(); + int depth = CV_MAT_DEPTH(type); + CV_CheckType(type, depth == CV_8U || depth == CV_16U || depth == CV_32F || depth == CV_64F, ""); + CV_CheckType(type, channels >= 1 && channels <= 4, ""); + + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, width)); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_IMAGELENGTH, height)); + + if (img_vec.size() > 1) + { + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE)); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PAGENUMBER, page, img_vec.size())); + } + + int compression_param = -1; // OPENCV_FUTURE + if (type == CV_32FC3 && (!readParam(params, IMWRITE_TIFF_COMPRESSION, compression_param) || compression_param == COMPRESSION_SGILOG)) + { + if (!write_32FC3_SGILOG(img, tif)) + return false; + continue; + } + + int page_compression = compression; int bitsPerChannel = -1; switch (depth) @@ -783,9 +777,20 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect bitsPerChannel = 16; break; } + case CV_32F: + { + bitsPerChannel = 32; + page_compression = COMPRESSION_NONE; + break; + } + case CV_64F: + { + bitsPerChannel = 64; + page_compression = COMPRESSION_NONE; + break; + } default: { - TIFFClose(pTiffHandle); return false; } } @@ -795,57 +800,42 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect int rowsPerStrip = (int)((1 << 13) / fileStep); readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip); + rowsPerStrip = std::max(1, std::min(height, rowsPerStrip)); - if (rowsPerStrip < 1) - rowsPerStrip = 1; + int colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK; - if (rowsPerStrip > height) - rowsPerStrip = height; + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitsPerChannel)); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_COMPRESSION, page_compression)); + 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)); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, rowsPerStrip)); - int colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK; + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, depth >= CV_32F ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT)); - if (!TIFFSetField(pTiffHandle, TIFFTAG_IMAGEWIDTH, width) - || !TIFFSetField(pTiffHandle, TIFFTAG_IMAGELENGTH, height) - || !TIFFSetField(pTiffHandle, TIFFTAG_BITSPERSAMPLE, bitsPerChannel) - || !TIFFSetField(pTiffHandle, TIFFTAG_COMPRESSION, compression) - || !TIFFSetField(pTiffHandle, TIFFTAG_PHOTOMETRIC, colorspace) - || !TIFFSetField(pTiffHandle, TIFFTAG_SAMPLESPERPIXEL, channels) - || !TIFFSetField(pTiffHandle, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG) - || !TIFFSetField(pTiffHandle, TIFFTAG_ROWSPERSTRIP, rowsPerStrip) - || (img_vec.size() > 1 && ( - !TIFFSetField(pTiffHandle, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE) - || !TIFFSetField(pTiffHandle, TIFFTAG_PAGENUMBER, page, img_vec.size() ))) - ) + if (page_compression != COMPRESSION_NONE) { - TIFFClose(pTiffHandle); - return false; + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor)); } - if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor)) + if (resUnit >= RESUNIT_NONE && resUnit <= RESUNIT_CENTIMETER) { - TIFFClose(pTiffHandle); - return false; + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, resUnit)); } - - if (((resUnit >= RESUNIT_NONE && resUnit <= RESUNIT_CENTIMETER) && !TIFFSetField(pTiffHandle, TIFFTAG_RESOLUTIONUNIT, resUnit)) - || (dpiX >= 0 && !TIFFSetField(pTiffHandle, TIFFTAG_XRESOLUTION, (float)dpiX)) - || (dpiY >= 0 && !TIFFSetField(pTiffHandle, TIFFTAG_YRESOLUTION, (float)dpiY)) - ) + if (dpiX >= 0) { - TIFFClose(pTiffHandle); - return false; + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)dpiX)); + } + if (dpiY >= 0) + { + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)dpiY)); } - // row buffer, because TIFFWriteScanline modifies the original data! - size_t scanlineSize = TIFFScanlineSize(pTiffHandle); + size_t scanlineSize = TIFFScanlineSize(tif); AutoBuffer _buffer(scanlineSize + 32); - uchar* buffer = _buffer.data(); - if (!buffer) - { - TIFFClose(pTiffHandle); - return false; - } + uchar* buffer = _buffer.data(); CV_DbgAssert(buffer); + Mat m_buffer(Size(width, 1), CV_MAKETYPE(depth, channels), buffer, (size_t)scanlineSize); for (int y = 0; y < height; ++y) { @@ -859,122 +849,54 @@ bool TiffEncoder::writeLibTiff( const std::vector& img_vec, const std::vect case 3: { - if (depth == CV_8U) - icvCvt_BGR2RGB_8u_C3R( img.ptr(y), 0, buffer, 0, cvSize(width, 1)); - else - icvCvt_BGR2RGB_16u_C3R( img.ptr(y), 0, (ushort*)buffer, 0, cvSize(width, 1)); + cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGR2RGB); break; } case 4: { - if (depth == CV_8U) - icvCvt_BGRA2RGBA_8u_C4R( img.ptr(y), 0, buffer, 0, cvSize(width, 1)); - else - icvCvt_BGRA2RGBA_16u_C4R( img.ptr(y), 0, (ushort*)buffer, 0, cvSize(width, 1)); + cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGRA2RGBA); break; } default: { - TIFFClose(pTiffHandle); - return false; + CV_Assert(0); } } - int writeResult = TIFFWriteScanline(pTiffHandle, buffer, y, 0); - if (writeResult != 1) - { - TIFFClose(pTiffHandle); - return false; - } + CV_TIFF_CHECK_CALL(TIFFWriteScanline(tif, buffer, y, 0) == 1); } - TIFFWriteDirectory(pTiffHandle); - + CV_TIFF_CHECK_CALL(TIFFWriteDirectory(tif)); } - TIFFClose(pTiffHandle); return true; } -bool TiffEncoder::write_32FC3(const Mat& _img) +bool TiffEncoder::write_32FC3_SGILOG(const Mat& _img, void* tif_) { + TIFF* tif = (TIFF*)tif_; + CV_Assert(tif); + Mat img; cvtColor(_img, img, COLOR_BGR2XYZ); - TIFF* tif; - - TiffEncoderBufHelper buf_helper(m_buf); - if ( m_buf ) + //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, 3)); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32)); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_SGILOG)); + CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, 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 = 3 * img.cols; + for (int i = 0; i < img.rows; i++) { - tif = buf_helper.open(); + CV_TIFF_CHECK_CALL(TIFFWriteEncodedStrip(tif, i, (tdata_t)img.ptr(i), strip_size * sizeof(float)) != (tsize_t)-1); } - else - { - tif = TIFFOpen(m_filename.c_str(), "w"); - } - - if (!tif) - { - return false; - } - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, img.cols); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, img.rows); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); - TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_SGILOG); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_LOGLUV); - TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT); - TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1); - int strip_size = 3 * img.cols; - float *ptr = const_cast(img.ptr()); - for (int i = 0; i < img.rows; i++, ptr += strip_size) - { - TIFFWriteEncodedStrip(tif, i, ptr, strip_size * sizeof(float)); - } - TIFFClose(tif); - return true; -} - -bool TiffEncoder::write_32FC1(const Mat& _img) -{ - - TIFF* tif; - - TiffEncoderBufHelper buf_helper(m_buf); - if ( m_buf ) - { - tif = buf_helper.open(); - } - else - { - tif = TIFFOpen(m_filename.c_str(), "w"); - } - - if (!tif) - { - return false; - } - - TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, _img.cols); - TIFFSetField(tif, TIFFTAG_IMAGELENGTH, _img.rows); - TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 1); - TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32); - TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_MINISBLACK); - TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_IEEEFP); - TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_NONE); - for (uint32 row = 0; row < (uint32)_img.rows; row++) - { - if (TIFFWriteScanline(tif, (tdata_t)_img.ptr(row), row, 1) != 1) - { - TIFFClose(tif); - return false; - } - } - TIFFWriteDirectory(tif); - TIFFClose(tif); - + CV_TIFF_CHECK_CALL(TIFFWriteDirectory(tif)); return true; } @@ -985,18 +907,10 @@ bool TiffEncoder::writemulti(const std::vector& img_vec, const std::vector< bool TiffEncoder::write( const Mat& img, const std::vector& params) { - int depth = img.depth(); + int type = img.type(); + int depth = CV_MAT_DEPTH(type); - if(img.type() == CV_32FC3) - { - return write_32FC3(img); - } - if(img.type() == CV_32FC1) - { - return write_32FC1(img); - } - - CV_Assert(depth == CV_8U || depth == CV_16U); + CV_CheckType(type, depth == CV_8U || depth == CV_16U || depth == CV_32F || depth == CV_64F, ""); std::vector img_vec; img_vec.push_back(img); diff --git a/modules/imgcodecs/src/grfmt_tiff.hpp b/modules/imgcodecs/src/grfmt_tiff.hpp index cd1b55ed02..ee5bcb7018 100644 --- a/modules/imgcodecs/src/grfmt_tiff.hpp +++ b/modules/imgcodecs/src/grfmt_tiff.hpp @@ -106,10 +106,8 @@ public: ImageDecoder newDecoder() const CV_OVERRIDE; protected: - void* m_tif; + cv::Ptr m_tif; int normalizeChannelsNumber(int channels) const; - bool readData_32FC3(Mat& img); - bool readData_32FC1(Mat& img); bool m_hdr; size_t m_buf_pos; @@ -139,8 +137,7 @@ protected: int count, int value ); bool writeLibTiff( const std::vector& img_vec, const std::vector& params ); - bool write_32FC3( const Mat& img ); - bool write_32FC1( const Mat& img ); + bool write_32FC3_SGILOG(const Mat& img, void* tif); private: TiffEncoder(const TiffEncoder &); // copy disabled diff --git a/modules/imgcodecs/src/utils.cpp b/modules/imgcodecs/src/utils.cpp index 6aeb631060..14cce8314b 100644 --- a/modules/imgcodecs/src/utils.cpp +++ b/modules/imgcodecs/src/utils.cpp @@ -42,6 +42,8 @@ #include "precomp.hpp" #include "utils.hpp" +namespace cv { + int validateToInt(size_t sz) { int valueInt = (int)sz; @@ -56,7 +58,7 @@ int validateToInt(size_t sz) void icvCvt_BGR2Gray_8u_C3C1R( const uchar* rgb, int rgb_step, uchar* gray, int gray_step, - CvSize size, int _swap_rb ) + Size size, int _swap_rb ) { int i; int swap_rb = _swap_rb ? 2 : 0; @@ -75,7 +77,7 @@ void icvCvt_BGR2Gray_8u_C3C1R( const uchar* rgb, int rgb_step, void icvCvt_BGRA2Gray_16u_CnC1R( const ushort* rgb, int rgb_step, ushort* gray, int gray_step, - CvSize size, int ncn, int _swap_rb ) + Size size, int ncn, int _swap_rb ) { int i; int swap_rb = _swap_rb ? 2 : 0; @@ -94,7 +96,7 @@ void icvCvt_BGRA2Gray_16u_CnC1R( const ushort* rgb, int rgb_step, void icvCvt_BGRA2Gray_8u_C4C1R( const uchar* rgba, int rgba_step, uchar* gray, int gray_step, - CvSize size, int _swap_rb ) + Size size, int _swap_rb ) { int i; int swap_rb = _swap_rb ? 2 : 0; @@ -112,7 +114,7 @@ void icvCvt_BGRA2Gray_8u_C4C1R( const uchar* rgba, int rgba_step, void icvCvt_Gray2BGR_8u_C1C3R( const uchar* gray, int gray_step, - uchar* bgr, int bgr_step, CvSize size ) + uchar* bgr, int bgr_step, Size size ) { int i; for( ; size.height--; gray += gray_step ) @@ -127,7 +129,7 @@ void icvCvt_Gray2BGR_8u_C1C3R( const uchar* gray, int gray_step, void icvCvt_Gray2BGR_16u_C1C3R( const ushort* gray, int gray_step, - ushort* bgr, int bgr_step, CvSize size ) + ushort* bgr, int bgr_step, Size size ) { int i; for( ; size.height--; gray += gray_step/sizeof(gray[0]) ) @@ -143,7 +145,7 @@ void icvCvt_Gray2BGR_16u_C1C3R( const ushort* gray, int gray_step, void icvCvt_BGRA2BGR_8u_C4C3R( const uchar* bgra, int bgra_step, uchar* bgr, int bgr_step, - CvSize size, int _swap_rb ) + Size size, int _swap_rb ) { int i; int swap_rb = _swap_rb ? 2 : 0; @@ -163,7 +165,7 @@ void icvCvt_BGRA2BGR_8u_C4C3R( const uchar* bgra, int bgra_step, void icvCvt_BGRA2BGR_16u_C4C3R( const ushort* bgra, int bgra_step, ushort* bgr, int bgr_step, - CvSize size, int _swap_rb ) + Size size, int _swap_rb ) { int i; int swap_rb = _swap_rb ? 2 : 0; @@ -182,7 +184,7 @@ void icvCvt_BGRA2BGR_16u_C4C3R( const ushort* bgra, int bgra_step, void icvCvt_BGRA2RGBA_8u_C4R( const uchar* bgra, int bgra_step, - uchar* rgba, int rgba_step, CvSize size ) + uchar* rgba, int rgba_step, Size size ) { int i; for( ; size.height--; ) @@ -200,7 +202,7 @@ void icvCvt_BGRA2RGBA_8u_C4R( const uchar* bgra, int bgra_step, } void icvCvt_BGRA2RGBA_16u_C4R( const ushort* bgra, int bgra_step, - ushort* rgba, int rgba_step, CvSize size ) + ushort* rgba, int rgba_step, Size size ) { int i; for( ; size.height--; ) @@ -220,7 +222,7 @@ void icvCvt_BGRA2RGBA_16u_C4R( const ushort* bgra, int bgra_step, void icvCvt_BGR2RGB_8u_C3R( const uchar* bgr, int bgr_step, - uchar* rgb, int rgb_step, CvSize size ) + uchar* rgb, int rgb_step, Size size ) { int i; for( ; size.height--; ) @@ -237,7 +239,7 @@ void icvCvt_BGR2RGB_8u_C3R( const uchar* bgr, int bgr_step, void icvCvt_BGR2RGB_16u_C3R( const ushort* bgr, int bgr_step, - ushort* rgb, int rgb_step, CvSize size ) + ushort* rgb, int rgb_step, Size size ) { int i; for( ; size.height--; ) @@ -256,7 +258,7 @@ void icvCvt_BGR2RGB_16u_C3R( const ushort* bgr, int bgr_step, typedef unsigned short ushort; void icvCvt_BGR5552Gray_8u_C2C1R( const uchar* bgr555, int bgr555_step, - uchar* gray, int gray_step, CvSize size ) + uchar* gray, int gray_step, Size size ) { int i; for( ; size.height--; gray += gray_step, bgr555 += bgr555_step ) @@ -273,7 +275,7 @@ void icvCvt_BGR5552Gray_8u_C2C1R( const uchar* bgr555, int bgr555_step, void icvCvt_BGR5652Gray_8u_C2C1R( const uchar* bgr565, int bgr565_step, - uchar* gray, int gray_step, CvSize size ) + uchar* gray, int gray_step, Size size ) { int i; for( ; size.height--; gray += gray_step, bgr565 += bgr565_step ) @@ -290,7 +292,7 @@ void icvCvt_BGR5652Gray_8u_C2C1R( const uchar* bgr565, int bgr565_step, void icvCvt_BGR5552BGR_8u_C2C3R( const uchar* bgr555, int bgr555_step, - uchar* bgr, int bgr_step, CvSize size ) + uchar* bgr, int bgr_step, Size size ) { int i; for( ; size.height--; bgr555 += bgr555_step ) @@ -308,7 +310,7 @@ void icvCvt_BGR5552BGR_8u_C2C3R( const uchar* bgr555, int bgr555_step, void icvCvt_BGR5652BGR_8u_C2C3R( const uchar* bgr565, int bgr565_step, - uchar* bgr, int bgr_step, CvSize size ) + uchar* bgr, int bgr_step, Size size ) { int i; for( ; size.height--; bgr565 += bgr565_step ) @@ -326,7 +328,7 @@ void icvCvt_BGR5652BGR_8u_C2C3R( const uchar* bgr565, int bgr565_step, void icvCvt_CMYK2BGR_8u_C4C3R( const uchar* cmyk, int cmyk_step, - uchar* bgr, int bgr_step, CvSize size ) + uchar* bgr, int bgr_step, Size size ) { int i; for( ; size.height--; ) @@ -346,7 +348,7 @@ void icvCvt_CMYK2BGR_8u_C4C3R( const uchar* cmyk, int cmyk_step, void icvCvt_CMYK2Gray_8u_C4C1R( const uchar* cmyk, int cmyk_step, - uchar* gray, int gray_step, CvSize size ) + uchar* gray, int gray_step, Size size ) { int i; for( ; size.height--; ) @@ -371,7 +373,7 @@ void CvtPaletteToGray( const PaletteEntry* palette, uchar* grayPalette, int entr int i; for( i = 0; i < entries; i++ ) { - icvCvt_BGR2Gray_8u_C3C1R( (uchar*)(palette + i), 0, grayPalette + i, 0, cvSize(1,1) ); + icvCvt_BGR2Gray_8u_C3C1R( (uchar*)(palette + i), 0, grayPalette + i, 0, Size(1,1) ); } } @@ -598,6 +600,9 @@ uchar* FillGrayRow1( uchar* data, uchar* indices, int len, uchar* palette ) return data; } +} // namespace + +using namespace cv; CV_IMPL void cvConvertImage( const CvArr* srcarr, CvArr* dstarr, int flags ) @@ -652,7 +657,7 @@ cvConvertImage( const CvArr* srcarr, CvArr* dstarr, int flags ) uchar *s = src->data.ptr, *d = dst->data.ptr; int s_step = src->step, d_step = dst->step; int code = src_cn*10 + dst_cn; - CvSize size = {src->cols, src->rows}; + Size size(src->cols, src->rows); if( CV_IS_MAT_CONT(src->type & dst->type) ) { diff --git a/modules/imgcodecs/src/utils.hpp b/modules/imgcodecs/src/utils.hpp index 6e0ec95826..43eb907f76 100644 --- a/modules/imgcodecs/src/utils.hpp +++ b/modules/imgcodecs/src/utils.hpp @@ -42,6 +42,8 @@ #ifndef _UTILS_H_ #define _UTILS_H_ +namespace cv { + int validateToInt(size_t step); template static inline @@ -68,53 +70,53 @@ struct PaletteEntry void icvCvt_BGR2Gray_8u_C3C1R( const uchar* bgr, int bgr_step, uchar* gray, int gray_step, - CvSize size, int swap_rb=0 ); + Size size, int swap_rb=0 ); void icvCvt_BGRA2Gray_8u_C4C1R( const uchar* bgra, int bgra_step, uchar* gray, int gray_step, - CvSize size, int swap_rb=0 ); + Size size, int swap_rb=0 ); void icvCvt_BGRA2Gray_16u_CnC1R( const ushort* bgra, int bgra_step, ushort* gray, int gray_step, - CvSize size, int ncn, int swap_rb=0 ); + Size size, int ncn, int swap_rb=0 ); void icvCvt_Gray2BGR_8u_C1C3R( const uchar* gray, int gray_step, - uchar* bgr, int bgr_step, CvSize size ); + uchar* bgr, int bgr_step, Size size ); void icvCvt_Gray2BGR_16u_C1C3R( const ushort* gray, int gray_step, - ushort* bgr, int bgr_step, CvSize size ); + ushort* bgr, int bgr_step, Size size ); void icvCvt_BGRA2BGR_8u_C4C3R( const uchar* bgra, int bgra_step, uchar* bgr, int bgr_step, - CvSize size, int swap_rb=0 ); + Size size, int swap_rb=0 ); void icvCvt_BGRA2BGR_16u_C4C3R( const ushort* bgra, int bgra_step, ushort* bgr, int bgr_step, - CvSize size, int _swap_rb ); + Size size, int _swap_rb ); void icvCvt_BGR2RGB_8u_C3R( const uchar* bgr, int bgr_step, - uchar* rgb, int rgb_step, CvSize size ); + uchar* rgb, int rgb_step, Size size ); #define icvCvt_RGB2BGR_8u_C3R icvCvt_BGR2RGB_8u_C3R void icvCvt_BGR2RGB_16u_C3R( const ushort* bgr, int bgr_step, - ushort* rgb, int rgb_step, CvSize size ); + ushort* rgb, int rgb_step, Size size ); #define icvCvt_RGB2BGR_16u_C3R icvCvt_BGR2RGB_16u_C3R void icvCvt_BGRA2RGBA_8u_C4R( const uchar* bgra, int bgra_step, - uchar* rgba, int rgba_step, CvSize size ); + uchar* rgba, int rgba_step, Size size ); #define icvCvt_RGBA2BGRA_8u_C4R icvCvt_BGRA2RGBA_8u_C4R void icvCvt_BGRA2RGBA_16u_C4R( const ushort* bgra, int bgra_step, - ushort* rgba, int rgba_step, CvSize size ); + ushort* rgba, int rgba_step, Size size ); #define icvCvt_RGBA2BGRA_16u_C4R icvCvt_BGRA2RGBA_16u_C4R void icvCvt_BGR5552Gray_8u_C2C1R( const uchar* bgr555, int bgr555_step, - uchar* gray, int gray_step, CvSize size ); + uchar* gray, int gray_step, Size size ); void icvCvt_BGR5652Gray_8u_C2C1R( const uchar* bgr565, int bgr565_step, - uchar* gray, int gray_step, CvSize size ); + uchar* gray, int gray_step, Size size ); void icvCvt_BGR5552BGR_8u_C2C3R( const uchar* bgr555, int bgr555_step, - uchar* bgr, int bgr_step, CvSize size ); + uchar* bgr, int bgr_step, Size size ); void icvCvt_BGR5652BGR_8u_C2C3R( const uchar* bgr565, int bgr565_step, - uchar* bgr, int bgr_step, CvSize size ); + uchar* bgr, int bgr_step, Size size ); void icvCvt_CMYK2BGR_8u_C4C3R( const uchar* cmyk, int cmyk_step, - uchar* bgr, int bgr_step, CvSize size ); + uchar* bgr, int bgr_step, Size size ); void icvCvt_CMYK2Gray_8u_C4C1R( const uchar* ycck, int ycck_step, - uchar* gray, int gray_step, CvSize size ); + uchar* gray, int gray_step, Size size ); void FillGrayPalette( PaletteEntry* palette, int bpp, bool negative = false ); bool IsColorPalette( PaletteEntry* palette, int bpp ); @@ -136,4 +138,6 @@ CV_INLINE bool isBigEndian( void ) return (((const int*)"\0\x1\x2\x3\x4\x5\x6\x7")[0] & 255) != 0; } +} // namespace + #endif/*_UTILS_H_*/ diff --git a/modules/imgcodecs/test/test_tiff.cpp b/modules/imgcodecs/test/test_tiff.cpp index 76a2cd4cd4..add15ff681 100644 --- a/modules/imgcodecs/test/test_tiff.cpp +++ b/modules/imgcodecs/test/test_tiff.cpp @@ -158,12 +158,68 @@ TEST(Imgcodecs_Tiff, readWrite_32FC1) ASSERT_TRUE(cv::imwrite(filenameOutput, img)); const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED); - ASSERT_EQ(img2.type(),img.type()); - ASSERT_EQ(img2.size(),img.size()); - EXPECT_GE(1e-3, cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE)); + ASSERT_EQ(img2.type(), img.type()); + ASSERT_EQ(img2.size(), img.size()); + EXPECT_LE(cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE), 1e-3); EXPECT_EQ(0, remove(filenameOutput.c_str())); } +TEST(Imgcodecs_Tiff, readWrite_64FC1) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/test64FC1.tiff"; + const string filenameOutput = cv::tempfile(".tiff"); + const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_64FC1, img.type()); + + ASSERT_TRUE(cv::imwrite(filenameOutput, img)); + const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED); + ASSERT_EQ(img2.type(), img.type()); + ASSERT_EQ(img2.size(), img.size()); + EXPECT_LE(cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE), 1e-3); + EXPECT_EQ(0, remove(filenameOutput.c_str())); +} + +TEST(Imgcodecs_Tiff, readWrite_32FC3_SGILOG) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/test32FC3_sgilog.tiff"; + const string filenameOutput = cv::tempfile(".tiff"); + const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_32FC3, img.type()); + + ASSERT_TRUE(cv::imwrite(filenameOutput, img)); + const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED); + ASSERT_EQ(img2.type(), img.type()); + ASSERT_EQ(img2.size(), img.size()); + EXPECT_LE(cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE), 0.01); + EXPECT_EQ(0, remove(filenameOutput.c_str())); +} + +TEST(Imgcodecs_Tiff, readWrite_32FC3_RAW) +{ + const string root = cvtest::TS::ptr()->get_data_path(); + const string filenameInput = root + "readwrite/test32FC3_raw.tiff"; + const string filenameOutput = cv::tempfile(".tiff"); + const Mat img = cv::imread(filenameInput, IMREAD_UNCHANGED); + ASSERT_FALSE(img.empty()); + ASSERT_EQ(CV_32FC3, img.type()); + + std::vector params; + params.push_back(IMWRITE_TIFF_COMPRESSION); + params.push_back(1/*COMPRESSION_NONE*/); + + ASSERT_TRUE(cv::imwrite(filenameOutput, img, params)); + const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED); + ASSERT_EQ(img2.type(), img.type()); + ASSERT_EQ(img2.size(), img.size()); + EXPECT_LE(cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE), 1e-3); + EXPECT_EQ(0, remove(filenameOutput.c_str())); +} + + //================================================================================================== typedef testing::TestWithParam Imgcodecs_Tiff_Modes;