DOBON.NETプログラミング道掲示板
(現在 過去ログ3 を表示中)

[ 最新記事及び返信フォームをトピックトップへ ]

■33878 / inTopicNo.1)  gifの容量をできるだけ軽くしたい
  
□投稿者/ にくにく 一般人(1回)-(2018/06/26(Tue) 19:28:28)
  • アイコン環境/言語:[VB.NET] 
    分類:[.NET] 

    はじめまして。
    お知恵をお借りできればと思います。

    現在デスクトップ上の作業をGifにするツールをvb.netで作ろうとしています。
    そこで以下のことしてみました。

    @「録画開始」ボタンクリック後「録画終了」ボタンクリックまで、画面HCをjpegに保存。
     本サイトの以下の記事を参考にしました。
     https://dobon.net/vb/dotnet/graphics/screencapture.html

    A@で保存したjpegをもとにGifファイルを作成。
     本サイトの以下の記事を参考にしました。
     https://dobon.net/vb/dotnet/graphics/createanimatedgif.html

    B上記@Aの結果Gifファイルは作成できましたが、30秒程度のGifでも25MB前後ありました。
     そこで軽量化を図り、@で画面HCを保存する際jpegの質を10にする処理を追加しました。
     本サイトの以下の記事を参考にしました。
     https://dobon.net/vb/dotnet/graphics/encoderparameters.html

    C上記Bの処理を追加した結果、@で作成する各jpegのサイズは小さくできました。
     しかしサイズを小さくできたjpegでAの処理を行うと、出来上がったGifはAのgifの数倍のサイズになってしまいます。

    できるだけ動画の容量を小さくしたいのですが、どのようなことをすればよいでしょうか。Cでサイズが大きくなるのは、カラーパレットあたりが原因かとも思いますが、画像系の知識に乏しいため見当がつきません。

    ご意見のほど、よろしくお願いします。

引用返信 削除キー/
■33879 / inTopicNo.2)  Re[1]: gifの容量をできるだけ軽くしたい
□投稿者/ Azulean 大御所(500回)-(2018/06/26(Tue) 21:56:00)
  • アイコンNo33878に返信(にくにくさんの記事)
    >  そこで軽量化を図り、@で画面HCを保存する際jpegの質を10にする処理を追加しました。

    残念ながらその結果は、最終的な GIF に良い効果を与えません。
    逆に JPEG 圧縮によって画像が劣化した結果、色数が増えたり、フレームが一致しないと判定される量が増えたりして最終結果の GIF の質が悪化するか、ファイルサイズが増えることになるはずです。

    このあたりは JPEG や GIF のアルゴリズム・フォーマットについて学んでいくと自然と理解できる範囲かもしれません。

    > できるだけ動画の容量を小さくしたいのですが、どのようなことをすればよいでしょうか。Cでサイズが大きくなるのは、カラーパレットあたりが原因かとも思いますが、画像系の知識に乏しいため見当がつきません。

    ファイルサイズを減らすことに効果があるのは、フレームを間引くとか、色数を減らすといったことです。
    ただ、.NET Framework の標準のクラスを組み合わせるだけではできない範囲と思われるので、何らかのグラフィックライブラリを探すか、サンプルを探すか、自作する必要がありそうです。
引用返信 削除キー/
■33880 / inTopicNo.3)  Re[2]: gifの容量をできるだけ軽くしたい
□投稿者/ にくにく 一般人(2回)-(2018/06/26(Tue) 22:22:12)
  • アイコンAzuleanさん

    ご回答ありがとうございます。
    そうですか、画質の変更は逆効果だったんですね。

    あと、一つおしえてください。
    https://dobon.net/vb/dotnet/graphics/createanimatedgif.html

    ↑のURLのGetGraphicControlExtensionで以下のような記述があります。

    '詰め込み欄 (Packed Field)
    '透過色指標を使う時は+1
    '消去方法:そのまま残す+4、背景色でつぶす+8、直前の画像に戻す+12
    bs(3) = &H0
    '遅延時間 (Delay Time)
    Dim delayTimeBytes As Byte() = BitConverter.GetBytes(delayTime)
    bs(4) = delayTimeBytes(0)
    bs(5) = delayTimeBytes(1)
    '透過色指標 (Transparency Index, Transparent Color Index)
    bs(6) = &H0

    これ、この状態だと透過性指標は使っていないですよね?
    使う場合にはどのような書き方になるのでしょうか?
    bs(3)とbs(6)あたりを直すのだろうと予想はしていますが、具体的には思い当たりません。
    重ねての質問で恐縮ですが、よろしくお願いします。
引用返信 削除キー/
■33881 / inTopicNo.4)  Re[3]: gifの容量をできるだけ軽くしたい
□投稿者/ 魔界の仮面弁士 大御所(1114回)-(2018/06/26(Tue) 23:33:48)
  • アイコンNo33880に返信(にくにくさんの記事)
    > ↑のURLのGetGraphicControlExtensionで以下のような記述があります。

    意味合いとしては、下記の『Graphic Control Extensionブロック』を参照
    http://www.tvg.ne.jp/menyukko/cauldron/dtgifformat.html


    > これ、この状態だと透過性指標は使っていないですよね?
    特定のアニメーションフレームを、セル画のように透過させたいということですか?
    何のために?


    > 使う場合にはどのような書き方になるのでしょうか?
    どういう目的で指定したいのかを書いて頂けると、アドバイスしやすいです。


    透過フレームを使えば、本来は最大 256 色まで扱えないはずの GIF で、
    下記のようにしてフルカラー GIF を擬似的に表現することもできるのですが、
    それはそのままサイズの増加に繋がるわけですから、今回は
    そういう目的で使うわけではないのですよね…?

    http://www.kero2.org/fg_ex.cgi
    http://bygzam.seesaa.net/article/110314782.html


    > bs(3)とbs(6)あたりを直すのだろうと予想はしていますが、具体的には思い当たりません。
    何のために使いたいのか理由が分かりませんが、それは
    Azulean さんが書かれている「フレームを間引くとか、色数を減らす」のために
    必要なことなのでしょうか。


    仮に、上記のフルカラーGIF 表現だとすれば
     '識別子:&H21,&HF9固定
     bs(0) = &H21
     bs(1) = &HF9
     'データサイズ:&H4固定
     bs(2) = &H4
     '描き方:5 = 前の画像を消さずに重ねて描画(4) + ユーザー入力不要(0) + 透明色を使う(1)
     bs(3) = &H5
     '遅延時間:前のフレームの後、直ちに(0ms後に)描画
     bs(4) = &H0
     bs(5) = &H0
     '透明色指定
     bs(6) = 色番号
     '終端子:&H0固定
     bs(7) = &H0
    といった感じになりますが、今回はそれが目的ではなさそう…。
引用返信 削除キー/
■33882 / inTopicNo.5)  Re[1]: gifの容量をできるだけ軽くしたい
□投稿者/ 魔界の仮面弁士 大御所(1115回)-(2018/06/26(Tue) 23:45:45)
  • アイコンNo33878に返信(にくにくさんの記事)
    > できるだけ動画の容量を小さくしたいのですが、

    アニメーションGIF ファイル以外の動画形式では駄目なのでしょうか?

    http://pronama.azurewebsites.net/2015/06/11/bitmap-avi/
    https://jehupc.exblog.jp/9457754/
引用返信 削除キー/
■33883 / inTopicNo.6)  Re[4]: gifの容量をできるだけ軽くしたい
□投稿者/ にくにく 一般人(3回)-(2018/06/27(Wed) 00:54:22)
  • アイコン魔界の仮面弁士さん

    返信ありがとうございます。

    >透過フレームを使えば、本来は最大 256 色まで扱えないはずの GIF で、
    >下記のようにしてフルカラー GIF を擬似的に表現することもできるのですが、
    >それはそのままサイズの増加に繋がるわけですから、今回は
    >そういう目的で使うわけではないのですよね…?

    透過フレームはサイズが増える処理だったんですね。
    以前Gifについて調べた際、「先のファイルと同じ個所は透明にして先の
    ファイルの色を使いまわしてサイズを軽くできる」と見たような記憶が
    あったので、透過性指標はそれのことだと思っていました。

    あと、別の書き込みで言及されている「他の形式」についてですが、動画
    の作成も閲覧も初期のWin7から最新のWin10まで色々なPCで行われる可能性
    があるので、コーデックの登録等をしなくてもすぐ動く形式がよいと考え
    ています。
    私が調べた感じではそれがGifかと思ったのですが、この認識も間違ってい
    るでしょうか? 初歩的な質問ばかりで申し訳ありません。

引用返信 削除キー/
■33884 / inTopicNo.7)  Re[5]: gifの容量をできるだけ軽くしたい
□投稿者/ 魔界の仮面弁士 大御所(1116回)-(2018/06/27(Wed) 07:22:48)
  • アイコンNo33883に返信(にくにくさんの記事)
    > 透過フレームはサイズが増える処理だったんですね。
    あ、いえ。使い方次第ということですけれどね。

    2枚目以降の絵をより少ない色数で表現するために
    使うという目的なら、当然、サイズは減らせます。


    > 以前Gifについて調べた際、「先のファイルと同じ個所は透明にして先の
    > ファイルの色を使いまわしてサイズを軽くできる」と見たような記憶が
    重ね合わせの際に使われる情報であるという事はご存知のようですが、
    そもそも、元画像の JPEG は透過色を持たないフォーマットですから、
    すべて不透明のままでは、透過性指標も意味を持ちませんよね。

    すなわち前後の画素の差分情報を収集して、どこを透明部にするのかという
    「事前作業」が重要になるわけで、やりたい事が分からないと、質問にあった
    >>> bs(3)とbs(6)あたりを直す
    という問いに答えにくかったということです。
    そのための「事前作業」は、自力実装しなければいけないわけですし。


    > あったので、透過性指標はそれのことだと思っていました。
    bs(3)はビットフラグになっていますが、今回のケースだと
     0: このフレームは透過部を持たず、すべてそのまま上書き描画する…bs(6)は未使用
     9: このフレームの透過部を、背景色すなわち screenDescriptor(6) の番号の色で塗る
     5: このフレームの透過部は、現在描画済みの内容をそのまま残しておく
     13:このフレームの透過部を、前回描画した画像に戻す
    の 4 種のいずれかが入ることになるのではないでしょうか。

    そして上記の値を説明しているのが、先のコメントにある
    >>> '詰め込み欄 (Packed Field)
    >>> '透過色指標を使う時は+1
    >>> '消去方法:そのまま残す+4、背景色でつぶす+8、直前の画像に戻す+12
    の部分ですよね。


    > 「他の形式」についてですが、動画の作成も閲覧も
    なるほど。JPEG からの変換なら、MPEG 系の方が圧縮効率が良い気がしたのですが、
    たとえ再生できたとしても、作成のために追加のエンコーダーを
    入れたくないという事情なんですね。

    画像によっては、Animated GIF や Animated PNG だとサイズが大きくなりがちなので、
    他の形式が良いかとも思ったのですが。


    > 初期のWin7から最新のWin10まで色々なPCで行われる可能性
    OS 標準で付属しているエンコーダーとデコーダーなら良いのでしょうけれど、
    標準で何が使えるのかを、私は把握していないです…。

    昔からある、MRLE (Microsoft RLE) や MSVC (Microsoft Video 1)あたりなら、
    さらに古い Windows でも対応している気もしますが、そんなこともないのかな。


    > 私が調べた感じではそれがGifかと思ったのですが、この認識も間違ってい
    > るでしょうか? 初歩的な質問ばかりで申し訳ありません。
    標準で非対応な Windows は、もはや淘汰されていますし、
    現行の Windows なら、アニメーション GIF はもちろん表示できるでしょう。

    ただ、今の OS だと画面の色数が 256 色で収まらないので
    Animated GIF だと、使える色数が少ないのがやはりネックですね。

    あとは、delayTimeBytes を下げすぎないように注意。
    http://daredemopc.blog51.fc2.com/blog-entry-712.html
引用返信 削除キー/
■33885 / inTopicNo.8)  Re[6]: gifの容量をできるだけ軽くしたい
□投稿者/ にくにく 一般人(5回)-(2018/06/27(Wed) 19:48:58)
  • アイコン魔界の仮面弁士さん

    回答ありがとうございます。

    特にJpegから変換するこだわりはないです。
    ここのコードがjpegだったからそのままJpegにしていました。

    透過ができるのがpngだったので、画面HCをPNGで保存し、これに
    「前の画像と同じなら透明にする」処理を追加しました。
    その際は本サイトの以下のURLを参考にしました(「GetPixelとSetPixelを使用する方法」)。

    https://dobon.net/vb/dotnet/graphics/colorbalance.html

    この結果「前のPngと同色のピクセルはcolor.transparentにする」ことはできたのですが、
    そのファイルでGifを作っても、透過処理がされません。color.transparentにした個所は
    白い状態で表示されてしまいます。下記のbs(3)とbs(6)を色々変えてみましたが駄目でした。

    '描き方:5 = 前の画像を消さずに重ねて描画(4) + ユーザー入力不要(0) + 透明色を使う(1)
    bs(3) = &H5
    '遅延時間:前のフレームの後、直ちに(0ms後に)描画
    bs(4) = &H0
    bs(5) = &H0
    '透明色指定
    bs(6) = 色番号

    そこで「前のPngと同じ色のピクセルはcolor.Blackにする」と修正したところ、

    bs(3) = &H5
    bs(4) = &H0
    bs(5) = &H0
    bs(6) = &H0

    この状態で透過処理がされました。しかしこれだと元々黒にところも透過処理してしまいます。
    どうすればcolor.transparentにした部分を透過処理できるでしょうか?

引用返信 削除キー/
■33886 / inTopicNo.9)  Re[7]: gifの容量をできるだけ軽くしたい
□投稿者/ 魔界の仮面弁士 大御所(1117回)-(2018/06/28(Thu) 11:15:55)
  • アイコン2018/06/28(Thu) 16:34:26 編集(投稿者)

    No33885に返信(にくにくさんの記事)
    > 特にJpegから変換するこだわりはないです。
    > ここのコードがjpegだったからそのままJpegにしていました。

    今回のように、前後の画像間におけるピクセル単位の差異を検出することを
    目的とする場合には、可逆圧縮形式もしくは非圧縮形式のフォーマットが
    望ましいと思います。(要するに、PNG や BMP でキャプチャーする)


    > 透過ができるのがpngだったので、画面HCをPNGで保存し、これに
    > 「前の画像と同じなら透明にする」処理を追加しました。

    GIF の場合は、
     「カラーテーブルの中のいずれかを背景色として扱う」
     「カラーテーブルの中のいずれかを透明色として扱う」
    という仕様です。透明色が未設定の GIF もあります。

    また、Global Color Table と Local Color Table とがありますので、
    サイズ圧縮が目的なら、両者を使い分けた方が良いでしょう。


    > そのファイルでGifを作っても、透過処理がされません。
    透明色の設定には、『MakeTransparent( 透明扱いにしたい色 )』メソッドを
    利用することができるかと思います。
    https://msdn.microsoft.com/ja-jp/library/8517ckds%28v=vs.100%29.aspx


    > どうすればcolor.transparentにした部分を透過処理できるでしょうか?
    各ピクセルに Color.Transparent をセットするのではなく、
    透明にしたい色を .MakeTransparent( 色 ) にするということです。

    あるいは既に透明色を SetPixel 済みならば、
    引数無しの .MakeTransparent オーバーロードを使うこともできます。


    ちなみに現在の情報を知りたい場合にはこんな感じ。

     ' https://docs.microsoft.com/ja-jp/windows/desktop/gdiplus/-gdiplus-constant-property-item-descriptions#propertytagindexbackground
     Const PropertyTagIndexBackground As Integer = &H5103

     ' https://docs.microsoft.com/ja-jp/windows/desktop/gdiplus/-gdiplus-constant-property-item-descriptions#propertytagindextransparent
     Const PropertyTagIndexTransparent As Integer = &H5104

     Dim img As Image = 画像
     Dim colBackground As Color? = Nothing
     Dim colTransparent As Color? = Nothing
     If ImageFormat.Gif.Equals(img.RawFormat) Then
      Dim idx As Byte?
      idx = img.PropertyItems().FirstOrDefault(Function(p) p.Id = PropertyTagIndexBackground)?.Value?(0)
      If idx.HasValue Then
       colBackground = img.Palette.Entries(idx.Value)
      End If
      idx = img.PropertyItems().FirstOrDefault(Function(p) p.Id = PropertyTagIndexTransparent)?.Value?(0)
      If idx.HasValue Then
       colTransparent = img.Palette.Entries(idx.Value)
      End If
     End If


    > そこで「前のPngと同じ色のピクセルはcolor.Blackにする」と修正したところ、
    > bs(3) = &H5
    > bs(4) = &H0
    > bs(5) = &H0
    > bs(6) = &H0
    > この状態で透過処理がされました。

    それは黒をセットしているのではなく、パレットの 0 番にある色をセットしただけですよ。
    たまたま 0 番に Black (A=0, R=0, G=0, B=0) がセットされていたというだけでしょう。


    > しかしこれだと元々黒にところも透過処理してしまいます。

    各フレームは最大 256 色ですが、透過処理が必要なら、使える色数は残り 255 色なので、
    カラーパレットにも注意を払ってみてください。


    そもそも最終目標が Animated GIF 化なので、キャプチャー結果を
    256色以下に抑える必要があるので、ここも悩みどころ。

    キャプチャー前の画面設定を 8bitカラーモードにしておけば
    簡単に 256 色画像を得られるのですが、現行環境では厳しいですね。

    一応、exe のプロパティの互換性タブから、8bit カラーモードを
    選択できるのですが、近年はディスプレイアダプター側が
    8bitカラーに対応していないことも珍しくないので…。

    ということで、「撮影後に減色する」必要があると思います。

    もしも最大数である8bitカラー(256色)ではなく、半分の4bitカラー(16色)で
    済ませるなら、ファイルサイズもほぼ半分に減らせます。
    といっても、正確なサイズは LZW 圧縮の結果に左右されるので、
    削減率は画像内容によっても変わりますが。


    しかし残念ながら、.NET の減色処理はあまり品質が良くありませんので、
    ここは工夫が必要かもしれません。

    また、元画像にグラデーションがあると、サイズ面で不利になります。
    ディザリングによる減色処理を施すと、GIF 化した際にサイズが増加しがちなので。
引用返信 削除キー/
■33887 / inTopicNo.10)  Re[8]: gifの容量をできるだけ軽くしたい
□投稿者/ にくにく 一般人(7回)-(2018/06/29(Fri) 17:10:03)
  • アイコン魔界の仮面弁士さん

    回答ありがとうございます。
    ご案内の内容も参考にして、透過処理については何とかなりそうです。
    ありがとうございます。

    あともう一つだけご存じなら教えてください。

    多数のpngファイルをまとめてGifにしようとするとメモリの不足が発生してしまいます。ファイルが少ないうちはちゃんとGifにできているので、単純にメモリの問題と思われます。ファイルが500個くらいになったあたりでメモリ不足になります。メモリ16GBのWin10のPCなのでスペックが低いということはないと思っています。

    http://dobon.net/vb/dotnet/graphics/createanimatedgif.html

    このソースだとSaveAnimatedGifのbaseImagesには動画に使うpngを全部読み込んでおく必要があり、途中でDisposeはできないように思えます。作ったGif同士を結合するような方法はあるでしょうか?

    質問ばかりで恐縮ですがよろしくお願いします。
引用返信 削除キー/
■33888 / inTopicNo.11)  Re[9]: gifの容量をできるだけ軽くしたい
□投稿者/ 魔界の仮面弁士 大御所(1118回)-(2018/06/29(Fri) 22:05:33)
  • アイコンNo33887に返信(にくにくさんの記事)
    > ファイルが500個くらいになったあたりでメモリ不足になります。
    > baseImagesには動画に使うpngを全部読み込んでおく必要があり、途中でDisposeはできないように思えます。

    サンプル中では、As Bitmap() な配列を受け渡して

     For i = 0 To baseImages.Length - 1
      Dim bmp As Bitmap = baseImages(i)
      bmp.Save(ms, ImageFormat.Gif)
      ms.Position = 0
       :
       :
     Next

    と処理していますが、本当に必要なのは bmp 変数では無く、それを Gif 形式で Save しなおしたときのバイナリ(上記で言うと変数 ms の内容)ですよね。
    しかも、ループ処理で 1 つずつ順次処理しているだけですから、そもそも、Bitmap クラスに 500 個同時にロードしておく必要は無さそうです。

    たとえば As Bitmap() ではなく As IEnumerable(Of Bitmap) に変更すれば
    Iterator Function を通じて
     Using bmp As New Bitmap(〜)
      Yield bmp
     End Using
    などを渡すこともできるかと思います。


    あるいはもっと単純に、フォルダー名だけを渡すようにして
     For Each filePath In Directory.EnumerateFiles(targetFolder, "*.png")
      Using bmp As New Bitmap(filePath)
       bmp.Save(ms, ImageFormat.Gif)
      End Using
      ms.Position = 0
       :
       :
     Next
    などとしても良いわけで。

    あるいは、ファイル名の配列を渡す形でも良いでしょう。


    なお、サンプル中では MemoryStream.GetBuffer が利用されていますが、
    状況によっては、ToArray あるいは TryGetBuffer の方が望ましいケースもあります。
    http://8thway.blogspot.com/2015/12/memorystream.html
引用返信 削除キー/
■33889 / inTopicNo.12)  Re[10]: gifの容量をできるだけ軽くしたい
□投稿者/ にくにく 一般人(8回)-(2018/06/30(Sat) 21:21:52)
  • アイコン魔界の仮面弁士さん

    回答ありがとうございます。

    ファイル名の配列を渡す形で対応できました。
    とりあえず最低限の機能は搭載できそうです。

    ついてはこれで解決済みとさせていただきます。

    丁寧な解説ありがとうございました。
解決み!
引用返信 削除キー/



トピック内ページ移動 / << 0 >>

このトピックに書きこむ

過去ログには書き込み不可

Mode/  Pass/


- Child Tree -