バイオ系だけどプログラミング始めました

ImageJ (Fiji)の使い方や Python でのプログラミングなどを、主にバイオ系の研究者・大学院生向けに書いていこうと思います。

10分でできる ImageJ 自動化

目次

この記事について

この記事では、細かい仕組みなどは脇に置いといて ImageJ での解析のついて記述します。主に ImageJ のマクロと PythonJython)で書いていきます。解析の自動化の便利さを知ってプログラミングに足を踏み入れるきっかけになれば幸いです。

解析の例として、いつもと同じで芸がないですが米粒のカウントを例にして説明します。

ImageJ で米粒(細胞)を数えて解析しよう

米粒の画像は EMBL > Samples > rice.tiff で開けます。

f:id:shatoshi:20170808221904j:plain

マクロの記録と実行

マクロとは、『パソコンで、複雑な操作の手順をあらかじめ登録しておき、必要なときに簡単に実行させる機能。』デジタル大辞泉 です。ImageJ にもマクロの記録と実行機能があり、これを用いることで ImageJ での作業を自動化できます。

Plugins > Macros > Record… でマクロの記録画面が表示されます。

f:id:shatoshi:20170808222023j:plain

この状態で ImageJ 上で動作をすると、その動作を行うための命令が記録されます。Record は BeanShell にしておきましょう。 まずはバックグラウンドの減算をしましょう。 Process > Subtract Background で開き、Rolling ball radius を 15 pixels にしてOKを押して実行します。

f:id:shatoshi:20170808222200j:plain

すると、Recorder に、

IJ.run(imp, "Subtract Background...", "rolling=15");

と記述されているはずです。

f:id:shatoshi:20170808222039j:plain

これが、アクティブなウィンドウの画像に対し Subtract Background を Rolling ball radius = 15 で行うための命令になります。

この状態で Create を押してみてください。すると TextWindow が開きましたね。

f:id:shatoshi:20170808222106j:plain

次に、バックグラウンドの減算をする前の rice.tif の画像を開いた状態で、TextWindow の左下の Run ボタンを押しましょう。すると、バックグラウンドの減算が実行されます。

このマクロを保存しておけば、同じ作業をするときにいちいちボタンを押して実行する必要がなくなります。バックグラウンドの減算をするだけだとそこまで便利には感じないかもしれませんが、行う手順が多ければ多いほどマクロでの自動化は役に立ちます。「ImageJ で米粒(細胞)を数えて解析しよう」の記事で行っていることをマクロで記録してみましょう。

では Recorder の内容を一旦削除しましょう。そして、以下の内容を実行してください。

  • バックグラウンド減算: Process > Subtruct Background で開き、Rolling ball radius を 15 pixels で実行。

f:id:shatoshi:20170808222200j:plain

  • 閾値の設定: Image > Adjust > Threshold で開き、 Method を Default にしてDark background にチェックを入れて、 Auto ボタンを押す。

f:id:shatoshi:20170808222249j:plain

  • 解析項目の設定: Analyze > Set Measuremnet で開き、Area、Mean gray value、Centroid、Fit ellipse にチェックを入れて OK ボタンを押す。

f:id:shatoshi:20170808222313j:plain

  • 粒子の解析: Analyze > Analyze Particles で開いて、size を 5-Infinity に設定して、Dsiplay results、Clear Results、Exclude on edges、にチェックを入れて OK ボタンを押す。

f:id:shatoshi:20170808222338j:plain

  • 結果の保存:先ほどの作業で Results と言う名前のウィンドウが開かれたと思います。Results の File > Save を選んで、デスクトップなどの適当な場所に拡張子を xls から csv に書き換えて保存する。

f:id:shatoshi:20170808222358j:plain

これで、先ほどと同様に Create を押してスクリプトを作製すると、開いた画像に対して解析を行い結果を保存するマクロが完成です。パチパチ~ :clap: これで一々ポチポチとボタンを押して同じ作業を繰り返すことから解放されました。ただ、もう少し便利にしたくありませんか?

フォルダの中の画像に対して同じ処理をする

さて、マクロを記録することで作業をある程度自動化できました、ここまでくるともうちょっと自動化して、あるフォルダに入っているすべての画像ファイルに対して同じ解析をするってことをしたくありませんか?

そこで先ほどのマクロを Jython を用いてもうちょっと自動化させてやりましょう。Jythonプログラミング言語 Python の、 JAVA 仮想マシン上で動くものになります。JAVA で書かれている ImageJ と相性が良く、Python 自体が初心者にとって分かりやすく、文字列やファイル操作もやりやすいので、マクロに少し書き加えて便利にするのに適しています。ImageJとJythonによるプログラミング・画像処理の詳しい説明にについてはこちらの記事を参考にしてください。ImageJ Fiji + Python で画像解析プログラムを書こう(前編)

  • File > New > TextWindow でTextWindow を開いて、Language > Python にチェックをいれましょう。そして、以下の文をコピペします。
from ij import IJ
from ij.io import DirectoryChooser
import os


def Analysis(imagepath):
    imp = IJ.openImage(imagepath)
    savefilepath = os.path.splitext(imagepath)[0]

#####################################################
# Write your script

#####################################################

    imp.close()


srcDir = DirectoryChooser("Choose Folder").getDirectory()
IJ.log("directory: " + srcDir)

for root, directories, filenames in os.walk(srcDir):
    for filename in filenames:
        if filename.endswith(".tif"):
            path = os.path.join(root, filename)
            IJ.log(path)
            Analysis(path)

IJ.log("Finish")
  • 先ほどの Record の内容を、
#####################################################
# Write your script

#####################################################

の部分にコピペします。マクロは上から順に実行されます。順番には気を付けましょう。

  • 次に、コピペした部分のセミコロン「;」を削除しましょう。(この作業はマクロを動かす上では必要ないですが、色々な理由で紛らわしいため、消しておきましょう。)

  • コピペした IJ.~ の文の前にスペースを4つ入れて、シャープ「#」で囲った前後の imp~ で始まる文と同じだけインデント(字下げ)しましょう。

  • 最後に、

    IJ.saveAs("Results", "C:\\Users\\shatoshi\\Desktop\\Results.csv")

の保存するファイルパスの部分を、

    IJ.saveAs("Results", savefilepath + "result.csv")

に書き換えましょう。これで、解析結果が「画像のファイル名 + result.csv」で保存されるようになりました。

以上でマクロは完成です。以下のようになっているはずです。

from ij import IJ
from ij.io import DirectoryChooser
import os


def Analysis(imagepath):
    imp = IJ.openImage(imagepath)
    savefilepath = os.path.splitext(imagepath)[0]

#####################################################
# Write your script

    IJ.run(imp, "Subtract Background...", "rolling=15")
    IJ.setAutoThreshold(imp, "Default dark")
    IJ.run("Set Measurements...", "area mean centroid fit limit redirect=None decimal=3")
    IJ.run(imp, "Analyze Particles...", "size=5-Infinity display exclude clear")
    IJ.saveAs("Results", savefilepath + "result.csv")

#####################################################

    imp.close()


srcDir = DirectoryChooser("Choose Folder").getDirectory()
IJ.log("directory: " + srcDir)

for root, directories, filenames in os.walk(srcDir):
    for filename in filenames:
        if filename.endswith(".tif"):
            path = os.path.join(root, filename)
            IJ.log(path)
            Analysis(path)

IJ.log("Finish")

では実際に動かしてみましょう。その前に、適当な場所にフォルダを作り、そのフォルダにお米画像 rice.tif を適当に名前を変えて複数枚保存してください。

次に、先ほどの TextWindow を開いて Run ボタンを押しましょう。するとフォルダを選ぶためのダイアログが出てくるので、先ほどの米画像を保存したフォルダを選んで select を押しましょう。これでマクロが選ばれたフォルダのすべてのtif画像に対して実行されました。この画像をtif以外にしたいときには28行目の

        if filename.endswith(".tif"):

この文の".tif"の部分を適当な拡張子に変えましょう。

自動化の方法については以上になります。これで明日から退屈な単純作業から解放されます。どうですか?すごく便利だと思いませんか?

10分でできると書きましたが、どうでしょうか?ちょっと盛り過ぎたかもしれません。

ただ、この方法でちゃんと想定している結果が得られるのかどうか?は、数回マニュアルで同じことをやってみて出力結果と見比べたほうが安全だと思います。 それと、この方法では簡単な解析しかできないですし、もしも想定していない結果が出た場合に何が原因なのか判断できないです。なのでできれば ImageJ Fiji + Python で画像解析プログラムを書こう(前編) を参考にして基礎的な部分を知っていただきたいです。

簡単に解説

  • デフォルトでは必要最低限の機能しかないので、必要な機能をインポートします。
from ij import IJ
from ij.io import DirectoryChooser
import os
  • 「IJ.openImage(imagepath) で imagepath の画像オブジェクトを開いて、画像処理・解析を行い、画像を閉じる」、この一連の作業を Analysis() という関数で定義します。マクロで実行している内容についてはよくよく読むと何を実行しているのか分かるように書かれています。確認してみましょう。
def Analysis(imagepath):
    imp = IJ.openImage(imagepath)
    savefilepath = os.path.splitext(imagepath)[0]

#####################################################
# Write your script

    IJ.run(imp, "Subtract Background...", "rolling=15")
    IJ.setAutoThreshold(imp, "Default dark")
    IJ.run("Set Measurements...", "area mean centroid fit limit redirect=None decimal=3")
    IJ.run(imp, "Analyze Particles...", "size=5-Infinity display exclude clear")
    IJ.saveAs("Results", savefilepath + "result.csv")

#####################################################

    imp.close()
  • DirectoryChooser でダイアログを開きユーザーにフォルダを選ばせ、getDirectory で選んだフォルダのパスを取得する。os.walk(srcDir) は srcDir 以下のフォルダを走査して、 (dirpath, dirnames, filenames) のタプル(書き換えできないリストのようなもの)を返します。os.walk そして、「.tif」で終わっている名前のファイルがあれば、先に定義した Analysis() を実行します。
srcDir = DirectoryChooser("Choose Folder").getDirectory()
IJ.log("directory: " + srcDir)

for root, directories, filenames in os.walk(srcDir):
    for filename in filenames:
        if filename.endswith(".tif"):
            path = os.path.join(root, filename)
            IJ.log(path)
            Analysis(path)

IJ.log("Finish")