省型旧形国電の残影を求めて

戦前型旧形国電および鉄道と変褪色フィルム写真を中心とした写真補正編集の話題を扱います。他のサイトでは得られない、筆者独自開発の写真補正ツールや補正技法についても情報提供しています。写真補正技法への質問はコメント欄へどうぞ

ImageJ / Python プログラミング Tips: ImageJ Ver. 1.53* → 1.54* へのバージョンアップに伴う仕様変更

 先月 (2023/7) に入って ImageJ / Fiji の自動バージョンアップを適用したところ、自作のプラグインの動作が突然変わってしまい、慌てふためきました。当初はバグではないかと思いましたが、Ver. 1.54 の動作の変化を丁寧に検証してみると、バグというよりは仕様変更なのではないかと思われました。

 そこで、気付いた範囲で、Ver. 1.54 へどのような仕様変更が起こったかを記します。これについてはリリースノートなどに詳しい情報が書かれていません。

 今年の 5 ~ 6 月頃の ImageJ / Fiji の自動バージョンアップで bio-format プラグインが6.12.x にアップデートされ、それに伴い普通の32bit TIFF ファイルが読み込めるようになりました (それまでは ImageJ 専用特殊形式の TIFF のみ)。今回のバージョンアップもその流れの一環で、本格的な 32bit 画像サポートに向けた仕様変更のように思われます。

 昨年末に、拙作プラグインの 32bit 画像サポートに向けた作業を始めましたが、非常に難航しました。その理由は、一つは 32bit 浮動小数点フォーマットが取る値が固定されていなかったり、正負符号を取ったり、所定の範囲を外れるとデータがクリップされるということがないという、32bit データの仕様自体に起因するところもありましたが、それだけではなく、仕様が非一貫的、特にディスプレイ範囲の設定周りの動作が良く分からないという点もありました。

 今回 Ver. 1.54 をいじってみて分かったのは、この訳の分からない動作が減っている (但し全部なくなっているわけではない) という点です。画像計算を行うと、自動的にディスプレイ範囲がシフトすることが多々ありました。もっともディスプレイ範囲の変更が、画像の値の最小値、最大値に合わせて変化するというならまだ理解できるのですが、それと関係なく勝手にシフトすることがあるので訳が分かりません。それが、Ver. 1.54 になって、訳の分からないディスプレイ範囲のシフトが起こることが減りました。

 元々、ImageJ においてディスプレイ範囲が自動的に調整される理由は、ディスプレイ出力が 8bit となっているので、元々 8bit の画像を出力する場合はそのまま出力すれば問題ありませんが、16bit 画像だと、何らかの形で割り付けし直さないと、そのままではディスプレイに出力できません。また、電子顕微鏡から取られた画像では、データがシャドウ部分のみに集中しているというケースもあります。そこでなるべく諧調を活かして表示できるように、画像のハイライト部分とシャドウ部分の一定の割合でクリップして出力させるために、自動的にディスプレイ範囲を調整する仕様が導入されたようです*1。そして、おそらくこの仕様が導入された時点では、32bit 画像は画像処理計算のための中間的な作業フォーマットとして導入され、画像表示フォーマットとしては前提とされていなかったと思われます。

 しかし、画像が 32bit で処理されることが常態化したり、あるいは表示画像フォーマットとして 32bit 画像が使われるようになると、32bit 画像の値の範囲が全く決まっていないことが、計算上都合が良い場合もありますが、ガンマ補正のように不都合な場合もあります。そこで32bit 画像の処理において、一部の計算についてディスプレイ範囲に基づいて計算の仕方を決めるような仕様が導入されたものと思われます。

 従って、1.53以前ではディスプレイ範囲の設定は、8bit, 16bit 画像に関しては、プログラマーが明示的にディスプレイ範囲を指定しない限りは、ディスプレイ範囲の設定は画像計算に影響を与えないという仕様であったようです。これに対し、32bit 画像は計算によっては、かなり影響がありました。実は昨年末から手を焼いていたのはこの32bit 画像の仕様です。

 ただそうなってくると、画像の bit 深度によって、処理の仕方がまちまちになり非常に煩雑になります。おそらく今回の Ver. 1.54 のバージョンアップによって、そのあたりを整理する方向で一部仕様変更が導入されたものと思われます。そして拙プログラムへの影響があったのはこの仕様変更です。

 Ver. 1.54へのバージョンアップによって、16bit 画像に関しては、明示的、非明示的に設定されたかにかかわらず、ディスプレイ範囲の設定がガンマ補正に影響を与えるようになりました (明示的に設定した場合は、Ver. 1.53 でも影響を与えていました)。つまりディスプレイ範囲の最高値、最低値に合わせてガンマがかかるようになりました。これは 32bit 画像の動作に近くなったと言えます。その結果ディスプレイ範囲の最高値でガンマを掛けた後の画像の最高値がクリップされるという現象が起こるようになったのです。ただ、ガンマ補正の原理を考えればディスプレイ範囲を最低 - 最高値としてガンマを掛けるというのは合理的です。なお、16bit 画像においてガンマ補正以外に影響がある他のメソッドもあるかもしれませんが、未確認です。

 というわけで、今回ディスプレイ範囲を、32bit画像以外でも常時明示的に指定するようにして、Ver. 1.54 でも正常に作動するように変更しました。

 なお、8bit 画像に関してはこの仕様は以前と変わっておらず、ディスプレイ範囲の設定が計算結果に影響を与えることはないようです。その意味で必ずしも動作の非一貫性が完全になくなったわけではありません。

 また、画像の加減乗算を行うと、結構細かく、ピクセルの分布の変化に伴って、ディスプレイ範囲が自動的に動いていましたが、それも、かなり大きな値の変更がない限り、あまり細かくディスプレイ範囲が動かないようになったようです。ディスプレイ範囲が計算に影響を与える機会が多くなったことを考えると、これも当然の仕様変更かと思います。

 ともあれ、今後本格的に 32bit 画像が活用されるようになると、このようなディスプレイ範囲周りの良く分からない動作は問題になってくるはずですので、おそらくその整理に向けた第1歩が、今回のバージョンアップによって踏み出されたのではないかと...

 いずれにせよ、今後は 32bit 画像に限らず、どの bit 深度の画像であれ、常時ディスプレイ範囲を意識したプログラミングを行うことが求められるようになりそうだ、という点に注意する必要があります。

*1:以下のページの、28.2.1 以下を参照。ImageJ User Guide - IJ 1.46r | Image Menu (nih.gov)
また、以下の議論も参照。

ImageJ default Display Range calculation - Image Analysis - Image.sc Forum