mirror of
https://github.com/opencv/opencv.git
synced 2025-08-01 02:18:01 +08:00
Merge pull request #26859 from Kumataro:fix26858
imgcodecs:gif: support IMREAD_UNCHANGED and IMREAD_GRAYSCALE #26859 Close #26858 ### 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
7563cebad5
commit
01e3fe8791
@ -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.
|
||||
|
||||
|
@ -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();
|
||||
|
@ -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<uint8_t> buf;
|
||||
vector<int> 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
|
||||
|
||||
|
@ -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<Size> all_sizes()
|
||||
|
Loading…
Reference in New Issue
Block a user