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
pull/14249/head
Alexander Alekhin 6 years ago committed by Alexander Alekhin
parent d8b8c3b145
commit ba5ddd6499
  1. 662
      modules/imgcodecs/src/grfmt_tiff.cpp
  2. 7
      modules/imgcodecs/src/grfmt_tiff.hpp
  3. 43
      modules/imgcodecs/src/utils.cpp
  4. 38
      modules/imgcodecs/src/utils.hpp
  5. 62
      modules/imgcodecs/test/test_tiff.cpp

@ -48,6 +48,8 @@
#include "precomp.hpp" #include "precomp.hpp"
#ifdef HAVE_TIFF #ifdef HAVE_TIFF
#include <opencv2/core/utils/logger.hpp>
#include "grfmt_tiff.hpp" #include "grfmt_tiff.hpp"
#include <limits> #include <limits>
@ -61,23 +63,58 @@ using namespace tiff_dummy_namespace;
namespace cv 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 fmtSignTiffII[] = "II\x2a\x00";
static const char fmtSignTiffMM[] = "MM\x00\x2a"; 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() 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_hdr = false;
m_buf_supported = true; m_buf_supported = true;
m_buf_pos = 0; m_buf_pos = 0;
@ -86,12 +123,7 @@ TiffDecoder::TiffDecoder()
void TiffDecoder::close() void TiffDecoder::close()
{ {
if( m_tif ) m_tif.release();
{
TIFF* tif = (TIFF*)m_tif;
TIFFClose( tif );
m_tif = 0;
}
} }
TiffDecoder::~TiffDecoder() TiffDecoder::~TiffDecoder()
@ -113,11 +145,13 @@ bool TiffDecoder::checkSignature( const String& signature ) const
int TiffDecoder::normalizeChannelsNumber(int channels) const int TiffDecoder::normalizeChannelsNumber(int channels) const
{ {
CV_Assert(channels <= 4);
return channels > 4 ? 4 : channels; return channels > 4 ? 4 : channels;
} }
ImageDecoder TiffDecoder::newDecoder() const ImageDecoder TiffDecoder::newDecoder() const
{ {
cv_tiffSetErrorHandler();
return makePtr<TiffDecoder>(); return makePtr<TiffDecoder>();
} }
@ -201,8 +235,8 @@ bool TiffDecoder::readHeader()
{ {
bool result = false; bool result = false;
TIFF* tif = static_cast<TIFF*>(m_tif); TIFF* tif = static_cast<TIFF*>(m_tif.get());
if (!m_tif) if (!tif)
{ {
// TIFFOpen() mode flags are different to fopen(). A 'b' in mode "rb" has no effect when reading. // 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 // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
@ -221,25 +255,30 @@ bool TiffDecoder::readHeader()
{ {
tif = TIFFOpen(m_filename.c_str(), "r"); 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; uint32 wdth = 0, hght = 0;
uint16 photometric = 0; uint16 photometric = 0;
m_tif = tif;
if( TIFFGetField( tif, TIFFTAG_IMAGEWIDTH, &wdth ) && CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &wdth));
TIFFGetField( tif, TIFFTAG_IMAGELENGTH, &hght ) && CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &hght));
TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric )) CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_PHOTOMETRIC, &photometric));
{ {
uint16 bpp=8, ncn = photometric > 1 ? 3 : 1; bool isGrayScale = photometric == PHOTOMETRIC_MINISWHITE || photometric == PHOTOMETRIC_MINISBLACK;
TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp ); uint16 bpp = 8, ncn = isGrayScale ? 1 : 3;
TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn ); CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bpp));
CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn));
m_width = wdth; m_width = wdth;
m_height = hght; m_height = hght;
if((bpp == 32 && ncn == 3) || photometric == PHOTOMETRIC_LOGLUV) if (ncn == 3 && photometric == PHOTOMETRIC_LOGLUV)
{ {
m_type = CV_32FC3; m_type = CV_32FC3;
m_hdr = true; m_hdr = true;
@ -256,23 +295,23 @@ bool TiffDecoder::readHeader()
switch(bpp) switch(bpp)
{ {
case 1: 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; result = true;
break; break;
case 8: 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; result = true;
break; break;
case 16: 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; result = true;
break; break;
case 32: case 32:
m_type = CV_MAKETYPE(CV_32F, photometric > 1 ? 3 : 1); m_type = CV_MAKETYPE(CV_32F, wanted_channels);
result = true; result = true;
break; break;
case 64: case 64:
m_type = CV_MAKETYPE(CV_64F, photometric > 1 ? 3 : 1); m_type = CV_MAKETYPE(CV_64F, wanted_channels);
result = true; result = true;
break; break;
default: default:
@ -290,206 +329,210 @@ bool TiffDecoder::readHeader()
bool TiffDecoder::nextPage() bool TiffDecoder::nextPage()
{ {
// Prepare the next page, if any. // Prepare the next page, if any.
return m_tif && return !m_tif.empty() &&
TIFFReadDirectory(static_cast<TIFF*>(m_tif)) && TIFFReadDirectory(static_cast<TIFF*>(m_tif.get())) &&
readHeader(); readHeader();
} }
bool TiffDecoder::readData( Mat& img ) bool TiffDecoder::readData( Mat& img )
{ {
if(m_hdr && img.type() == CV_32FC3) int type_ = img.type();
{ int depth = CV_MAT_DEPTH(type_);
return readData_32FC3(img);
} CV_Assert(!m_tif.empty());
if(img.type() == CV_32FC1) 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_32FC1(img); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT));
} }
bool result = false;
bool color = img.channels() > 1; bool color = img.channels() > 1;
if( img.depth() != CV_8U && img.depth() != CV_16U && img.depth() != CV_32F && img.depth() != CV_64F ) CV_CheckType(type_, depth == CV_8U || depth == CV_16U || depth == CV_32F || depth == CV_64F, "");
return false;
if( m_tif && m_width && m_height ) if (m_width && m_height)
{ {
TIFF* tif = (TIFF*)m_tif; int is_tiled = TIFFIsTiled(tif) != 0;
uint32 tile_width0 = m_width, tile_height0 = 0; bool isGrayScale = photometric == PHOTOMETRIC_MINISWHITE || photometric == PHOTOMETRIC_MINISBLACK;
int x, y, i; uint16 bpp = 8, ncn = isGrayScale ? 1 : 3;
int is_tiled = TIFFIsTiled(tif); CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_BITSPERSAMPLE, &bpp));
uint16 photometric; CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_SAMPLESPERPIXEL, &ncn));
TIFFGetField( tif, TIFFTAG_PHOTOMETRIC, &photometric );
uint16 bpp = 8, ncn = photometric > 1 ? 3 : 1;
TIFFGetField( tif, TIFFTAG_BITSPERSAMPLE, &bpp );
TIFFGetField( tif, TIFFTAG_SAMPLESPERPIXEL, &ncn );
uint16 img_orientation = ORIENTATION_TOPLEFT; 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) || bool vert_flip = (img_orientation == ORIENTATION_BOTRIGHT) || (img_orientation == ORIENTATION_RIGHTBOT) ||
(img_orientation == ORIENTATION_BOTLEFT) || (img_orientation == ORIENTATION_LEFTBOT); (img_orientation == ORIENTATION_BOTLEFT) || (img_orientation == ORIENTATION_LEFTBOT);
const int bitsPerByte = 8; const int bitsPerByte = 8;
int dst_bpp = (int)(img.elemSize1() * bitsPerByte); int dst_bpp = (int)(img.elemSize1() * bitsPerByte);
int wanted_channels = normalizeChannelsNumber(img.channels()); int wanted_channels = normalizeChannelsNumber(img.channels());
if(dst_bpp == 8) if (dst_bpp == 8)
{ {
char errmsg[1024]; char errmsg[1024];
if(!TIFFRGBAImageOK( tif, errmsg )) if (!TIFFRGBAImageOK(tif, errmsg))
{ {
CV_LOG_WARNING(NULL, "OpenCV TIFF: TIFFRGBAImageOK: " << errmsg);
close(); close();
return false; return false;
} }
} }
if( (!is_tiled) || uint32 tile_width0 = m_width, tile_height0 = 0;
(is_tiled &&
TIFFGetField( tif, TIFFTAG_TILEWIDTH, &tile_width0 ) && if (is_tiled)
TIFFGetField( tif, TIFFTAG_TILELENGTH, &tile_height0 ))) {
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_TILEWIDTH, &tile_width0));
CV_TIFF_CHECK_CALL(TIFFGetField(tif, TIFFTAG_TILELENGTH, &tile_height0));
}
else
{ {
if(!is_tiled) // optional
TIFFGetField( tif, TIFFTAG_ROWSPERSTRIP, &tile_height0 ); CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &tile_height0));
}
if( tile_width0 <= 0 ) {
if (tile_width0 == 0)
tile_width0 = m_width; tile_width0 = m_width;
if( tile_height0 <= 0 || if (tile_height0 == 0 ||
(!is_tiled && tile_height0 == std::numeric_limits<uint32>::max()) ) (!is_tiled && tile_height0 == std::numeric_limits<uint32>::max()) )
tile_height0 = m_height; tile_height0 = m_height;
if(dst_bpp == 8) { if (dst_bpp == 8)
{
// we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA // we will use TIFFReadRGBA* functions, so allocate temporary buffer for 32bit RGBA
bpp = 8; bpp = 8;
ncn = 4; ncn = 4;
} }
const size_t buffer_size = (bpp/bitsPerByte) * ncn * tile_height0 * tile_width0; else if (dst_bpp == 32 || dst_bpp == 64)
AutoBuffer<uchar> _buffer( buffer_size ); {
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<uchar> _buffer(buffer_size);
uchar* buffer = _buffer.data(); uchar* buffer = _buffer.data();
ushort* buffer16 = (ushort*)buffer; ushort* buffer16 = (ushort*)buffer;
float* buffer32 = (float*)buffer;
double* buffer64 = (double*)buffer;
int tileidx = 0; 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 ) const int img_y = vert_flip ? m_height - y - tile_height : y;
tile_height = m_height - y;
uchar* data = img.ptr(vert_flip ? m_height - y - tile_height : y); for(int x = 0; x < m_width; x += (int)tile_width0, tileidx++)
for( x = 0; x < m_width; x += 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: case 8:
{ {
uchar * bstart = buffer; uchar* bstart = buffer;
if( !is_tiled ) if (!is_tiled)
ok = TIFFReadRGBAStrip( tif, y, (uint32*)buffer );
else
{ {
ok = TIFFReadRGBATile( tif, x, y, (uint32*)buffer ); CV_TIFF_CHECK_CALL(TIFFReadRGBAStrip(tif, y, (uint32*)buffer));
//Tiles fill the buffer from the bottom up
bstart += (tile_height0 - tile_height) * tile_width0 * 4;
} }
if( !ok ) else
{ {
close(); CV_TIFF_CHECK_CALL(TIFFReadRGBATile(tif, x, y, (uint32*)buffer));
return false; // Tiles fill the buffer from the bottom up
bstart += (tile_height0 - tile_height) * tile_width0 * 4;
} }
for( i = 0; i < tile_height; i++ ) for (int i = 0; i < tile_height; i++)
if( color ) {
if (color)
{ {
if (wanted_channels == 4) if (wanted_channels == 4)
{ {
icvCvt_BGRA2RGBA_8u_C4R( bstart + i*tile_width0*4, 0, icvCvt_BGRA2RGBA_8u_C4R(bstart + i*tile_width0*4, 0,
data + x*4 + img.step*(tile_height - i - 1), 0, img.ptr(img_y + tile_height - i - 1, x), 0,
cvSize(tile_width,1) ); Size(tile_width, 1) );
} }
else else
{ {
icvCvt_BGRA2BGR_8u_C4C3R( bstart + i*tile_width0*4, 0, icvCvt_BGRA2BGR_8u_C4C3R(bstart + i*tile_width0*4, 0,
data + x*3 + img.step*(tile_height - i - 1), 0, img.ptr(img_y + tile_height - i - 1, x), 0,
cvSize(tile_width,1), 2 ); Size(tile_width, 1), 2);
} }
} }
else else
{
icvCvt_BGRA2Gray_8u_C4C1R( bstart + i*tile_width0*4, 0, icvCvt_BGRA2Gray_8u_C4C1R( bstart + i*tile_width0*4, 0,
data + x + img.step*(tile_height - i - 1), 0, img.ptr(img_y + tile_height - i - 1, x), 0,
cvSize(tile_width,1), 2 ); Size(tile_width, 1), 2);
}
}
break; break;
} }
case 16: case 16:
{ {
if( !is_tiled ) if (!is_tiled)
ok = (int)TIFFReadEncodedStrip( tif, tileidx, (uint32*)buffer, buffer_size ) >= 0; {
CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, (uint32*)buffer, buffer_size) >= 0);
}
else else
ok = (int)TIFFReadEncodedTile( tif, tileidx, (uint32*)buffer, buffer_size ) >= 0;
if( !ok )
{ {
close(); CV_TIFF_CHECK_CALL((int)TIFFReadEncodedTile(tif, tileidx, (uint32*)buffer, buffer_size) >= 0);
return false;
} }
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, icvCvt_Gray2BGR_16u_C1C3R(buffer16 + i*tile_width0*ncn, 0,
(ushort*)(data + img.step*i) + x*3, 0, img.ptr<ushort>(img_y + i, x), 0,
cvSize(tile_width,1) ); Size(tile_width, 1));
} }
else if( ncn == 3 ) else if (ncn == 3)
{ {
icvCvt_RGB2BGR_16u_C3R(buffer16 + i*tile_width0*ncn, 0, icvCvt_RGB2BGR_16u_C3R(buffer16 + i*tile_width0*ncn, 0,
(ushort*)(data + img.step*i) + x*3, 0, img.ptr<ushort>(img_y + i, x), 0,
cvSize(tile_width,1) ); Size(tile_width, 1));
} }
else if (ncn == 4) else if (ncn == 4)
{ {
if (wanted_channels == 4) if (wanted_channels == 4)
{ {
icvCvt_BGRA2RGBA_16u_C4R(buffer16 + i*tile_width0*ncn, 0, icvCvt_BGRA2RGBA_16u_C4R(buffer16 + i*tile_width0*ncn, 0,
(ushort*)(data + img.step*i) + x * 4, 0, img.ptr<ushort>(img_y + i, x), 0,
cvSize(tile_width, 1)); Size(tile_width, 1));
} }
else else
{ {
icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0, icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0,
(ushort*)(data + img.step*i) + x * 3, 0, img.ptr<ushort>(img_y + i, x), 0,
cvSize(tile_width, 1), 2); Size(tile_width, 1), 2);
} }
} }
else else
{ {
icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0, icvCvt_BGRA2BGR_16u_C4C3R(buffer16 + i*tile_width0*ncn, 0,
(ushort*)(data + img.step*i) + x*3, 0, img.ptr<ushort>(img_y + i, x), 0,
cvSize(tile_width,1), 2 ); Size(tile_width, 1), 2);
} }
} }
else else
{ {
if( ncn == 1 ) if( ncn == 1 )
{ {
memcpy((ushort*)(data + img.step*i)+x, memcpy(img.ptr<ushort>(img_y + i, x),
buffer16 + i*tile_width0*ncn, buffer16 + i*tile_width0*ncn,
tile_width*sizeof(buffer16[0])); tile_width*sizeof(ushort));
} }
else else
{ {
icvCvt_BGRA2Gray_16u_CnC1R(buffer16 + i*tile_width0*ncn, 0, icvCvt_BGRA2Gray_16u_CnC1R(buffer16 + i*tile_width0*ncn, 0,
(ushort*)(data + img.step*i) + x, 0, img.ptr<ushort>(img_y + i, x), 0,
cvSize(tile_width,1), ncn, 2 ); Size(tile_width, 1), ncn, 2);
} }
} }
} }
@ -500,120 +543,43 @@ bool TiffDecoder::readData( Mat& img )
case 64: case 64:
{ {
if( !is_tiled ) if( !is_tiled )
ok = (int)TIFFReadEncodedStrip( tif, tileidx, buffer, buffer_size ) >= 0;
else
ok = (int)TIFFReadEncodedTile( tif, tileidx, buffer, buffer_size ) >= 0;
if( !ok || ncn != 1 )
{ {
close(); CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, buffer, buffer_size) >= 0);
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 else
{ {
memcpy((double*)(data + img.step*i)+x, CV_TIFF_CHECK_CALL((int)TIFFReadEncodedTile(tif, tileidx, buffer, buffer_size) >= 0);
buffer64 + i*tile_width0*ncn,
tile_width*sizeof(buffer64[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; break;
} }
default: default:
{ {
close(); CV_Assert(0 && "OpenCV TIFF: unsupported depth");
return false;
}
} }
} // switch (dst_bpp)
} // for x
} // for y
} }
} }
result = true; if (m_hdr && depth >= CV_32F)
}
}
return result;
}
bool TiffDecoder::readData_32FC3(Mat& img)
{
int rows_per_strip = 0, photometric = 0;
if(!m_tif)
{
return false;
}
TIFF *tif = static_cast<TIFF*>(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<float>();
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)
{ {
CV_Assert(photometric == PHOTOMETRIC_LOGLUV);
cvtColor(img, img, COLOR_XYZ2BGR); cvtColor(img, img, COLOR_XYZ2BGR);
} }
else
{
cvtColor(img, img, COLOR_RGB2BGR);
}
return true; return true;
} }
bool TiffDecoder::readData_32FC1(Mat& img)
{
if(!m_tif)
{
return false;
}
TIFF *tif = static_cast<TIFF*>(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<img_width; i++)
{
img.at<float>(row,i) = data[i];
}
}
_TIFFfree(buf);
close();
return result;
}
////////////////////////////////////////////////////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////
TiffEncoder::TiffEncoder() TiffEncoder::TiffEncoder()
@ -633,7 +599,7 @@ ImageEncoder TiffEncoder::newEncoder() const
bool TiffEncoder::isFormatSupported( int depth ) 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, void TiffEncoder::writeTag( WLByteStream& strm, TiffTag tag,
@ -656,6 +622,8 @@ public:
TIFF* open () 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<thandle_t>(this), &TiffEncoderBufHelper::read, return TIFFClientOpen( "", "w", reinterpret_cast<thandle_t>(this), &TiffEncoderBufHelper::read,
&TiffEncoderBufHelper::write, &TiffEncoderBufHelper::seek, &TiffEncoderBufHelper::write, &TiffEncoderBufHelper::seek,
&TiffEncoderBufHelper::close, &TiffEncoderBufHelper::size, &TiffEncoderBufHelper::close, &TiffEncoderBufHelper::size,
@ -721,35 +689,39 @@ private:
toff_t m_buf_pos; toff_t m_buf_pos;
}; };
static void readParam(const std::vector<int>& params, int key, int& value) static bool readParam(const std::vector<int>& params, int key, int& value)
{ {
for(size_t i = 0; i + 1 < params.size(); i += 2) for (size_t i = 0; i + 1 < params.size(); i += 2)
if(params[i] == key)
{ {
value = params[i+1]; if (params[i] == key)
break; {
value = params[i + 1];
return true;
}
} }
return false;
} }
bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params) bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params)
{ {
// do NOT put "wb" as the mode, because the b means "big endian" mode, not "binary" mode. // 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 // http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
TIFF* pTiffHandle; TIFF* tif = NULL;
TiffEncoderBufHelper buf_helper(m_buf); TiffEncoderBufHelper buf_helper(m_buf);
if ( m_buf ) if ( m_buf )
{ {
pTiffHandle = buf_helper.open(); tif = buf_helper.open();
} }
else else
{ {
pTiffHandle = TIFFOpen(m_filename.c_str(), "w"); tif = TIFFOpen(m_filename.c_str(), "w");
} }
if (!pTiffHandle) if (!tif)
{ {
return false; return false;
} }
cv::Ptr<void> tif_cleanup(tif, cv_tiffCloseHandle);
//Settings that matter to all images //Settings that matter to all images
int compression = COMPRESSION_LZW; int compression = COMPRESSION_LZW;
@ -768,7 +740,29 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
const Mat& img = img_vec[page]; const Mat& img = img_vec[page];
int channels = img.channels(); int channels = img.channels();
int width = img.cols, height = img.rows; 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; int bitsPerChannel = -1;
switch (depth) switch (depth)
@ -783,9 +777,20 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
bitsPerChannel = 16; bitsPerChannel = 16;
break; break;
} }
case CV_32F:
{
bitsPerChannel = 32;
page_compression = COMPRESSION_NONE;
break;
}
case CV_64F:
{
bitsPerChannel = 64;
page_compression = COMPRESSION_NONE;
break;
}
default: default:
{ {
TIFFClose(pTiffHandle);
return false; return false;
} }
} }
@ -795,57 +800,42 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
int rowsPerStrip = (int)((1 << 13) / fileStep); int rowsPerStrip = (int)((1 << 13) / fileStep);
readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip); readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
rowsPerStrip = std::max(1, std::min(height, rowsPerStrip));
if (rowsPerStrip < 1) int colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK;
rowsPerStrip = 1;
if (rowsPerStrip > height) CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, bitsPerChannel));
rowsPerStrip = height; 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) if (page_compression != COMPRESSION_NONE)
|| !TIFFSetField(pTiffHandle, TIFFTAG_IMAGELENGTH, height) {
|| !TIFFSetField(pTiffHandle, TIFFTAG_BITSPERSAMPLE, bitsPerChannel) CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PREDICTOR, predictor));
|| !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() )))
)
{
TIFFClose(pTiffHandle);
return false;
} }
if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor)) if (resUnit >= RESUNIT_NONE && resUnit <= RESUNIT_CENTIMETER)
{ {
TIFFClose(pTiffHandle); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, resUnit));
return false;
} }
if (dpiX >= 0)
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))
)
{ {
TIFFClose(pTiffHandle); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_XRESOLUTION, (float)dpiX));
return false; }
if (dpiY >= 0)
{
CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_YRESOLUTION, (float)dpiY));
} }
// row buffer, because TIFFWriteScanline modifies the original data! // row buffer, because TIFFWriteScanline modifies the original data!
size_t scanlineSize = TIFFScanlineSize(pTiffHandle); size_t scanlineSize = TIFFScanlineSize(tif);
AutoBuffer<uchar> _buffer(scanlineSize + 32); AutoBuffer<uchar> _buffer(scanlineSize + 32);
uchar* buffer = _buffer.data(); uchar* buffer = _buffer.data(); CV_DbgAssert(buffer);
if (!buffer) Mat m_buffer(Size(width, 1), CV_MAKETYPE(depth, channels), buffer, (size_t)scanlineSize);
{
TIFFClose(pTiffHandle);
return false;
}
for (int y = 0; y < height; ++y) for (int y = 0; y < height; ++y)
{ {
@ -859,122 +849,54 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
case 3: case 3:
{ {
if (depth == CV_8U) cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGR2RGB);
icvCvt_BGR2RGB_8u_C3R( img.ptr(y), 0, buffer, 0, cvSize(width, 1));
else
icvCvt_BGR2RGB_16u_C3R( img.ptr<ushort>(y), 0, (ushort*)buffer, 0, cvSize(width, 1));
break; break;
} }
case 4: case 4:
{ {
if (depth == CV_8U) cvtColor(img(Rect(0, y, width, 1)), (const Mat&)m_buffer, COLOR_BGRA2RGBA);
icvCvt_BGRA2RGBA_8u_C4R( img.ptr(y), 0, buffer, 0, cvSize(width, 1));
else
icvCvt_BGRA2RGBA_16u_C4R( img.ptr<ushort>(y), 0, (ushort*)buffer, 0, cvSize(width, 1));
break; break;
} }
default: default:
{ {
TIFFClose(pTiffHandle); CV_Assert(0);
return false;
} }
} }
int writeResult = TIFFWriteScanline(pTiffHandle, buffer, y, 0); CV_TIFF_CHECK_CALL(TIFFWriteScanline(tif, buffer, y, 0) == 1);
if (writeResult != 1)
{
TIFFClose(pTiffHandle);
return false;
}
} }
TIFFWriteDirectory(pTiffHandle); CV_TIFF_CHECK_CALL(TIFFWriteDirectory(tif));
} }
TIFFClose(pTiffHandle);
return true; 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; Mat img;
cvtColor(_img, img, COLOR_BGR2XYZ); cvtColor(_img, img, COLOR_BGR2XYZ);
TIFF* tif; //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));
TiffEncoderBufHelper buf_helper(m_buf); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3));
if ( m_buf ) CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, 32));
{ CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_SGILOG));
tif = buf_helper.open(); CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_LOGLUV));
} CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG));
else CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT));
{ CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, 1));
tif = TIFFOpen(m_filename.c_str(), "w"); const int strip_size = 3 * img.cols;
} for (int i = 0; i < img.rows; i++)
{
if (!tif) CV_TIFF_CHECK_CALL(TIFFWriteEncodedStrip(tif, i, (tdata_t)img.ptr<float>(i), strip_size * sizeof(float)) != (tsize_t)-1);
{ }
return false; CV_TIFF_CHECK_CALL(TIFFWriteDirectory(tif));
}
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<float*>(img.ptr<float>());
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<float>(row), row, 1) != 1)
{
TIFFClose(tif);
return false;
}
}
TIFFWriteDirectory(tif);
TIFFClose(tif);
return true; return true;
} }
@ -985,18 +907,10 @@ bool TiffEncoder::writemulti(const std::vector<Mat>& img_vec, const std::vector<
bool TiffEncoder::write( const Mat& img, const std::vector<int>& params) bool TiffEncoder::write( const Mat& img, const std::vector<int>& 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<Mat> img_vec; std::vector<Mat> img_vec;
img_vec.push_back(img); img_vec.push_back(img);

@ -106,10 +106,8 @@ public:
ImageDecoder newDecoder() const CV_OVERRIDE; ImageDecoder newDecoder() const CV_OVERRIDE;
protected: protected:
void* m_tif; cv::Ptr<void> m_tif;
int normalizeChannelsNumber(int channels) const; int normalizeChannelsNumber(int channels) const;
bool readData_32FC3(Mat& img);
bool readData_32FC1(Mat& img);
bool m_hdr; bool m_hdr;
size_t m_buf_pos; size_t m_buf_pos;
@ -139,8 +137,7 @@ protected:
int count, int value ); int count, int value );
bool writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params ); bool writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params );
bool write_32FC3( const Mat& img ); bool write_32FC3_SGILOG(const Mat& img, void* tif);
bool write_32FC1( const Mat& img );
private: private:
TiffEncoder(const TiffEncoder &); // copy disabled TiffEncoder(const TiffEncoder &); // copy disabled

@ -42,6 +42,8 @@
#include "precomp.hpp" #include "precomp.hpp"
#include "utils.hpp" #include "utils.hpp"
namespace cv {
int validateToInt(size_t sz) int validateToInt(size_t sz)
{ {
int valueInt = (int)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, void icvCvt_BGR2Gray_8u_C3C1R( const uchar* rgb, int rgb_step,
uchar* gray, int gray_step, uchar* gray, int gray_step,
CvSize size, int _swap_rb ) Size size, int _swap_rb )
{ {
int i; int i;
int swap_rb = _swap_rb ? 2 : 0; 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, void icvCvt_BGRA2Gray_16u_CnC1R( const ushort* rgb, int rgb_step,
ushort* gray, int gray_step, ushort* gray, int gray_step,
CvSize size, int ncn, int _swap_rb ) Size size, int ncn, int _swap_rb )
{ {
int i; int i;
int swap_rb = _swap_rb ? 2 : 0; 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, void icvCvt_BGRA2Gray_8u_C4C1R( const uchar* rgba, int rgba_step,
uchar* gray, int gray_step, uchar* gray, int gray_step,
CvSize size, int _swap_rb ) Size size, int _swap_rb )
{ {
int i; int i;
int swap_rb = _swap_rb ? 2 : 0; 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, 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; int i;
for( ; size.height--; gray += gray_step ) 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, 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; int i;
for( ; size.height--; gray += gray_step/sizeof(gray[0]) ) 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, void icvCvt_BGRA2BGR_8u_C4C3R( const uchar* bgra, int bgra_step,
uchar* bgr, int bgr_step, uchar* bgr, int bgr_step,
CvSize size, int _swap_rb ) Size size, int _swap_rb )
{ {
int i; int i;
int swap_rb = _swap_rb ? 2 : 0; 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, void icvCvt_BGRA2BGR_16u_C4C3R( const ushort* bgra, int bgra_step,
ushort* bgr, int bgr_step, ushort* bgr, int bgr_step,
CvSize size, int _swap_rb ) Size size, int _swap_rb )
{ {
int i; int i;
int swap_rb = _swap_rb ? 2 : 0; 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, 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; int i;
for( ; size.height--; ) 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, 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; int i;
for( ; size.height--; ) 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, 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; int i;
for( ; size.height--; ) 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, 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; int i;
for( ; size.height--; ) for( ; size.height--; )
@ -256,7 +258,7 @@ void icvCvt_BGR2RGB_16u_C3R( const ushort* bgr, int bgr_step,
typedef unsigned short ushort; typedef unsigned short ushort;
void icvCvt_BGR5552Gray_8u_C2C1R( const uchar* bgr555, int bgr555_step, 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; int i;
for( ; size.height--; gray += gray_step, bgr555 += bgr555_step ) 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, 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; int i;
for( ; size.height--; gray += gray_step, bgr565 += bgr565_step ) 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, 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; int i;
for( ; size.height--; bgr555 += bgr555_step ) 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, 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; int i;
for( ; size.height--; bgr565 += bgr565_step ) 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, 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; int i;
for( ; size.height--; ) 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, 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; int i;
for( ; size.height--; ) for( ; size.height--; )
@ -371,7 +373,7 @@ void CvtPaletteToGray( const PaletteEntry* palette, uchar* grayPalette, int entr
int i; int i;
for( i = 0; i < entries; 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; return data;
} }
} // namespace
using namespace cv;
CV_IMPL void CV_IMPL void
cvConvertImage( const CvArr* srcarr, CvArr* dstarr, int flags ) 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; uchar *s = src->data.ptr, *d = dst->data.ptr;
int s_step = src->step, d_step = dst->step; int s_step = src->step, d_step = dst->step;
int code = src_cn*10 + dst_cn; 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) ) if( CV_IS_MAT_CONT(src->type & dst->type) )
{ {

@ -42,6 +42,8 @@
#ifndef _UTILS_H_ #ifndef _UTILS_H_
#define _UTILS_H_ #define _UTILS_H_
namespace cv {
int validateToInt(size_t step); int validateToInt(size_t step);
template <typename _Tp> static inline template <typename _Tp> static inline
@ -68,53 +70,53 @@ struct PaletteEntry
void icvCvt_BGR2Gray_8u_C3C1R( const uchar* bgr, int bgr_step, void icvCvt_BGR2Gray_8u_C3C1R( const uchar* bgr, int bgr_step,
uchar* gray, int gray_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, void icvCvt_BGRA2Gray_8u_C4C1R( const uchar* bgra, int bgra_step,
uchar* gray, int gray_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, void icvCvt_BGRA2Gray_16u_CnC1R( const ushort* bgra, int bgra_step,
ushort* gray, int gray_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, 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, 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, void icvCvt_BGRA2BGR_8u_C4C3R( const uchar* bgra, int bgra_step,
uchar* bgr, int bgr_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, void icvCvt_BGRA2BGR_16u_C4C3R( const ushort* bgra, int bgra_step,
ushort* bgr, int bgr_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, 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 #define icvCvt_RGB2BGR_8u_C3R icvCvt_BGR2RGB_8u_C3R
void icvCvt_BGR2RGB_16u_C3R( const ushort* 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 );
#define icvCvt_RGB2BGR_16u_C3R icvCvt_BGR2RGB_16u_C3R #define icvCvt_RGB2BGR_16u_C3R icvCvt_BGR2RGB_16u_C3R
void icvCvt_BGRA2RGBA_8u_C4R( const uchar* 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 );
#define icvCvt_RGBA2BGRA_8u_C4R icvCvt_BGRA2RGBA_8u_C4R #define icvCvt_RGBA2BGRA_8u_C4R icvCvt_BGRA2RGBA_8u_C4R
void icvCvt_BGRA2RGBA_16u_C4R( const ushort* 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 );
#define icvCvt_RGBA2BGRA_16u_C4R icvCvt_BGRA2RGBA_16u_C4R #define icvCvt_RGBA2BGRA_16u_C4R icvCvt_BGRA2RGBA_16u_C4R
void icvCvt_BGR5552Gray_8u_C2C1R( const uchar* bgr555, int bgr555_step, 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, 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, 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, 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, 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, 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 ); void FillGrayPalette( PaletteEntry* palette, int bpp, bool negative = false );
bool IsColorPalette( PaletteEntry* palette, int bpp ); 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; return (((const int*)"\0\x1\x2\x3\x4\x5\x6\x7")[0] & 255) != 0;
} }
} // namespace
#endif/*_UTILS_H_*/ #endif/*_UTILS_H_*/

@ -158,12 +158,68 @@ TEST(Imgcodecs_Tiff, readWrite_32FC1)
ASSERT_TRUE(cv::imwrite(filenameOutput, img)); ASSERT_TRUE(cv::imwrite(filenameOutput, img));
const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED); const Mat img2 = cv::imread(filenameOutput, IMREAD_UNCHANGED);
ASSERT_EQ(img2.type(),img.type()); ASSERT_EQ(img2.type(), img.type());
ASSERT_EQ(img2.size(),img.size()); ASSERT_EQ(img2.size(), img.size());
EXPECT_GE(1e-3, cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE)); EXPECT_LE(cvtest::norm(img, img2, NORM_INF | NORM_RELATIVE), 1e-3);
EXPECT_EQ(0, remove(filenameOutput.c_str())); 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<int> 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<int> Imgcodecs_Tiff_Modes; typedef testing::TestWithParam<int> Imgcodecs_Tiff_Modes;

Loading…
Cancel
Save