mirror of
https://github.com/opencv/opencv.git
synced 2024-11-27 12:40:05 +08:00
Merge pull request #26211 from Kumataro:fix26207
imgcodecs: implement imencodemulti() #26211 Close #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
This commit is contained in:
parent
3919f33e21
commit
35dbf32227
@ -405,7 +405,7 @@ The function imencode compresses the image and stores it in the memory buffer th
|
||||
result. See cv::imwrite for the list of supported formats and flags description.
|
||||
|
||||
@param ext File extension that defines the output format. Must include a leading period.
|
||||
@param img Image to be written.
|
||||
@param img Image to be compressed.
|
||||
@param buf Output buffer resized to fit the compressed image.
|
||||
@param params Format-specific parameters. See cv::imwrite and cv::ImwriteFlags.
|
||||
*/
|
||||
@ -413,6 +413,20 @@ CV_EXPORTS_W bool imencode( const String& ext, InputArray img,
|
||||
CV_OUT std::vector<uchar>& buf,
|
||||
const std::vector<int>& params = std::vector<int>());
|
||||
|
||||
/** @brief Encodes array of images into a memory buffer.
|
||||
|
||||
The function is analog to cv::imencode for in-memory multi-page image compression.
|
||||
See cv::imwrite for the list of supported formats and flags description.
|
||||
|
||||
@param ext File extension that defines the output format. Must include a leading period.
|
||||
@param imgs Vector of images to be written.
|
||||
@param buf Output buffer resized to fit the compressed data.
|
||||
@param params Format-specific parameters. See cv::imwrite and cv::ImwriteFlags.
|
||||
*/
|
||||
CV_EXPORTS_W bool imencodemulti( const String& ext, InputArrayOfArrays imgs,
|
||||
CV_OUT std::vector<uchar>& buf,
|
||||
const std::vector<int>& params = std::vector<int>());
|
||||
|
||||
/** @brief Checks if the specified image file can be decoded by OpenCV.
|
||||
|
||||
The function haveImageReader checks if OpenCV is capable of reading the specified file.
|
||||
|
@ -171,7 +171,7 @@ public:
|
||||
{
|
||||
n = size - pos;
|
||||
}
|
||||
memcpy(buffer, buf.ptr() + pos, n);
|
||||
std::memcpy(buffer, buf.ptr() + pos, n);
|
||||
helper->m_buf_pos += n;
|
||||
return n;
|
||||
}
|
||||
@ -848,9 +848,9 @@ bool TiffDecoder::readData( Mat& img )
|
||||
switch ( convert_flag )
|
||||
{
|
||||
case MAKE_FLAG( 1, 1 ): // GRAY to GRAY
|
||||
memcpy( (void*) img_line_buffer,
|
||||
(void*) bstart,
|
||||
tile_width * sizeof(uchar) );
|
||||
std::memcpy( (void*) img_line_buffer,
|
||||
(void*) bstart,
|
||||
tile_width * sizeof(uchar) );
|
||||
break;
|
||||
|
||||
case MAKE_FLAG( 1, 3 ): // GRAY to BGR
|
||||
@ -867,9 +867,9 @@ bool TiffDecoder::readData( Mat& img )
|
||||
|
||||
case MAKE_FLAG( 3, 3 ): // RGB to BGR
|
||||
if (m_use_rgb)
|
||||
memcpy( (void*) img_line_buffer,
|
||||
(void*) bstart,
|
||||
tile_width * sizeof(uchar) );
|
||||
std::memcpy( (void*) img_line_buffer,
|
||||
(void*) bstart,
|
||||
tile_width * sizeof(uchar) );
|
||||
else
|
||||
icvCvt_BGR2RGB_8u_C3R( bstart, 0,
|
||||
img_line_buffer, 0,
|
||||
@ -979,7 +979,7 @@ bool TiffDecoder::readData( Mat& img )
|
||||
{
|
||||
CV_CheckEQ(wanted_channels, 3, "");
|
||||
if (m_use_rgb)
|
||||
memcpy(buffer16, img.ptr<ushort>(img_y + i, x), tile_width * sizeof(ushort));
|
||||
std::memcpy(buffer16, img.ptr<ushort>(img_y + i, x), tile_width * sizeof(ushort));
|
||||
else
|
||||
icvCvt_RGB2BGR_16u_C3R(buffer16, 0,
|
||||
img.ptr<ushort>(img_y + i, x), 0,
|
||||
@ -1011,9 +1011,9 @@ bool TiffDecoder::readData( Mat& img )
|
||||
CV_CheckEQ(wanted_channels, 1, "");
|
||||
if( ncn == 1 )
|
||||
{
|
||||
memcpy(img.ptr<ushort>(img_y + i, x),
|
||||
buffer16,
|
||||
tile_width*sizeof(ushort));
|
||||
std::memcpy(img.ptr<ushort>(img_y + i, x),
|
||||
buffer16,
|
||||
tile_width*sizeof(ushort));
|
||||
}
|
||||
else
|
||||
{
|
||||
@ -1118,10 +1118,16 @@ public:
|
||||
/*map=*/0, /*unmap=*/0 );
|
||||
}
|
||||
|
||||
static tmsize_t read( thandle_t /*handle*/, void* /*buffer*/, tmsize_t /*n*/ )
|
||||
static tmsize_t read( thandle_t handle, void* buffer, tmsize_t n )
|
||||
{
|
||||
// Not used for encoding.
|
||||
return 0;
|
||||
// Used for imencodemulti() to stores multi-images.
|
||||
TiffEncoderBufHelper *helper = reinterpret_cast<TiffEncoderBufHelper*>(handle);
|
||||
size_t begin = (size_t)helper->m_buf_pos;
|
||||
size_t end = begin + n;
|
||||
CV_CheckGT( helper->m_buf->size(), end , "do not be over-run buffer");
|
||||
std::memcpy(buffer, &(*helper->m_buf)[begin], n);
|
||||
helper->m_buf_pos = end;
|
||||
return n;
|
||||
}
|
||||
|
||||
static tmsize_t write( thandle_t handle, void* buffer, tmsize_t n )
|
||||
@ -1133,7 +1139,7 @@ public:
|
||||
{
|
||||
helper->m_buf->resize(end);
|
||||
}
|
||||
memcpy(&(*helper->m_buf)[begin], buffer, n);
|
||||
std::memcpy(&(*helper->m_buf)[begin], buffer, n);
|
||||
helper->m_buf_pos = end;
|
||||
return n;
|
||||
}
|
||||
@ -1350,7 +1356,7 @@ bool TiffEncoder::writeLibTiff( const std::vector<Mat>& img_vec, const std::vect
|
||||
{
|
||||
case 1:
|
||||
{
|
||||
memcpy(buffer, img.ptr(y), scanlineSize);
|
||||
std::memcpy(buffer, img.ptr(y), scanlineSize);
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -723,6 +723,7 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
|
||||
Mat temp;
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
{
|
||||
CV_LOG_ONCE_WARNING(NULL, "Unsupported depth image for selected encoder is fallbacked to CV_8U.");
|
||||
CV_Assert( encoder->isFormatSupported(CV_8U) );
|
||||
image.convertTo( temp, CV_8U );
|
||||
image = temp;
|
||||
@ -787,10 +788,12 @@ static bool imwrite_( const String& filename, const std::vector<Mat>& img_vec,
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "imwrite_('" << filename << "'): can't write data: " << e.what());
|
||||
code = false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "imwrite_('" << filename << "'): can't write data: unknown exception");
|
||||
code = false;
|
||||
}
|
||||
|
||||
return code;
|
||||
@ -978,7 +981,7 @@ imdecodemulti_(const Mat& buf, int flags, std::vector<Mat>& mats, int start, int
|
||||
|
||||
ImageDecoder decoder = findDecoder(buf_row);
|
||||
if (!decoder)
|
||||
return 0;
|
||||
return false;
|
||||
|
||||
// Try to decode image by RGB instead of BGR.
|
||||
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
|
||||
filename = tempfile();
|
||||
FILE* f = fopen(filename.c_str(), "wb");
|
||||
if (!f)
|
||||
return 0;
|
||||
return false;
|
||||
size_t bufSize = buf_row.total() * buf.elemSize();
|
||||
if (fwrite(buf_row.ptr(), 1, bufSize, f) != bufSize)
|
||||
{
|
||||
@ -1121,27 +1124,44 @@ bool imdecodemulti(InputArray _buf, int flags, CV_OUT std::vector<Mat>& mats, co
|
||||
}
|
||||
}
|
||||
|
||||
bool imencode( const String& ext, InputArray _image,
|
||||
bool imencode( const String& ext, InputArray _img,
|
||||
std::vector<uchar>& buf, const std::vector<int>& params_ )
|
||||
{
|
||||
CV_TRACE_FUNCTION();
|
||||
|
||||
Mat image = _image.getMat();
|
||||
CV_Assert(!image.empty());
|
||||
|
||||
int channels = image.channels();
|
||||
CV_Assert( channels == 1 || channels == 3 || channels == 4 );
|
||||
|
||||
ImageEncoder encoder = findEncoder( ext );
|
||||
if( !encoder )
|
||||
CV_Error( Error::StsError, "could not find encoder for the specified extension" );
|
||||
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
std::vector<Mat> img_vec;
|
||||
CV_Assert(!_img.empty());
|
||||
if (_img.isMatVector() || _img.isUMatVector())
|
||||
_img.getMatVector(img_vec);
|
||||
else
|
||||
img_vec.push_back(_img.getMat());
|
||||
|
||||
CV_Assert(!img_vec.empty());
|
||||
const bool isMultiImg = img_vec.size() > 1;
|
||||
|
||||
std::vector<Mat> write_vec;
|
||||
for (size_t page = 0; page < img_vec.size(); page++)
|
||||
{
|
||||
CV_Assert( encoder->isFormatSupported(CV_8U) );
|
||||
Mat image = img_vec[page];
|
||||
CV_Assert(!image.empty());
|
||||
|
||||
const int channels = image.channels();
|
||||
CV_Assert( channels == 1 || channels == 3 || channels == 4 );
|
||||
|
||||
Mat temp;
|
||||
image.convertTo(temp, CV_8U);
|
||||
image = temp;
|
||||
if( !encoder->isFormatSupported(image.depth()) )
|
||||
{
|
||||
CV_LOG_ONCE_WARNING(NULL, "Unsupported depth image for selected encoder is fallbacked to CV_8U.");
|
||||
CV_Assert( encoder->isFormatSupported(CV_8U) );
|
||||
image.convertTo( temp, CV_8U );
|
||||
image = temp;
|
||||
}
|
||||
|
||||
write_vec.push_back(image);
|
||||
}
|
||||
|
||||
#if CV_VERSION_MAJOR < 5 && defined(HAVE_IMGCODEC_HDR)
|
||||
@ -1166,23 +1186,37 @@ bool imencode( const String& ext, InputArray _image,
|
||||
CV_Check(params.size(), (params.size() & 1) == 0, "Encoding 'params' must be key-value pairs");
|
||||
CV_CheckLE(params.size(), (size_t)(CV_IO_MAX_IMAGE_PARAMS*2), "");
|
||||
|
||||
bool code;
|
||||
if( encoder->setDestination(buf) )
|
||||
bool code = false;
|
||||
String filename;
|
||||
if( !encoder->setDestination(buf) )
|
||||
{
|
||||
code = encoder->write(image, params);
|
||||
filename = tempfile();
|
||||
code = encoder->setDestination(filename);
|
||||
CV_Assert( code );
|
||||
}
|
||||
|
||||
try {
|
||||
if (!isMultiImg)
|
||||
code = encoder->write(write_vec[0], params);
|
||||
else
|
||||
code = encoder->writemulti(write_vec, params);
|
||||
|
||||
encoder->throwOnEror();
|
||||
CV_Assert( code );
|
||||
}
|
||||
else
|
||||
catch (const cv::Exception& e)
|
||||
{
|
||||
String filename = tempfile();
|
||||
code = encoder->setDestination(filename);
|
||||
CV_Assert( code );
|
||||
|
||||
code = encoder->write(image, params);
|
||||
encoder->throwOnEror();
|
||||
CV_Assert( code );
|
||||
CV_LOG_ERROR(NULL, "imencode(): can't encode data: " << e.what());
|
||||
code = false;
|
||||
}
|
||||
catch (...)
|
||||
{
|
||||
CV_LOG_ERROR(NULL, "imencode(): can't encode data: unknown exception");
|
||||
code = false;
|
||||
}
|
||||
|
||||
if( !filename.empty() && code )
|
||||
{
|
||||
FILE* f = fopen( filename.c_str(), "rb" );
|
||||
CV_Assert(f != 0);
|
||||
fseek( f, 0, SEEK_END );
|
||||
@ -1196,6 +1230,12 @@ bool imencode( const String& ext, InputArray _image,
|
||||
return code;
|
||||
}
|
||||
|
||||
bool imencodemulti( const String& ext, InputArrayOfArrays imgs,
|
||||
std::vector<uchar>& buf, const std::vector<int>& params)
|
||||
{
|
||||
return imencode(ext, imgs, buf, params);
|
||||
}
|
||||
|
||||
bool haveImageReader( const String& filename )
|
||||
{
|
||||
ImageDecoder decoder = cv::findDecoder(filename);
|
||||
|
@ -161,14 +161,14 @@ TEST_P(Imgcodecs_Avif_Image_EncodeDecodeSuite, imencode_imdecode) {
|
||||
|
||||
// Encode.
|
||||
std::vector<unsigned char> buf;
|
||||
if (!IsBitDepthValid()) {
|
||||
EXPECT_THROW(cv::imencode(".avif", img_original, buf, encoding_params_),
|
||||
cv::Exception);
|
||||
return;
|
||||
}
|
||||
bool result = true;
|
||||
EXPECT_NO_THROW(
|
||||
result = cv::imencode(".avif", img_original, buf, encoding_params_););
|
||||
|
||||
if (!IsBitDepthValid()) {
|
||||
EXPECT_FALSE(result);
|
||||
return;
|
||||
}
|
||||
EXPECT_TRUE(result);
|
||||
|
||||
// Read back.
|
||||
|
@ -314,4 +314,27 @@ TEST(Imgcodecs_EXR, read_RGBA_unchanged)
|
||||
EXPECT_EQ(0, remove(filenameOutput.c_str()));
|
||||
}
|
||||
|
||||
// See https://github.com/opencv/opencv/pull/26211
|
||||
// ( related with https://github.com/opencv/opencv/issues/26207 )
|
||||
TEST(Imgcodecs_EXR, imencode_regression_26207_extra)
|
||||
{
|
||||
// CV_8U is not supported depth for EXR Encoder.
|
||||
const cv::Mat src(100, 100, CV_8UC1, cv::Scalar::all(0));
|
||||
std::vector<uchar> buf;
|
||||
bool ret = false;
|
||||
EXPECT_ANY_THROW(ret = imencode(".exr", src, buf));
|
||||
EXPECT_FALSE(ret);
|
||||
}
|
||||
TEST(Imgcodecs_EXR, imwrite_regression_26207_extra)
|
||||
{
|
||||
// CV_8U is not supported depth for EXR Encoder.
|
||||
const cv::Mat src(100, 100, CV_8UC1, cv::Scalar::all(0));
|
||||
const string filename = cv::tempfile(".exr");
|
||||
bool ret = false;
|
||||
EXPECT_ANY_THROW(ret = imwrite(filename, src));
|
||||
EXPECT_FALSE(ret);
|
||||
remove(filename.c_str());
|
||||
}
|
||||
|
||||
|
||||
}} // namespace
|
||||
|
@ -520,8 +520,78 @@ TEST(ImgCodecs, multipage_collection_two_iterator_operatorpp)
|
||||
EXPECT_TRUE(cv::norm(img1, img[i], NORM_INF) == 0);
|
||||
}
|
||||
}
|
||||
|
||||
// See https://github.com/opencv/opencv/issues/26207
|
||||
TEST(Imgcodecs, imencodemulti_regression_26207)
|
||||
{
|
||||
vector<Mat> imgs;
|
||||
const cv::Mat img(100, 100, CV_8UC1, cv::Scalar::all(0));
|
||||
imgs.push_back(img);
|
||||
std::vector<uchar> buf;
|
||||
bool ret = false;
|
||||
|
||||
// Encode single image
|
||||
EXPECT_NO_THROW(ret = imencode(".tiff", img, buf));
|
||||
EXPECT_TRUE(ret);
|
||||
EXPECT_NO_THROW(ret = imencode(".tiff", imgs, buf));
|
||||
EXPECT_TRUE(ret);
|
||||
EXPECT_NO_THROW(ret = imencodemulti(".tiff", imgs, buf));
|
||||
EXPECT_TRUE(ret);
|
||||
|
||||
// Encode multiple images
|
||||
imgs.push_back(img.clone());
|
||||
EXPECT_NO_THROW(ret = imencode(".tiff", imgs, buf));
|
||||
EXPECT_TRUE(ret);
|
||||
EXPECT_NO_THROW(ret = imencodemulti(".tiff", imgs, buf));
|
||||
EXPECT_TRUE(ret);
|
||||
|
||||
// Count stored images from buffer.
|
||||
// imcount() doesn't support buffer, so encoded buffer outputs to file temporary.
|
||||
const size_t len = buf.size();
|
||||
const string filename = cv::tempfile(".tiff");
|
||||
FILE *f = fopen(filename.c_str(), "wb");
|
||||
EXPECT_NE(f, nullptr);
|
||||
EXPECT_EQ(len, fwrite(&buf[0], 1, len, f));
|
||||
fclose(f);
|
||||
|
||||
EXPECT_EQ(2, (int)imcount(filename));
|
||||
EXPECT_EQ(0, remove(filename.c_str()));
|
||||
}
|
||||
#endif
|
||||
|
||||
// See https://github.com/opencv/opencv/pull/26211
|
||||
// ( related with https://github.com/opencv/opencv/issues/26207 )
|
||||
TEST(Imgcodecs, imencode_regression_26207_extra)
|
||||
{
|
||||
// CV_32F is not supported depth for BMP Encoder.
|
||||
// Encoded buffer contains CV_8U image which is fallbacked.
|
||||
const cv::Mat src(100, 100, CV_32FC1, cv::Scalar::all(0));
|
||||
std::vector<uchar> buf;
|
||||
bool ret = false;
|
||||
EXPECT_NO_THROW(ret = imencode(".bmp", src, buf));
|
||||
EXPECT_TRUE(ret);
|
||||
|
||||
cv::Mat dst;
|
||||
EXPECT_NO_THROW(dst = imdecode(buf, IMREAD_GRAYSCALE));
|
||||
EXPECT_FALSE(dst.empty());
|
||||
EXPECT_EQ(CV_8UC1, dst.type());
|
||||
}
|
||||
TEST(Imgcodecs, imwrite_regression_26207_extra)
|
||||
{
|
||||
// CV_32F is not supported depth for BMP Encoder.
|
||||
// Encoded buffer contains CV_8U image which is fallbacked.
|
||||
const cv::Mat src(100, 100, CV_32FC1, cv::Scalar::all(0));
|
||||
const string filename = cv::tempfile(".bmp");
|
||||
bool ret = false;
|
||||
EXPECT_NO_THROW(ret = imwrite(filename, src));
|
||||
EXPECT_TRUE(ret);
|
||||
|
||||
cv::Mat dst;
|
||||
EXPECT_NO_THROW(dst = imread(filename, IMREAD_GRAYSCALE));
|
||||
EXPECT_FALSE(dst.empty());
|
||||
EXPECT_EQ(CV_8UC1, dst.type());
|
||||
EXPECT_EQ(0, remove(filename.c_str()));
|
||||
}
|
||||
|
||||
TEST(Imgcodecs_Params, imwrite_regression_22752)
|
||||
{
|
||||
|
Loading…
Reference in New Issue
Block a user