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今回は、業務中によく使用する画像間演算であるサブトラクションをやってみたいと思います。
サブトラクションをやるには最低2枚の画像が必要となりますので、画像の読み込み工程を2つ作成します。その際、どちらの画像からどちらの画像を引き算するのか決めなくてはなりませんが、今回はコードを簡単にするため初めに取り込んだ画像から、2枚目に取り込んだ画像をひき算することにします。
続いて、計算結果を格納する配列を作成します。この配列は、取り込んだ画像と同じサイズの配列を作成します。
画像間演算、引き算のコードを書いていきます。
最後に、画像表示のコードを書いて完成となります。
まずは、画像を読み込む工程からやっていきます。込みこむ工程は、画像のパスを変数に取り込んでから、それをクラスに渡して画像のピクセルデータを配列に取り込み形になります。
それではコードを書いていきたいと思います。
DICOM画像を使いますので、pydicomのインポートをします。
画像の読み込みは、毎回使用しているfileselectのモジュールを使用します。まだ、ご覧になっていない方はこちら。fileselectのファイルは、今回書いているコードと同じフォルダの中に入れておいてくださいね。
今回もフォルダで一括選択する方法で画像を読み込みたいと思います。
# -- coding utf-8 -- import pydicom import fileselect as fs filenames = fs.folder_fileselect()
まずは、fileselectのモジュールを用いてフォルダ内の画像パスをfilenamesの変数に取り込みます。
そして、その変数をClassに渡し画像のピクセルデータを配列に取り込みます。
以前の記事から、Classの部分のコードをコピーします。画像読み込みをクラスを使ってやってみたの記事はこちらの記事を参照ください。
クラスがよくわからない方はこちらを参照ください。
このクラスではnumpyを用いていますのでnumpyのインポートを忘れないようにしましょう。
# -- coding utf-8 -- import pydicom import fileselect as fs 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) filenames = fs.folder_fileselect() f1 = Pixarr(filenames)
読み込んだ画像パスをClassに渡すのですが、ひかれる画像をf0、ひく画像をf1と名前を付けてクラスに渡します。(上記コード28行目)
これで、ひかれる画像のピクセルデータを取り込むことができました。
同様に、引く画像をf1として取り込みます。f0の時と同様にコードを書きます。(上記コード30~31行目)
これで、画像の読み込みは完了です。
画像間演算のコードを書く前に、計算結果を格納する配列を作成します。
引く画像、引かれる画像と同一サイズの配列を作成します。f0,f1どちらでも構わないのですが同じ大きさの配列f2を作成します。その際に、0で初期化しておきます。
同一サイズの配列を作成するコードは
f2 = numpy.full_like(f0.pix_arr,0)
で作成できます。numpyはインポートする際npの名称でインポートしていますので、実際のコードは下記コード33行目の様にnp.full_likeの形になります。
# -- coding utf-8 -- import pydicom import fileselect as fs 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) filenames = fs.folder_fileselect() f1 = Pixarr(filenames) f2 = np.full_like(f0.pix_arr,0)
サブトラクションはピクセル同士の引き算ですので本来であれば
f2[i] = f0.pix_arr[i] – f1.pix_arr[i]
のコードをfou文で繰り返すと考えるかもしれませんが、実はpythonでの配列計算はとても簡単で
f2 = f0.pix_arr – f1.pix_arr
の一文だけで済んでしまいます。もちろん、引き算だけでなく足し算でも使えますし、掛け算、割り算でも使用可能です。
for文でピクセルごとに計算しているよりも全然高速です。
ここまでのコードは以下となります。
# -- coding utf-8 -- import pydicom import fileselect as fs 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) filenames = fs.folder_fileselect() f1 = Pixarr(filenames) f2 = np.full_like(f0.pix_arr,0) f2 = f0.pix_arr - f1.pix_arr
画像表示はmatplotlibを使用したいと思いますので、まずはmatplotlibをインポートします。
# -- coding utf-8 -- import pydicom import fileselect as fs 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) filenames = fs.folder_fileselect() f1 = Pixarr(filenames) f2 = np.full_like(f0.pix_arr,0) f2 = f0.pix_arr - f1.pix_arr
画像表示は引かれる画像、引く画像、計算後の画像の3画像表示したいと思います。
まずは、画像を表示設定からです。matplotlibで画像や、グラフ等を表示する際にまずはfigの設定をし、その後にaxの設定をします。ここが、よくわからないところかもしれませんが、考え方としては何もないところで絵を描こうとするときにまずは、画板や机を準備すると思います。それがfigにあたります。その後、その上に絵を描く紙を準備すると思います。それがaxにあたると考えやすいと思います。
figの設定は
fig = plt.figure()
チュートリアルはこちら
axの設定は
ax = fig.add_subplot()
で設定していきます。チュートリアルはこちら。
figの大きさの設定をします。引数の中にfigsize =(横、縦)で設定をします。
fig = plt.figure(figsize=(15, 5))
どこに画像を表示するかはaxの括弧の中に引数として指定します。
ax0 = fig.add_subplot(1,3,1) #一番左に表示 ax1 = fig.add_subplot(1,3,2) #真ん中に表示 ax2 = fig.add_subplot(1,3,3) #一番右に表示
括弧内の初めの引数は縦方向の画像数を、2個目の引数は横方向の画像数、3個目の引数を何番目に画像を表示するかという設定になります。
続いて、axにどの画像を表示するかの設定に入ります。
引かれる画像をax0に、引く画像をax1に、演算後の画像をax2として設定していきます。
ax0.imshow(f0.pix_arr[sl],cmap= 'bone') ax1.imshow(f1.pix_arr[sl],cmap= 'bone') ax2.imshow(f2[sl],cmap= 'bone')
これで、画像表示の設定は完了です。
# -- 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) filenames = fs.folder_fileselect() f1 = Pixarr(filenames) f2 = np.full_like(f0.pix_arr,0) f2 = f0.pix_arr - f1.pix_arr fig = plt.figure(figsize=(15, 5)) plt.subplots_adjust(left=0, right=1, bottom=0, top=1, wspace=0.05, hspace=0.05) ax0 = fig.add_subplot(1,3,1) ax1 = fig.add_subplot(1,3,2) ax2 = fig.add_subplot(1,3,3) ax0.imshow(f0.pix_arr[sl],cmap= 'bone') ax1.imshow(f1.pix_arr[sl],cmap= 'bone') ax2.imshow(f2[sl],cmap= 'bone')
axに画像の設定をした際に [sl] とありますが、これは、何枚目の画像を表示するかの変数になります。マウスホイールを回すことで画像を切り替えるための変数です。(上記コードの38行目 sl = 0 とコードを追加してください。)
その機能を実装していきます。
これは、マウスイベントと言って右クリックや、左クリック、マウスホイールを回す、または押す等のイベントで何をするか決められます。
マウスホイールのイベントは
fig.canvas.mpl_connect('scroll_event', wheel_scroll)
動作設定はdefで関数を作成して決めます。今回はwheel_scrollという名前で設定しますが、次回matplotlibのマウスイベントで詳しく説明したいと思いますので今回はコードの提示だけとします。
# -- 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 ax0.imshow(f0.pix_arr[sl], cmap='bone') ax1.imshow(f1.pix_arr[sl], cmap='bone') ax2.imshow(f2[sl], cmap='bone') fig.canvas.draw() filenames = fs.folder_fileselect() f0 = Pixarr(filenames) filenames = fs.folder_fileselect() f1 = Pixarr(filenames) f2 = np.full_like(f0.pix_arr,0) f2 = f0.pix_arr - f1.pix_arr sl =0 fig = plt.figure(figsize=(15, 5)) plt.subplots_adjust(left=0, right=1, bottom=0, top=1, wspace=0.05, hspace=0.05) ax0 = fig.add_subplot(1,3,1) ax1 = fig.add_subplot(1,3,2) ax2 = fig.add_subplot(1,3,3) ax0.imshow(f0.pix_arr[sl],cmap= 'bone') ax1.imshow(f1.pix_arr[sl],cmap= 'bone') ax2.imshow(f2[sl],cmap= 'bone') fig.canvas.mpl_connect('scroll_event', wheel_scroll) plt.show()
いかがでしたか?お持ちのDICOM画像でサブトラクションできましたでしょうか?
上記コードで実行した結果です。マンモのMRI Dynamic画像、一番左が単純、真ん中が動脈相、一番左がサブトラクション画像となります。
正常乳腺は引き算され、腫瘍は濃染されているのが確認できると思います。
皆さんも試してみてください。
お疲れ様でした。
The post 画像間演算、サブトラクションをやってみる。 first appeared on 診療放射線技師がPythonをはじめました。.]]>今回は、画像のヒストグラムを求めてみたいと思います。
ヒストグラムを使用する場面としては、グラフで視覚的に信号値の分布を見たい時と、信号値の分布を数値として知りたい時があると思います。
そこで、今回はグラフとして求める方法と、数値として得る方法2つを紹介したいと思います。
求める手段としては以下の2つをやっていきます。
それでは、下の左側の画像を用いてそれぞれ紹介していきたいと思います。
ちなみに、右側は左側の画像のヒストグラムをimage-Jで表示したものです。
まずはmatplotlibを用いて表示する方法を紹介します。
使用するライブラリーはmatplotlibのpyplotとなります。
それでは、コードを表示したいと思います。
# -- coding utf-8 -- import pydicom import matplotlib.pyplot as plt import numpy as np import fileselect as fs filename = fs.single_fileselect() dcm = pydicom.dcmread(filename) plt.hist(dcm.pixel_array) plt.show()
ライブラリーのfileselectのコードはこちらを参照ください
上記コードで、ヒストグラムを求めるコードは12行目のplt.hist()となります。
カッコ内に第1引数としてピクセルデータの配列を指定します。
何か変だと思いませんか?ところどころ途切れているように見えます。階級であるbinの設定をしてみたいと思います。image-Jのヒストグラムと同一の設定にしてみましょう。image-Jではbinが256で設定されていますので上記コード12行目を以下に変更します。
# -- coding utf-8 -- import pydicom import matplotlib.pyplot as plt import numpy as np import fileselect as fs filename = fs.single_fileselect() dcm = pydicom.dcmread(filename) plt.hist(dcm.pixel_array,bins = 256) plt.show()
いかがでしょうか?同様の結果となりましたね。
その他、グラフの色を変えたり、グラフのタイプを変えてみたりと様々な設定はカッコ内に記載していきます。詳しくは以下のチュートリアルを参照してください。
matplotlib ヒストグラムのチュートリアルはこちら
追伸
ヒストグラムを求める際、多次元配列の場合に行を1つのデータと認識してしまい思い通りのヒストグラムが得られない場合があります。その場合は、1次元配列に変換して求めると思い通りのヒストグラムを求めることができる場合があります。
早速、コードから入ってみたいと思います。
# -- coding utf-8 -- import pydicom import numpy as np import fileselect as fs filename = fs.single_fileselect() dcm = pydicom.dcmread(filename) hist_np = np.histogram(dcm.pixel_array.ravel()) print(hist_np)
上記コードでヒストグラムを求めるコードは11行目のmp.histogram()となります。matplotlibの時と同様にカッコ内に引数にピクセルデータの配列を指定します。
しかし、注意していただきたいところがあります。
numpyでヒストグラムを求める場合、配列は1次元配列でなければならないということです。なので、11行目で多次元配列を1次元配列にするravel()を用いてdcm.pixel_arrayを1次元配列に変更しています。
それでは、コードを実行してみましょう。結果は以下となりました。
結果は
[36106,22314,4192,1611,780,300,133,66,22,4]
[0,110.7,237.4,356.1,474.8,593.5,712.2,830.9,949.6,1060.3,1187]
という2つの配列が返ってくる結果となりました。1つ目の配列の要素数は10個、2つ目の配列の要素は11個となっています。
それぞれの配列は何を示しているのでしょうか?それは、1つ目は度数を示しており、2つ目の配列は階級の幅であるbinの幅を示しています。
なので、2つ目の配列は0~118.7、118.8~237.4と言うようにその範囲を示しています。よって10個の範囲があることが分かります。
これを、先ほどと同じように階級binの設定をして再度表示させてみましょう。
# -- coding utf-8 -- import pydicom import numpy as np import fileselect as fs filename = fs.single_fileselect() dcm = pydicom.dcmread(filename) hist_np = np.histogram(dcm.pixel_array.ravel(),bins=256) print(hist_np)
matoplotlibの時と同様にヒストグラムを求めるコード(11行目)の引数にbins=256と足してあげます。結果はどうだったでしょうか?
先ほどよりも、要素数が増えているのが分かります。はじめにimage-Jで求めたヒストグラムをリストとして出力したものが下の図のようになります。
いかがでしょうか?階級の幅、度数共に同様の結果となっています。
numpyにおいても様々な引数が用意されています。こちらのチュートリアルを参照してください。
いかがでしたか?今回はヒストグラムを求めるをやってみました。ヒストグラムは、画像にフィルタをかけたり、再構成をしたりした時に元画像との比較にする際に非常に役に立ちますね。是非ともマスターしておきたい技術ですね。
お疲れ様でした。
import fileselect as fs #ファイルパス取得のモジュールをインポート import numpy np import pydicom import math import copy filenames = fs.multi_fileselect dcm = pydicom.dcmread(filenames[0]) row,columns = dcm.pixel_array.shape[0],dcm.pixel_array.shape[1] #読み込んだ画像の横方向を変数row、縦方向を変数columnsとして取得します。 dcm_copy = np.zeros((len(filenames), row, columns),dtype = int) #dcm_copyという名前で0で初期化した配列を作成 for i in range(len(filenames)): dcm = pydicom.dcmread(filenames[i]) dcm_arr = dcm.pixel_array dcm_copy[i] = dcm_arr.astype(np.int64) #np.int64としてデータの型を指定しておきます。 dcm_main = copy.deepcopy(dcm_copy) #深いコピーで複製します。 cv2.namedWindow('dcm_image',cv2.WINDOW_NORMAL) #ウインドウ名を'dcm_image'とし、ウインドウサイズを変更できるように設定 maxvalue = dcm_copy.max().astype(np.int64) lookup_tbl = np.zeros(maxvalue+1, dtype=np.int64) #作成要素数を最大値+1としておく。 cv2.createTrackbar("WL", "dcm_image", (maxvalue // 2), maxvalue, make_LUT) cv2.createTrackbar("WW", "dcm_image", (maxvalue // 4), maxvalue, make_LUT) while 1: wl = cv2.getTrackbarPos('WL', 'dcm_image') ww = cv2.getTrackbarPos('WW', 'dcm_image') ww_low = wl - ww // 2 ww_high = wl + ww // 2 lookup_tbl[0:ww_low] = 0 lookup_tbl[ww_high:maxvalue] = 255 for i in range(ww_low, ww_high, 1): lookup_tbl[i] = math.ceil((i - ww_low) * (256 / (ww_high - ww_low))) dcm_copy = lookup_tbl[dcm_main] dcm_copy =cv2.convertScaleAbs(dcm_copy, alpha=255/dcm_copy.max()) cv2.imshow('dcm_image', dcm_copy[0]) k = cv2.waitKey(1) if k == ord('q'): break def make_LUT(val): pass #何もしない
CTや、MRI画像は16ビット階調だということです。その為、DICOMヘッダー[0028,0100]に登録されているBits Allocatedの画像階調度も16と登録(自施設の装置では16)されていますので、それに合わせていきたい友ます。
まずは上記コードの14行目
dcm_copy = np.zeros((len(filenames), row, columns),dtype = int)
の 「dtype = int」を 「dtype = ‘int16’」と変更し以下となります。
dcm_copy = np.zeros((len(filenames), row, columns),dtype = ‘int16’)
続いて20行目は14行目で指定しているので「.astype(np.int64)」を削除して
dcm_copy[i] = dcm_arr
とスッキリさせてしまいましょう。
29~30行目は
maxvalueはdcm_copyの最大値を入れる変数であるので、元の配列の型を指定する必要もないので「.astype(np.int64)」を削除。
30行目の
lookup_tbl = np.zeros(maxvalue+1, dtype=np.int64)
は、「dtype=np.int64」を「dtype=’int16’」とし
lookup_tbl = np.zeros(maxvalue+1, dtype=’int16′)
以上で型の修正は終わりです。
最後に・・・・
上記コードはopenCVのimportを忘れていました。以下のコードで追加しておきます。また、関数のmake_LUTを上に持ってきました。
修正後のコードは以下となります。
import fileselect as fs #ファイルパス取得のモジュールをインポート import numpy as np import pydicom import math import copy import cv2 def make_LUT(val): pass #何もしない filenames = fs.multi_fileselect dcm = pydicom.dcmread(filenames[0]) row,columns = dcm.pixel_array.shape[0],dcm.pixel_array.shape[1] dcm_copy = np.zeros((len(filenames), row, columns),dtype = 'int16') for i in range(len(filenames)): dcm = pydicom.dcmread(filenames[i]) dcm_arr = dcm.pixel_array dcm_copy[i] = dcm_arr dcm_main = copy.deepcopy(dcm_copy) cv2.namedWindow('dcm_image',cv2.WINDOW_NORMAL) maxvalue = dcm_copy.max() lookup_tbl = np.zeros(maxvalue+1, dtype='int16') cv2.createTrackbar("WL", "dcm_image", (maxvalue // 2), maxvalue, make_LUT) cv2.createTrackbar("WW", "dcm_image", (maxvalue // 4), maxvalue, make_LUT) while 1: wl = cv2.getTrackbarPos('WL', 'dcm_image') ww = cv2.getTrackbarPos('WW', 'dcm_image') ww_low = wl - ww // 2 ww_high = wl + ww // 2 lookup_tbl[0:ww_low] = 0 lookup_tbl[ww_high:maxvalue] = 255 for i in range(ww_low, ww_high, 1): lookup_tbl[i] = math.ceil((i - ww_low) * (256 / (ww_high - ww_low))) dcm_copy = lookup_tbl[dcm_main] dcm_copy =cv2.convertScaleAbs(dcm_copy, alpha=255/dcm_copy.max()) cv2.imshow('dcm_image', dcm_copy[0]) k = cv2.waitKey(1) if k == ord('q'): break
次回はマウスホイールの機能を入れていきたいと思います。