diff --git a/modules/imgcodecs/include/opencv2/imgcodecs.hpp b/modules/imgcodecs/include/opencv2/imgcodecs.hpp index d737a28e45..03bffebbcc 100644 --- a/modules/imgcodecs/include/opencv2/imgcodecs.hpp +++ b/modules/imgcodecs/include/opencv2/imgcodecs.hpp @@ -413,13 +413,13 @@ can be saved using this function, with these exceptions: - With JPEG 2000 encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved. - With JPEG XL encoder, 8-bit unsigned (CV_8U), 16-bit unsigned (CV_16U) and 32-bit float(CV_32F) images can be saved. - JPEG XL images with an alpha channel can be saved using this function. - To do this, create 8-bit (or 16-bit, 32-bit float) 4-channel image BGRA, where the alpha channel goes last. - Fully transparent pixels should have alpha set to 0, fully opaque pixels should have alpha set to 255/65535/1.0. + To achieve this, create an 8-bit 4-channel (CV_8UC4) / 16-bit 4-channel (CV_16UC4) / 32-bit float 4-channel (CV_32FC4) BGRA image, ensuring the alpha channel is the last component. + Fully transparent pixels should have an alpha value of 0, while fully opaque pixels should have an alpha value of 255/65535/1.0. - With PAM encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved. - With PNG encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved. - - PNG images with an alpha channel can be saved using this function. To do this, create - 8-bit (or 16-bit) 4-channel image BGRA, where the alpha channel goes last. Fully transparent pixels - should have alpha set to 0, fully opaque pixels should have alpha set to 255/65535 (see the code sample below). + - PNG images with an alpha channel can be saved using this function. + To achieve this, create an 8-bit 4-channel (CV_8UC4) / 16-bit 4-channel (CV_16UC4) BGRA image, ensuring the alpha channel is the last component. + Fully transparent pixels should have an alpha value of 0, while fully opaque pixels should have an alpha value of 255/65535(see the code sample below). - With PGM/PPM encoder, 8-bit unsigned (CV_8U) and 16-bit unsigned (CV_16U) images can be saved. - With TIFF encoder, 8-bit unsigned (CV_8U), 8-bit signed (CV_8S), 16-bit unsigned (CV_16U), 16-bit signed (CV_16S), @@ -428,6 +428,11 @@ can be saved using this function, with these exceptions: - Multiple images (vector of Mat) can be saved in TIFF format (see the code sample below). - 32-bit float 3-channel (CV_32FC3) TIFF images will be saved using the LogLuv high dynamic range encoding (4 bytes per pixel) +- With GIF encoder, 8-bit unsigned (CV_8U) images can be saved. + - GIF images with an alpha channel can be saved using this function. + To achieve this, create an 8-bit 4-channel (CV_8UC4) BGRA image, ensuring the alpha channel is the last component. + Fully transparent pixels should have an alpha value of 0, while fully opaque pixels should have an alpha value of 255. + - 8-bit single-channel images (CV_8UC1) are not supported due to GIF's limitation to indexed color formats. If the image format is not supported, the image will be converted to 8-bit unsigned (CV_8U) and saved that way. diff --git a/modules/imgcodecs/src/grfmt_gif.cpp b/modules/imgcodecs/src/grfmt_gif.cpp index 5758cc38f7..5e5695b07b 100644 --- a/modules/imgcodecs/src/grfmt_gif.cpp +++ b/modules/imgcodecs/src/grfmt_gif.cpp @@ -14,7 +14,7 @@ namespace cv ////////////////////////////////////////////////////////////////////// GifDecoder::GifDecoder() { m_signature = R"(GIF)"; - m_type = CV_8UC4; + m_type = CV_8UC3; bgColor = -1; m_buf_supported = true; globalColorTableSize = 0; @@ -172,12 +172,17 @@ bool GifDecoder::readData(Mat &img) { } else { cvtColor(img_, img, COLOR_BGRA2BGR); } - } else { + } else if (img.channels() == 4){ if (m_use_rgb) { cvtColor(img_, img, COLOR_BGRA2RGBA); } else { img_.copyTo(img); } + } else if (img.channels() == 1){ + cvtColor(img_, img, COLOR_BGRA2GRAY); + } else { + CV_LOG_WARNING(NULL, cv::format("Unsupported channels: %d", img.channels())); + hasRead = false; } } @@ -414,6 +419,7 @@ bool GifDecoder::getFrameCount_() { if (extension == 0xFF) { int len = m_strm.getByte(); while (len) { + // TODO: In strictly, Application Identifier and Authentication Code should be checked. if (len == 3) { if (m_strm.getByte() == 0x01) { m_animation.loop_count = m_strm.getWord(); @@ -427,9 +433,28 @@ bool GifDecoder::getFrameCount_() { } len = m_strm.getByte(); } + } else if (extension == 0xF9) { + int len = m_strm.getByte(); + while (len) { + if (len == 4) { + int packedFields = m_strm.getByte(); + // 3 bit : Reserved + // 3 bit : Disposal Method + // 1 bit : User Input Flag + // 1 bit : Transparent Color Flag + if ( (packedFields & 0x01)== 0x01) { + m_type = CV_8UC4; // Transparent Index is given. + } + m_strm.skip(2); // Delay Time + m_strm.skip(1); // Transparent Color Index + } else { + m_strm.skip(len); + } + len = m_strm.getByte(); + } } else { // if it does not belong to any of the extension type mentioned in the GIF Specification - if (extension != 0xF9 && extension != 0xFE && extension != 0x01) { + if (extension != 0xFE && extension != 0x01) { CV_LOG_WARNING(NULL, "found Unknown Extension Type: " + std::to_string(extension)); } int len = m_strm.getByte(); diff --git a/modules/imgcodecs/test/test_gif.cpp b/modules/imgcodecs/test/test_gif.cpp index b9c49c0bdb..50d0be6c3a 100644 --- a/modules/imgcodecs/test/test_gif.cpp +++ b/modules/imgcodecs/test/test_gif.cpp @@ -241,17 +241,17 @@ TEST(Imgcodecs_Gif, read_gif_special){ 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_NO_THROW(gif_img1 = cv::imread(gif_filename1,IMREAD_COLOR)); ASSERT_FALSE(gif_img1.empty()); cv::Mat png_img1; - ASSERT_NO_THROW(png_img1 = cv::imread(png_filename1,IMREAD_UNCHANGED)); + ASSERT_NO_THROW(png_img1 = cv::imread(png_filename1,IMREAD_COLOR)); 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_NO_THROW(gif_img2 = cv::imread(gif_filename2,IMREAD_COLOR)); ASSERT_FALSE(gif_img2.empty()); cv::Mat png_img2; - ASSERT_NO_THROW(png_img2 = cv::imread(png_filename2,IMREAD_UNCHANGED)); + ASSERT_NO_THROW(png_img2 = cv::imread(png_filename2,IMREAD_COLOR)); ASSERT_FALSE(png_img2.empty()); EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), gif_img2, png_img2); } @@ -351,6 +351,21 @@ TEST(Imgcodecs_Gif, write_gif_multi) { EXPECT_EQ(0, remove(gif_filename.c_str())); } +TEST(Imgcodecs_Gif, encode_IMREAD_GRAYSCALE) { + cv::Mat src; + cv::Mat decoded; + vector buf; + vector param; + bool ret = false; + + src = cv::Mat(240,240,CV_8UC3,cv::Scalar(128,64,32)); + EXPECT_NO_THROW(ret = imencode(".gif", src, buf, param)); + EXPECT_TRUE(ret); + EXPECT_NO_THROW(decoded = imdecode(buf, cv::IMREAD_GRAYSCALE)); + EXPECT_FALSE(decoded.empty()); + EXPECT_EQ(decoded.channels(), 1); +} + }//opencv_test }//namespace diff --git a/modules/imgcodecs/test/test_grfmt.cpp b/modules/imgcodecs/test/test_grfmt.cpp index 947e560c81..f44e64ec7a 100644 --- a/modules/imgcodecs/test/test_grfmt.cpp +++ b/modules/imgcodecs/test/test_grfmt.cpp @@ -164,6 +164,8 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq) continue; if (cn != 3 && ext == ".ppm") continue; + if (cn == 1 && ext == ".gif") + continue; string filename = cv::tempfile(format("%d%s", cn, ext.c_str()).c_str()); Mat img_gt(size, CV_MAKETYPE(CV_8U, cn), Scalar::all(0)); @@ -179,8 +181,14 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq) ASSERT_TRUE(imwrite(filename, img_gt, parameters)); Mat img = imread(filename, IMREAD_UNCHANGED); ASSERT_FALSE(img.empty()); - EXPECT_EQ(img.size(), img.size()); - EXPECT_EQ(img.type(), img.type()); + EXPECT_EQ(img_gt.size(), img.size()); + EXPECT_EQ(img_gt.channels(), img.channels()); + if (ext == ".pfm") { + EXPECT_EQ(img_gt.depth(), CV_8U); + EXPECT_EQ(img.depth(), CV_32F); + } else { + EXPECT_EQ(img_gt.depth(), img.depth()); + } EXPECT_EQ(cn, img.channels()); @@ -200,6 +208,14 @@ TEST_P(Imgcodecs_ExtSize, write_imageseq) EXPECT_LT(n, 1.); EXPECT_PRED_FORMAT2(cvtest::MatComparator(0, 0), img, img_gt); } + else if (ext == ".gif") + { + // GIF encoder will reduce the number of colors to 256. + // It is hard to compare image comparison by pixel unit. + double n = cvtest::norm(img, img_gt, NORM_L1); + double expected = 0.03 * img.size().area(); + EXPECT_LT(n, expected); + } else { double n = cvtest::norm(img, img_gt, NORM_L2); @@ -238,6 +254,9 @@ const string all_exts[] = #ifdef HAVE_IMGCODEC_PFM ".pfm", #endif +#ifdef HAVE_IMGCODEC_GIF + ".gif", +#endif }; vector all_sizes()