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

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

ImageJ / Pyhon Tips 画像ピクセルの輝度値の取得 / 設定・書換え

[2021.12追記:  誤りがありましたので一部記述を訂正しました]

 ImageJでPython (Jython) を使って画像の個々のピクセルにアクセスし、そこに記されている輝度(明るさ)の値を取得したり、設定(書換え)する方法についてのメモを記しておきます。

ピクセルの輝度値の取得

これに必要なライブラリの呼び出しは次の通りです。

from ij import IJ, ImagePlus

そして、具体的には次のようなコードで取得します。

a = imp.getPixel(x, y)

a: 取得した値を格納する任意の名前の変数
imp: 値を読む画像 (任意の名前の imagePlus オブジェクト)
x: 値を読みだすピクセルの x 座標値(整数)
y: 値を読みだすピクセルの y 座標値(整数)

getPixel( , ): ピクセル値を読みだすimagePlus メソッド

緑色の字は任意に名前を変えられる部分です。

 ところで、bio-formatプラグインで読み込んだ画像ファイルをRGB分解に掛け、分解した単独のチャンネル画像からこれでピクセル値を取得したところ、てっきり単独の値が取得できるものと思っていましたら、実は取得した値が配列(リスト)になっていることが分かりました。どのような値かというと、

a[int0, int1, int2, int3]

という構造になるようです。これはimageProcessor (通常32bit) の値を8bit x 4 に分割して取得するようです。つまり、一番下の8bit (2進数8桁) の値を 配列の 1 番目に(index番号は0)、次の8bitを 2番目(同 1)に、下から3番目の8bitを 3 番目(同 2)に、そして最も上位の8bitを4番目(同 3)に取得します。ですので、8bit画像の輝度だけ読みたい場合は、取得した後、 a[0] というような形で扱わないとプログラムエラーになってしまいます(輝度値の場合は一番下の8bitしか使いませんので)。また、a[1]~[3]は0になります。

 また、RGBカラーデータ (ColorProcessor) の場合は、つまり、RGBのそれぞれが別々のスタックに読み込まれているのではない場合は、一番下の8bitがR値、次の8bitがG値、3番目の8bitがB値になりますので、Rがa[0]、Gがa[1]、Bがa[2]に取得されます。そして最上位の8bitは使われませんので、a[3]は0になります。

ピクセルの輝度値の設定

 getPixel メソッドでピクセルの値が読み出せたので、てっきりsetPixelというようなメソッドが imagePlusオブジェクトにあるのではないかと探しましたが見当たりません(setPixels はありましたが用途が違います)。どうも、ImageJのクラスやメソッドの体系がまだよくわかっていませんので、やみくもに検索をかけて探さなければなりません。そのためやりたいことを実現するためのメソッドやクラスを探すのに時間がかかっています。

 結局、ピクセルを書き換えるには imagePlusオブジェクトのメソッドだけではだめで、imageProcessor を使わないといけないようです。で、必要なライブラリの呼び出しは、上に加えて...

from ij.process import ImageProcessor

具体的な作業コードは、

imp.getProcessor().set(x, y, int)

getProcessor():
 imageProcessorの呼び出し。単純に getProcessor()と書くと、impの中の現在アクティブなimageProcessorオブジェクト(= 画像データ自体。imagePlus オブジェクトがスタック構造であれば 一つの imagePlus オブジェクトに複数の imageProcessor が存在しうる)に対し、操作を行う。stack.getProcessor( i )と書くと、impの中の i 番目のスタックの imageProcessor を呼び出す。なおimagePlusオブジェクトからstackへアクセスする方法は、こちらを参照。imp.getProcessor( i ) だと無効。

set(x, y, int):
 x, yで指定された数値(整数)の座標のピクセルに対し、輝度値を書き込むimageProcessorのメソッド。intには具体的な書き込みたい整数の輝度値が入る。8bit画像であれば 0~255までの値、16bit画像であれば 0~65535の値を入れる。0が最暗値 255もしくは65535が最明値になる。上の例の場合、getProcessor(i) の i にスタックのスライス番号を指定しその番号の imageProcessor 対して、輝度値を設定する。

 

になります。

なお、この set メソッドは putPixel と書き換えても同じです。

imp.getProcessor(  ).putPixel(x, y, int)

ただし putPixel は set よりも高機能です。例えば set だと輝度値がクリップするかどうかチェックしませんが、 putPixel はチェックして値がクリップしないよう自動修正します。その分 set の方が実行が高速です。また、getPixel で値を取得すると配列になると記しましたが、putPixel はRGBカラーのような場合、配列で値を書き込むことができます( putPixel(x, y, [int0, int1, int2, int3]) )。set は単一の輝度値しか書き込めません。

 さらに、32bit浮動小数点で画像を扱う場合は、輝度値の書き込みに、putPixelValue メソッドを使うようです。

f:id:yasuo_ssi:20210316170627j:plain

ImagePlusとImageProcessorの関係

 なお、imagePlusオブジェクトにせよimageProcessorにせよ、任意の名前を付けることができますが、それではそのオブジェクトの種類(クラス)が何なのか分かりません。imagePlusなら imp何とか あるいは 何とかimp、imageProcessor なら ip何とか あるいは 何とかip という風に名前を付けるのが慣例です。サンプルプログラムの類はそうなっています。こうすることで名前から imagePlusなのかimageProcessorなのか判断できるので、プログラムの可読性が上がります。

 なおImageProcessorの変種として、ByteProcessor (8bit画像に限定されたImageProcessor), ShortProcessor(同16bit画像), FloatProcessor(同32bit 浮動小数点画像), ColorProcessor(RGB 24bit画像)があります。

-----------------------------

この記事には以下に補足記事があります(2021.12)。

yasuo-ssi.hatenablog.com