Merge pull request #25691 from redhecker:gifSupport

[GSoC] Add GIF decode and encode for imgcodecs #25691

this is related to #24855 

we add  gif support for `imread`, `imreadmulti`, `imwrite` and `imwritemulti`

opencv_extra: https://github.com/opencv/opencv_extra/pull/1203

### 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.
- [ ] The feature is well documented and sample code can be built with the project CMake
This commit is contained in:
Super 2024-12-07 15:17:41 +08:00 committed by GitHub
parent 7fbf3c1fec
commit 082cd7a74e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
9 changed files with 1729 additions and 5 deletions

View File

@ -432,6 +432,9 @@ OCV_OPTION(WITH_ITT "Include Intel ITT support" ON
OCV_OPTION(WITH_PROTOBUF "Enable libprotobuf" ON
VISIBLE_IF TRUE
VERIFY HAVE_PROTOBUF)
OCV_OPTION(WITH_IMGCODEC_GIF "Include GIF support" OFF
VISIBLE_IF TRUE
VERIFY HAVE_IMGCODEC_GIF)
OCV_OPTION(WITH_IMGCODEC_HDR "Include HDR support" ON
VISIBLE_IF TRUE
VERIFY HAVE_IMGCODEC_HDR)
@ -1557,6 +1560,10 @@ if(WITH_GDCM OR HAVE_GDCM)
status(" GDCM:" HAVE_GDCM THEN "YES (${GDCM_VERSION})" ELSE "NO")
endif()
if(WITH_IMGCODEC_GIF OR DEFINED HAVE_IMGCODEC_GIF)
status(" GIF:" HAVE_IMGCODEC_GIF THEN "YES" ELSE "NO")
endif()
if(WITH_IMGCODEC_HDR OR DEFINED HAVE_IMGCODEC_HDR)
status(" HDR:" HAVE_IMGCODEC_HDR THEN "YES" ELSE "NO")
endif()

View File

@ -371,6 +371,11 @@ if(WITH_GDCM)
endif()
endif()
if(WITH_IMGCODEC_GIF)
set(HAVE_IMGCODEC_GIF ON)
elseif(DEFINED WITH_IMGCODEC_GIF)
set(HAVE_IMGCODEC_GIF OFF)
endif()
if(WITH_IMGCODEC_HDR)
set(HAVE_IMGCODEC_HDR ON)
elseif(DEFINED WITH_IMGCODEC_HDR)

View File

@ -88,6 +88,10 @@ if(HAVE_GDAL)
list(APPEND GRFMT_LIBS ${GDAL_LIBRARY})
endif()
if(HAVE_IMGCODEC_GIF)
add_definitions(-DHAVE_IMGCODEC_GIF)
endif()
if(HAVE_IMGCODEC_HDR)
add_definitions(-DHAVE_IMGCODEC_HDR)
endif()

View File

@ -111,7 +111,13 @@ enum ImwriteFlags {
IMWRITE_JPEG2000_COMPRESSION_X1000 = 272,//!< For JPEG2000, use to specify the target compression rate (multiplied by 1000). The value can be from 0 to 1000. Default is 1000.
IMWRITE_AVIF_QUALITY = 512,//!< For AVIF, it can be a quality between 0 and 100 (the higher the better). Default is 95.
IMWRITE_AVIF_DEPTH = 513,//!< For AVIF, it can be 8, 10 or 12. If >8, it is stored/read as CV_32F. Default is 8.
IMWRITE_AVIF_SPEED = 514 //!< For AVIF, it is between 0 (slowest) and (fastest). Default is 9.
IMWRITE_AVIF_SPEED = 514,//!< For AVIF, it is between 0 (slowest) and (fastest). Default is 9.
IMWRITE_GIF_LOOP = 1024,//!< For GIF, it can be a loop flag from 0 to 65535. Default is 0 - loop forever.
IMWRITE_GIF_SPEED = 1025,//!< For GIF, it is between 1 (slowest) and 100 (fastest). Default is 96.
IMWRITE_GIF_QUALITY = 1026, //!< For GIF, it can be a quality from 1 to 8. Default is 2. See cv::ImwriteGifCompressionFlags.
IMWRITE_GIF_DITHER = 1027, //!< For GIF, it can be a quality from -1(most dither) to 3(no dither). Default is 0.
IMWRITE_GIF_TRANSPARENCY = 1028, //!< For GIF, the alpha channel lower than this will be set to transparent. Default is 1.
IMWRITE_GIF_COLORTABLE = 1029 //!< For GIF, 0 means global color table is used, 1 means local color table is used. Default is 0.
};
enum ImwriteJPEGSamplingFactorParams {
@ -216,6 +222,18 @@ enum ImwriteHDRCompressionFlags {
IMWRITE_HDR_COMPRESSION_RLE = 1
};
//! Imwrite GIF specific values for IMWRITE_GIF_QUALITY parameter key, if larger than 3, then its related to the size of the color table.
enum ImwriteGIFCompressionFlags {
IMWRITE_GIF_FAST_NO_DITHER = 1,
IMWRITE_GIF_FAST_FLOYD_DITHER = 2,
IMWRITE_GIF_COLORTABLE_SIZE_8 = 3,
IMWRITE_GIF_COLORTABLE_SIZE_16 = 4,
IMWRITE_GIF_COLORTABLE_SIZE_32 = 5,
IMWRITE_GIF_COLORTABLE_SIZE_64 = 6,
IMWRITE_GIF_COLORTABLE_SIZE_128 = 7,
IMWRITE_GIF_COLORTABLE_SIZE_256 = 8
};
//! @} imgcodecs_flags
/** @brief Loads an image from a file.
@ -229,6 +247,7 @@ returns an empty matrix.
Currently, the following file formats are supported:
- Windows bitmaps - \*.bmp, \*.dib (always supported)
- GIF files - \*.gif (always supported)
- JPEG files - \*.jpeg, \*.jpg, \*.jpe (see the *Note* section)
- JPEG 2000 files - \*.jp2 (see the *Note* section)
- Portable Network Graphics - \*.png (see the *Note* section)

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,183 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level
// directory of this distribution and at http://opencv.org/license.html
#ifndef OPENCV_GRFMT_GIF_HPP
#define OPENCV_GRFMT_GIF_HPP
#ifdef HAVE_IMGCODEC_GIF
#include "grfmt_base.hpp"
namespace cv
{
enum GifOpMode
{
GRFMT_GIF_Nothing = 0,
GRFMT_GIF_PreviousImage = 1,
GRFMT_GIF_Background = 2,
GRFMT_GIF_Cover = 3
};
//////////////////////////////////////////////////////////////////////
//// GIF Decoder ////
//////////////////////////////////////////////////////////////////////
class GifDecoder CV_FINAL : public BaseImageDecoder
{
public:
GifDecoder();
~GifDecoder() CV_OVERRIDE;
bool readHeader() CV_OVERRIDE;
bool readData(Mat& img) CV_OVERRIDE;
bool nextPage() CV_OVERRIDE;
void close();
ImageDecoder newDecoder() const CV_OVERRIDE;
protected:
RLByteStream m_strm;
int bgColor;
int depth;
int idx;
GifOpMode opMode;
bool hasTransparentColor;
uchar transparentColor;
int top, left, width, height;
bool hasRead;
std::vector<uchar> globalColorTable;
std::vector<uchar> localColorTable;
int lzwMinCodeSize;
int globalColorTableSize;
int localColorTableSize;
Mat lastImage;
std::vector<uchar> imgCodeStream;
struct lzwNodeD
{
int length;
uchar suffix;
std::vector<uchar> prefix;
};
void readExtensions();
void code2pixel(Mat& img, int start, int k);
bool lzwDecode();
bool getFrameCount_();
bool skipHeader();
};
//////////////////////////////////////////////////////////////////////
//// GIF Encoder ////
//////////////////////////////////////////////////////////////////////
class GifEncoder CV_FINAL : public BaseImageEncoder {
public:
GifEncoder();
~GifEncoder() CV_OVERRIDE;
bool isFormatSupported(int depth) const CV_OVERRIDE;
bool write(const Mat& img, const std::vector<int>& params) CV_OVERRIDE;
bool writemulti(const std::vector<Mat>& img_vec,
const std::vector<int>& params) CV_OVERRIDE;
ImageEncoder newEncoder() const CV_OVERRIDE;
private:
/** Color Quantization **/
class OctreeColorQuant
{
struct OctreeNode
{
bool isLeaf;
std::shared_ptr<OctreeNode> children[8]{};
int level;
uchar index;
int leaf;
int pixelCount;
size_t redSum, greenSum, blueSum;
OctreeNode();
};
std::shared_ptr<OctreeNode> root;
std::vector<std::shared_ptr<OctreeNode>> m_nodeList[8];
int32_t m_bitLength;
int32_t m_maxColors;
int32_t m_leafCount;
uchar m_criticalTransparency;
uchar r, g, b; // color under transparent color
public:
explicit OctreeColorQuant(int maxColors = 256, int bitLength = 8, uchar criticalTransparency = 1);
int getPalette(uchar* colorTable);
uchar getLeaf(uchar red, uchar green, uchar blue);
void addMat(const Mat& img);
void addMats(const std::vector<Mat>& img_vec);
void addColor(int red, int green, int blue);
void reduceTree();
void recurseReduce(const std::shared_ptr<OctreeNode>& node);
};
enum GifDithering // normal dithering level is -1 to 2
{
GRFMT_GIF_None = 3,
GRFMT_GIF_FloydSteinberg = 4
};
WLByteStream strm;
int m_width, m_height;
int globalColorTableSize;
int localColorTableSize;
uchar opMode;
uchar criticalTransparency;
uchar transparentColor;
Vec3b transparentRGB;
int top, left, width, height;
OctreeColorQuant quantG;
OctreeColorQuant quantL;
std::vector<int16_t> lzwTable;
std::vector<uchar> imgCodeStream;
std::vector<uchar> globalColorTable;
std::vector<uchar> localColorTable;
// params
int loopCount;
int frameDelay;
int colorNum;
int bitDepth;
int dithering;
int lzwMinCodeSize, lzwMaxCodeSize;
bool fast;
bool writeFrames(const std::vector<Mat>& img_vec, const std::vector<int>& params);
bool writeHeader(const std::vector<Mat>& img_vec);
bool writeFrame(const Mat& img);
bool pixel2code(const Mat& img);
void getColorTable(const std::vector<Mat>& img_vec, bool isGlobal);
static int ditheringKernel(const Mat &img, Mat &img_, int depth, uchar transparency);
bool lzwEncode();
void close();
};
} // namespace cv
#endif // HAVE_IMGCODEC_GIF
#endif //OPENCV_GRFMT_GIF_HPP

View File

@ -45,6 +45,7 @@
#include "grfmt_base.hpp"
#include "grfmt_avif.hpp"
#include "grfmt_bmp.hpp"
#include "grfmt_gif.hpp"
#include "grfmt_sunras.hpp"
#include "grfmt_jpeg.hpp"
#include "grfmt_pxm.hpp"

View File

@ -151,14 +151,18 @@ struct ImageCodecInitializer
*/
ImageCodecInitializer()
{
#ifdef HAVE_AVIF
decoders.push_back(makePtr<AvifDecoder>());
encoders.push_back(makePtr<AvifEncoder>());
#endif
/// BMP Support
decoders.push_back( makePtr<BmpDecoder>() );
encoders.push_back( makePtr<BmpEncoder>() );
#ifdef HAVE_IMGCODEC_GIF
decoders.push_back( makePtr<GifDecoder>() );
encoders.push_back( makePtr<GifEncoder>() );
#endif
#ifdef HAVE_AVIF
decoders.push_back(makePtr<AvifDecoder>());
encoders.push_back(makePtr<AvifEncoder>());
#endif
#ifdef HAVE_IMGCODEC_HDR
decoders.push_back( makePtr<HdrDecoder>() );
encoders.push_back( makePtr<HdrEncoder>() );

View File

@ -0,0 +1,357 @@
// This file is part of OpenCV project.
// It is subject to the license terms in the LICENSE file found in the top-level directory
// of this distribution and at http://opencv.org/license.html
#include "test_precomp.hpp"
#ifdef HAVE_IMGCODEC_GIF
namespace opencv_test { namespace {
const string gifsuite_files_multi[]={
"basi3p01",
"basi3p02",
"basi3p04",
"basn3p01",
"basn3p02",
"basn3p04",
"ccwn3p08",
"ch1n3p04",
"cs3n3p08",
"cs5n3p08",
"cs8n3p08",
"g03n3p04",
"g04n3p04",
"g05n3p04",
"g07n3p04",
"g10n3p04",
"g25n3p04",
"s32i3p04",
"s32n3p04",
"tp0n3p08",
};
const string gifsuite_files_read_single[] = {
"basi3p01",
"basi3p02",
"basi3p04",
"basn3p01",
"basn3p02",
"basn3p04",
"ccwn3p08",
"cdfn2c08",
"cdhn2c08",
"cdsn2c08",
"cdun2c08",
"ch1n3p04",
"cs3n3p08",
"cs5n2c08",
"cs5n3p08",
"cs8n2c08",
"cs8n3p08",
"exif2c08",
"g03n2c08",
"g03n3p04",
"g04n2c08",
"g04n3p04",
"g05n2c08",
"g05n3p04",
"g07n2c08",
"g07n3p04",
"g10n2c08",
"g10n3p04"
};
const string gifsuite_files_read_write_suite[]={
"g25n2c08",
"g25n3p04",
"s01i3p01",
"s01n3p01",
"s02i3p01",
"s02n3p01",
"s03i3p01",
"s03n3p01",
"s04i3p01",
"s04n3p01",
"s05i3p02",
"s05n3p02",
"s06i3p02",
"s06n3p02",
"s07i3p02",
"s07n3p02",
"s08i3p02",
"s08n3p02",
"s09i3p02",
"s09n3p02",
"s32i3p04",
"s32n3p04",
"s33i3p04",
"s33n3p04",
"s34i3p04",
"s34n3p04",
"s35i3p04",
"s35n3p04",
"s36i3p04",
"s36n3p04",
"s37i3p04",
"s37n3p04",
"s38i3p04",
"s38n3p04",
"s39i3p04",
"s39n3p04",
"s40i3p04",
"s40n3p04",
"tp0n3p08",
};
const std::pair<string,int> gifsuite_files_bgra[]={
make_pair("gif_bgra1",53287),
make_pair("gif_bgra2",52651),
make_pair("gif_bgra3",54809),
make_pair("gif_bgra4",57562),
make_pair("gif_bgra5",56733),
make_pair("gif_bgra6",52110),
};
TEST(Imgcodecs_Gif, read_gif_multi)
{
const string root = cvtest::TS::ptr()->get_data_path();
const string filename = root + "gifsuite/gif_multi.gif";
vector<cv::Mat> img_vec_8UC4;
ASSERT_NO_THROW(cv::imreadmulti(filename, img_vec_8UC4,0,20,IMREAD_UNCHANGED));
EXPECT_EQ(img_vec_8UC4.size(), imcount(filename));
vector<cv::Mat> img_vec_8UC3;
for(const auto & i : img_vec_8UC4){
cv::Mat img_tmp;
cvtColor(i,img_tmp,COLOR_BGRA2BGR);
img_vec_8UC3.push_back(img_tmp);
}
const long unsigned int expected_size=20;
EXPECT_EQ(img_vec_8UC3.size(),expected_size);
for(long unsigned int i=0;i<img_vec_8UC3.size();i++){
cv::Mat img=img_vec_8UC3[i];
const string png_filename = root + "pngsuite/" + gifsuite_files_multi[i] + ".png";
cv::Mat img_png;
ASSERT_NO_THROW(img_png = imread(png_filename,IMREAD_UNCHANGED));
ASSERT_FALSE(img_png.empty());
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), img, img_png);
}
}
typedef testing::TestWithParam<string> Imgcodecs_Gif_GifSuite_SingleFrame;
TEST_P(Imgcodecs_Gif_GifSuite_SingleFrame, read_gif_single)
{
const string root = cvtest::TS::ptr()->get_data_path();
const string filename = root + "gifsuite/" + GetParam() + ".gif";
const string png_filename=root + "pngsuite/" + GetParam() + ".png";
const long unsigned int expected_size = 1;
EXPECT_EQ(expected_size, imcount(filename));
cv::Mat img_8UC4;
ASSERT_NO_THROW(img_8UC4 = cv::imread(filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img_8UC4.empty());
cv::Mat img_8UC3;
ASSERT_NO_THROW(cvtColor(img_8UC4, img_8UC3, COLOR_BGRA2BGR));
cv::Mat img_png;
ASSERT_NO_THROW(img_png = cv::imread(png_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img_png.empty());
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), img_8UC3, img_png);
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Gif_GifSuite_SingleFrame,
testing::ValuesIn(gifsuite_files_read_single));
TEST(Imgcodecs_Gif, read_gif_big){
const string root = cvtest::TS::ptr()->get_data_path();
const string gif_filename = root + "gifsuite/gif_big.gif";
const string png_filename = root + "gifsuite/gif_big.png";
cv::Mat img_8UC4;
ASSERT_NO_THROW(img_8UC4 = imread(gif_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img_8UC4.empty());
cv::Mat img_8UC3;
const int expected_col=1303;
const int expected_row=1391;
EXPECT_EQ(expected_col, img_8UC4.cols);
EXPECT_EQ(expected_row, img_8UC4.rows);
ASSERT_NO_THROW(cvtColor(img_8UC4, img_8UC3,COLOR_BGRA2BGR));
EXPECT_EQ(expected_col, img_8UC3.cols);
EXPECT_EQ(expected_row, img_8UC3.rows);
cv::Mat img_png;
ASSERT_NO_THROW(img_png=imread(png_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img_png.empty());
cv::Mat img_png_8UC3;
ASSERT_NO_THROW(cvtColor(img_png,img_png_8UC3, COLOR_BGRA2BGR));
EXPECT_EQ(img_8UC3.size, img_png_8UC3.size);
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), img_8UC3, img_png_8UC3);
}
typedef testing::TestWithParam<std::pair<string,int>> Imgcodecs_Gif_GifSuite_SingleFrame_BGRA;
TEST_P(Imgcodecs_Gif_GifSuite_SingleFrame_BGRA, read_gif_single_bgra){
const string root = cvtest::TS::ptr()->get_data_path();
const string gif_filename = root + "gifsuite/" + GetParam().first + ".gif";
const string png_filename = root + "gifsuite/" + GetParam().first + ".png";
cv::Mat gif_img;
cv::Mat png_img;
ASSERT_NO_THROW(gif_img = cv::imread(gif_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(gif_img.empty());
ASSERT_NO_THROW(png_img = cv::imread(png_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(png_img.empty());
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), gif_img, png_img);
int transparent_count = 0;
for(int i=0; i<gif_img.rows; i++){
for(int j=0; j<gif_img.cols; j++){
cv::Vec4b pixel1 = gif_img.at<cv::Vec4b>(i,j);
if((int)(pixel1[3]) == 0){
transparent_count++;
}
}
}
EXPECT_EQ(transparent_count,GetParam().second);
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Gif_GifSuite_SingleFrame_BGRA ,
testing::ValuesIn(gifsuite_files_bgra));
TEST(Imgcodecs_Gif,read_gif_multi_bgra){
const string root = cvtest::TS::ptr()->get_data_path();
const string gif_filename = root + "gifsuite/gif_multi_bgra.gif";
vector<cv::Mat> img_vec;
ASSERT_NO_THROW(cv::imreadmulti(gif_filename, img_vec, IMREAD_UNCHANGED));
EXPECT_EQ(imcount(gif_filename), img_vec.size());
const int fixed_transparent_count = 53211;
for(auto & frame_count : img_vec){
int transparent_count=0;
for(int i=0; i<frame_count.rows; i++){
for(int j=0; j<frame_count.cols; j++){
cv::Vec4b pixel1 = frame_count.at<cv::Vec4b>(i,j);
if((int)(pixel1[3]) == 0){
transparent_count++;
}
}
}
EXPECT_EQ(fixed_transparent_count,transparent_count);
}
}
TEST(Imgcodecs_Gif, read_gif_special){
const string root = cvtest::TS::ptr()->get_data_path();
const string gif_filename1 = root + "gifsuite/special1.gif";
const string png_filename1 = root + "gifsuite/special1.png";
const string gif_filename2 = root + "gifsuite/special2.gif";
const string png_filename2 = root + "gifsuite/special2.png";
cv::Mat gif_img1;
ASSERT_NO_THROW(gif_img1 = cv::imread(gif_filename1,IMREAD_UNCHANGED));
ASSERT_FALSE(gif_img1.empty());
cv::Mat png_img1;
ASSERT_NO_THROW(png_img1 = cv::imread(png_filename1,IMREAD_UNCHANGED));
ASSERT_FALSE(png_img1.empty());
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), gif_img1, png_img1);
cv::Mat gif_img2;
ASSERT_NO_THROW(gif_img2 = cv::imread(gif_filename2,IMREAD_UNCHANGED));
ASSERT_FALSE(gif_img2.empty());
cv::Mat png_img2;
ASSERT_NO_THROW(png_img2 = cv::imread(png_filename2,IMREAD_UNCHANGED));
ASSERT_FALSE(png_img2.empty());
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), gif_img2, png_img2);
}
TEST(Imgcodecs_Gif,write_gif_flags){
const string root = cvtest::TS::ptr()->get_data_path();
const string png_filename = root + "gifsuite/special1.png";
vector<uchar> buff;
const int expected_rows=611;
const int expected_cols=293;
Mat img_gt = Mat::ones(expected_rows, expected_cols, CV_8UC1);
vector<int> param;
param.push_back(IMWRITE_GIF_QUALITY);
param.push_back(7);
param.push_back(IMWRITE_GIF_DITHER);
param.push_back(2);
EXPECT_NO_THROW(imencode(".png", img_gt, buff, param));
Mat img;
EXPECT_NO_THROW(img = imdecode(buff, IMREAD_ANYDEPTH)); // hang
EXPECT_FALSE(img.empty());
EXPECT_EQ(img.cols, expected_cols);
EXPECT_EQ(img.rows, expected_rows);
EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), img, img_gt);
}
TEST(Imgcodecs_Gif, write_gif_big) {
const string root = cvtest::TS::ptr()->get_data_path();
const string png_filename = root + "gifsuite/gif_big.png";
const string gif_filename = cv::tempfile(".png");
cv::Mat img;
ASSERT_NO_THROW(img = cv::imread(png_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img.empty());
EXPECT_EQ(1303, img.cols);
EXPECT_EQ(1391, img.rows);
ASSERT_NO_THROW(imwrite(gif_filename, img));
cv::Mat img_gif;
ASSERT_NO_THROW(img_gif = cv::imread(gif_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img_gif.empty());
EXPECT_EQ(1303, img_gif.cols);
EXPECT_EQ(1391, img_gif.rows);
EXPECT_EQ(0, remove(gif_filename.c_str()));
}
typedef testing::TestWithParam<string> Imgcodecs_Gif_GifSuite_Read_Write_Suite;
TEST_P(Imgcodecs_Gif_GifSuite_Read_Write_Suite ,read_gif_single)
{
const string root = cvtest::TS::ptr()->get_data_path();
const string png_filename = root + "pngsuite/"+GetParam()+".png";
const string gif_filename = cv::tempfile(".gif");
cv::Mat img;
ASSERT_NO_THROW(img = cv::imread(png_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img.empty());
vector<int> param;
param.push_back(IMWRITE_GIF_QUALITY);
param.push_back(8);
param.push_back(IMWRITE_GIF_DITHER);
param.push_back(3);
ASSERT_NO_THROW(imwrite(gif_filename, img, param));
cv::Mat img_gif;
ASSERT_NO_THROW(img_gif = cv::imread(gif_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img_gif.empty());
cv::Mat img_8UC3;
ASSERT_NO_THROW(cv::cvtColor(img_gif, img_8UC3, COLOR_BGRA2BGR));
EXPECT_PRED_FORMAT2(cvtest::MatComparator(29, 0), img, img_8UC3);
EXPECT_EQ(0, remove(gif_filename.c_str()));
}
INSTANTIATE_TEST_CASE_P(/*nothing*/, Imgcodecs_Gif_GifSuite_Read_Write_Suite ,
testing::ValuesIn(gifsuite_files_read_write_suite));
TEST(Imgcodecs_Gif, write_gif_multi) {
const string root = cvtest::TS::ptr()->get_data_path();
const string gif_filename = cv::tempfile(".gif");
vector<cv::Mat> img_vec;
for (long unsigned int i = 0; i < 20; i++) {
const string png_filename = root + "pngsuite/" + gifsuite_files_multi[i] + ".png";
cv::Mat img;
ASSERT_NO_THROW(img = cv::imread(png_filename, IMREAD_UNCHANGED));
ASSERT_FALSE(img.empty());
img_vec.push_back(img);
}
vector<int> param;
param.push_back(IMWRITE_GIF_QUALITY);
param.push_back(8);
param.push_back(IMWRITE_GIF_DITHER);
param.push_back(3);
ASSERT_NO_THROW(cv::imwritemulti(gif_filename, img_vec, param));
vector<cv::Mat> img_vec_gif;
ASSERT_NO_THROW(cv::imreadmulti(gif_filename, img_vec_gif));
EXPECT_EQ(img_vec.size(), img_vec_gif.size());
for (long unsigned int i = 0; i < img_vec.size(); i++) {
cv::Mat img_8UC3;
ASSERT_NO_THROW(cv::cvtColor(img_vec_gif[i], img_8UC3, COLOR_BGRA2BGR));
EXPECT_PRED_FORMAT2(cvtest::MatComparator(29, 0), img_vec[i], img_8UC3);
}
EXPECT_EQ(0, remove(gif_filename.c_str()));
}
}//opencv_test
}//namespace
#endif