Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings

Commit 7481cb5

Browse filesBrowse files
authored
Merge pull request opencv#27013 from asmorkalov:as/imencode_animation
Test for in-memory animation encoding and decoding opencv#27013 Tests for opencv#26964 ### 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 - [ ] The PR is proposed to the proper branch - [ ] There is a reference to the original bug report and related work - [ ] There is accuracy test, performance test and test data in opencv_extra repository, if applicable Patch to opencv_extra has the same branch name. - [ ] The feature is well documented and sample code can be built with the project CMake
1 parent bbcdbca commit 7481cb5
Copy full SHA for 7481cb5

File tree

Expand file treeCollapse file tree

3 files changed

+223
-0
lines changed
Filter options
Expand file treeCollapse file tree

3 files changed

+223
-0
lines changed

‎modules/imgcodecs/include/opencv2/imgcodecs.hpp

Copy file name to clipboardExpand all lines: modules/imgcodecs/include/opencv2/imgcodecs.hpp
+33Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -388,6 +388,19 @@ The function imreadanimation loads frames from an animated image file (e.g., GIF
388388
*/
389389
CV_EXPORTS_W bool imreadanimation(const String& filename, CV_OUT Animation& animation, int start = 0, int count = INT16_MAX);
390390

391+
/** @brief Loads frames from an animated image buffer into an Animation structure.
392+
393+
The function imdecodeanimation loads frames from an animated image buffer (e.g., GIF, AVIF, APNG, WEBP) into the provided Animation struct.
394+
395+
@param buf A reference to an InputArray containing the image buffer.
396+
@param animation A reference to an Animation structure where the loaded frames will be stored. It should be initialized before the function is called.
397+
@param start The index of the first frame to load. This is optional and defaults to 0.
398+
@param count The number of frames to load. This is optional and defaults to 32767.
399+
400+
@return Returns true if the buffer was successfully loaded and frames were extracted; returns false otherwise.
401+
*/
402+
CV_EXPORTS_W bool imdecodeanimation(InputArray buf, CV_OUT Animation& animation, int start = 0, int count = INT16_MAX);
403+
391404
/** @brief Saves an Animation to a specified file.
392405
393406
The function imwriteanimation saves the provided Animation data to the specified file in an animated format.
@@ -402,6 +415,26 @@ These parameters are used to specify additional options for the encoding process
402415
*/
403416
CV_EXPORTS_W bool imwriteanimation(const String& filename, const Animation& animation, const std::vector<int>& params = std::vector<int>());
404417

418+
/** @brief Encodes an Animation to a memory buffer.
419+
420+
The function imencodeanimation encodes the provided Animation data into a memory
421+
buffer in an animated format. Supported formats depend on the implementation and
422+
may include formats like GIF, AVIF, APNG, or WEBP.
423+
424+
@param ext The file extension that determines the format of the encoded data.
425+
@param animation A constant reference to an Animation struct containing the
426+
frames and metadata to be encoded.
427+
@param buf A reference to a vector of unsigned chars where the encoded data will
428+
be stored.
429+
@param params Optional format-specific parameters encoded as pairs (paramId_1,
430+
paramValue_1, paramId_2, paramValue_2, ...). These parameters are used to
431+
specify additional options for the encoding process. Refer to `cv::ImwriteFlags`
432+
for details on possible parameters.
433+
434+
@return Returns true if the animation was successfully encoded; returns false otherwise.
435+
*/
436+
CV_EXPORTS_W bool imencodeanimation(const String& ext, const Animation& animation, CV_OUT std::vector<uchar>& buf, const std::vector<int>& params = std::vector<int>());
437+
405438
/** @brief Returns the number of images inside the given file
406439
407440
The function imcount returns the number of pages in a multi-page image (e.g. TIFF), the number of frames in an animation (e.g. AVIF), and 1 otherwise.

‎modules/imgcodecs/src/loadsave.cpp

Copy file name to clipboardExpand all lines: modules/imgcodecs/src/loadsave.cpp
+142Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -811,6 +811,116 @@ bool imreadanimation(const String& filename, CV_OUT Animation& animation, int st
811811
return imreadanimation_(filename, IMREAD_UNCHANGED, start, count, animation);
812812
}
813813

814+
static bool imdecodeanimation_(InputArray buf, int flags, int start, int count, Animation& animation)
815+
{
816+
bool success = false;
817+
if (start < 0) {
818+
start = 0;
819+
}
820+
if (count < 0) {
821+
count = INT16_MAX;
822+
}
823+
824+
/// Search for the relevant decoder to handle the imagery
825+
ImageDecoder decoder;
826+
decoder = findDecoder(buf.getMat());
827+
828+
/// if no decoder was found, return false.
829+
if (!decoder) {
830+
CV_LOG_WARNING(NULL, "Decoder for buffer not found!\n");
831+
return false;
832+
}
833+
834+
/// set the filename in the driver
835+
decoder->setSource(buf.getMat());
836+
// read the header to make sure it succeeds
837+
try
838+
{
839+
// read the header to make sure it succeeds
840+
if (!decoder->readHeader())
841+
return false;
842+
}
843+
catch (const cv::Exception& e)
844+
{
845+
CV_LOG_ERROR(NULL, "imdecodeanimation_(): can't read header: " << e.what());
846+
return false;
847+
}
848+
catch (...)
849+
{
850+
CV_LOG_ERROR(NULL, "imdecodeanimation_(): can't read header: unknown exception");
851+
return false;
852+
}
853+
854+
int current = 0;
855+
int frame_count = (int)decoder->getFrameCount();
856+
count = count + start > frame_count ? frame_count - start : count;
857+
858+
uint64 pixels = (uint64)decoder->width() * (uint64)decoder->height() * (uint64)(count + 4);
859+
if (pixels > CV_IO_MAX_IMAGE_PIXELS) {
860+
CV_LOG_WARNING(NULL, "\nyou are trying to read " << pixels <<
861+
" bytes that exceed CV_IO_MAX_IMAGE_PIXELS.\n");
862+
return false;
863+
}
864+
865+
while (current < start + count)
866+
{
867+
// grab the decoded type
868+
const int type = calcType(decoder->type(), flags);
869+
870+
// established the required input image size
871+
Size size = validateInputImageSize(Size(decoder->width(), decoder->height()));
872+
873+
// read the image data
874+
Mat mat(size.height, size.width, type);
875+
success = false;
876+
try
877+
{
878+
if (decoder->readData(mat))
879+
success = true;
880+
}
881+
catch (const cv::Exception& e)
882+
{
883+
CV_LOG_ERROR(NULL, "imreadanimation_: can't read data: " << e.what());
884+
}
885+
catch (...)
886+
{
887+
CV_LOG_ERROR(NULL, "imreadanimation_: can't read data: unknown exception");
888+
}
889+
if (!success)
890+
break;
891+
892+
// optionally rotate the data if EXIF' orientation flag says so
893+
if ((flags & IMREAD_IGNORE_ORIENTATION) == 0 && flags != IMREAD_UNCHANGED)
894+
{
895+
ApplyExifOrientation(decoder->getExifTag(ORIENTATION), mat);
896+
}
897+
898+
if (current >= start)
899+
{
900+
int duration = decoder->animation().durations.size() > 0 ? decoder->animation().durations.back() : 1000;
901+
animation.durations.push_back(duration);
902+
animation.frames.push_back(mat);
903+
}
904+
905+
if (!decoder->nextPage())
906+
{
907+
break;
908+
}
909+
++current;
910+
}
911+
animation.bgcolor = decoder->animation().bgcolor;
912+
animation.loop_count = decoder->animation().loop_count;
913+
914+
return success;
915+
}
916+
917+
bool imdecodeanimation(InputArray buf, Animation& animation, int start, int count)
918+
{
919+
CV_TRACE_FUNCTION();
920+
921+
return imdecodeanimation_(buf, IMREAD_UNCHANGED, start, count, animation);
922+
}
923+
814924
static
815925
size_t imcount_(const String& filename, int flags)
816926
{
@@ -994,6 +1104,38 @@ bool imwriteanimation(const String& filename, const Animation& animation, const
9941104
return imwriteanimation_(filename, animation, params);
9951105
}
9961106

1107+
static bool imencodeanimation_(const String& ext, const Animation& animation, std::vector<uchar>& buf, const std::vector<int>& params)
1108+
{
1109+
ImageEncoder encoder = findEncoder(ext);
1110+
if (!encoder)
1111+
CV_Error(Error::StsError, "could not find a writer for the specified extension");
1112+
1113+
encoder->setDestination(buf);
1114+
1115+
bool code = false;
1116+
try
1117+
{
1118+
code = encoder->writeanimation(animation, params);
1119+
}
1120+
catch (const cv::Exception& e)
1121+
{
1122+
CV_LOG_ERROR(NULL, "imencodeanimation_('" << ext << "'): can't write data: " << e.what());
1123+
}
1124+
catch (...)
1125+
{
1126+
CV_LOG_ERROR(NULL, "imencodeanimation_('" << ext << "'): can't write data: unknown exception");
1127+
}
1128+
1129+
return code;
1130+
}
1131+
1132+
bool imencodeanimation(const String& ext, const Animation& animation, std::vector<uchar>& buf, const std::vector<int>& params)
1133+
{
1134+
CV_Assert(!animation.frames.empty());
1135+
CV_Assert(animation.frames.size() == animation.durations.size());
1136+
return imencodeanimation_(ext, animation, buf, params);
1137+
}
1138+
9971139
static bool
9981140
imdecode_( const Mat& buf, int flags, Mat& mat )
9991141
{

‎modules/imgcodecs/test/test_animation.cpp

Copy file name to clipboardExpand all lines: modules/imgcodecs/test/test_animation.cpp
+48Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -588,6 +588,54 @@ INSTANTIATE_TEST_CASE_P(/**/,
588588
Imgcodecs_ImageCollection,
589589
testing::ValuesIn(exts_multi));
590590

591+
TEST(Imgcodecs_APNG, imdecode_animation)
592+
{
593+
Animation gt_animation, mem_animation;
594+
// Set the path to the test image directory and filename for loading.
595+
const string root = cvtest::TS::ptr()->get_data_path();
596+
const string filename = root + "pngsuite/tp1n3p08.png";
597+
598+
EXPECT_TRUE(imreadanimation(filename, gt_animation));
599+
EXPECT_EQ(1000, gt_animation.durations.back());
600+
601+
std::vector<unsigned char> buf;
602+
readFileBytes(filename, buf);
603+
EXPECT_TRUE(imdecodeanimation(buf, mem_animation));
604+
605+
EXPECT_EQ(mem_animation.frames.size(), gt_animation.frames.size());
606+
EXPECT_EQ(mem_animation.bgcolor, gt_animation.bgcolor);
607+
EXPECT_EQ(mem_animation.loop_count, gt_animation.loop_count);
608+
for (size_t i = 0; i < gt_animation.frames.size(); i++)
609+
{
610+
EXPECT_EQ(0, cvtest::norm(mem_animation.frames[i], gt_animation.frames[i], NORM_INF));
611+
EXPECT_EQ(mem_animation.durations[i], gt_animation.durations[i]);
612+
}
613+
}
614+
615+
TEST(Imgcodecs_APNG, imencode_animation)
616+
{
617+
Animation gt_animation, mem_animation;
618+
// Set the path to the test image directory and filename for loading.
619+
const string root = cvtest::TS::ptr()->get_data_path();
620+
const string filename = root + "pngsuite/tp1n3p08.png";
621+
622+
EXPECT_TRUE(imreadanimation(filename, gt_animation));
623+
EXPECT_EQ(1000, gt_animation.durations.back());
624+
625+
std::vector<unsigned char> buf;
626+
EXPECT_TRUE(imencodeanimation(".png", gt_animation, buf));
627+
EXPECT_TRUE(imdecodeanimation(buf, mem_animation));
628+
629+
EXPECT_EQ(mem_animation.frames.size(), gt_animation.frames.size());
630+
EXPECT_EQ(mem_animation.bgcolor, gt_animation.bgcolor);
631+
EXPECT_EQ(mem_animation.loop_count, gt_animation.loop_count);
632+
for (size_t i = 0; i < gt_animation.frames.size(); i++)
633+
{
634+
EXPECT_EQ(0, cvtest::norm(mem_animation.frames[i], gt_animation.frames[i], NORM_INF));
635+
EXPECT_EQ(mem_animation.durations[i], gt_animation.durations[i]);
636+
}
637+
}
638+
591639
#endif // HAVE_PNG
592640

593641
}} // namespace

0 commit comments

Comments
0 (0)
Morty Proxy This is a proxified and sanitized view of the page, visit original site.