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

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

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 が ColorProcessor の場合、下記のように、R, G, B 値を分けて読み込む必要があるため、常に配列形式で読み込むということのようです。但し、ColorProcessor でないばあいは、int0 にしか値を読み込みません。

 因みに bio-format プラグインで、ハイパースタック形式でカラー画像データを読み込むと、ImagePlus の中に、R, G, B 別々の ImageProcessor がある形で読み込みますが、その場合、imp.getPixel(x, yで取得した値は、常に最初の R 画像の値となります。

※追記: ImageProcessor に対しても getPixel メソッドが使えますが、これで取得した場合は、リスト変数になりません。なお、ハイパースタック形式のカラー画像データからのピクセル値の取得は、 ImagePlus, ImageProcessor, ImageStackの関係 記事の、「スタック構造のカラー画像へのピクセルアクセス」というセクションをご覧ください。

 また、24bit (8bit x 3) 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 は ColorProcessor の場合、配列で値を書き込むことができます( 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画像)があります。

 

 なお、ImageJのマクロ言語に便利そうな組み込み関数が用意されていますが、Python (Jython)から、マクロ言語の関数を呼び出して使うことはできないようです。マクロ言語の関数を呼び出すライブラリーのようなものがあれば、そういうことも可能なのかもしれませんが、今のところ発見できていません。参考までに、以下のページにマクロ言語の組み込み関数の一覧があります。

imagej.nih.gov なお、別途作成しておいたマクロスクリプト・ファイルをPythonから呼び出して実行することは可能なようです。IJ.runMacro メソッドがあります。例えば以下のサイトをご覧ください。

sites.google.com

memoyo4.blogspot.com

ImageJ API IJ レファレンス

imagej.nih.gov

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

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

yasuo-ssi.hatenablog.com