Skip to content

Commit f61076a

Browse files
committed
Merge pull request opencv#10367 from savuor:multiwrite_tiff_renew
2 parents 5f5fcef + 27b1f8f commit f61076a

File tree

6 files changed

+191
-114
lines changed

6 files changed

+191
-114
lines changed

modules/imgcodecs/src/grfmt_base.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -127,6 +127,11 @@ bool BaseImageEncoder::setDestination( std::vector<uchar>& buf )
127127
return true;
128128
}
129129

130+
bool BaseImageEncoder::writemulti(const std::vector<Mat>&, const std::vector<int>& )
131+
{
132+
return false;
133+
}
134+
130135
ImageEncoder BaseImageEncoder::newEncoder() const
131136
{
132137
return ImageEncoder();

modules/imgcodecs/src/grfmt_base.hpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,7 @@ class BaseImageEncoder
101101
virtual bool setDestination( const String& filename );
102102
virtual bool setDestination( std::vector<uchar>& buf );
103103
virtual bool write( const Mat& img, const std::vector<int>& params ) = 0;
104+
virtual bool writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params);
104105

105106
virtual String getDescription() const;
106107
virtual ImageEncoder newEncoder() const;

modules/imgcodecs/src/grfmt_tiff.cpp

Lines changed: 112 additions & 95 deletions
Original file line numberDiff line numberDiff line change
@@ -539,7 +539,6 @@ bool TiffDecoder::readData( Mat& img )
539539

540540
bool TiffDecoder::readData_32FC3(Mat& img)
541541
{
542-
543542
int rows_per_strip = 0, photometric = 0;
544543
if(!m_tif)
545544
{
@@ -724,44 +723,8 @@ static void readParam(const std::vector<int>& params, int key, int& value)
724723
}
725724
}
726725

727-
bool TiffEncoder::writeLibTiff( const Mat& img, const std::vector<int>& params)
726+
bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params)
728727
{
729-
int channels = img.channels();
730-
int width = img.cols, height = img.rows;
731-
int depth = img.depth();
732-
733-
int bitsPerChannel = -1;
734-
switch (depth)
735-
{
736-
case CV_8U:
737-
{
738-
bitsPerChannel = 8;
739-
break;
740-
}
741-
case CV_16U:
742-
{
743-
bitsPerChannel = 16;
744-
break;
745-
}
746-
default:
747-
{
748-
return false;
749-
}
750-
}
751-
752-
const int bitsPerByte = 8;
753-
size_t fileStep = (width * channels * bitsPerChannel) / bitsPerByte;
754-
755-
int rowsPerStrip = (int)((1 << 13)/fileStep);
756-
readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
757-
758-
if( rowsPerStrip < 1 )
759-
rowsPerStrip = 1;
760-
761-
if( rowsPerStrip > height )
762-
rowsPerStrip = height;
763-
764-
765728
// do NOT put "wb" as the mode, because the b means "big endian" mode, not "binary" mode.
766729
// http://www.remotesensing.org/libtiff/man/TIFFOpen.3tiff.html
767730
TIFF* pTiffHandle;
@@ -780,86 +743,133 @@ bool TiffEncoder::writeLibTiff( const Mat& img, const std::vector<int>& params)
780743
return false;
781744
}
782745

746+
//Settings that matter to all images
783747
// defaults for now, maybe base them on params in the future
784-
int compression = COMPRESSION_LZW;
785-
int predictor = PREDICTOR_HORIZONTAL;
748+
int compression = COMPRESSION_LZW;
749+
int predictor = PREDICTOR_HORIZONTAL;
786750

787751
readParam(params, TIFFTAG_COMPRESSION, compression);
788752
readParam(params, TIFFTAG_PREDICTOR, predictor);
789753

790-
int colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK;
791-
792-
if ( !TIFFSetField(pTiffHandle, TIFFTAG_IMAGEWIDTH, width)
793-
|| !TIFFSetField(pTiffHandle, TIFFTAG_IMAGELENGTH, height)
794-
|| !TIFFSetField(pTiffHandle, TIFFTAG_BITSPERSAMPLE, bitsPerChannel)
795-
|| !TIFFSetField(pTiffHandle, TIFFTAG_COMPRESSION, compression)
796-
|| !TIFFSetField(pTiffHandle, TIFFTAG_PHOTOMETRIC, colorspace)
797-
|| !TIFFSetField(pTiffHandle, TIFFTAG_SAMPLESPERPIXEL, channels)
798-
|| !TIFFSetField(pTiffHandle, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)
799-
|| !TIFFSetField(pTiffHandle, TIFFTAG_ROWSPERSTRIP, rowsPerStrip)
800-
)
801-
{
802-
TIFFClose(pTiffHandle);
803-
return false;
804-
}
805-
806-
if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor) )
754+
//Iterate through each image in the vector and write them out as Tiff directories
755+
for (size_t page = 0; page < img_vec.size(); page++)
807756
{
808-
TIFFClose(pTiffHandle);
809-
return false;
810-
}
811-
812-
// row buffer, because TIFFWriteScanline modifies the original data!
813-
size_t scanlineSize = TIFFScanlineSize(pTiffHandle);
814-
AutoBuffer<uchar> _buffer(scanlineSize+32);
815-
uchar* buffer = _buffer;
816-
if (!buffer)
817-
{
818-
TIFFClose(pTiffHandle);
819-
return false;
820-
}
757+
const Mat& img = img_vec[page];
758+
int channels = img.channels();
759+
int width = img.cols, height = img.rows;
760+
int depth = img.depth();
821761

822-
for (int y = 0; y < height; ++y)
823-
{
824-
switch(channels)
762+
int bitsPerChannel = -1;
763+
switch (depth)
825764
{
826-
case 1:
827-
{
828-
memcpy(buffer, img.ptr(y), scanlineSize);
829-
break;
830-
}
831-
832-
case 3:
765+
case CV_8U:
833766
{
834-
if (depth == CV_8U)
835-
icvCvt_BGR2RGB_8u_C3R( img.ptr(y), 0, buffer, 0, cvSize(width,1) );
836-
else
837-
icvCvt_BGR2RGB_16u_C3R( img.ptr<ushort>(y), 0, (ushort*)buffer, 0, cvSize(width,1) );
767+
bitsPerChannel = 8;
838768
break;
839769
}
840-
841-
case 4:
770+
case CV_16U:
842771
{
843-
if (depth == CV_8U)
844-
icvCvt_BGRA2RGBA_8u_C4R( img.ptr(y), 0, buffer, 0, cvSize(width,1) );
845-
else
846-
icvCvt_BGRA2RGBA_16u_C4R( img.ptr<ushort>(y), 0, (ushort*)buffer, 0, cvSize(width,1) );
772+
bitsPerChannel = 16;
847773
break;
848774
}
849-
850775
default:
851776
{
852-
TIFFClose(pTiffHandle);
853777
return false;
854778
}
855779
}
856780

857-
int writeResult = TIFFWriteScanline(pTiffHandle, buffer, y, 0);
858-
if (writeResult != 1)
781+
const int bitsPerByte = 8;
782+
size_t fileStep = (width * channels * bitsPerChannel) / bitsPerByte;
783+
784+
int rowsPerStrip = (int)((1 << 13) / fileStep);
785+
readParam(params, TIFFTAG_ROWSPERSTRIP, rowsPerStrip);
786+
787+
if (rowsPerStrip < 1)
788+
rowsPerStrip = 1;
789+
790+
if (rowsPerStrip > height)
791+
rowsPerStrip = height;
792+
793+
int colorspace = channels > 1 ? PHOTOMETRIC_RGB : PHOTOMETRIC_MINISBLACK;
794+
795+
if (!TIFFSetField(pTiffHandle, TIFFTAG_IMAGEWIDTH, width)
796+
|| !TIFFSetField(pTiffHandle, TIFFTAG_IMAGELENGTH, height)
797+
|| !TIFFSetField(pTiffHandle, TIFFTAG_BITSPERSAMPLE, bitsPerChannel)
798+
|| !TIFFSetField(pTiffHandle, TIFFTAG_COMPRESSION, compression)
799+
|| !TIFFSetField(pTiffHandle, TIFFTAG_PHOTOMETRIC, colorspace)
800+
|| !TIFFSetField(pTiffHandle, TIFFTAG_SAMPLESPERPIXEL, channels)
801+
|| !TIFFSetField(pTiffHandle, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)
802+
|| !TIFFSetField(pTiffHandle, TIFFTAG_ROWSPERSTRIP, rowsPerStrip)
803+
|| (img_vec.size() > 1 && (
804+
!TIFFSetField(pTiffHandle, TIFFTAG_SUBFILETYPE, FILETYPE_PAGE)
805+
|| !TIFFSetField(pTiffHandle, TIFFTAG_PAGENUMBER, page, img_vec.size() )))
806+
)
807+
{
808+
TIFFClose(pTiffHandle);
809+
return false;
810+
}
811+
812+
if (compression != COMPRESSION_NONE && !TIFFSetField(pTiffHandle, TIFFTAG_PREDICTOR, predictor))
813+
{
814+
TIFFClose(pTiffHandle);
815+
return false;
816+
}
817+
818+
// row buffer, because TIFFWriteScanline modifies the original data!
819+
size_t scanlineSize = TIFFScanlineSize(pTiffHandle);
820+
AutoBuffer<uchar> _buffer(scanlineSize + 32);
821+
uchar* buffer = _buffer;
822+
if (!buffer)
859823
{
860824
TIFFClose(pTiffHandle);
861825
return false;
862826
}
827+
828+
for (int y = 0; y < height; ++y)
829+
{
830+
switch (channels)
831+
{
832+
case 1:
833+
{
834+
memcpy(buffer, img.ptr(y), scanlineSize);
835+
break;
836+
}
837+
838+
case 3:
839+
{
840+
if (depth == CV_8U)
841+
icvCvt_BGR2RGB_8u_C3R( img.ptr(y), 0, buffer, 0, cvSize(width, 1));
842+
else
843+
icvCvt_BGR2RGB_16u_C3R( img.ptr<ushort>(y), 0, (ushort*)buffer, 0, cvSize(width, 1));
844+
break;
845+
}
846+
847+
case 4:
848+
{
849+
if (depth == CV_8U)
850+
icvCvt_BGRA2RGBA_8u_C4R( img.ptr(y), 0, buffer, 0, cvSize(width, 1));
851+
else
852+
icvCvt_BGRA2RGBA_16u_C4R( img.ptr<ushort>(y), 0, (ushort*)buffer, 0, cvSize(width, 1));
853+
break;
854+
}
855+
856+
default:
857+
{
858+
TIFFClose(pTiffHandle);
859+
return false;
860+
}
861+
}
862+
863+
int writeResult = TIFFWriteScanline(pTiffHandle, buffer, y, 0);
864+
if (writeResult != 1)
865+
{
866+
TIFFClose(pTiffHandle);
867+
return false;
868+
}
869+
}
870+
871+
TIFFWriteDirectory(pTiffHandle);
872+
863873
}
864874

865875
TIFFClose(pTiffHandle);
@@ -946,6 +956,11 @@ bool TiffEncoder::write_32FC1(const Mat& _img)
946956
return true;
947957
}
948958

959+
bool TiffEncoder::writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params)
960+
{
961+
return writeLibTiff(img_vec, params);
962+
}
963+
949964
bool TiffEncoder::write( const Mat& img, const std::vector<int>& params)
950965
{
951966
int depth = img.depth();
@@ -961,7 +976,9 @@ bool TiffEncoder::write( const Mat& img, const std::vector<int>& params)
961976

962977
CV_Assert(depth == CV_8U || depth == CV_16U);
963978

964-
return writeLibTiff(img, params);
979+
std::vector<Mat> img_vec;
980+
img_vec.push_back(img);
981+
return writeLibTiff(img_vec, params);
965982
}
966983

967984
} // namespace

modules/imgcodecs/src/grfmt_tiff.hpp

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -128,14 +128,17 @@ class TiffEncoder : public BaseImageEncoder
128128
bool isFormatSupported( int depth ) const;
129129

130130
bool write( const Mat& img, const std::vector<int>& params );
131+
132+
bool writemulti(const std::vector<Mat>& img_vec, const std::vector<int>& params);
133+
131134
ImageEncoder newEncoder() const;
132135

133136
protected:
134137
void writeTag( WLByteStream& strm, TiffTag tag,
135138
TiffFieldType fieldType,
136139
int count, int value );
137140

138-
bool writeLibTiff( const Mat& img, const std::vector<int>& params );
141+
bool writeLibTiff( const std::vector<Mat>& img_vec, const std::vector<int>& params );
139142
bool write_32FC3( const Mat& img );
140143
bool write_32FC1( const Mat& img );
141144

modules/imgcodecs/src/loadsave.cpp

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -667,33 +667,45 @@ bool imreadmulti(const String& filename, std::vector<Mat>& mats, int flags)
667667
return imreadmulti_(filename, flags, mats);
668668
}
669669

670-
static bool imwrite_( const String& filename, const Mat& image,
670+
static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
671671
const std::vector<int>& params, bool flipv )
672672
{
673-
Mat temp;
674-
const Mat* pimage = &image;
675-
676-
CV_Assert( image.channels() == 1 || image.channels() == 3 || image.channels() == 4 );
673+
bool isMultiImg = img_vec.size() > 1;
674+
std::vector<Mat> write_vec;
677675

678676
ImageEncoder encoder = findEncoder( filename );
679677
if( !encoder )
680678
CV_Error( CV_StsError, "could not find a writer for the specified extension" );
681-
if( !encoder->isFormatSupported(image.depth()) )
682-
{
683-
CV_Assert( encoder->isFormatSupported(CV_8U) );
684-
image.convertTo( temp, CV_8U );
685-
pimage = &temp;
686-
}
687679

688-
if( flipv )
680+
for (size_t page = 0; page < img_vec.size(); page++)
689681
{
690-
flip(*pimage, temp, 0);
691-
pimage = &temp;
682+
Mat image = img_vec[page];
683+
CV_Assert( image.channels() == 1 || image.channels() == 3 || image.channels() == 4 );
684+
685+
Mat temp;
686+
if( !encoder->isFormatSupported(image.depth()) )
687+
{
688+
CV_Assert( encoder->isFormatSupported(CV_8U) );
689+
image.convertTo( temp, CV_8U );
690+
image = temp;
691+
}
692+
693+
if( flipv )
694+
{
695+
flip(image, temp, 0);
696+
image = temp;
697+
}
698+
699+
write_vec.push_back(image);
692700
}
693701

694702
encoder->setDestination( filename );
695703
CV_Assert(params.size() <= CV_IO_MAX_IMAGE_PARAMS*2);
696-
bool code = encoder->write( *pimage, params );
704+
bool code;
705+
if (!isMultiImg)
706+
code = encoder->write( write_vec[0], params );
707+
else
708+
code = encoder->writemulti( write_vec, params ); //to be implemented
697709

698710
// CV_Assert( code );
699711
return code;
@@ -703,9 +715,14 @@ bool imwrite( const String& filename, InputArray _img,
703715
const std::vector<int>& params )
704716
{
705717
CV_TRACE_FUNCTION();
706-
707-
Mat img = _img.getMat();
708-
return imwrite_(filename, img, params, false);
718+
std::vector<Mat> img_vec;
719+
//Did we get a Mat or a vector of Mats?
720+
if (_img.isMat())
721+
img_vec.push_back(_img.getMat());
722+
else if (_img.isMatVector())
723+
_img.getMatVector(img_vec);
724+
725+
return imwrite_(filename, img_vec, params, false);
709726
}
710727

711728
static void*

0 commit comments

Comments
 (0)