all-in-one-seo-pack
domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init
action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php on line 6114easy-fancybox
domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init
action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php on line 6114urvanov-syntax-highlighter
domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init
action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php on line 6114breadcrumb-navxt
domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init
action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php on line 6114advanced-ads
domain was triggered too early. This is usually an indicator for some code in the plugin or theme running too early. Translations should be loaded at the init
action or later. Please see Debugging in WordPress for more information. (This message was added in version 6.7.0.) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php on line 6114lancr
ドメインの翻訳の読み込みが早すぎました。これは通常、プラグインまたはテーマの一部のコードが早すぎるタイミングで実行されていることを示しています。翻訳は init
アクション以降で読み込む必要があります。 詳しくは WordPress のデバッグをご覧ください。 (このメッセージはバージョン 6.7.0 で追加されました) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php on line 6114私たち診療放射線技師がプログラムを使って解析を行う時まず初めに画像を読み込むことではないでしょうか。
今まで、私が書いてきたコードは画像読み込みのコードはクラスを使わずに行ってきました。しかし、数回前にクラスの記事を書きましたので、今回はクラスを用いて画像を読み込むコードを書いていきたいと思います。
まずは、コードの流れを考えてみましょう。
の作業となり、そこで決めなくてはいけないことは以下となります。
まずはファイルの選択方法です。個別で選択するか、フォルダで選択するか。今回はフォルダで選択する方法をやってみたいと思います。
フォルダで一括の関連記事は以下にありますので興味がある方はご覧ください。
なお、今回も「画像選択のプログラムを使いやすく!!」で作成したモジュールを用います。
次にクラスに持たせる情報です。
を持たせたいと思います。全て予約タグとなっていますのでほぼすべてのモダリティー画像であるタグです。
ピクセル数のデータは(0028,0010)、(0028,0011)
WW、WLは(0028,1050)、(0028,1051)
に登録されています。
なお、コード中に記載する際は16進数である事を示す”0x”を追加して記載することを忘れずに。
ピクセルデータを画像配列に取り込む作業ですが、今回はフォルダで一括取り込みで行います。以前の記事で記載しましたがフォルダで一括選択の場合は画像番号を文字列とし扱われてしまうので順番に取り込んでいくと画像が途中で飛んでしまいます。
詳しくはこちらの記事でご確認ください(フォルダでファイルを一括選択した時の問題点)
それでは、コードを書いていきたいと思います。
まずは、モジュールのインポートです。ファイル選択のモジュールをインポートします。
# -- coding utf-8 -- import import fileselect as fs
このモジュールは過去の記事で記載していますので以下のリンクよりご確認ください。
インポートしたモジュール内でフォルダでファイル一括選択の関数はfolder_fileselect()で作成してありますので、folder_fileselect()を呼び出しfilenamesの変数で受けとります。(下のコードの5行目になります。)
# -- coding utf-8 -- import fileselect as fs filenames = fs.folder_fileselect()
これで、第1工程のファイルを選択が完了しました。
クラスで画像配列を取得していく工程ですが以下の順に進めていきます。
まずは、クラスの宣言となりますが、クラスの宣言は関数を書く時と同じで初めを
class
で始めます。その後に名前が続くのですが名前の1文字目を大文字で書くのが通例となっています。
今回の目的は画像の配列を取得することなので名前はPixarrとすることにします。
class Pixarr:
関数の時と違うのはクラスの宣言では引数を受けないという事です。(その後の初期化や、クラス内の関数で引数を受けます。)
ちなみに、クラスの宣言はモジュールや、ライブラリーのインポートの後、コードの初めに書きます。
初期設定です。初期設定も関数の時と同じですが一つ違うところが関数名は__init__(アンダーバーは前と後ろ2つづつ)で、引数でselfという物が必須という事です。なので
def __init__(self):
という事になります。
当初私は、ここでつまづきました。。。。。selfの意味が分からない。何故こんなことをしなければならないのか。同じように疑問に思われる方は以下の記事に私なりの解釈を記載していますので一読いただければと思います。
話が脱線しました。初期化の話に戻ります。
# -- coding utf-8 -- import fileselect as fs class Pixarr: def __init__(self): filenames = fs.folder_fileselect()
初期化の部分では、それぞれに持たせる情報を設定するところですので持たせたい情報は縦、横のピクセル数、画像番号、WW、WLの5つとなりますので
それぞれ、row、column、ww、wlという変数にしたいと思います。
それぞれのデータは[0028,0010]、[0028,0011]、[0028,1050]、[0028,1051]のタグに登録されています。
ではどの様にしてそのデータを取得するかですが、今回はクラスを呼び出した時(インスタンスを生成するといいます。)にファイルパスを渡し、クラス初期化の時に1枚画像を読み込んでDICOMタグを取得したいと思います。
それでは、コードに足していきたいと思います。
DICOM画像を扱うのでまず、pydicomをインポートします。
そして、引数として受け取ったファイルパスから一番初めの画像を読み込みます。(下記コード8行目)そしてタグを順番に取得します。(下記コード10~14行目)
そして、忘れてはいけないインスタンス生成のコードを足します。(下記コード17行目)
# -- coding utf-8 -- import fileselect as fs import pydicom class Pixarr: def __init__(self,filenames): dcm = pydicom.dcmread(filenames[0]) self.row = dcm[0x0028,0x0010].value self.column = dcm[0x0028,0x0011].value self.ww = dcm[0x0028,0x1050].value self.wl = dcm[0x0028,0x1050].value filenames = fs.folder_fileselect() f0 = Pixarr(filenames)
ここまでで、持たせる情報は設定は完了しました。
続いて、ピクセルデータをを配列に取り込んでいく作業に入りたいと思います。
まずは、ピクセルデータを取り込むための0で初期化した配列を生成したいと思います。変数名はpix_arrとします。配列生成はnumpyを使用しますのでライブラリーのインポートをします。(下記コード5行目)
次にピクセルデータを配列に入れていく工程に入るのですが、問題とな事が2つあります。フォルダ内に画像が1枚だけの場合for文で画像取り込みを行うとエラーが出てしまう件。フォルダ内画像が10枚以上になると画像番号を文字列に捉えられてしまい順番に画像が取り込まれない件があります。
まず、画像が一枚の時の対策として、if文で処理を変えていきたいと思います。まずlen関数を用いてfilenamesの数をカウントし、処理を変えることで対策したいと思います。(下記コード19~20)
その次に、画像番号順にピクセルデータを配列に取り込んでいく方法です。
対策としては、画像を込みこんで変数img_noに画像番号を取り込み、画像番号-1の配列にピクセルデータを入れていく感じになります。(下記コード22~25行目)
# -- coding utf-8 -- import fileselect as fs import pydicom import numpy as np class Pixarr: def __init__(self,filenames): dcm = pydicom.dcmread(filenames[0]) self.row = dcm[0x0028,0x0010].value self.column = dcm[0x0028,0x0011].value self.ww = dcm[0x0028,0x1050].value self.wl = dcm[0x0028,0x1050].value self.pix_arr = np.zeros((len(filenames),self.row,self.column), dtype ='int16') if len(filenames) == 1: self.pix_arr = dcm.pixel_array else: for i in range(len(filenames)): dcm = pydicom.dcmread(filenames[i]) img_no = dcm[0x0020,0x0013].value self.pix_arr[img_no-1] = dcm.pixel_array filenames = fs.folder_fileselect() f0 = Pixarr(filenames)
これで、コードは完了です。
さて、きちんとクラスを用いて配列内にピクセルデータが取り込まれたか確認作業に入りたいと思います。
まずは、ピクセル数の確認です。コード30行目にprint文で表示してみましょう。
# -- coding utf-8 -- import fileselect as fs import pydicom import numpy as np class Pixarr: def __init__(self,filenames): dcm = pydicom.dcmread(filenames[0]) self.row = dcm[0x0028,0x0010].value self.column = dcm[0x0028,0x0011].value self.ww = dcm[0x0028,0x1050].value self.wl = dcm[0x0028,0x1050].value self.pix_arr = np.zeros((len(filenames),self.row,self.column), dtype ='int16') if len(filenames) == 1: self.pix_arr = dcm.pixel_array else: for i in range(len(filenames)): dcm = pydicom.dcmread(filenames[i]) img_no = dcm[0x0020,0x0013].value self.pix_arr[img_no-1] = dcm.pixel_array filenames = fs.folder_fileselect() f0 = Pixarr(filenames) print('画像の縦方向のピクセル数は\t' + str(f0.row)) print('画像の横方向のピクセル数は\t' + str(f0.column)) print('画像のウインドウレベルは\t' + str(f0.wl)) print('画像のウインドウ幅は\t' + str(f0.ww))
いかがでしたか?情報はきちんと表示されましたか?
続いてmatplotlibを用いて画像表示をしてみたいと思います。
まずは、matplotlibのライブラリーをインポートします。(下記コード6行目)
表示する画像番号の変数を指定します。slとして初期値0を指定します。(下記コード36行目)
画像を表示するウインドウ、figの設定をして(下記コード38行目)画像を表示する領域の設定をします。(下記コード39行目)
表示する画像の指定をします。(下記コード41行目)
画像表示のコードを42行目に記載します。
# -- coding utf-8 -- import fileselect as fs import pydicom import numpy as np import matplotlib.pyplot as plt class Pixarr: def __init__(self,filenames): dcm = pydicom.dcmread(filenames[0]) self.row = dcm[0x0028,0x0010].value self.column = dcm[0x0028,0x0011].value self.ww = dcm[0x0028,0x1050].value self.wl = dcm[0x0028,0x1050].value self.pix_arr = np.zeros((len(filenames),self.row,self.column), dtype ='int16') if len(filenames) == 1: self.pix_arr = dcm.pixel_array else: for i in range(len(filenames)): dcm = pydicom.dcmread(filenames[i]) img_no = dcm[0x0020,0x0013].value self.pix_arr[img_no-1] = dcm.pixel_array filenames = fs.folder_fileselect() f0 = Pixarr(filenames) print('画像の縦方向のピクセル数は\t' + str(f0.row)) print('画像の横方向のピクセル数は\t' + str(f0.column)) print('画像のウインドウレベルは\t' + str(f0.wl)) print('画像のウインドウ幅は\t' + str(f0.ww)) sl =0 fig = plt.figure() ax = fig.add_subplot() ax.imshow(f0.pix_arr[sl],cmap= 'bone') plt.show()
どうですか?画像移動のコードを記載していないので1枚目の画像が表示されましたか?
最後に、トラックボールで画像を変えるコードを書きます。
ここに関しては、今回は長くなっているので詳細を省きたいと思います。(下記コード28~43行目)
とマウスイベントに繋げるコード59行目を記載して完了です。
# -- coding utf-8 -- import fileselect as fs import pydicom import numpy as np import matplotlib.pyplot as plt class Pixarr: def __init__(self,filenames): dcm = pydicom.dcmread(filenames[0]) self.row = dcm[0x0028,0x0010].value self.column = dcm[0x0028,0x0011].value self.ww = dcm[0x0028,0x1050].value self.wl = dcm[0x0028,0x1050].value self.pix_arr = np.zeros((len(filenames),self.row,self.column), dtype ='int16') if len(filenames) == 1: self.pix_arr = dcm.pixel_array else: for i in range(len(filenames)): dcm = pydicom.dcmread(filenames[i]) img_no = dcm[0x0020,0x0013].value self.pix_arr[img_no-1] = dcm.pixel_array def wheel_scroll(event): global sl if event.button == 'down': sl += 1 if sl > len(filenames)-1: sl =0 if event.button == 'up': sl -= 1 if sl < 0: sl = len(filenames)-1 ax.imshow(f0.pix_arr[sl], cmap='bone') fig.canvas.draw() filenames = fs.folder_fileselect() f0 = Pixarr(filenames) print('画像の縦方向のピクセル数は\t' + str(f0.row)) print('画像の横方向のピクセル数は\t' + str(f0.column)) print('画像のウインドウレベルは\t' + str(f0.wl)) print('画像のウインドウ幅は\t' + str(f0.ww)) sl =0 fig = plt.figure() ax = fig.add_subplot() ax.imshow(f0.pix_arr[sl],cmap= 'bone') fig.canvas.mpl_connect('scroll_event', wheel_scroll) plt.show()
いかがでしたか?きちんとトラックボールで画像変更できましたでしょうか?
クラスを用いて画像ピクセルデータ、DICOMタグの情報を取り込む方法を行いました。画像間演算や、画像比較等2つ以上の画像ファイルを用いて解析をやる時にクラスを用いた方法というのは大変便利です。
是非ともマスターして自分なりに使ってみてください。
お疲れ様でした。
Pythonを使い始めて、なんとも理解しにくかったのが”クラス”なるもの。
まずは、インスタンスの生成、イニシャライズ、selfを使わなくてはいけないとか何が何だか、なんでこんなことをしなくてはならないのか・・・・・
まったく理解できませんでした。
クラスなんて使わなくてもリストで対応できるじゃん!!
なんて思っていたのですが、苦労しながらも使っているとクラスって本当に便利な物だなと分かってきました。
今回は、そのクラスなるものの説明をしたいと思います。
ただ、自分なりの解釈なので間違っていたらごめんなさん。
何かの書籍に、同じ属性をもったものを複数扱う時に使うと書いてあった気がします。
同じ属性?単語の意味はわかっても、イメージできない。プログラミングの中で使うものの属性?
わからない。。。。
答えは簡単でした。
職場のスタッフを例えに説明したいと思います。
スタッフは複数人いて、それぞれに名前、性別、年齢、担当モダリティー、所有資格等様々な情報があります。それは、全員が持ち合わせている情報となります。その情報をクラスを使わずに使おうとするとそれぞれに変数名を割り当てるか、リストを作成して対応する場所に情報を格納しなくてはいけません。
変数が増えると、その管理がとても煩雑になりますし、変数を用いた計算をしたい時にそれぞれの変数に対してコードを書く必要ができコードが長くなってしまう事があります。
Aさん、Bさん、Cさん、Dさんを考えましょう。それぞれに変数名を作成してみると
A_name、 A_sex、 A_age、 A_moda、 A_capa
B_name、 B_sex、 B_age、 B_moda、 B_capa
C_name、 C_sex、 C_age、 C_moda、 C_capa
D_name、 D_sex、 D_age、 D_moda、 D_capa
と合計20個の変数が必要になってきます。
リストを使った場合は、何番目にどの情報を入れたか管理しなくてはならなくてとても面倒です。(私は、クラスが分からずにこの方法を取っていましたがすごく煩わしかったです。。。。)
しかし、クラスを使うことで同じ変数名の使いまわしができるようになります。ここから先は、使い方の方で説明していきたいと思います。
私は、以下の本で理解することができました。(Python関連の書籍ではないですが・・・・)クラスの部分だけ理解するのであれば本屋さんで立ち読みでも十分です。オブジェクト指向プログラミングの概要を簡単に知りたければお勧めの書籍です。
まずは、コードを
class Staff: def __init__(self,name,sex,age,moda,capa): self.name = name self.sex = sex self.age = age self.moda = moda self.capa = capa staff1 = Staff('A','man','30','MRI','MRI technologist') staff2 = Staff('B','female','25','Mammo','Mammo technologist') staff3 = Staff('C','female','50','CT','CT technologist') staff4 = Staff('D','man','60','Angio','Angio technologist')
クラスを使用するにはまず、インスタンス(staff1やstaff2)といって箱のようなものを作成します。上記コードの10~13行目となります。今回の例では人物を設定します。その設定に名前、性別、年齢、担当モダリティー、所有資格等様々な情報を追加していきます。その作業が初期化にあたり
def __ini__():
の部分にあたります。
私は、ここでつまずきました。何故、selfを使うのか。
答えは簡単で、クラスは同じような物を複数個作成します。なので、同じ変数名も複数個作成されることになるので、自分自身の変数名と示しているのだと思います。ここは、おまじない的に覚えてしまうのがいいと思います。
後は、それぞれの値を使いたい時にインスタンス名(staff1やstaff2)に変数名
インスタンス名.変数名
で呼び出すことができます。例えば以下のようになります。
print(staff2.name + ' さんの所有資格は '+ staff2.capa) ⇒ B さんの所有資格は Mammo technologist print(staff4.name + ' さんの年齢は '+ staff4.age) ⇒ D さんの年齢は 60
いかがですか?
クラスを使うことで、同じ変数名を使用できる事はすごくすっきりしていいと思いませんか?
また、個人作成でのプログラミングではあまり大きな効果はないかもしれませんが数人でコードを作成していく時にはとても効果があるみたいです。
是非とも、クラスを使いこなして効率よくプログラミングをしていきましょう。
お疲れ様でした。