#include "precomp.hpp"
#ifdef HAVE_TIFF
+#include <opencv2/core/utils/logger.hpp>
+
#include "grfmt_tiff.hpp"
#include <limits>
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;
void TiffDecoder::close()
{
- if( m_tif )
- {
- TIFF* tif = (TIFF*)m_tif;
- TIFFClose( tif );
- m_tif = 0;
- }
+ m_tif.release();
}
TiffDecoder::~TiffDecoder()
int TiffDecoder::normalizeChannelsNumber(int channels) const
{
+ CV_Assert(channels <= 4);
return channels > 4 ? 4 : channels;
}
ImageDecoder TiffDecoder::newDecoder() const
{
+ cv_tiffSetErrorHandler();
return makePtr<TiffDecoder>();
}
{
bool result = false;
- TIFF* tif = static_cast<TIFF*>(m_tif);
- if (!m_tif)
+ TIFF* tif = static_cast<TIFF*>(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
{
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;
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:
bool TiffDecoder::nextPage()
{
// Prepare the next page, if any.
- return m_tif &&
- TIFFReadDirectory(static_cast<TIFF*>(m_tif)) &&
+ return !m_tif.empty() &&
+ TIFFReadDirectory(static_cast<TIFF*>(m_tif.get())) &&
readHeader();
}
bool TiffDecoder::readData( Mat& img )
{
- if(m_hdr && img.type() == CV_32FC3)
- {
- return readData_32FC3(img);
- }
- if(img.type() == CV_32FC1)
+ 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_32FC1(img);
+ CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SGILOGDATAFMT, SGILOGDATAFMT_FLOAT));
}
- 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 )))
+ uint32 tile_width0 = m_width, tile_height0 = 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
{
- if(!is_tiled)
- TIFFGetField( tif, TIFFTAG_ROWSPERSTRIP, &tile_height0 );
+ // optional
+ CV_TIFF_CHECK_CALL_DEBUG(TIFFGetField(tif, TIFFTAG_ROWSPERSTRIP, &tile_height0));
+ }
- if( tile_width0 <= 0 )
+ {
+ if (tile_width0 == 0)
tile_width0 = m_width;
- if( tile_height0 <= 0 ||
- (!is_tiled && tile_height0 == std::numeric_limits<uint32>::max()) )
+ if (tile_height0 == 0 ||
+ (!is_tiled && tile_height0 == std::numeric_limits<uint32>::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<uchar> _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<uchar> _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;
-
- if( y + tile_height > m_height )
- tile_height = m_height - y;
+ int tile_height = std::min((int)tile_height0, m_height - y);
- uchar* data = img.ptr(vert_flip ? m_height - y - tile_height : y);
+ const int img_y = 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 );
- else
+ uchar* bstart = buffer;
+ if (!is_tiled)
{
- ok = TIFFReadRGBATile( tif, x, y, (uint32*)buffer );
- //Tiles fill the buffer from the bottom up
- bstart += (tile_height0 - tile_height) * tile_width0 * 4;
+ CV_TIFF_CHECK_CALL(TIFFReadRGBAStrip(tif, y, (uint32*)buffer));
}
- if( !ok )
+ else
{
- close();
- return false;
+ 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;
}
- 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;
+ if (!is_tiled)
+ {
+ CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, (uint32*)buffer, buffer_size) >= 0);
+ }
else
- ok = (int)TIFFReadEncodedTile( tif, tileidx, (uint32*)buffer, buffer_size ) >= 0;
-
- if( !ok )
{
- close();
- return false;
+ 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<ushort>(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<ushort>(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<ushort>(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<ushort>(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<ushort>(img_y + i, x), 0,
+ Size(tile_width, 1), 2);
}
}
else
{
if( ncn == 1 )
{
- memcpy((ushort*)(data + img.step*i)+x,
+ memcpy(img.ptr<ushort>(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<ushort>(img_y + i, x), 0,
+ Size(tile_width, 1), ncn, 2);
}
}
}
case 64:
{
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();
- return false;
+ CV_TIFF_CHECK_CALL((int)TIFFReadEncodedStrip(tif, tileidx, buffer, buffer_size) >= 0);
}
-
- for( i = 0; i < tile_height; i++ )
+ else
{
- 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<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)
+ 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<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()
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,
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,
&TiffEncoderBufHelper::write, &TiffEncoderBufHelper::seek,
&TiffEncoderBufHelper::close, &TiffEncoderBufHelper::size,
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)
- 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<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.
// 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<void> tif_cleanup(tif, cv_tiffCloseHandle);
//Settings that matter to all images
int compression = COMPRESSION_LZW;
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)
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;
}
}
int rowsPerStrip = (int)((1 << 13) / fileStep);
readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
+ rowsPerStrip = std::max(1, std::min(height, rowsPerStrip));
+
+ int colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK;
+
+ 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));
- if (rowsPerStrip < 1)
- rowsPerStrip = 1;
-
- if (rowsPerStrip > height)
- rowsPerStrip = height;
-
- int colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK;
-
- 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() )))
- )
+ CV_TIFF_CHECK_CALL(TIFFSetField(tif, TIFFTAG_SAMPLEFORMAT, depth >= CV_32F ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT));
+
+ 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<uchar> _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)
{
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<ushort>(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<ushort>(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 )
- {
- 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, 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);
-
+ //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++)
+ {
+ CV_TIFF_CHECK_CALL(TIFFWriteEncodedStrip(tif, i, (tdata_t)img.ptr<float>(i), strip_size * sizeof(float)) != (tsize_t)-1);
+ }
+ CV_TIFF_CHECK_CALL(TIFFWriteDirectory(tif));
return true;
}
bool TiffEncoder::write( const Mat& img, const std::vector<int>& params)
{
- int depth = img.depth();
-
- if(img.type() == CV_32FC3)
- {
- return write_32FC3(img);
- }
- if(img.type() == CV_32FC1)
- {
- return write_32FC1(img);
- }
+ int type = img.type();
+ int depth = CV_MAT_DEPTH(type);
- 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;
img_vec.push_back(img);
#include "precomp.hpp"
#include "utils.hpp"
+namespace cv {
+
int validateToInt(size_t sz)
{
int valueInt = (int)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;
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;
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;
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 )
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]) )
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;
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;
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--; )
}
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--; )
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--; )
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--; )
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 )
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 )
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 )
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 )
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--; )
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--; )
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) );
}
}
return data;
}
+} // namespace
+
+using namespace cv;
CV_IMPL void
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) )
{