1
0

More than 1 year has passed since last update.

PythonでOpenEXRファイルの圧縮方法を変換する

Last updated at Posted at 2022-10-11

自己紹介

株式会社digitalbigmoのパイプラインエンジニアの小池@plinecomです。

これは何?

仕事で、何万枚もの大量の非圧縮OpenEXRファイルを貰ってしまった。OpenEXRには可逆圧縮のオプションがあるので、この大量の非圧縮OpenEXRファイルを可逆圧縮なZIPのOpenEXRに変換したい。
Pythonを使って、一括処理する方法を記録する。

とりあえず、コード

main.py
import OpenEXR
import Imath
import glob
import os

if __name__ == '__main__':

    for path in glob.glob('/foo/bar/*.exr'):
        print(path)
        dir_name = os.path.dirname(path)
        base_name = os.path.basename(path)

        ret = OpenEXR.InputFile(path)  # Read EXR file
        header = ret.header()  # Get OpenEXR header info
        print(header)
        # Change Compression Type
        header['compression'] = Imath.Compression(Imath.Compression.ZIP_COMPRESSION)
        print(header)

        pix = {}
        for ch in header['channels']:   # Get channel name from header
            pix[ch] = ret.channel(ch)   # Get channel image data
        ret.close()  # Close Read File handler

        # Open write file and Set header info
        exr = OpenEXR.OutputFile(dir_name + '/' + base_name, header)
        exr.writePixels(pix)    # Write Pixel Data
        exr.close()  # Close Write File handler

必要なPython外部モジュール

  • OpenEXR
terminal
pip install OpenEXR

pipを使ってPyPIからダウンロードしてくる。Imathモジュールも一緒にインストールされる。
ただし、インストール時のビルドの際Python自身のheaderファイルが必要になるので、Linuxではpython-develなパッケージを管理ソフトで突っ込むこと。用意できない環境の場合は、いっそのことPythonからビルドした方が楽。
一応Dockerfileも用意してみた。一部rpmfusionからダウンロードする形で気持ち悪いけど許してほしい。良い方法があれば教えてください。

Dockerfile
FROM rockylinux:8
RUN dnf install -y which python3 python3-devel gcc gcc-c++ epel-release zlib-devel
RUN dnf config-manager --set-enabled powertools
RUN dnf localinstall -y --nogpgcheck https://download1.rpmfusion.org/free/el/rpmfusion-free-release-8.noarch.rpm
RUN dnf install -y OpenEXR-devel
RUN pip3 install OpenEXR
terminal
docker pull plinecom/py_exr_zip

コードの説明

EXRファイルの読み込み

        ret = OpenEXR.InputFile(path)  # Read EXR file

圧縮方法の指定

        header = ret.header()
        # Change Compression Type
        header['compression'] = Imath.Compression(Imath.Compression.ZIP_COMPRESSION)

直接テキストで圧縮方法を書いて代入したら失敗する。Imath.CompressionオブジェクトをImath.Compression.*列挙子で初期化したものを渡さないといけない。今回はZIP圧縮したいので、Imath.Compression.ZIP_COMPRESSIONを渡している。圧縮方法以外は変更しないので、それ以外のヘッダ情報は元のファイルのヘッダ情報を流用する。

ファイルの書き出し

        # Open write file and Set header info
        exr = OpenEXR.OutputFile(dir_name + '/' + base_name, header)
        exr.writePixels(pix)    # Write Pixel Data
        exr.close()  # Close Write File handler

流用したヘッダを元に、書き出し先を指定してファイルを作成し、画素データを書き込んでファイルを閉じる。

OpenEXRの圧縮オプションについての解説

可逆(ロスレス)圧縮オプション

RLE

ランレングス圧縮。単純な圧縮処理だが1パスで終わる上に計算量も少ないので非常に高速。非圧縮にするのと大差ない速度で書き出せる。これだけでも概ね90%のサイズになるので、非圧縮を選ぶ理由がない。ので、非圧縮OpenEXRファイルなんか送ってくるな。

ZIP

みなさんご存知ZIP圧縮。画像を16ライン毎に区切って圧縮してくれる。これを使えば非圧縮の半分くらいのサイズにできる。

ZIPS

ZIPでは16ライン毎に圧縮するが、ZIPSでは1ライン毎にZIP圧縮する。SはスキャンラインのS。複数形のSではない。Nukeなどのコンポジットソフトでは処理が速くなるという噂を聞いたが、体感できるほどの差を感じたことはない。ZIPよりは圧縮効率が落ちる。

PIZ

ウエーブレット変換というフーリエ変換の上位互換的なものを使って、周波数領域に画像データを持っていって圧縮する。最高の圧縮率が出るものの、計算量も非常に大きい。ZIP圧縮よりさらに数%くらい小さくなる割には計算量の犠牲が大きすぎると思う。

可逆圧縮になったり非可逆圧縮になったりするもの

PXR24

24bitに量子化を下げて圧縮する。24bit以下の16bit半精度浮動小数点データや16bit整数データならロスレスな可逆圧縮だが、32bit単精度浮動小数点データ等ではロッシーな非可逆圧縮となる。この点、取扱注意なので、よくわからないときは選ばない方がいい。

非可逆圧縮オプション

他に非可逆圧縮オプションとして以下のものがある。

  • B44
  • B44A
  • DWAA
  • DWAB

ただ、高品位を狙ってOpenEXR利用することがほとんどなのと、納品先で解凍結果が異なって事故る可能性のあるロッシーな非可逆圧縮は、あんまり使わないかなと思う。自社でマスターまで出す大規模スタジオならストレージ効率の面から選択肢に入るかもしれない。

宣伝

株式会社digitalbigmoでは美肌プラグインの販売や映像VFXの制作業務を行なっています。ご興味のある方は、webページを見にきてください。一緒にお仕事しましょう。

参考文献

PythonのOpenEXRモジュールについて(英語)
OpenEXR本家(英語)
WikipediaのOpenEXRのページ(英語)
WikipediaのOpenEXRのページ(日本語)

1
0
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
1
0