|
|
|
@ -46,21 +46,25 @@ |
|
|
|
|
\****************************************************************************************/ |
|
|
|
|
|
|
|
|
|
#include "precomp.hpp" |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_TIFF |
|
|
|
|
#include "grfmt_tiff.hpp" |
|
|
|
|
#include <opencv2/imgproc.hpp> |
|
|
|
|
#include <limits> |
|
|
|
|
|
|
|
|
|
// TODO FIXIT Conflict declarations for common types like int64/uint64
|
|
|
|
|
namespace tiff_dummy_namespace { |
|
|
|
|
#include "tiff.h" |
|
|
|
|
#include "tiffio.h" |
|
|
|
|
} |
|
|
|
|
using namespace tiff_dummy_namespace; |
|
|
|
|
|
|
|
|
|
namespace cv |
|
|
|
|
{ |
|
|
|
|
static const char fmtSignTiffII[] = "II\x2a\x00"; |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_TIFF |
|
|
|
|
|
|
|
|
|
static const char fmtSignTiffII[] = "II\x2a\x00"; |
|
|
|
|
static const char fmtSignTiffMM[] = "MM\x00\x2a"; |
|
|
|
|
|
|
|
|
|
#include "tiff.h" |
|
|
|
|
#include "tiffio.h" |
|
|
|
|
|
|
|
|
|
static int grfmt_tiff_err_handler_init = 0; |
|
|
|
|
static void GrFmtSilentTIFFErrorHandler( const char*, const char*, va_list ) {} |
|
|
|
|
|
|
|
|
@ -119,19 +123,24 @@ ImageDecoder TiffDecoder::newDecoder() const |
|
|
|
|
|
|
|
|
|
class TiffDecoderBufHelper |
|
|
|
|
{ |
|
|
|
|
Mat& m_buf; |
|
|
|
|
size_t& m_buf_pos; |
|
|
|
|
public: |
|
|
|
|
TiffDecoderBufHelper(Mat& buf, size_t& buf_pos) : |
|
|
|
|
m_buf(buf), m_buf_pos(buf_pos) |
|
|
|
|
{} |
|
|
|
|
static tmsize_t read( thandle_t handle, void* buffer, tmsize_t n ) |
|
|
|
|
{ |
|
|
|
|
TiffDecoder *decoder = reinterpret_cast<TiffDecoder*>(handle); |
|
|
|
|
const Mat& buf = decoder->m_buf; |
|
|
|
|
TiffDecoderBufHelper *helper = reinterpret_cast<TiffDecoderBufHelper*>(handle); |
|
|
|
|
const Mat& buf = helper->m_buf; |
|
|
|
|
const tmsize_t size = buf.cols*buf.rows*buf.elemSize(); |
|
|
|
|
tmsize_t pos = decoder->m_buf_pos; |
|
|
|
|
tmsize_t pos = helper->m_buf_pos; |
|
|
|
|
if ( n > (size - pos) ) |
|
|
|
|
{ |
|
|
|
|
n = size - pos; |
|
|
|
|
} |
|
|
|
|
memcpy(buffer, buf.ptr() + pos, n); |
|
|
|
|
decoder->m_buf_pos += n; |
|
|
|
|
helper->m_buf_pos += n; |
|
|
|
|
return n; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
@ -143,10 +152,10 @@ public: |
|
|
|
|
|
|
|
|
|
static toff_t seek( thandle_t handle, toff_t offset, int whence ) |
|
|
|
|
{ |
|
|
|
|
TiffDecoder *decoder = reinterpret_cast<TiffDecoder*>(handle); |
|
|
|
|
const Mat& buf = decoder->m_buf; |
|
|
|
|
TiffDecoderBufHelper *helper = reinterpret_cast<TiffDecoderBufHelper*>(handle); |
|
|
|
|
const Mat& buf = helper->m_buf; |
|
|
|
|
const toff_t size = buf.cols*buf.rows*buf.elemSize(); |
|
|
|
|
toff_t new_pos = decoder->m_buf_pos; |
|
|
|
|
toff_t new_pos = helper->m_buf_pos; |
|
|
|
|
switch (whence) |
|
|
|
|
{ |
|
|
|
|
case SEEK_SET: |
|
|
|
@ -160,14 +169,14 @@ public: |
|
|
|
|
break; |
|
|
|
|
} |
|
|
|
|
new_pos = std::min(new_pos, size); |
|
|
|
|
decoder->m_buf_pos = (size_t)new_pos; |
|
|
|
|
helper->m_buf_pos = (size_t)new_pos; |
|
|
|
|
return new_pos; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int map( thandle_t handle, void** base, toff_t* size ) |
|
|
|
|
{ |
|
|
|
|
TiffDecoder *decoder = reinterpret_cast<TiffDecoder*>(handle); |
|
|
|
|
Mat& buf = decoder->m_buf; |
|
|
|
|
TiffDecoderBufHelper *helper = reinterpret_cast<TiffDecoderBufHelper*>(handle); |
|
|
|
|
Mat& buf = helper->m_buf; |
|
|
|
|
*base = buf.ptr(); |
|
|
|
|
*size = buf.cols*buf.rows*buf.elemSize(); |
|
|
|
|
return 0; |
|
|
|
@ -175,14 +184,15 @@ public: |
|
|
|
|
|
|
|
|
|
static toff_t size( thandle_t handle ) |
|
|
|
|
{ |
|
|
|
|
TiffDecoder *decoder = reinterpret_cast<TiffDecoder*>(handle); |
|
|
|
|
const Mat& buf = decoder->m_buf; |
|
|
|
|
TiffDecoderBufHelper *helper = reinterpret_cast<TiffDecoderBufHelper*>(handle); |
|
|
|
|
const Mat& buf = helper->m_buf; |
|
|
|
|
return buf.cols*buf.rows*buf.elemSize(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
static int close( thandle_t /*handle*/ ) |
|
|
|
|
static int close( thandle_t handle ) |
|
|
|
|
{ |
|
|
|
|
// Do nothing.
|
|
|
|
|
TiffDecoderBufHelper *helper = reinterpret_cast<TiffDecoderBufHelper*>(handle); |
|
|
|
|
delete helper; |
|
|
|
|
return 0; |
|
|
|
|
} |
|
|
|
|
}; |
|
|
|
@ -199,7 +209,8 @@ bool TiffDecoder::readHeader() |
|
|
|
|
if ( !m_buf.empty() ) |
|
|
|
|
{ |
|
|
|
|
m_buf_pos = 0; |
|
|
|
|
tif = TIFFClientOpen( "", "r", reinterpret_cast<thandle_t>(this), &TiffDecoderBufHelper::read, |
|
|
|
|
TiffDecoderBufHelper* buf_helper = new TiffDecoderBufHelper(this->m_buf, this->m_buf_pos); |
|
|
|
|
tif = TIFFClientOpen( "", "r", reinterpret_cast<thandle_t>(buf_helper), &TiffDecoderBufHelper::read, |
|
|
|
|
&TiffDecoderBufHelper::write, &TiffDecoderBufHelper::seek, |
|
|
|
|
&TiffDecoderBufHelper::close, &TiffDecoderBufHelper::size, |
|
|
|
|
&TiffDecoderBufHelper::map, /*unmap=*/0 ); |
|
|
|
@ -548,8 +559,6 @@ bool TiffDecoder::readHdrData(Mat& img) |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
|
|
TiffEncoder::TiffEncoder() |
|
|
|
@ -569,11 +578,7 @@ ImageEncoder TiffEncoder::newEncoder() const |
|
|
|
|
|
|
|
|
|
bool TiffEncoder::isFormatSupported( int depth ) const |
|
|
|
|
{ |
|
|
|
|
#ifdef HAVE_TIFF |
|
|
|
|
return depth == CV_8U || depth == CV_16U || depth == CV_32F; |
|
|
|
|
#else |
|
|
|
|
return depth == CV_8U || depth == CV_16U; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
void TiffEncoder::writeTag( WLByteStream& strm, TiffTag tag, |
|
|
|
@ -586,8 +591,6 @@ void TiffEncoder::writeTag( WLByteStream& strm, TiffTag tag, |
|
|
|
|
strm.putDWord( value ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_TIFF |
|
|
|
|
|
|
|
|
|
class TiffEncoderBufHelper |
|
|
|
|
{ |
|
|
|
|
public: |
|
|
|
@ -854,204 +857,20 @@ bool TiffEncoder::writeHdr(const Mat& _img) |
|
|
|
|
return true; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_TIFF |
|
|
|
|
bool TiffEncoder::write( const Mat& img, const std::vector<int>& params) |
|
|
|
|
#else |
|
|
|
|
bool TiffEncoder::write( const Mat& img, const std::vector<int>& /*params*/) |
|
|
|
|
#endif |
|
|
|
|
{ |
|
|
|
|
int depth = img.depth(); |
|
|
|
|
#ifdef HAVE_TIFF |
|
|
|
|
|
|
|
|
|
if(img.type() == CV_32FC3) |
|
|
|
|
{ |
|
|
|
|
return writeHdr(img); |
|
|
|
|
return writeHdr(img); // TODO Rename
|
|
|
|
|
} |
|
|
|
|
#endif |
|
|
|
|
|
|
|
|
|
if (depth != CV_8U && depth != CV_16U) |
|
|
|
|
return false; |
|
|
|
|
CV_Assert(depth == CV_8U || depth == CV_16U); |
|
|
|
|
|
|
|
|
|
#ifdef HAVE_TIFF |
|
|
|
|
return writeLibTiff(img, params); |
|
|
|
|
#else |
|
|
|
|
int channels = img.channels(); |
|
|
|
|
int width = img.cols, height = img.rows; |
|
|
|
|
int bytesPerChannel = depth == CV_8U ? 1 : 2; |
|
|
|
|
int fileStep = width * channels * bytesPerChannel; |
|
|
|
|
|
|
|
|
|
WLByteStream strm; |
|
|
|
|
|
|
|
|
|
if( m_buf ) |
|
|
|
|
{ |
|
|
|
|
if( !strm.open(*m_buf) ) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
if( !strm.open(m_filename) ) |
|
|
|
|
return false; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
int rowsPerStrip = (1 << 13)/fileStep; |
|
|
|
|
|
|
|
|
|
if( rowsPerStrip < 1 ) |
|
|
|
|
rowsPerStrip = 1; |
|
|
|
|
|
|
|
|
|
if( rowsPerStrip > height ) |
|
|
|
|
rowsPerStrip = height; |
|
|
|
|
|
|
|
|
|
int i, stripCount = (height + rowsPerStrip - 1) / rowsPerStrip; |
|
|
|
|
|
|
|
|
|
if( m_buf ) |
|
|
|
|
m_buf->reserve( alignSize(stripCount*8 + fileStep*height + 256, 256) ); |
|
|
|
|
|
|
|
|
|
/*#if defined _DEBUG || !defined _WIN32
|
|
|
|
|
int uncompressedRowSize = rowsPerStrip * fileStep; |
|
|
|
|
#endif*/ |
|
|
|
|
int directoryOffset = 0; |
|
|
|
|
|
|
|
|
|
AutoBuffer<int> stripOffsets(stripCount); |
|
|
|
|
AutoBuffer<short> stripCounts(stripCount); |
|
|
|
|
AutoBuffer<uchar> _buffer(fileStep+32); |
|
|
|
|
uchar* buffer = _buffer; |
|
|
|
|
int stripOffsetsOffset = 0; |
|
|
|
|
int stripCountsOffset = 0; |
|
|
|
|
int bitsPerSample = 8 * bytesPerChannel; |
|
|
|
|
int y = 0; |
|
|
|
|
|
|
|
|
|
strm.putBytes( fmtSignTiffII, 4 ); |
|
|
|
|
strm.putDWord( directoryOffset ); |
|
|
|
|
|
|
|
|
|
// write an image data first (the most reasonable way
|
|
|
|
|
// for compressed images)
|
|
|
|
|
for( i = 0; i < stripCount; i++ ) |
|
|
|
|
{ |
|
|
|
|
int limit = y + rowsPerStrip; |
|
|
|
|
|
|
|
|
|
if( limit > height ) |
|
|
|
|
limit = height; |
|
|
|
|
|
|
|
|
|
stripOffsets[i] = strm.getPos(); |
|
|
|
|
|
|
|
|
|
for( ; y < limit; y++ ) |
|
|
|
|
{ |
|
|
|
|
if( channels == 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) ); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
if( channels == 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) ); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
strm.putBytes( channels > 1 ? buffer : img.ptr(y), fileStep ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
stripCounts[i] = (short)(strm.getPos() - stripOffsets[i]); |
|
|
|
|
/*assert( stripCounts[i] == uncompressedRowSize ||
|
|
|
|
|
stripCounts[i] < uncompressedRowSize && |
|
|
|
|
i == stripCount - 1);*/ |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if( stripCount > 2 ) |
|
|
|
|
{ |
|
|
|
|
stripOffsetsOffset = strm.getPos(); |
|
|
|
|
for( i = 0; i < stripCount; i++ ) |
|
|
|
|
strm.putDWord( stripOffsets[i] ); |
|
|
|
|
|
|
|
|
|
stripCountsOffset = strm.getPos(); |
|
|
|
|
for( i = 0; i < stripCount; i++ ) |
|
|
|
|
strm.putWord( stripCounts[i] ); |
|
|
|
|
} |
|
|
|
|
else if(stripCount == 2) |
|
|
|
|
{ |
|
|
|
|
stripOffsetsOffset = strm.getPos(); |
|
|
|
|
for (i = 0; i < stripCount; i++) |
|
|
|
|
{ |
|
|
|
|
strm.putDWord (stripOffsets [i]); |
|
|
|
|
} |
|
|
|
|
stripCountsOffset = stripCounts [0] + (stripCounts [1] << 16); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
stripOffsetsOffset = stripOffsets[0]; |
|
|
|
|
stripCountsOffset = stripCounts[0]; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
if( channels > 1 ) |
|
|
|
|
{ |
|
|
|
|
int bitsPerSamplePos = strm.getPos(); |
|
|
|
|
strm.putWord(bitsPerSample); |
|
|
|
|
strm.putWord(bitsPerSample); |
|
|
|
|
strm.putWord(bitsPerSample); |
|
|
|
|
if( channels == 4 ) |
|
|
|
|
strm.putWord(bitsPerSample); |
|
|
|
|
bitsPerSample = bitsPerSamplePos; |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
directoryOffset = strm.getPos(); |
|
|
|
|
|
|
|
|
|
// write header
|
|
|
|
|
strm.putWord( 9 ); |
|
|
|
|
|
|
|
|
|
/* warning: specification 5.0 of Tiff want to have tags in
|
|
|
|
|
ascending order. This is a non-fatal error, but this cause |
|
|
|
|
warning with some tools. So, keep this in ascending order */ |
|
|
|
|
|
|
|
|
|
writeTag( strm, TIFF_TAG_WIDTH, TIFF_TYPE_LONG, 1, width ); |
|
|
|
|
writeTag( strm, TIFF_TAG_HEIGHT, TIFF_TYPE_LONG, 1, height ); |
|
|
|
|
writeTag( strm, TIFF_TAG_BITS_PER_SAMPLE, |
|
|
|
|
TIFF_TYPE_SHORT, channels, bitsPerSample ); |
|
|
|
|
writeTag( strm, TIFF_TAG_COMPRESSION, TIFF_TYPE_LONG, 1, TIFF_UNCOMP ); |
|
|
|
|
writeTag( strm, TIFF_TAG_PHOTOMETRIC, TIFF_TYPE_SHORT, 1, channels > 1 ? 2 : 1 ); |
|
|
|
|
|
|
|
|
|
writeTag( strm, TIFF_TAG_STRIP_OFFSETS, TIFF_TYPE_LONG, |
|
|
|
|
stripCount, stripOffsetsOffset ); |
|
|
|
|
|
|
|
|
|
writeTag( strm, TIFF_TAG_SAMPLES_PER_PIXEL, TIFF_TYPE_SHORT, 1, channels ); |
|
|
|
|
writeTag( strm, TIFF_TAG_ROWS_PER_STRIP, TIFF_TYPE_LONG, 1, rowsPerStrip ); |
|
|
|
|
|
|
|
|
|
writeTag( strm, TIFF_TAG_STRIP_COUNTS, |
|
|
|
|
stripCount > 1 ? TIFF_TYPE_SHORT : TIFF_TYPE_LONG, |
|
|
|
|
stripCount, stripCountsOffset ); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
strm.putDWord(0); |
|
|
|
|
strm.close(); |
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
|
|
if( m_buf ) |
|
|
|
|
{ |
|
|
|
|
(*m_buf)[4] = (uchar)directoryOffset; |
|
|
|
|
(*m_buf)[5] = (uchar)(directoryOffset >> 8); |
|
|
|
|
(*m_buf)[6] = (uchar)(directoryOffset >> 16); |
|
|
|
|
(*m_buf)[7] = (uchar)(directoryOffset >> 24); |
|
|
|
|
} |
|
|
|
|
else |
|
|
|
|
{ |
|
|
|
|
// write directory offset
|
|
|
|
|
FILE* f = fopen( m_filename.c_str(), "r+b" ); |
|
|
|
|
buffer[0] = (uchar)directoryOffset; |
|
|
|
|
buffer[1] = (uchar)(directoryOffset >> 8); |
|
|
|
|
buffer[2] = (uchar)(directoryOffset >> 16); |
|
|
|
|
buffer[3] = (uchar)(directoryOffset >> 24); |
|
|
|
|
|
|
|
|
|
fseek( f, 4, SEEK_SET ); |
|
|
|
|
fwrite( buffer, 1, 4, f ); |
|
|
|
|
fclose(f); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
return true; |
|
|
|
|
#endif |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
} |
|
|
|
|