mirror of
https://github.com/opencv/opencv.git
synced 2025-01-18 22:44:02 +08:00
imwrite for multipage images implemented
This commit is contained in:
parent
c6e1e3acdc
commit
27b1f8f446
@ -127,6 +127,11 @@ bool BaseImageEncoder::setDestination( std::vector<uchar>& buf )
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BaseImageEncoder::writemulti(const std::vector<Mat>&, const std::vector<int>& )
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
ImageEncoder BaseImageEncoder::newEncoder() const
|
||||
{
|
||||
return ImageEncoder();
|
||||
|
@ -101,6 +101,7 @@ public:
|
||||
virtual bool setDestination( const String& filename );
|
||||
virtual bool setDestination( std::vector<uchar>& buf );
|
||||
virtual bool write( const Mat& img, const std::vector<int>& params ) = 0;
|
||||
virtual bool writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params);
|
||||
|
||||
virtual String getDescription() const;
|
||||
virtual ImageEncoder newEncoder() const;
|
||||
|
@ -539,7 +539,6 @@ bool TiffDecoder::readData( Mat& img )
|
||||
|
||||
bool TiffDecoder::readData_32FC3(Mat& img)
|
||||
{
|
||||
|
||||
int rows_per_strip = 0, photometric = 0;
|
||||
if(!m_tif)
|
||||
{
|
||||
@ -724,44 +723,8 @@ static void readParam(const std::vector<int>& params, int key, int& value)
|
||||
}
|
||||
}
|
||||
|
||||
bool TiffEncoder::writeLibTiff( const Mat& img, const std::vector<int>& params)
|
||||
bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params)
|
||||
{
|
||||
int channels = img.channels();
|
||||
int width = img.cols, height = img.rows;
|
||||
int depth = img.depth();
|
||||
|
||||
int bitsPerChannel = -1;
|
||||
switch (depth)
|
||||
{
|
||||
case CV_8U:
|
||||
{
|
||||
bitsPerChannel = 8;
|
||||
break;
|
||||
}
|
||||
case CV_16U:
|
||||
{
|
||||
bitsPerChannel = 16;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int bitsPerByte = 8;
|
||||
size_t fileStep = (width * channels * bitsPerChannel) / bitsPerByte;
|
||||
|
||||
int rowsPerStrip = (int)((1 << 13)/fileStep);
|
||||
readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
|
||||
|
||||
if( rowsPerStrip < 1 )
|
||||
rowsPerStrip = 1;
|
||||
|
||||
if( rowsPerStrip > height )
|
||||
rowsPerStrip = height;
|
||||
|
||||
|
||||
// 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;
|
||||
@ -780,86 +743,133 @@ bool TiffEncoder::writeLibTiff( const Mat& img, const std::vector<int>& params)
|
||||
return false;
|
||||
}
|
||||
|
||||
//Settings that matter to all images
|
||||
// defaults for now, maybe base them on params in the future
|
||||
int compression = COMPRESSION_LZW;
|
||||
int predictor = PREDICTOR_HORIZONTAL;
|
||||
int compression = COMPRESSION_LZW;
|
||||
int predictor = PREDICTOR_HORIZONTAL;
|
||||
|
||||
readParam(params, TIFFTAG_COMPRESSION, compression);
|
||||
readParam(params, TIFFTAG_PREDICTOR, predictor);
|
||||
|
||||
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)
|
||||
)
|
||||
//Iterate through each image in the vector and write them out as Tiff directories
|
||||
for (size_t page = 0; page < img_vec.size(); page++)
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
const Mat& img = img_vec[page];
|
||||
int channels = img.channels();
|
||||
int width = img.cols, height = img.rows;
|
||||
int depth = img.depth();
|
||||
|
||||
if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor) )
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
// row buffer, because TIFFWriteScanline modifies the original data!
|
||||
size_t scanlineSize = TIFFScanlineSize(pTiffHandle);
|
||||
AutoBuffer<uchar> _buffer(scanlineSize+32);
|
||||
uchar* buffer = _buffer;
|
||||
if (!buffer)
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
switch(channels)
|
||||
int bitsPerChannel = -1;
|
||||
switch (depth)
|
||||
{
|
||||
case 1:
|
||||
case CV_8U:
|
||||
{
|
||||
memcpy(buffer, img.ptr(y), scanlineSize);
|
||||
bitsPerChannel = 8;
|
||||
break;
|
||||
}
|
||||
|
||||
case 3:
|
||||
case CV_16U:
|
||||
{
|
||||
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) );
|
||||
bitsPerChannel = 16;
|
||||
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) );
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
const int bitsPerByte = 8;
|
||||
size_t fileStep = (width * channels * bitsPerChannel) / bitsPerByte;
|
||||
|
||||
int rowsPerStrip = (int)((1 << 13) / fileStep);
|
||||
readParam(params, 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() )))
|
||||
)
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor))
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
// row buffer, because TIFFWriteScanline modifies the original data!
|
||||
size_t scanlineSize = TIFFScanlineSize(pTiffHandle);
|
||||
AutoBuffer<uchar> _buffer(scanlineSize + 32);
|
||||
uchar* buffer = _buffer;
|
||||
if (!buffer)
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (int y = 0; y < height; ++y)
|
||||
{
|
||||
switch (channels)
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
memcpy(buffer, img.ptr(y), scanlineSize);
|
||||
break;
|
||||
}
|
||||
|
||||
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));
|
||||
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));
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int writeResult = TIFFWriteScanline(pTiffHandle, buffer, y, 0);
|
||||
if (writeResult != 1)
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
int writeResult = TIFFWriteScanline(pTiffHandle, buffer, y, 0);
|
||||
if (writeResult != 1)
|
||||
{
|
||||
TIFFClose(pTiffHandle);
|
||||
return false;
|
||||
}
|
||||
TIFFWriteDirectory(pTiffHandle);
|
||||
|
||||
}
|
||||
|
||||
TIFFClose(pTiffHandle);
|
||||
@ -946,6 +956,11 @@ bool TiffEncoder::write_32FC1(const Mat& _img)
|
||||
return true;
|
||||
}
|
||||
|
||||
bool TiffEncoder::writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params)
|
||||
{
|
||||
return writeLibTiff(img_vec, params);
|
||||
}
|
||||
|
||||
bool TiffEncoder::write( const Mat& img, const std::vector<int>& params)
|
||||
{
|
||||
int depth = img.depth();
|
||||
@ -961,7 +976,9 @@ bool TiffEncoder::write( const Mat& img, const std::vector<int>& params)
|
||||
|
||||
CV_Assert(depth == CV_8U || depth == CV_16U);
|
||||
|
||||
return writeLibTiff(img, params);
|
||||
std::vector<Mat> img_vec;
|
||||
img_vec.push_back(img);
|
||||
return writeLibTiff(img_vec, params);
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
@ -128,6 +128,9 @@ public:
|
||||
bool isFormatSupported( int depth ) const;
|
||||
|
||||
bool write( const Mat& img, const std::vector<int>& params );
|
||||
|
||||
bool writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params);
|
||||
|
||||
ImageEncoder newEncoder() const;
|
||||
|
||||
protected:
|
||||
@ -135,7 +138,7 @@ protected:
|
||||
TiffFieldType fieldType,
|
||||
int count, int value );
|
||||
|
||||
bool writeLibTiff( const Mat& img, 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_32FC1( const Mat& img );
|
||||
|
||||
|
@ -667,33 +667,45 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int flags)
|
||||
return imreadmulti_(filename, flags, mats);
|
||||
}
|
||||
|
||||
static bool imwrite_( const String& filename, const Mat& image,
|
||||
static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
|
||||
const std::vector<int>& params, bool flipv )
|
||||
{
|
||||
Mat temp;
|
||||
const Mat* pimage = ℑ
|
||||
|
||||
CV_Assert( image.channels() == 1 || image.channels() == 3 || image.channels() == 4 );
|
||||
bool isMultiImg = img_vec.size() > 1;
|
||||
std::vector<Mat> write_vec;
|
||||
|
||||
ImageEncoder encoder = findEncoder( filename );
|
||||
if( !encoder )
|
||||
CV_Error( CV_StsError, "could not find a writer for the specified extension" );
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
{
|
||||
CV_Assert( encoder->isFormatSupported(CV_8U) );
|
||||
image.convertTo( temp, CV_8U );
|
||||
pimage = &temp;
|
||||
}
|
||||
|
||||
if( flipv )
|
||||
for (size_t page = 0; page < img_vec.size(); page++)
|
||||
{
|
||||
flip(*pimage, temp, 0);
|
||||
pimage = &temp;
|
||||
Mat image = img_vec[page];
|
||||
CV_Assert( image.channels() == 1 || image.channels() == 3 || image.channels() == 4 );
|
||||
|
||||
Mat temp;
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
{
|
||||
CV_Assert( encoder->isFormatSupported(CV_8U) );
|
||||
image.convertTo( temp, CV_8U );
|
||||
image = temp;
|
||||
}
|
||||
|
||||
if( flipv )
|
||||
{
|
||||
flip(image, temp, 0);
|
||||
image = temp;
|
||||
}
|
||||
|
||||
write_vec.push_back(image);
|
||||
}
|
||||
|
||||
encoder->setDestination( filename );
|
||||
CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2);
|
||||
bool code = encoder->write( *pimage, params );
|
||||
bool code;
|
||||
if (!isMultiImg)
|
||||
code = encoder->write( write_vec[0], params );
|
||||
else
|
||||
code = encoder->writemulti( write_vec, params ); //to be implemented
|
||||
|
||||
// CV_Assert( code );
|
||||
return code;
|
||||
@ -703,9 +715,14 @@ bool imwrite( const String& filename, InputArray _img,
|
||||
const std::vector<int>& params )
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
std::vector<Mat> img_vec;
|
||||
//Did we get a Mat or a vector of Mats?
|
||||
if (_img.isMat())
|
||||
img_vec.push_back(_img.getMat());
|
||||
else if (_img.isMatVector())
|
||||
_img.getMatVector(img_vec);
|
||||
|
||||
Mat img = _img.getMat();
|
||||
return imwrite_(filename, img, params, false);
|
||||
return imwrite_(filename, img_vec, params, false);
|
||||
}
|
||||
|
||||
static void*
|
||||
|
@ -206,6 +206,40 @@ INSTANTIATE_TEST_CASE_P(AllModes, Imgcodecs_Tiff_Modes, testing::ValuesIn(all_mo
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
TEST(Imgcodecs_Tiff_Modes, write_multipage)
|
||||
{
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
const string filename = root + "readwrite/multipage.tif";
|
||||
const string page_files[] = {
|
||||
"readwrite/multipage_p1.tif",
|
||||
"readwrite/multipage_p2.tif",
|
||||
"readwrite/multipage_p3.tif",
|
||||
"readwrite/multipage_p4.tif",
|
||||
"readwrite/multipage_p5.tif",
|
||||
"readwrite/multipage_p6.tif"
|
||||
};
|
||||
const size_t page_count = sizeof(page_files) / sizeof(page_files[0]);
|
||||
vector<Mat> pages;
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
const Mat page = imread(root + page_files[i]);
|
||||
pages.push_back(page);
|
||||
}
|
||||
|
||||
string tmp_filename = cv::tempfile(".tiff");
|
||||
bool res = imwrite(tmp_filename, pages);
|
||||
ASSERT_TRUE(res);
|
||||
|
||||
vector<Mat> read_pages;
|
||||
imreadmulti(tmp_filename, read_pages);
|
||||
for (size_t i = 0; i < page_count; i++)
|
||||
{
|
||||
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), read_pages[i], pages[i]);
|
||||
}
|
||||
}
|
||||
|
||||
//==================================================================================================
|
||||
|
||||
TEST(Imgcodecs_Tiff, imdecode_no_exception_temporary_file_removed)
|
||||
{
|
||||
const string root = cvtest::TS::ptr()->get_data_path();
|
||||
|
Loading…
Reference in New Issue
Block a user