Skip to content

Commit 35dbf32

Browse files
authored
Merge pull request opencv#26211 from Kumataro:fix26207
imgcodecs: implement imencodemulti() opencv#26211 Close opencv#26207 ### Pull Request Readiness Checklist See details at https://github.com/opencv/opencv/wiki/How_to_contribute#making-a-good-pull-request - [x] I agree to contribute to the project under Apache 2 License. - [x] To the best of my knowledge, the proposed patch is not based on a code under GPL or another license that is incompatible with OpenCV - [x] The PR is proposed to the proper branch - [x] There is a reference to the original bug report and related work - [x] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [x] The feature is well documented and sample code can be built with the project CMake
1 parent 3919f33 commit 35dbf32

File tree

6 files changed

+198
-45
lines changed

6 files changed

+198
-45
lines changed

modules/imgcodecs/include/opencv2/imgcodecs.hpp

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -405,14 +405,28 @@ The function imencode compresses the image and stores it in the memory buffer th
405405
result. See cv::imwrite for the list of supported formats and flags description.
406406
407407
@param ext File extension that defines the output format. Must include a leading period.
408-
@param img Image to be written.
408+
@param img Image to be compressed.
409409
@param buf Output buffer resized to fit the compressed image.
410410
@param params Format-specific parameters. See cv::imwrite and cv::ImwriteFlags.
411411
*/
412412
CV_EXPORTS_W bool imencode( const String& ext, InputArray img,
413413
CV_OUT std::vector<uchar>& buf,
414414
const std::vector<int>& params = std::vector<int>());
415415

416+
/** @brief Encodes array of images into a memory buffer.
417+
418+
The function is analog to cv::imencode for in-memory multi-page image compression.
419+
See cv::imwrite for the list of supported formats and flags description.
420+
421+
@param ext File extension that defines the output format. Must include a leading period.
422+
@param imgs Vector of images to be written.
423+
@param buf Output buffer resized to fit the compressed data.
424+
@param params Format-specific parameters. See cv::imwrite and cv::ImwriteFlags.
425+
*/
426+
CV_EXPORTS_W bool imencodemulti( const String& ext, InputArrayOfArrays imgs,
427+
CV_OUT std::vector<uchar>& buf,
428+
const std::vector<int>& params = std::vector<int>());
429+
416430
/** @brief Checks if the specified image file can be decoded by OpenCV.
417431
418432
The function haveImageReader checks if OpenCV is capable of reading the specified file.

modules/imgcodecs/src/grfmt_tiff.cpp

Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -171,7 +171,7 @@ class TiffDecoderBufHelper
171171
{
172172
n = size - pos;
173173
}
174-
memcpy(buffer, buf.ptr() + pos, n);
174+
std::memcpy(buffer, buf.ptr() + pos, n);
175175
helper->m_buf_pos += n;
176176
return n;
177177
}
@@ -848,9 +848,9 @@ bool TiffDecoder::readData( Mat& img )
848848
switch ( convert_flag )
849849
{
850850
case MAKE_FLAG( 1, 1 ): // GRAY to GRAY
851-
memcpy( (void*) img_line_buffer,
852-
(void*) bstart,
853-
tile_width * sizeof(uchar) );
851+
std::memcpy( (void*) img_line_buffer,
852+
(void*) bstart,
853+
tile_width * sizeof(uchar) );
854854
break;
855855

856856
case MAKE_FLAG( 1, 3 ): // GRAY to BGR
@@ -867,9 +867,9 @@ bool TiffDecoder::readData( Mat& img )
867867

868868
case MAKE_FLAG( 3, 3 ): // RGB to BGR
869869
if (m_use_rgb)
870-
memcpy( (void*) img_line_buffer,
871-
(void*) bstart,
872-
tile_width * sizeof(uchar) );
870+
std::memcpy( (void*) img_line_buffer,
871+
(void*) bstart,
872+
tile_width * sizeof(uchar) );
873873
else
874874
icvCvt_BGR2RGB_8u_C3R( bstart, 0,
875875
img_line_buffer, 0,
@@ -979,7 +979,7 @@ bool TiffDecoder::readData( Mat& img )
979979
{
980980
CV_CheckEQ(wanted_channels, 3, "");
981981
if (m_use_rgb)
982-
memcpy(buffer16, img.ptr<ushort>(img_y + i, x), tile_width * sizeof(ushort));
982+
std::memcpy(buffer16, img.ptr<ushort>(img_y + i, x), tile_width * sizeof(ushort));
983983
else
984984
icvCvt_RGB2BGR_16u_C3R(buffer16, 0,
985985
img.ptr<ushort>(img_y + i, x), 0,
@@ -1011,9 +1011,9 @@ bool TiffDecoder::readData( Mat& img )
10111011
CV_CheckEQ(wanted_channels, 1, "");
10121012
if( ncn == 1 )
10131013
{
1014-
memcpy(img.ptr<ushort>(img_y + i, x),
1015-
buffer16,
1016-
tile_width*sizeof(ushort));
1014+
std::memcpy(img.ptr<ushort>(img_y + i, x),
1015+
buffer16,
1016+
tile_width*sizeof(ushort));
10171017
}
10181018
else
10191019
{
@@ -1118,10 +1118,16 @@ class TiffEncoderBufHelper
11181118
/*map=*/0, /*unmap=*/0 );
11191119
}
11201120

1121-
static tmsize_t read( thandle_t /*handle*/, void* /*buffer*/, tmsize_t /*n*/ )
1121+
static tmsize_t read( thandle_t handle, void* buffer, tmsize_t n )
11221122
{
1123-
// Not used for encoding.
1124-
return 0;
1123+
// Used for imencodemulti() to stores multi-images.
1124+
TiffEncoderBufHelper *helper = reinterpret_cast<TiffEncoderBufHelper*>(handle);
1125+
size_t begin = (size_t)helper->m_buf_pos;
1126+
size_t end = begin + n;
1127+
CV_CheckGT( helper->m_buf->size(), end , "do not be over-run buffer");
1128+
std::memcpy(buffer, &(*helper->m_buf)[begin], n);
1129+
helper->m_buf_pos = end;
1130+
return n;
11251131
}
11261132

11271133
static tmsize_t write( thandle_t handle, void* buffer, tmsize_t n )
@@ -1133,7 +1139,7 @@ class TiffEncoderBufHelper
11331139
{
11341140
helper->m_buf->resize(end);
11351141
}
1136-
memcpy(&(*helper->m_buf)[begin], buffer, n);
1142+
std::memcpy(&(*helper->m_buf)[begin], buffer, n);
11371143
helper->m_buf_pos = end;
11381144
return n;
11391145
}
@@ -1350,7 +1356,7 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
13501356
{
13511357
case 1:
13521358
{
1353-
memcpy(buffer, img.ptr(y), scanlineSize);
1359+
std::memcpy(buffer, img.ptr(y), scanlineSize);
13541360
break;
13551361
}
13561362

modules/imgcodecs/src/loadsave.cpp

Lines changed: 63 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -723,6 +723,7 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
723723
Mat temp;
724724
if( !encoder->isFormatSupported(image.depth()) )
725725
{
726+
CV_LOG_ONCE_WARNING(NULL, "Unsupported depth image for selected encoder is fallbacked to CV_8U.");
726727
CV_Assert( encoder->isFormatSupported(CV_8U) );
727728
image.convertTo( temp, CV_8U );
728729
image = temp;
@@ -787,10 +788,12 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
787788
catch (const cv::Exception& e)
788789
{
789790
CV_LOG_ERROR(NULL, "imwrite_('" << filename << "'): can't write data: " << e.what());
791+
code = false;
790792
}
791793
catch (...)
792794
{
793795
CV_LOG_ERROR(NULL, "imwrite_('" << filename << "'): can't write data: unknown exception");
796+
code = false;
794797
}
795798

796799
return code;
@@ -978,7 +981,7 @@ imdecodemulti_(const Mat& buf, int flags, std::vector<Mat>& mats, int start, int
978981

979982
ImageDecoder decoder = findDecoder(buf_row);
980983
if (!decoder)
981-
return 0;
984+
return false;
982985

983986
// Try to decode image by RGB instead of BGR.
984987
if (flags & IMREAD_COLOR_RGB && flags != IMREAD_UNCHANGED)
@@ -995,7 +998,7 @@ imdecodemulti_(const Mat& buf, int flags, std::vector<Mat>& mats, int start, int
995998
filename = tempfile();
996999
FILE* f = fopen(filename.c_str(), "wb");
9971000
if (!f)
998-
return 0;
1001+
return false;
9991002
size_t bufSize = buf_row.total() * buf.elemSize();
10001003
if (fwrite(buf_row.ptr(), 1, bufSize, f) != bufSize)
10011004
{
@@ -1121,27 +1124,44 @@ bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector<Mat>& mats, co
11211124
}
11221125
}
11231126

1124-
bool imencode( const String& ext, InputArray _image,
1127+
bool imencode( const String& ext, InputArray _img,
11251128
std::vector<uchar>& buf, const std::vector<int>& params_ )
11261129
{
11271130
CV_TRACE_FUNCTION();
11281131

1129-
Mat image = _image.getMat();
1130-
CV_Assert(!image.empty());
1131-
1132-
int channels = image.channels();
1133-
CV_Assert( channels == 1 || channels == 3 || channels == 4 );
1134-
11351132
ImageEncoder encoder = findEncoder( ext );
11361133
if( !encoder )
11371134
CV_Error( Error::StsError, "could not find encoder for the specified extension" );
11381135

1139-
if( !encoder->isFormatSupported(image.depth()) )
1136+
std::vector<Mat> img_vec;
1137+
CV_Assert(!_img.empty());
1138+
if (_img.isMatVector() || _img.isUMatVector())
1139+
_img.getMatVector(img_vec);
1140+
else
1141+
img_vec.push_back(_img.getMat());
1142+
1143+
CV_Assert(!img_vec.empty());
1144+
const bool isMultiImg = img_vec.size() > 1;
1145+
1146+
std::vector<Mat> write_vec;
1147+
for (size_t page = 0; page < img_vec.size(); page++)
11401148
{
1141-
CV_Assert( encoder->isFormatSupported(CV_8U) );
1149+
Mat image = img_vec[page];
1150+
CV_Assert(!image.empty());
1151+
1152+
const int channels = image.channels();
1153+
CV_Assert( channels == 1 || channels == 3 || channels == 4 );
1154+
11421155
Mat temp;
1143-
image.convertTo(temp, CV_8U);
1144-
image = temp;
1156+
if( !encoder->isFormatSupported(image.depth()) )
1157+
{
1158+
CV_LOG_ONCE_WARNING(NULL, "Unsupported depth image for selected encoder is fallbacked to CV_8U.");
1159+
CV_Assert( encoder->isFormatSupported(CV_8U) );
1160+
image.convertTo( temp, CV_8U );
1161+
image = temp;
1162+
}
1163+
1164+
write_vec.push_back(image);
11451165
}
11461166

11471167
#if CV_VERSION_MAJOR < 5 && defined(HAVE_IMGCODEC_HDR)
@@ -1166,23 +1186,37 @@ bool imencode( const String& ext, InputArray _image,
11661186
CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs");
11671187
CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), "");
11681188

1169-
bool code;
1170-
if( encoder->setDestination(buf) )
1171-
{
1172-
code = encoder->write(image, params);
1173-
encoder->throwOnEror();
1174-
CV_Assert( code );
1175-
}
1176-
else
1189+
bool code = false;
1190+
String filename;
1191+
if( !encoder->setDestination(buf) )
11771192
{
1178-
String filename = tempfile();
1193+
filename = tempfile();
11791194
code = encoder->setDestination(filename);
11801195
CV_Assert( code );
1196+
}
1197+
1198+
try {
1199+
if (!isMultiImg)
1200+
code = encoder->write(write_vec[0], params);
1201+
else
1202+
code = encoder->writemulti(write_vec, params);
11811203

1182-
code = encoder->write(image, params);
11831204
encoder->throwOnEror();
11841205
CV_Assert( code );
1206+
}
1207+
catch (const cv::Exception& e)
1208+
{
1209+
CV_LOG_ERROR(NULL, "imencode(): can't encode data: " << e.what());
1210+
code = false;
1211+
}
1212+
catch (...)
1213+
{
1214+
CV_LOG_ERROR(NULL, "imencode(): can't encode data: unknown exception");
1215+
code = false;
1216+
}
11851217

1218+
if( !filename.empty() && code )
1219+
{
11861220
FILE* f = fopen( filename.c_str(), "rb" );
11871221
CV_Assert(f != 0);
11881222
fseek( f, 0, SEEK_END );
@@ -1196,6 +1230,12 @@ bool imencode( const String& ext, InputArray _image,
11961230
return code;
11971231
}
11981232

1233+
bool imencodemulti( const String& ext, InputArrayOfArrays imgs,
1234+
std::vector<uchar>& buf, const std::vector<int>& params)
1235+
{
1236+
return imencode(ext, imgs, buf, params);
1237+
}
1238+
11991239
bool haveImageReader( const String& filename )
12001240
{
12011241
ImageDecoder decoder = cv::findDecoder(filename);

modules/imgcodecs/test/test_avif.cpp

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -161,14 +161,14 @@ TEST_P(Imgcodecs_Avif_Image_EncodeDecodeSuite, imencode_imdecode) {
161161

162162
// Encode.
163163
std::vector<unsigned char> buf;
164-
if (!IsBitDepthValid()) {
165-
EXPECT_THROW(cv::imencode(".avif", img_original, buf, encoding_params_),
166-
cv::Exception);
167-
return;
168-
}
169164
bool result = true;
170165
EXPECT_NO_THROW(
171166
result = cv::imencode(".avif", img_original, buf, encoding_params_););
167+
168+
if (!IsBitDepthValid()) {
169+
EXPECT_FALSE(result);
170+
return;
171+
}
172172
EXPECT_TRUE(result);
173173

174174
// Read back.

modules/imgcodecs/test/test_exr.impl.hpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -314,4 +314,27 @@ TEST(Imgcodecs_EXR, read_RGBA_unchanged)
314314
EXPECT_EQ(0, remove(filenameOutput.c_str()));
315315
}
316316

317+
// See https://github.com/opencv/opencv/pull/26211
318+
// ( related with https://github.com/opencv/opencv/issues/26207 )
319+
TEST(Imgcodecs_EXR, imencode_regression_26207_extra)
320+
{
321+
// CV_8U is not supported depth for EXR Encoder.
322+
const cv::Mat src(100, 100, CV_8UC1, cv::Scalar::all(0));
323+
std::vector<uchar> buf;
324+
bool ret = false;
325+
EXPECT_ANY_THROW(ret = imencode(".exr", src, buf));
326+
EXPECT_FALSE(ret);
327+
}
328+
TEST(Imgcodecs_EXR, imwrite_regression_26207_extra)
329+
{
330+
// CV_8U is not supported depth for EXR Encoder.
331+
const cv::Mat src(100, 100, CV_8UC1, cv::Scalar::all(0));
332+
const string filename = cv::tempfile(".exr");
333+
bool ret = false;
334+
EXPECT_ANY_THROW(ret = imwrite(filename, src));
335+
EXPECT_FALSE(ret);
336+
remove(filename.c_str());
337+
}
338+
339+
317340
}} // namespace

0 commit comments

Comments
 (0)