5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OpenCVのGIF codecを試してみよう!

Last updated at Posted at 2024-12-14

この記事はOpenCV Advent Calendar 2024の15日目の記事です。
他の記事は目次にまとめられています。

TL;DR

  • OpeCV 4.11にGIF Codecsが追加されました!使いたい時はcmakeで有効化してね
  • delay time設定が独自なので、ちょーっといやーんです!

はじめに

GIF画像形式は、遥か昔「インターネット」が普及し始めた時に、共にあり、滅んだ。

後に言う「LZW特許問題」である。「LZW特許の使用料、個人のフリーソフトについては免除っていったけど、やっぱ徴収するわ!」で、(# ゚Д゚)という話。

そして、米国では2003年6月20日、日本でも2004年6月20日に、特許の有効期限が切れました。

あれから20年経過した2024年、OpenCVにもGIFサポートが追加されたのです!!

こちらのGSoC_2024の成果でございます!

アニメーション画像を作る簡単な手段

例えば、連番画像を作って、imagemagikに入れたら、GIFもAPNGでもアニメーションする画像ファイル作れます。あるいは、videoio module使って、ffmpeg経由で動画ファイルも作れます。

でも、それってちょーっと、めんどくさくないですかね?ライブラリとかも必要になりますし。

もしOpenCVだけで簡単にアニメーション画像作れたら、それはそれで幸せだと思うのですよ!

よし、やっていこう!

GIF Codec有効化して、OpenCVをbuildしよう!

GIF Codecを有効化するためには、明示的に-DWITH_IMGCODEC_GIF=ONを指定しなければならない。なお、最新のOpenCV 4.xブランチだけあれば、外部ライブラリなどは不要。

git clone https://github.com/opencv/opencv.git opencv4
cmake -S opencv4 -B build4-main.GIF -GNinja -DWITH_IMGCODEC_GIF=ON
cmake --build build4-main.GIF

そうすると、Media I/O:の欄に「GIF」の文字が、やったね!

--   Media I/O:
--     ZLib:                        /usr/lib/x86_64-linux-gnu/libz.so (ver 1.3)
--     JPEG:                        /usr/lib/x86_64-linux-gnu/libjpeg.so (ver 80)
--     WEBP:                        /usr/lib/x86_64-linux-gnu/libwebp.so (ver encoder: 0x020f)
--     AVIF:                        /usr/lib/x86_64-linux-gnu/libavif.so.16.0.4 (ver 1.0.4)
--     PNG:                         /usr/lib/x86_64-linux-gnu/libpng.so (ver 1.6.43)
--     TIFF:                        /usr/lib/x86_64-linux-gnu/libtiff.so (ver 42 / 4.5.1)
--     JPEG 2000:                   OpenJPEG (ver 2.5.0)
--     OpenEXR:                     build (ver 2.3.0)
--     GIF:                         YES
--     HDR:                         YES
--     SUNRASTER:                   YES
--     PXM:                         YES
--     PFM:                         YES

あとはいつも通り、build と installすれば準備はOK!

cmake --build build4-main.GIF
sudo cmake --install build4-main.GIF

GIF画像を作ってみる

サンプルコードはこんな感じですね

  • std::vector<cv::Mat> に画像データをぶち込む
  • imwrite()する
// g++ main.cpp -o a.out -I/usr/local/include/opencv4 -lopencv_core -lopencv_imgcodecs -lopencv_imgproc

#include <iostream>
#include <opencv2/core.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>

int main()
{
  cv::Mat src = cv::imread("opencv-logo-white.png", cv::IMREAD_COLOR);

  std::vector<cv::Mat> imgs;
  std::vector<int> params;
  params.push_back(cv::IMWRITE_GIF_LOOP);
  params.push_back(1);  // Do not loop
  params.push_back(cv::IMWRITE_GIF_SPEED);
  params.push_back(91); // ( 100 - (91 - 1) ) * 10 ms = 100ms

  for(int i = 1; i < 21 ; i++){
    cv::Mat frame;
    cv::blur(src, frame, cv::Size(i,i));
    char text[64]; sprintf(text, "%d", i); // std::format() is supported after C++20.
    cv::putText(frame, text, cv::Point(32,32), cv::FONT_HERSHEY_SIMPLEX, 1, cv::Scalar(255,255,255));
    imgs.push_back(frame);
  }

  std::reverse(imgs.begin(), imgs.end());
  std::cout << "imgs.size() = " << imgs.size() << std::endl;
  cv::imwrite("test.gif", imgs, params);

  return 0;
}

test.gif

ちょっとした悲しみ

loop1回が指定できない!

本当は、ループの最後でビタ!と止め絵にしたいのですが・・・。
どうにもChromiumでもFirefoxでも画像ビューワでも、LOOP指定がちゃんと動かないですね。

なお、バイナリエディアでみて指定できていることは確認済みなので、表示側のバグですかねえ・・・ 0x30d付近から

Add Size Value contents
0x30d 1 0x21 Entension Introducer
0x30e 1 0xff Entension Label
0x30f 1 0x0b Block Size #1
0x310 8 NETSCAPE Application Identifier
0x318 3 2.0 Application Authentication Code
0x31b 1 0x03 Block Size #2
0x31d 3 0x01_0100 Application Data
先頭1byteは0x01固定、残り2byteはループ回数
0x31f 1 0x00 Block Terminator
kmtr@kmtr-VMware-Virtual-Platform:~/work/qiita$ hexdump -C test.gif  | grep NETSCAPE -A5 -B5
000002c0  24 aa fc 24 ff fc 48 00  fc 48 55 fc 48 aa fc 48  |$..$..H..HU.H..H|
000002d0  ff fc 6c 00 fc 6c 55 fc  6c aa fc 6c ff fc 90 00  |..l..lU.l..l....|
000002e0  fc 90 55 fc 90 aa fc 90  ff fc b4 00 fc b4 55 fc  |..U...........U.|
000002f0  b4 aa fc b4 ff fc d8 00  fc d8 55 fc d8 aa fc d8  |..........U.....|
00000300  ff fc fc 00 fc fc 55 fc  fc aa fc fc ff 21 ff 0b  |......U......!..|
00000310  4e 45 54 53 43 41 50 45  32 2e 30 03 01 01 00 00  |NETSCAPE2.0.....|
00000320  21 f9 04 0c 0a 00 00 00  2c 00 00 00 00 b4 00 ee  |!.......,.......|
00000330  00 07 08 ff 00 01 08 1c  48 b0 a0 c1 83 08 13 22  |........H......"|
00000340  04 01 00 84 43 20 40 88  00 21 13 86 0c a0 42 84  |....C @..!....B.|
00000350  4a 91 2a 55 8c 58 b1 62  e5 40 96 1b 49 b2 a4 c9  |J.*U.X.b.@..I...|
00000360  93 24 41 7e f4 a8 b1 14  a1 42 80 ca 80 21 03 11  |.$A~.....B...!..|

IMWRITE_GIF_SPEEDの指定が何か変

フレーム毎のdelay time指定方法が直感的ではない(なんでこういうデザインにしたのですかね)

Application側での指定方法

  params.push_back(cv::IMWRITE_GIF_SPEED);
  params.push_back(91); // ( 100 - (91 - 1) ) * 10 ms = 100ms

OpenCV側でのハンドリング部分

    // confirm the params
    for (size_t i = 0; i < params.size(); i += 2) {
        switch (params[i]) {
            case IMWRITE_GIF_LOOP:
                loopCount = std::min(std::max(params[i + 1], 0), 65535); // loop count is in 2 bytes
                break;
            case IMWRITE_GIF_SPEED:
                frameDelay = 100 - std::min(std::max(params[i + 1] - 1, 0), 99); // from 10ms to 1000ms
                break;
  • GIFフォーマットのdelay time指定は、10ms単位で、0~65535まで
  • だけど、なぜか1~100の値域に絞ってる?
  • しかも、最大値が1000msに限定?

10ms単位を直接指定できればいいんじゃないですかね。。。

            case IMWRITE_GIF_DELAYTIME_10MS:
                frameDelay = std::min(std::max(params[i + 1], 0), 65535); // from 0ms to 65535 * 10ms
                break;

まとめ

  • OpeCV 4.11にGIF Codecsが追加されました!使いたい時はcmakeで有効化してね
  • delay time設定が独自なので、ちょーっといやーんです!

お忙しい中、ご精読頂きましてありがとうございました!!

明日、12/16は、dandelion先生の「OpenCVのzlib-ng連携で高速化を図る」です! new generationの実力を見せていただきましょう!!(?)

5
1
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
1

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?