Monthly Archives: 8月 2023

2023-08-09

ImageMagick vs GraphicsMagick vs FFmpeg (Emacsのimage-converter変数にはどれを指定すべきか)

Emacs 29からGraphicsMagick対応が入りました。image系の機能でImageMagick(convertコマンド)を使う箇所がGraphicsMagick(gmコマンド)にも対応した形です。

早速試してみたのですが、image-diredでmp4ファイルのサムネイルが表示できなくなってしまいました(image-diredで多様なファイル形式を扱うには確か色々設定が必要だったと思うのですが、それはまた別の機会に)。どうもImageMagickとGraphicsMagickでは対応している形式に差があるようです。

というわけで、具体的にどのような差があるのか調べてみました。Emacsにはimage-converterという仕組みがあって、Emacsが標準で対応していない形式の画像でも外部のコンバーターを使用して変換し、表示することが出来ます。image-converter.elの中で定義されている image-converter--probe 関数は指定されたコンバーターがサポートする形式をリストアップします。それを使ってみました。

ImageMagickが対応する形式:

(setq image-converter--converters
  '((graphicsmagick :command ("gm" "convert") :probe ("-list" "format"))
    (ffmpeg :command "ffmpeg" :probe "-decoders")
    (imagemagick :command ("magick" "convert") :probe ("-list" "format")))) ;; magickコマンドを使うように修正する(WindowsだとSystem32にconvert.exeがあるので)
(setq im-formats (image-converter--probe 'imagemagick))
("3fr" "3g2" "3gp" "aai" "ai" "apng" "art" "arw" "avi" "avif" "avs" "bayer"
 "bayera" "bgr" "bgra" "bgro" "bie" "bmp" "bmp2" "bmp3" "cal" "cals" "canvas"
 "caption" "cin" "clip" "clipboard" "cmyk" "cmyka" "cr2" "cr3" "crw" "cube"
 "cur" "cut" "data" "dcm" "dcr" "dcraw" "dcx" "dds" "dfont" "djvu" "dng"
 "dpx" "dxt1" "dxt5" "emf" "epdf" "epi" "eps" "epsf" "epsi" "ept" "ept2"
 "ept3" "erf" "farbfeld" "fax" "ff" "file" "fits" "fl32" "flif" "flv"
 "fractal" "ftp" "fts" "ftxt" "g3" "g4" "gif" "gif87" "gradient" "gray"
 "graya" "group4" "hald" "hdr" "heic" "heif" "hrz" "http" "https" "icb" "ico"
 "icon" "iiq" "inline" "ipl" "j2c" "j2k" "jbg" "jbig" "jng" "jnx" "jp2" "jpc"
 "jpe" "jpeg" "jpg" "jpm" "jps" "jpt" "k25" "kdc" "label" "m2v" "m4v" "mac"
 "map" "mask" "mat" "mef" "miff" "mkv" "mng" "mono" "mov" "mp4" "mpc" "mpeg"
 "mpg" "mpo" "mrw" "msl" "msvg" "mtv" "mvg" "nef" "nrw" "null" "orf" "otb"
 "otf" "pal" "palm" "pam" "pango" "pattern" "pbm" "pcd" "pcds" "pcl" "pct"
 "pcx" "pdb" "pdf" "pdfa" "pef" "pes" "pfa" "pfb" "pfm" "pgm" "pgx" "phm"
 "picon" "pict" "pix" "pjpeg" "plasma" "png" "png00" "png24" "png32" "png48"
 "png64" "png8" "pnm" "pocketmod" "ppm" "ps" "psb" "psd" "ptif" "pwp" "qoi"
 "raf" "ras" "raw" "rgb" "rgb565" "rgba" "rgbo" "rgf" "rla" "rle" "rmf"
 "rsvg" "rw2" "scr" "sct" "sfw" "sgi" "six" "sixel" "sr2" "srf" "stegano"
 "strimg" "sun" "svg" "svgz" "text" "tga" "tiff" "tiff64" "tile" "tim" "tm2"
 "ttc" "ttf" "txt" "uyvy" "vda" "vicar" "vid" "viff" "vips" "vst" "wbmp"
 "webm" "webp" "wmf" "wmv" "wpg" "x3f" "xbm" "xc" "xcf" "xpm" "xps" "xv"
 "ycbcr" "ycbcra" "yuv" "r")

ちなみに処理に9.3秒もかかりました。何にそんなにかかっているんだろう。

次いでGraphicsMagickが対応する形式:

(setq gm-formats (image-converter--probe 'graphicsmagick))
("3fr" "8bim" "8bimtext" "8bimwtext" "app1" "app1jpeg" "art" "arw" "avif"
 "avs" "b" "bie" "bigtiff" "bmp" "c" "cals" "caption" "cin" "clipboard"
 "cmyk" "cmyka" "cr2" "crw" "cur" "cut" "dcm" "dcr" "dcx" "dng" "dpx" "emf"
 "epdf" "epi" "eps" "epsf" "epsi" "ept" "ept2" "ept3" "erf" "exif" "fax"
 "file" "fits" "fractal" "g" "gif" "gif87" "gradient" "gray" "graya" "heic"
 "heif" "hrz" "http" "icb" "icc" "icm" "ico" "icon" "identity" "image" "iptc"
 "iptctext" "iptcwtext" "j2c" "jbg" "jbig" "jng" "jnx" "jp2" "jpc" "jpeg"
 "jpg" "k" "k25" "kdc" "label" "m" "mac" "map" "mat" "mef" "miff" "mng"
 "mono" "mpc" "mrw" "msl" "mtv" "mvg" "nef" "null" "o" "orf" "otb" "p7" "pal"
 "palm" "pam" "pbm" "pcd" "pcds" "pct" "pcx" "pdb" "pdf" "pef" "pfa" "pfb"
 "pgm" "pgx" "picon" "pict" "pix" "plasma" "png" "png00" "png24" "png32"
 "png48" "png64" "png8" "pnm" "ppm" "ps" "ptif" "pwp" "r" "raf" "ras" "rgb"
 "rgba" "rla" "rle" "sct" "sfw" "sgi" "sr2" "srf" "stegano" "sun" "svg"
 "svgz" "text" "tga" "tiff" "tile" "tim" "topol" "ttf" "txt" "uyvy" "vda"
 "vicar" "vid" "viff" "vst" "wbmp" "webp" "wmf" "wmfwin32" "wpg" "x3f" "xbm"
 "xc" "xcf" "xmp" "xpm" "xv" "y" "yuv")

こちらは3.3秒です。

対応フォーマット数の比較:

(list (list "ImageMagick" "GraphicsMagick")
      'hline
      (list (length im-formats) (length gm-formats)))
ImageMagick GraphicsMagick
238 172

どちらにもあるもの:

(seq-intersection im-formats gm-formats)
("3fr" "art" "arw" "avif" "avs" "bie" "bmp" "cals" "caption" "cin"
 "clipboard" "cmyk" "cmyka" "cr2" "crw" "cur" "cut" "dcm" "dcr" "dcx" "dng"
 "dpx" "emf" "epdf" "epi" "eps" "epsf" "epsi" "ept" "ept2" "ept3" "erf" "fax"
 "file" "fits" "fractal" "gif" "gif87" "gradient" "gray" "graya" "heic" "heif"
 "hrz" "http" "icb" "ico" "icon" "j2c" "jbg" "jbig" "jng" "jnx" "jp2" "jpc"
 "jpeg" "jpg" "k25" "kdc" "label" "mac" "map" "mat" "mef" "miff" "mng" "mono"
 "mpc" "mrw" "msl" "mtv" "mvg" "nef" "null" "orf" "otb" "pal" "palm" "pam"
 "pbm" "pcd" "pcds" "pct" "pcx" "pdb" "pdf" "pef" "pfa" "pfb" "pgm" "pgx"
 "picon" "pict" "pix" "plasma" "png" "png00" "png24" "png32" "png48" "png64"
 "png8" "pnm" "ppm" "ps" "ptif" "pwp" "raf" "ras" "rgb" "rgba" "rla" "rle"
 "sct" "sfw" "sgi" "sr2" "srf" "stegano" "sun" "svg" "svgz" "text" "tga"
 "tiff" "tile" "tim" "ttf" "txt" "uyvy" "vda" "vicar" "vid" "viff" "vst"
 "wbmp" "webp" "wmf" "wpg" "x3f" "xbm" "xc" "xcf" "xpm" "xv" "yuv" "r")

これらはどちらでもサポートされます。

ImageMagickにだけあるもの:

(seq-difference im-formats gm-formats)
("3g2" "3gp" "aai" "ai" "apng" "avi" "bayer" "bayera" "bgr" "bgra" "bgro"
 "bmp2" "bmp3" "cal" "canvas" "clip" "cr3" "cube" "data" "dcraw" "dds"
 "dfont" "djvu" "dxt1" "dxt5" "farbfeld" "ff" "fl32" "flif" "flv" "ftp" "fts"
 "ftxt" "g3" "g4" "group4" "hald" "hdr" "https" "iiq" "inline" "ipl" "j2k"
 "jpe" "jpm" "jps" "jpt" "m2v" "m4v" "mask" "mkv" "mov" "mp4" "mpeg" "mpg"
 "mpo" "msvg" "nrw" "otf" "pango" "pattern" "pcl" "pdfa" "pes" "pfm" "phm"
 "pjpeg" "pocketmod" "psb" "psd" "qoi" "raw" "rgb565" "rgbo" "rgf" "rmf"
 "rsvg" "rw2" "scr" "six" "sixel" "strimg" "tiff64" "tm2" "ttc" "vips" "webm"
 "wmv" "xps" "ycbcr" "ycbcra")

GraphicsMagickにだけあるもの:

(seq-difference gm-formats im-formats)
("8bim" "8bimtext" "8bimwtext" "app1" "app1jpeg" "b" "bigtiff" "c" "exif" "g"
 "icc" "icm" "identity" "image" "iptc" "iptctext" "iptcwtext" "k" "m" "o"
 "p7" "topol" "wmfwin32" "xmp" "y")

うーん、やはりmp4など動画系のファイルはImageMagickじゃないと対応していないみたいですね。GraphicsMagick Supported Formatsを見てもmp4はありません。地味にpsdがImageMagickのみというのは大きいです。

ということで私はImageMagickを使うように戻しました。Windowsだとinit.elに多少ごちゃごちゃ書かないといけませんが、その程度は我慢します。GraphicsMagickに対応する前にImageMagick7(magickコマンド)に対応してほしいと思うのは私だけでしょうか……。

ちなみにコンバーターとしてはffmpegも直接指定出来ます。そちらも軽く調べてみましょう。

FFmpegが対応する形式:

(image-converter--probe 'ffmpeg)
("012v" "4xm" "8bps" "aasc" "agm" "aic" "alias_pix" "amv" "anm" "ansi" "apng"
 "arbc" "argo" "asv1" "asv2" "aura" "aura2" "libdav1d" "libaom" "av1"
 "av1_cuvid" "av1_qsv" "avrn" "avrp" "avs" "avui" "ayuv" "bethsoftvid" "bfi"
 "binkvideo" "bintext" "bitpacked" "bmp" "bmv_video" "brender_pix" "c93"
 "cavs" "cdgraphics" "cdtoons" "cdxl" "cfhd" "cinepak" "clearvideo" "cljr"
 "cllc" "eacmv" "cpia" "cri" "camstudio" "cyuv" "dds" "dfa" "dirac" "dnxhd"
 "dpx" "dsicinvideo" "dvvideo" "dxa" "dxtory" "dxv" "escape124" "escape130"
 "exr" "ffv1" "ffvhuff" "fic" "fits" "flashsv" "flashsv2" "flic" "flv" "fmvc"
 "fraps" "frwu" "g2m" "gdv" "gem" "gif" "h261" "h263" "h263i" "h263p" "h264"
 "h264_qsv" "h264_cuvid" "hap" "hdr" "hevc" "hevc_qsv" "hevc_cuvid"
 "hnm4video" "hq_hqa" "hqx" "huffyuv" "hymt" "idcinvideo" "idf" "iff" "imm4"
 "imm5" "indeo2" "indeo3" "indeo4" "indeo5" "interplayvideo" "ipu" "jpeg2000"
 "libopenjpeg" "jpegls" "jv" "kgv1" "kmvc" "lagarith" "loco" "lscr" "m101"
 "eamad" "magicyuv" "mdec" "media100" "mimic" "mjpeg" "mjpeg_cuvid"
 "mjpeg_qsv" "mjpegb" "mmvideo" "mobiclip" "motionpixels" "mpeg1video"
 "mpeg1_cuvid" "mpeg2video" "mpegvideo" "mpeg2_qsv" "mpeg2_cuvid" "mpeg4"
 "mpeg4_cuvid" "msa1" "mscc" "msmpeg4v1" "msmpeg4v2" "msmpeg4" "msp2" "msrle"
 "mss1" "mss2" "msvideo1" "mszh" "mts2" "mv30" "mvc1" "mvc2" "mvdv" "mvha"
 "mwsc" "mxpeg" "notchlc" "nuv" "paf_video" "pam" "pbm" "pcx" "pfm" "pgm"
 "pgmyuv" "pgx" "phm" "photocd" "pictor" "pixlet" "png" "ppm" "prores"
 "prosumer" "psd" "ptx" "qdraw" "qoi" "qpeg" "qtrle" "r10k" "r210" "rasc"
 "rawvideo" "rl2" "roqvideo" "rpza" "rscc" "rv10" "rv20" "rv30" "rv40" "sanm"
 "scpr" "screenpresso" "sga" "sgi" "sgirle" "sheervideo" "simbiosis_imx"
 "smackvid" "smc" "smvjpeg" "snow" "sp5x" "speedhq" "srgc" "sunrast"
 "librsvg" "svq1" "svq3" "targa" "targa_y216" "tdsc" "eatgq" "eatgv" "theora"
 "thp" "tiertexseqvideo" "tiff" "tmv" "eatqi" "truemotion1" "truemotion2"
 "truemotion2rt" "camtasia" "tscc2" "txd" "ultimotion" "utvideo" "v210"
 "v210x" "v308" "v408" "v410" "vb" "vble" "vbn" "vc1" "vc1_qsv" "vc1_cuvid"
 "vc1image" "vcr1" "xl" "vmdvideo" "vmnc" "vnull" "vp3" "vp4" "vp5" "vp6"
 "vp6a" "vp6f" "vp7" "vp8" "libvpx" "vp8_cuvid" "vp8_qsv" "vp9" "libvpx"
 "vp9_cuvid" "vp9_qsv" "vqc" "wbmp" "wcmv" "webp" "wmv1" "wmv2" "wmv3"
 "wmv3image" "wnv1" "wrapped_avframe" "vqavideo" "xan_wc3" "xan_wc4" "xbin"
 "xbm" "xface" "xpm" "xwd" "y41p" "ylc" "yop" "yuv4" "zerocodec" "zlib"
 "zmbv")

数こそ多いもののやはり動画系が中心のようです。画像コンバーターとしてffmpegだけを指定するのはあまりおすすめ出来なそうです。

全部のコンバーターを切り替えて使えば良さそうにも思えますが、基本的にはimage-converter.elはそのようには出来ていないようです。image-converter-add-handler関数を使って逐一登録すればおそらく可能だとは思いますが。


余談ですが、とうとう長年使っていたCygwinを止めてMSYS2に統一しました。FFmpegやGraphicsMagickもパッケージとして登録されていますし。ノートPCの方ではMSYS2だけにしていて特に問題が無かったので。EmacsもMSYS2のを使えば良いかなと思っていたのですが、先日MSYS2版のEmacsにトラブルがあって以来、ノートPCの方でも公式ビルドを使っています。

image-diredでDiredの中に多様な形式の画像をサムネイル表示する方法についてですが、Emacs起動直後には標準で対応している形式しか表示できず、コンバーターを一回でも使った後は表示できるようになるようです(何か私がおかしな設定をしているのでなければ)。これは、image-diredが表示できる形式かどうかを (image-file-name-regexp) が返す正規表現でチェックしているからです。image-file-name-regexp関数は内部でimage-converter-file-name-extensionsという変数を参照しているのですが、その変数は何かimage-converter.el内の関数を呼ぶまでnilのままだからです。init.elで呼び出して初期化してやろうにも、上記の通り9秒もかかったりするのでおいそれとはできません。まぁ、その辺りがちゃんと初期化していない理由なのかもしれません。手動で必要な拡張子だけinit.elで設定するのが現実的かもしれません。

2023-08-06 ,

org-inline-image-fixのEmacs 29対応

先日も書いたように、Emacs 29に移行したらorg-modeで警告が繰り返し沢山出るようになった。

⛔ Warning (emacs): Redefining ‘file-exists-p’ might break native compilation of trampolines.
⛔ Warning (emacs): Redefining ‘expand-file-name’ might break native compilation of trampolines.

file-exists-pexpand-file-name を再定義? そんなことしてないだろう……と思ったが、ふと思い当たってorg-datauri-image.elorg-http-inline-image.elを無効化したら治まった。

これらはorg-inline-image-fixの中にあるEmacs Lispで、 [[data:[[http:[[https: で始まるリンクをインライン画像表示するためのものだ。それをorg-flyimage.elを使ってfont-lockのタイミングで自動的に即事画像化しているので、警告が繰り返し沢山出るというわけだ。

misohena/org-inline-image-fix: A collection of fixes related to the image display feature in org-mode

それらのEmacs Lispは、cl-letfを使ってインライン画像表示関数(org-display-inline-images)の中にいる間だけそこから呼び出される各種関数の挙動を変更し、無理矢理機能を実現している。その挙動を変更した関数の中にfile-exists-pやexpand-file-nameといったC言語で実装された関数があるため、何らかの理由でnative compilationと相性が悪いのだろう。

この方法はかなり強引だが、結果的にはうまく行った。過去何回かのorg-modeのバージョンアップに伴いorg-display-inline-images関数には度々変更が加えられたが、これらのEmacs Lispは何も変更せずに動作し続けた。もしorg-display-inline-imagesの一部をコピーした新しい関数を作成してそれに置き換えたりしていたら、org-modeのバージョンアップに伴い度々変更を取りこむ必要があったことだろう。もちろんこれはたまたま変更箇所が衝突しなかったということであり運が良かっただけとも言えるのだが、その賭けに私は勝ったわけだ。

しかし今、そのcl-letfを使う方法は封じられた。org-display-inline-images関数は一つの関数の中で多くのことをやり過ぎている。単純なadviceの追加ではどうにもならない。もはやorg-display-inline-images関数をコピーして、バラバラに切り刻み、よりカスタマイズしやすい形に再構成するしか道は無いように思える。

というわけで作成したのがorg-better-inline-images.elだ。これはorg-display-inline-images関数をよりカスタマイズしやすいものに置き換える。

そしてorg-datauri-image.elとorg-http-inline-image.elはそれを使うように書き替えた。

それによってEmacs 29でも警告が出ずにdata、http、httpsのリンクをインライン画像表示できるようになった。その代わり、org-modeのバージョンアップに伴うorg-display-inline-images関数の変化に注視し、必要な変更を取りこむ負担を負うことにもなったわけだ。

めでたしめでたし。

ちなみにorg-ytというパッケージがある。YouTubeリンクを実現するためのものだが、インライン画像表示にも対応している。ytリンクタイプのインライン画像表示は、org-display-inline-images関数に:after adviceを仕込むことで実現している。更新範囲の走査が二回になってしまうのが多少気になるところだ。また、結局はorg-display-inline-imagesの一部をコピーしたorg-image-update-overlayという関数を作成しているので、org-display-inline-imagesの変化に追従していく手間は避けられないだろう。一方で、ytリンクタイプに限らず任意のリンクタイプをサポートするための枠組みを提供しているのは興味深い点だ。org-modeが元々そのような仕組みを提供していたら皆ここまで悩まずに済んだことだろう(ただし、org-ytはdescription部分の画像リンクには対応していないように見える)。