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

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

ランダムな砂嵐画像を作る ImageJ + Python のプログラム

目次

ランダムな砂嵐画像を作るプログラムのコード

ImageJ + Python のプログラミングの例として書いてみました。 解説はまた今度書きます。しかし、そこまで長くないので、分からないモジュールや機能を調べてみてどのように動いているのか理解してみたり、いろんな数字を変化させて遊んでみるといいと思います。

6/27にコードの解説を追記しました。 知識的には、ImageJ Fiji + Python で画像解析プログラムを書こう(後編) - バイオ系だけどプログラミング始めましたまでを読んでいる前提で書いています。

以下コード*1

import random
from ij import ImagePlus
from ij.process import ByteProcessor


def IntByteConverter(inputnumber):
    if inputnumber <= 127:
        return inputnumber

    else:
        return -(256 - inputnumber)


width = 100
height = 100
image_values = []
for i in range(width):
    for j in range(height):
        image_values += [IntByteConverter(random.randrange(0, 256, 1))]

bp = ByteProcessor(width, height, image_values)
imp = ImagePlus("Random", bp)

imp.show()

コードの解説

random ライブラリ

まず、random というライブラリをインポートします。https://docs.python.jp/2.7/library/random.htmlこのライブラリは、ランダムな値(疑似乱数)を生成するためのライブラリです。*2

random には様々な関数が用意されています。ある範囲の整数からランダムに値を生成する関数、リストからランダムに要素を選ぶ関数、様々な分布(ガウス分布、ガンマ分布、etc)の実数を生成する関数・・・。こういったランダムな値を生成する関数は、例えばシミュレーションに使ったり、解析プログラムのテスト用のデータにコントロール可能なノイズを乗せてテストしたり、一定確率でふぶきを外したり、・・・と様々な用途に使えます。

今回は0~255までのランダムな値を生成するために、randrange()を使います。この関数は、range(0,256,1)で精製できるリスト(0~255の範囲の1刻みのリスト)から値をランダムに選ぶ関数になります。

ByteProcessor

ImagePlus については ImageJ Fiji + Python で画像解析プログラムを書こう(後編) - バイオ系だけどプログラミング始めましたで書いているので省略します。画像のクラスです。

ByteProcessor(ByteProcessor (ImageJ API))は、 ImageProcessor という特にピクセルの値そのものを扱うためのクラスがあり、それを拡張するクラスで、 8-bit の画像をのピクセル値を扱うためのクラスになります。javadoc にも、「This is an 8-bit image and methods that operate on that image.」と書いてあります。このクラスを使うことで、指定のピクセルの値をいじったり、指定の値の画像を生成することができます。例えば、

from ij import ImagePlus
from ij.process import ByteProcessor

width = 2
height = 2

bp = ByteProcessor(width, height, [0, 0, 0, 0])
imp = ImagePlus("zero", bp)

imp.show()

とすることで、2x2 の大きさでピクセルの値がゼロの画像を作り出すことができます。 詳しくは、IJ Javadoc で ImagePlus と ByteProcessor の Constructor を読んでもらえばいいですが例示したコードでは、

  1. ImagePlus を作るのに画像の「名前」とImageProcessor を引数にとる方法を使って ImagePlus のオブジェクトを作っています。

  2. ImagePlus に渡すための ImageProcessor はByteProcessor(width, height, [0,0,0,0]) で横・縦・ピクセルの値を指定して生成しています。このとき、縦x横=リストの長さである必要があります。

IntByteConverter

(一応書いたけど読み飛ばしてかまいません。ただしこれは8-bit画像ののピクセル値を指定するときには必須かもしれない?) 注釈にも書いたんだけど、ByteProcessor に int を入れると 127 以上の値でエラーがでます。-1とすると255の値になりました。おそらく、符号付の整数か符号なしの整数かっていうところが変になってるっぽかった。Python は整数などの数値の型を自動で判断して良しなにしてくれるのだけど、それが裏目に出ているようです。128 以上の値の時、-(256 - 値) とすれば解決するようなので、これをかませます。

コードの流れ
  1. 必要なライブラリをインポートして、

  2. 上記の IntByteConveerter を定義。条件分岐と単純な引き算をするだけ。

  3. 画像の横幅と高さを width と height という名前の変数で設定する。この様に書いておくと、プログラムがどういう動作をしているのかが後で読んで分かりやすくなるし、プログラムの色んな所で横幅と高さの値を使って計算するときには、この代入するパラメータを変えるだけで済みます。

  4. for loop を回して、横と縦についてピクセルの値を入れるためのリストにランダムな値を入れていく。 また、この様に書くと i と j がピクセルの座標を表すことになるので、それに基づいた計算もできます。試してみましょう。

  5. bp という名前で、横・縦・値を引数にして ByteProcessor オブジェクトを生成し、それを使って ImagePlus オブジェクトを生成します。そして、imp.show() で画像を表示。

てな具合です。

実行例

f:id:shatoshi:20170626232243j:plain

追記 6/27

とのことでした。自分でも確かめてみましたが確かに、append()の方が速く動きますね。

というわけで、修正した ver を以下に書きます。

import random
from ij import ImagePlus
from ij.process import ByteProcessor


def IntByteConverter(inputnumber):
    if inputnumber <= 127:
        return inputnumber

    else:
        return -(256 - inputnumber)


width = 100
height = 100
image_values = []
for i in range(width):
    for j in range(height):
        image_values.append(IntByteConverter(random.randrange(0, 256, 1)))

bp = ByteProcessor(width, height, image_values)
imp = ImagePlus("Random", bp)
imp.show()

動画ver

あと、ついでに動画 ver も貼っておきます。

さあ!君もオリジナルの砂嵐動画を作ろう! ピクセル数を増やして無駄に高画質にすると砂嵐感が上がります。

import random
from ij import ImagePlus, ImageStack
from ij.process import ByteProcessor


def IntByteConverter(inputnumber):
    if inputnumber <= 127:
        return inputnumber

    else:
        return -(256 - inputnumber)


def randImageBP(width, height):
    image_values = []
    for i in range(width):
        for j in range(height):
            image_values.append(IntByteConverter(random.randrange(0, 256, 1)))

    bp = ByteProcessor(width, height, image_values)

    return bp


width = 100
height = 100
frame = 30
stack = ImageStack(width, height)
for i in range(frame):
    stack.addSlice(randImageBP(width, height))

imp = ImagePlus("randmovie", stack)
imp.show()
実行例

コード実行後に save as > gif にしてアップロードしてみました。

動画ver 解説

ImageJ の動画や、異なる焦点面で撮った顕微鏡画像の集まりといった画像の集合は Stack と呼ばれます。そして、Stack の一枚一枚の画像を Slice と言います。この動画verでは上記の一枚の砂嵐画像を作る部分を関数にしています。そして、stack に一枚一枚の Slice をstack.addSlice() で追加して動画を作っています。最後にImagePlusをStackを引数にして作って show() で表示させています。

*1:ByteProcessor に int を入れると 127 以上の値でエラーがでた。-1とすると255の値になった。おそらく、符号付の整数か符号なしの整数かっていうところが変になってるっぽかった。それを解決するためにIntByteConverterという関数を作ったけど、他に解決策はあったのだろうか?あれば誰か教えてほしい。

*2:疑似乱数については以下を参考に。 擬似乱数 - Wikipedia Pythonでは、メルセンヌ・ツイスタと呼ばれる比較的良い乱数を生み出すアルゴリズムが使われています。 メルセンヌ・ツイスタ - Wikipedia