Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the 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 6114

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the easy-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 6114

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the urvanov-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 6114

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the breadcrumb-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 6114

Notice: Function _load_textdomain_just_in_time was called incorrectly. Translation loading for the advanced-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 6114

Notice: 関数 _load_textdomain_just_in_time が誤って呼び出されました。lancr ドメインの翻訳の読み込みが早すぎました。これは通常、プラグインまたはテーマの一部のコードが早すぎるタイミングで実行されていることを示しています。翻訳は init アクション以降で読み込む必要があります。 詳しくは WordPress のデバッグをご覧ください。 (このメッセージはバージョン 6.7.0 で追加されました) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php on line 6114

Warning: Cannot modify header information - headers already sent by (output started at /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php:6114) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-content/plugins/all-in-one-seo-pack/app/Common/Meta/Robots.php on line 87

Warning: Cannot modify header information - headers already sent by (output started at /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/functions.php:6114) in /virtual/mcu03iphuk/public_html/radiology-technologist.info/wp-includes/feed-rss2.php on line 8
DICOM | 診療放射線技師がPythonをはじめました。 http://radiology-technologist.info 診療放射線技師のPython日記。解析等で使えるコードを作成、アップしていきたいと思っています。その他いろいろ Fri, 21 May 2021 05:53:41 +0000 ja hourly 1 https://wordpress.org/?v=6.7 https://i0.wp.com/radiology-technologist.info/wp-content/uploads/2018/09/cropped-logo5.png?fit=32%2C32 DICOM | 診療放射線技師がPythonをはじめました。 http://radiology-technologist.info 32 32 164362728 DICOM画像を用いてopenCVの領域抽出をやってみた② http://radiology-technologist.info/post-1074 Thu, 21 Jan 2021 01:50:16 +0000 http://radiology-technologist.info/?p=1074 前回記事、DICOM画像を用いてopenCVの領域 […]

The post DICOM画像を用いてopenCVの領域抽出をやってみた② first appeared on 診療放射線技師がPythonをはじめました。.]]>
前回記事、DICOM画像を用いてopenCVの領域抽出をやってみた①

前回、画像領域値をせて値を確認できるプログラムを組んでみたをベースとして画像領域を一つ追加し、4種類の領域抽出できるプログラムを組みました。

今回は、それらプログラムを連結させてプログラムとして完成させましょう。


前回までのプログラム

import tkinter as tk
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
import pydicom
import fileselect as fs

def cont():
    img_unit8 = copy.deepcopy(img_arr) #img_unit8としてコピー
    np.clip(img_unit8, ww_low, ww_high, out= img_unit8)
      #コピーした配列の最低値以下をウインドウ幅最低値に、
       # 最高値以上も同様に)

    img_unit8 -= img_unit8.min()
      #配列全体をウインドウ幅最低値を引くことで0からに変更)

    np.floor_divide(img_unit8, (img_unit8.max() + 1) / 256,
            out = img_unit8, casting='unsafe')
      #ウインドウ幅を256分割する。

    cv2.imwrite('./img_8bit.png',img_unit8)

def external():
    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 0,   cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()

def list():
    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 1,  cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()
 
def ccomp():
    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 2, cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()

def tree():
    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 3,   cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()

def level():
    global ww_low, ww_high, w_level, w_width
 
    window_level, window_width = level_sc.get(), w_width_sc.get()
 
    ww_low = window_level - (window_width // 2)
    ww_high = window_level + (window_width // 2)

def window(self):
    global ww_low, ww_high, img_arr, ax1
 
    level()
 
    ax1.imshow(img_arr, cmap='bone', vmin=ww_low, vmax=ww_high)
    fig.canvas.draw()

def thresho(self):
    global img_arr, ax2, fig
 
    ret, thresh = cv2.threshold(img_arr, int(self), 255, cv2.THRESH_BINARY)
    ax2.imshow(thresh, cmap='bone')
 
    fig.canvas.draw()

def main():
    global ww_low, ww_high, img_arr, thre_sc, ax2, fig,\
        img_arr, ax1, level_sc, w_width_sc, ww_low, ww_high, w_level, w_width
 
 
    filename = fs.single_fileselect()
    dcm = pydicom.dcmread(filename)
    img_arr = np.array(dcm.pixel_array)
    w_level = int(dcm[0x0028,0x1050].value)
    w_width = int(dcm[0x0028, 0x1051].value)
 
    fig = plt.figure(figsize=(12, 6))
    ax1 = fig.add_subplot(1, 3, 1)
    ax2 = fig.add_subplot(1, 3, 2)
    ax3 = fig.add_subplot(1, 3, 3)
 
    ax1.axes.xaxis.set_visible(False), ax1.axes.yaxis.set_visible(False)
    ax2.axes.xaxis.set_visible(False), ax2.axes.yaxis.set_visible(False)
    ax3.axes.xaxis.set_visible(False), ax3.axes.yaxis.set_visible(False)
 
 
    root = tk.Tk()
    root.geometry("1410x600")
 
    Canvas = FigureCanvasTkAgg(fig, master=root)
    Canvas.get_tk_widget().grid(row=0, column=0, rowspan=10)
 
 
    var_scale_level = tk.IntVar()
    level_sc = tk.Scale(root,
        label='Window Level',
        variable=var_scale_level,
        orient=tk.HORIZONTAL,
        length=200,
        from_= np.min(img_arr),
        to=np.max(img_arr),
        command=window)
    level_sc.set(w_level)
    level_sc.grid(row=1, column=1)
 
    var_scale_w_width = tk.IntVar()
    w_width_sc = tk.Scale(root,
        label = 'Window Width',
        variable=var_scale_w_width,
        orient=tk.HORIZONTAL,
        length=200,
        from_=0,
        to=(np.max(img_arr) - np.min(img_arr))//2,
        command=window)
    w_width_sc .set(w_width)
    w_width_sc.grid(row=3, column=1)
 
 
    thre = 0
 
    var_thre = tk.IntVar()
    thre_sc = tk.Scale(root,
        label='Threshold',
        variable=var_thre,
        orient=tk.HORIZONTAL,
        length=200,
        from_=np.min(img_arr),
        to=np.max(img_arr),
        command=thresho)
    thre_sc.set(thre)
    thre_sc.grid(row=5, column=1)


    ext_but = tk.Button(root, text="EXTERNAL", command=external, width = 10)
    ext_but.grid(row=8, column=1)
 
    list_but = tk.Button(root, text="LIST", command=list, width = 10)
    list_but.grid(row=8, column=2)
 
    ccomp_but = tk.Button(root, text="CCOMP", command=ccomp, width = 10)
    ccomp_but.grid(row=9, column=1)
 
    TREE_but = tk.Button(root, text="TREE", command=tree, width = 10)
    TREE_but.grid(row=9, column=2)

    window(1)
    thresho(1)
 
    root.mainloop()
 
if __name__ == "__main__":
    main()


広告
デル株式会社

今回の流れ

それでは、今回の流れです。

前回は4つの領域抽出のプログラムを作成しましたが、流れとして繋がっていないのでまずは、これを繋げて動くようにしたいと思います。

その後、領域抽出した面積や、周囲の長さを求めるコードを書いていきたいと思います。


ボタンに関数を紐づける

それぞれのボタンが押された時の動作を紐づけます。

ボタンを作成した際にcommandの引数で指定してあげることで動作を紐づけることが出来ます。

    ext_but = tk.Button(root, text=”EXTERNAL”, command=external, width = 10) 

作成した4つのボタンに関数の紐づけをしましょう。

領域抽出までのプログラムを繋げる

それでは、繋げていきたいと思います。

領域抽出のボタンが押された際に、まず一番左の画像を汎用画像として保存するプログラムを走らせます。その後に領域抽出を行う必要があります。

関数のexternal,list,ccomp,treeの関数が呼び出された際にまず、contの関数を呼び出し、左側の画像を汎用画像として保存させますので、それぞれの関数の初めに

cont()

と4つの関数の初めに入力しておきましょう。

そして、保存した画像を読み込みます。

img_png = cv2.imread(‘img_8bit.png’)


これで、プログラムが動くようになりました。


抽出領域の面積、長さを求める

抽出した領域の面積は、

cv2.contourArea()

周囲の長さを求めるのは

cv2.arcLength()

で求めることが出来ます。ただ、この関数の場合は第2引数が必要で、閉じている線(True)か閉じていない線(False)なのかを指定してあげる必要があります。

ちなみに、重心を求めるのは

cv2.moments()

で求めることが出来ます。

それぞれ、引数にはcontoursの何番目のデータかを指定する必要があります。

それでは、これも関数として作成しましょう。

関数名はcont_momentとして作成しました。

その際、それぞれの領域抽出した結果を引数として受けることにします。

後は、for文で回して格納してある分だけプロントアウトしていくだけです。

def cont_moment(contours):
    print('I got ' + str(len(contours)) + ' contours')

    for i in range(len(contours)):
        print('Contour area\t' + str(cv2.contourArea(contours[i])) + '\t arc length\t' + str(cv2.arcLength(contours[i],True)))

そして、4つの領域抽出の関数の最後に

cont_moment(contours)

を追加します。


値を微調整できるボタンを設置

私のコードがいけないのか、パソコンのスペックが低いのか、スライダーを動かすと動作がカクついてしまうので5程度づつ調整できるボタンを設置したいと思います。

ボタンの設置は領域抽出の時と同じです。

例えば、ウインドウレベルの場合は以下の様にボタンの設置を行います。

down_wl_btn = tk.Button(root, text="down", command=down_wl)
down_wl_btn.grid(row=2, column=1)

up_wl_btn = tk.Button(root, text=" up ", command=up_wl)
up_wl_btn.grid(row=2, column=2)

コマンドの関数は以下になります。

def down_wl():
    val = level_sc.get() - 5
    level_sc.set(val)

def up_wl():
    val = level_sc.get() + 5
    level_sc.set(val)

現在のスケールの値とそこから増減したい数値を増減した値(今回は5増減)をvalという変数に持たせ、それをスケールの値にセットする形になります。

スケールにセットするやり方は

スケール名.set(値)

で出来ます。

ウインドウ幅、閾値に関しても同様に作成してください


追加

忘れていました・・・・・

画像領域を追加した際にax3を追加しました。

しかし、このax3はmain関数の中で宣言していますので、他の関数では使うことが出来ませんのでグローバル宣言をしておきましょう。

main関数 def main(): のすぐ下にglobalと記載がありますが、そこにax3を追加します。

同様にthreshも追加してください。また、threshはスケールを動かした時にも変化しますので関数threshoにおいてもthreshをグローバル化しておいてください

あと、汎用画像を作成する際に配列をコピーするのに、copyというライブラリーを使用していませんがインポートしていませんでした。

import copy

と追加してください。

調整

プログラムは完成しましたが、最終的に見た目の調整をします。

今までのコードを実行すると以下のような画面となり、ちょっとカッコ悪いので調整していきたいと思います。

まずは、画像間隔を調整します。

axの軸設定の後に

plt.subplots_adjust(left=0.01, right=0.995, bottom=0, top=1, wspace=0.01)

のコードを挿入し画像間隔を設定します。詳しくはこちら

画像間隔が狭まりました。

新たに作成したax3の領域が大きいので画像サイズを(13,4)に変更してみます。

整ってきました、スケールの部分が切れてしまっているので領域のジオメトリーを 1510×500 に変更します

後はボタン類の配置を修正すれば見栄えが良くなりそうです

スケールの配置で1枠を使ってしまっているので2つを使い表示させるようにします。

スケールの設定で columnspan = 2

2枠を使った表示にできますのでそれぞれのスケールに追加します。

w_width_sc.grid(row=3, column=1,columnspan=2)

きれいに整いました!!

広告
HP Directplus -HP公式オンラインストア-


完成したコード

import tkinter as tk
import cv2
import matplotlib.pyplot as plt
from matplotlib.backends.backend_tkagg import FigureCanvasTkAgg
import numpy as np
import pydicom
import fileselect as fs
import copy

def cont():
    img_unit8 = copy.deepcopy(img_arr) #img_unit8としてコピー
    np.clip(img_unit8, ww_low, ww_high, out= img_unit8)
      #コピーした配列の最低値以下をウインドウ幅最低値に、
      # 最高値以上も同様に)

    img_unit8 -= img_unit8.min()
      #配列全体をウインドウ幅最低値を引くことで0からに変更)

    np.floor_divide(img_unit8, (img_unit8.max() + 1) / 256,
            out = img_unit8, casting='unsafe')
      #ウインドウ幅を256分割する。

    cv2.imwrite('./img_8bit.png',img_unit8)

def external():
    cont()
    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 0,   cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()
    cont_moment(contours)

def list():
    cont()

    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 1,  cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()
    cont_moment(contours)
 
def ccomp():
    cont()
    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 2, cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()
    cont_moment(contours)

def tree():
    cont()
    img_png = cv2.imread('img_8bit.png')
    thresh_8bit = thresh.astype('u1')
    ret, contours, hierarchy = cv2.findContours(thresh_8bit, 3,   cv2.CHAIN_APPROX_SIMPLE)
 
    for contour in contours:
        cv2.drawContours(img_png, contours, -1, (255, 255, 0), 1)
 
    ax3.imshow(img_png, cmap='bone')
    fig.canvas.draw()
    cont_moment(contours)

def cont_moment(contours):
    print('I got ' + str(len(contours)) + ' contours')

    for i in range(len(contours)):
        print('Contour area\t' + str(cv2.contourArea(contours[i])) + '\t arc length\t' + str(cv2.arcLength(contours[i],True)))


def level():
    global ww_low, ww_high, w_level, w_width
 
    window_level, window_width = level_sc.get(), w_width_sc.get()
 
    ww_low = window_level - (window_width // 2)
    ww_high = window_level + (window_width // 2)

def window(self):
    global ww_low, ww_high, img_arr, ax1
 
    level()
 
    ax1.imshow(img_arr, cmap='bone', vmin=ww_low, vmax=ww_high)
    fig.canvas.draw()

def thresho(self):
    global img_arr, ax2, fig
, thresh
 
    ret, thresh = cv2.threshold(img_arr, int(self), 255, cv2.THRESH_BINARY)
    ax2.imshow(thresh, cmap='bone')
 
    fig.canvas.draw()

def up():
    val = thre_sc.get() + 5
    thre_sc.set(val)

def down():
    val = thre_sc.get() - 5
    thre_sc.set(val)

def down_wl():
    val = level_sc.get() - 5
    level_sc.set(val)

def up_wl():
    val = level_sc.get() + 5
    level_sc.set(val)


def down_ww():
    val = level_sc.get() - 5
    level_sc.set(val)


def up_ww():
    val = level_sc.get() + 5
    level_sc.set(val)

def main():

    global ww_low, ww_high, img_arr, thre_sc, ax2, fig, \
        img_arr, ax1, level_sc, w_width_sc, ww_low, ww_high, w_level, w_width, \
        ax3, thresh
 
 
    filename = fs.single_fileselect()
    dcm = pydicom.dcmread(filename)
    img_arr = np.array(dcm.pixel_array)
    w_level = int(dcm[0x0028,0x1050].value)
    w_width = int(dcm[0x0028, 0x1051].value)
 
    fig = plt.figure(figsize=(13, 4))
    ax1 = fig.add_subplot(1, 3, 1)
    ax2 = fig.add_subplot(1, 3, 2)
    ax3 = fig.add_subplot(1, 3, 3)
 
    ax1.axes.xaxis.set_visible(False), ax1.axes.yaxis.set_visible(False)
    ax2.axes.xaxis.set_visible(False), ax2.axes.yaxis.set_visible(False)
    ax3.axes.xaxis.set_visible(False), ax3.axes.yaxis.set_visible(False)
    plt.subplots_adjust(left=0.01, right=0.995, bottom=0, top=1, wspace=0.01)
 
 
    root = tk.Tk()
    root.geometry("1510x500")
 
    Canvas = FigureCanvasTkAgg(fig, master=root)
    Canvas.get_tk_widget().grid(row=0, column=0, rowspan=10)
 
 
    var_scale_level = tk.IntVar()
    level_sc = tk.Scale(root,
        label='Window Level',
        variable=var_scale_level,
        orient=tk.HORIZONTAL,
        length=200,
        from_= np.min(img_arr),
        to=np.max(img_arr),
        command=window)
    level_sc.set(w_level)
    level_sc.grid(row=1, column=1,columnspan=2)

    down_wl_btn = tk.Button(root, text="down", command=down_wl)
    down_wl_btn.grid(row=2, column=1)

    up_wl_btn = tk.Button(root, text=" up ", command=up_wl)
    up_wl_btn.grid(row=2, column=2) 

    var_scale_w_width = tk.IntVar()
    w_width_sc = tk.Scale(root,
        label = 'Window Width',
        variable=var_scale_w_width,
        orient=tk.HORIZONTAL,
        length=200,
        from_=0,
        to=(np.max(img_arr) - np.min(img_arr))//2,
        command=window)
    w_width_sc .set(w_width)
    w_width_sc.grid(row=3, column=1,columnspan=2)
 
    down_ww_btn = tk.Button(root, text="down", command=down_ww)
    down_ww_btn.grid(row=4, column=1)

    up_ww_btn = tk.Button(root, text=" up ", command=up_ww)
    up_ww_btn.grid(row=4, column=2)
 
    thre = 0
 
    var_thre = tk.IntVar()
    thre_sc = tk.Scale(root,
        label='Threshold',
        variable=var_thre,
        orient=tk.HORIZONTAL,
        length=200,
        from_=np.min(img_arr),
        to=np.max(img_arr),
        command=thresho)
    thre_sc.set(thre)
    thre_sc.grid(row=5, column=1)

    down_btn = tk.Button(root, text="down", command=down)
    down_btn.grid(row=6, column=1,columnspan=2)

    up_btn = tk.Button(root, text=" up ", command=up)
    up_btn.grid(row=6, column=2)

    ext_but = tk.Button(root, text="EXTERNAL", command=external, width = 10)
    ext_but.grid(row=8, column=1)
 
    list_but = tk.Button(root, text="LIST", command=list, width = 10)
    list_but.grid(row=8, column=2)
 
    ccomp_but = tk.Button(root, text="CCOMP", command=ccomp, width = 10)
    ccomp_but.grid(row=9, column=1)
 
    TREE_but = tk.Button(root, text="TREE", command=tree, width = 10)
    TREE_but.grid(row=9, column=2)

    window(1)
    thresho(1)
 
    root.mainloop()
 
if __name__ == "__main__":
    main()

広告
BTOパソコン・パソコン関連商品がお買い得!パソコン工房のセール

最後に

いかがでしたか?領域抽出のプログラムきちんと動きましたか?

このコードを更に発展させていけば、内臓脂肪計測のプログラムも作れそうですね。

興味のある方は挑戦してみてください。

お疲れ様でした。


環境

  • windows10
  • python3.6.1
  • Anaconda custom(64-bit)
  • PyCharm2020.2(Communication Edition)

広告
上新電機 パソコン買取サービス
The post DICOM画像を用いてopenCVの領域抽出をやってみた② first appeared on 診療放射線技師がPythonをはじめました。.]]>
1074
ドラッグアンドドロップでDICOMタグが見れるプログラムを作ってみた。 http://radiology-technologist.info/post-712 Sun, 14 Jun 2020 00:18:31 +0000 http://radiology-technologist.info/?p=712 はじめに 私のブログの中でとても閲覧数が多いのがD […]

The post ドラッグアンドドロップでDICOMタグが見れるプログラムを作ってみた。 first appeared on 診療放射線技師がPythonをはじめました。.]]>
はじめに

私のブログの中でとても閲覧数が多いのがDICOMタグ関係の記事となっています。皆さん、DICOMタグを確認したり、いじったりする必要があるんだなとつくづく感じます。

ただ、DICOMタグを確認するのにPythonのプログラムを立ち上げてとか、Image-J、OSIRIX等のDICOMビューアを立ち上げるのは面倒ですよね。

だったら、ドラッグドロップでDICOMタグを見れるプログラムを作ってしまおうという思いから今回この記事を書いています。


広告
デル株式会社

手順を考える

まずは、始める前に手順を考えてみたいと思います。

とりあえず、DICOMタグを表示するプログラムを書いてから、それを改造していく事にしましょう。

その際、ファイル1枚なのか、フォルダなのかの判定をしてDICOMタグの表示方法を考えます。

その後、ドラッグドロップされた時の処理を追加します。


広告
HP Directplus -HP公式オンラインストア-

DICOMタグを表示するプログラム

これに関しては、以前記事にしていますのでこちらを見てください。

import pydicom

dcm = pydicom.dcmread('ファイル名')

print(dcm)

たった3行のコードで出来上がりですが、これでは、指定したファイルしか見れないので’ファイル名’の所を変数に変えておきましょう。

とりあえず’filename’としておきます。

import pydicom

dcm = pydicom.dcmread(filename)

print(dcm)


広告
BTOパソコン・パソコン関連商品がお買い得!パソコン工房のセール

ファイルか、フォルダかの判定

上記プログラムはファイル単体のDICOMタグを表示するプログラムで、フォルダの際にはエラーとなってしまいます。

私個人的には、DICOMタグを見るのならフォルダで一気に入れてしまいたい気分です。

なので、ドラッグドロップされたものがDICOM画像1枚なのか、フォルダでドラッグドロップされたものなのかを判定する必要があります。

その判定をするのは”os”というライブラリーの”isfile”を使います。

返り値はboolean値で”True”か,”False”の2値で返ってきます。

まずは、ライブラリーをインポートします。

import pydicom
import os

dcm = pydicom.dcmread(filename)

print(dcm)

その後filenameという変数がファイルかフォルダなのかの判定を、DICOMファイル読み込み前に入れます。下記コードでは5行目に入れてあります。

if文で判定します。もし、ファイルである場合は”True”が返ってきますので、そのままスルーします。

import pydicom
import os

if os.path.isfile(filename): 
    pass

dcm = pydicom.dcmread(filename)

print(dcm)

さて、”False”の場合はフォルダ内のファイルを見に行かなければなりません。この作業は以前、フォルダでファイルを一括選択でやっているのでそのコードを持ってきます。

ライブラリー”glob”をインポートします。その後、フォルダ内のファイルを変数内に入れていきましょう。以下コードの9行目です。

本来であれば、ここでこれもファイルかどうかの判定をするべきなのですが今回は省略します。

import pydicom
import os
import glob

if os.path.isfile(filename): 
    pass

else:
    filename = glob.glob(filename + "/*")

dcm = pydicom.dcmread(filename)
print(dcm)

後は、ファイルを読み込んで表示する部分を修正します。for文で繰り返します。変数名に[i]を付け加えるのを忘れないでくださいね。

import pydicom
import os
import glob

if os.path.isfile(filename): 
    pass

else:
    filename = glob.glob(filename + "/*")

for i in range(len(filename)):
    dcm = pydicom.dcmread(filename)
    print(dcm)


ドラッグドロップされた時の処理

ここはライブラリー”sys”を使います。引数としてコマンドライン引数の”argv”を用います。詳しくはチュートリアルをご覧ください。(私は理解していませんが・・・・・)

まずは、インポート(5行目)

ドラッグドロップされたファイルを変数に入れます。(入れなくても大丈夫ですが、いつも使っている変数名にした方が分かりやすいので・・・・)

import pydicom
import os
import glob
import sys

filename = sys.argv[-1]

if os.path.isfile(filename): 
    pass

else:
    filename = glob.glob(filename + "/*")

for i in range(len(filename)):
    dcm = pydicom.dcmread(filename[i])
    print(dcm)


動作確認 バッチファイルを作成

いよいよ最後の工程です。バッチファイルを作成します。

以前jupyter notebookの起動をアイコン化でも記事にしましたバッチファイルを作成します。

まずはデスクトップにテキストファイルを作成します。

そのファイルに

cd [pythonプログラムが置いてあるフォルダのパス]
python [プログラム名.py] %1
PAUSE

とコードを書き保存します。

拡張子を”bat”と変更してください。2行目、3行目のコードはそれぞれの環境に合わせて書き換えてくださいね。

そのファイルにDICOMファイルをドラッグドロップしてみてください。

どうですか?DICOMタグの一覧が表示されましたか?


最後に

いろいろとプログラムを書いていくと、DICOMタグの重要性が分かってきている今日この頃です。なにかと確認することが多く、このプログラムは大変重宝しています。みなさんも是非とも作成して利用してみてください。

おつかれさまでした。


広告
上新電機 パソコン買取サービス
The post ドラッグアンドドロップでDICOMタグが見れるプログラムを作ってみた。 first appeared on 診療放射線技師がPythonをはじめました。.]]>
712
DICOMタグをいじってみる http://radiology-technologist.info/post-601 Sun, 10 May 2020 21:31:54 +0000 http://radiology-technologist.info/?p=601 医療画像はDICOM画像として保存されており、その […]

The post DICOMタグをいじってみる first appeared on 診療放射線技師がPythonをはじめました。.]]>
医療画像はDICOM画像として保存されており、そのタグには患者情報や、検査情報、画像情報と様々な内容が入っています。

画像処理を行っていく際に、タグ情報を使うことは多々あります。

そこで、今回はpythonでDICOMタグを扱っていく方法をやってみたいと思います。

DICOMを読み込む方法や、表示方法は以下の記事に記載していますのでそちらを参照ください。

DICOMタグの取得


DICOMタグの構成

まずは、タグ情報を扱うにはDICOMタグの構成を知らなければなりません。タグの一つ一つの中には以下のようないろいろな情報がまとまって一つの情報となっています。(記載事項以外にもあるかも。。。)

Tagタグの番号
Attribute Name項目名
Attribute Name J日本語の項目名
(上図に記載されていません)
Attribute Typeデータの必須事項、条件付き必須事項等
(上図に記載されていません)
VRデータの型
Lengthデータの長さ
(上図に記載されていません)
Value

なので、プログラミングで使っていく際にはDICOMデータのどの項目を扱っていくのか指定しなければならないということになります。


広告
デル株式会社

予約タグとプライベートタグ

タグには必須事項となるタグがあり、それが予約タグとなります。

また、必須項目のタグはモダリティーにより異なってきます。

また、患者情報であったり、検査情報であったり、必須項目は偶数番のブロック(タグ番号の前側の番号)で指定されています。一方、プライベートタグに関しては奇数番で指定できます。

各モダリティーごとの必須項目タグはLIBERWORKS社のサイトに記載されていますので興味があるかたはご覧ください。


広告
HP Directplus -HP公式オンラインストア-

タグ情報を変更する

既存のタグ情報を変更するする場合は以下のコードでできます。

dcm[0x0008,0x0070].value = ‘変更する値’

なお、このままではタグ情報を変更しただけで保存できていませんので次に保存していきましょう。


広告
BTOパソコン・パソコン関連商品がお買い得!パソコン工房のセール

タグ情報を画像に保存する

タグ情報を変更したら今度は、画像に保存しなければなりません。

save_as(file_path, write_like_original=False)

で保存することができます。

write_like_original=False

の部分は、DICOMに準拠した形式ではなく個人で指定した形式で保存しますか?という事です。なので、Trueにしてしまうと形式に反してしまうかもしれませんのでここの部分はFalseのままがいいでしょう。


プライベートタグを作成する

プライベートタグに関しては、企業がそれぞれ作成しています。

例えば、SiemensのMRI装置でDiffusionを撮像した場合、b値等がプライベートタグとして登録されていますが、10年前のcanon製のMRI 装置では記載されていません。

プライベートタグを画像に載せたい場合はまず、ブロックを作成しなくてはなりません。患者に関する情報を載せているグループや、検査情報を載せているグループといったものです。

block = dcm.private_block(0x0019, “diffusion parameter”, create=True)
block.add_new(0x0c, ‘SH’, b_val)

で画像に登録をしたら保存をします。
dcm.save_as(file_path, write_like_original=False)

これで、プライべートタグの登録が完了です。


資料

DICOM規格に関してはJIRAのホームページに詳細な記載がありますので興味がある方は以下のリンクからご覧ください。

JIRA DICOMの世界

JIRA 勉強会資料 DICOMに慣れる -現場で DICOM 接続に慌てないための知識 (3) 画像系の通信 -



環境

  • windows10
  • python3.6.1
  • Anaconda custom(64-bit)
  • PyCharm2020.2(Communication Edition)

広告
上新電機 パソコン買取サービス
The post DICOMタグをいじってみる first appeared on 診療放射線技師がPythonをはじめました。.]]>
601
DICOM画像からjpg、png等の汎用画像に変更 http://radiology-technologist.info/post-413 Wed, 22 Apr 2020 02:57:22 +0000 http://radiology-technologist.info/?p=413 前回、OpenCVを用いてマウスホイールを使って画 […]

The post DICOM画像からjpg、png等の汎用画像に変更 first appeared on 診療放射線技師がPythonをはじめました。.]]>
前回、OpenCVを用いてマウスホイールを使って画像を切り替えるといった記事を書きました。しかし、そのままではつまらないので、DICOM画像をjpeg,pngといった汎用画像として保存する方法を記載したいと思います。


手順

手順としては、

  1. DICOM画像を読み込み
  2. ピクセルデータを配列として読み込み
  3. ww、wlで最適な条件に合わせる
  4. その配列をww、wlの範囲に落とし込む
  5. 汎用画像として保存

の手順となります。

ただ、前回までの記事で1~4までの作業は完了しいるので残り5番の作業となります。(前回のコードは下のリンクから)

http://radiology-technologist.info/2020/04/17/post-501/

それでは、5番の作業をさらに細かく分けていきましょう。

  1. 何をきっかけに画像保存をするか?
  2. 保存先の指定は?
  3. ファイル名は?
  4. 画像形式は何にするか?
  5. コードを作成

それでは、それぞれ決めていきましょう。


何をきっかけに画像保存をする?

まずは、何をきっかけに画像保存をするか?

せっかく前回マウスイベントで作業をしたので今回もマウスイベントで画像保存する方法をやっていきましょう。

右クリックを押した時に、画像保存ができるようにします。(過去のマウスイベントの記事はこちら

http://radiology-technologist.info/2019/11/18/post-306/

右クリックを押した時の指定は

if event == cv2.EVENT_RBUTTONDOWN:

でできます。

前回のマウスホイールを回した時の関数の後に記載します。

インデントの位置は

if event == cv2.EVENT_MOUSEWHEEL:

と同じ位置にします。


保存先の指定は?

新しく作成する画像の保存先の指定はどのようにするか?

それはユーザーが指定できるように組みたいと思います。

右クリックを押した時に、ファイルダイアログが開いて画像の保存フォルダを指定できるようにします。

これも以前、記事で書いたフォルダ選択でやってみたいと思います。

http://radiology-technologist.info/2019/12/21/post-371/

以前の記事では、フォルダ内のファイル全部を選択する形となっていたので少しそこを修正、追加して使いたいと思います。前回のコードは以下となります。

# -*- coding: utf-8 -*-

import tkinter  
from tkinter import filedialog as tkFileDialog
import glob

#ファイルを一つ選択::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
def single_fileselect():
    root = tkinter.Tk()
    root.withdraw()

    fTyp = [('', '*')]

    iDir = 'C:/Desktop'

    filename = tkFileDialog.askopenfilename(filetypes=fTyp, initialdir=iDir)
    return filename

#ファイルを複数選択:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
def multi_fileselect():
    root = tkinter.Tk()
    root.withdraw()

    fTyp = [('', '*')]
    iDir = 'C:/Desktop'

    filenames = tkFileDialog.askopenfilenames(filetypes=fTyp, initialdir=iDir)

    return filenames

#ファイルをフォルダで一括選択:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
def folder_fileselect():
    root = tkinter.Tk()  
    root.withdraw()  

    iDir = 'C:/Desktop'  

    dirname = tkFileDialog.askdirectory(initialdir=iDir)

    filenames = glob.glob(dirname +"/*")
    return filenames

フォルダだけ選択できればいいので

#ファイルをフォルダで一括選択の下から2段目

filenames = glob.glob(dirname +”/*”)

を削除すればフォルダのパスのみ取得できますので

関数名 folder として作成してしまいましょう

# -*- coding: utf-8 -*-

import tkinter  
from tkinter import filedialog as tkFileDialog
import glob

#ファイルを一つ選択::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
def single_fileselect():
    root = tkinter.Tk()
    root.withdraw()

    fTyp = [('', '*')]

    iDir = 'C:/Desktop'

    filename = tkFileDialog.askopenfilename(filetypes=fTyp, initialdir=iDir)
    return filename

#ファイルを複数選択:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
def multi_fileselect():
    root = tkinter.Tk()
    root.withdraw()

    fTyp = [('', '*')]
    iDir = 'C:/Desktop'

    filenames = tkFileDialog.askopenfilenames(filetypes=fTyp, initialdir=iDir)

    return filenames

#ファイルをフォルダで一括選択:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
def folder_fileselect():
    root = tkinter.Tk()  
    root.withdraw()  

    iDir = 'C:/Desktop'  

    dirname = tkFileDialog.askdirectory(initialdir=iDir)

    filenames = glob.glob(dirname +"/*")
    return filenames

#フォルダを選択:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::::
def folder():
    root = tkinter.Tk()  
    root.withdraw()  

    iDir = 'C:/Desktop'  

    dirname = tkFileDialog.askdirectory(initialdir=iDir)

    return filenames

この関数を呼び出すことでフォルダの指定ができます。

フォルダパスはfolという変数名で受け取るようにします。

if event == cv2.EVENT_RBUTTONDOWN:

の後に

fol = fs.folder()

とすることでfolの変数内に指定したフォルダパスを入れることができます。


ファイル名は?

ファイル名ですが、DICOM画像と同じ名前で保存する方法としましょう。

ファイル選択したパスからファイル名だけを抽出する方法は以下の記事に記載してあります。

http://radiology-technologist.info/2020/01/28/post-417/

.rfind(“/”)

で最後から数えて “/” が何文字目にあるかを調べます。

そしてそのファイルパスから、その文字分だけ切り抜いてあげ、先ほど選択したフォルダの後ろにつけてあげればいいわけです。

変数couとして画像ファイル名の前にある”/”までの文字数を入れます。

cou = filenames[i].rfind(“/”)

(なお、[i]は後でfor文の中でこのコードを使うので入れています。)

その数字文ファイルパスから抜き出せばいいので

filenames[i][cou:]

これで、ファイル名の抜き出しが完了です。


画像形式は何にするか?

画像形式は、jpg,png,bmp,tiffどの形式にするか決まったら、保存する時、パスの最後に付けてあげればいいだけです。

なので

“.jpg” “.png” “.bmp” “.tiff”

これだけで、画像形式の指定が終わりです。


画像保存

次は、画像を保存していく工程になります。

cv2.imwrite(ファイルパス,データ)

のコードで行います。

まずは保存先のファイルパスを作成します。

ファイルパスは、画像保存のフォルダにファイル名、拡張子を付けて作成します。

保存フォルダはfolの変数名で取得しましたね。

次にファイル名は

cou = filenames[i].rfind(“/”)

filenames[i][cou:]

の2文で抜き出しができました。

画像形式はjpegとしますと”.jpg”となりますので

ファイルパスfnameは

fname = fol + filenames[i][cou:] + “.jpg”

となります。

画像データはdcm_copyとなります。

後はfor文の中に上のコードを入れてあげればいいので、マウスイベントの文も合わせると

if event == cv2.EVENT_RBUTTONDOWN:
	fol = fs.folder()

	for i in range(len(dcm_copy)):

		cou = filenames[i].rfind("/")
		fname = fol + filenames[i][cou:] + ".jpg"

		cv2.imwrite(fname,dcm_copy[i])

これでコードは完成しました。


ただ、ここで問題があります。

今作成しているコードはマウスイベントの関数内でありますので、関数外で宣言しているfilenamesおよび、画像表示に作成した配列dcm_copyは使用できません。なのでこれを使うにはglobalで宣言しておかなければなりません。なので、マウスイベントの関数を宣言した直下に宣言を追加します。前回のコードでgという変数を宣言したと思いますが、その後に追加します。

def onMouse(event, x, y, flag, params):
global g,dcm_copy,filenames

これで、関数外で宣言していたfilenamesおよび、dcm_copyが使えるようになりました。


広告
デル株式会社

完成したコード

# coding: UTF-8
import fileselect as fs   #ファイルパス取得のモジュールをインポート
import numpy as np
import pydicom
import math
import copy
import cv2

def onMouse(event, x, y, flag, params):
    global g,dcm_copy,filenames
    g = params

    if event == cv2.EVENT_MOUSEWHEEL:
        if flag > 0:
            g -= 1
        elif flag < 0:
            g += 1

        if g <= 0:
            g = 0
        elif g >= len(filenames)-1:
            g = len(filenames)-1

    if event == cv2.EVENT_RBUTTONDOWN:
        fol = fs.folder()

        for i in range(len(dcm_copy)):

            cou = filenames[i].rfind("/")
            fname = fol + filenames[i][cou:] + ".jpg"

            cv2.imwrite(fname,dcm_copy[i])

def make_LUT(val):
    global lookup_tbl, dcm_copy
    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())

filenames = fs.multi_files()

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)

make_LUT(0)
g = 0

while 1:
    params = g
    cv2.setMouseCallback('dcm_image', onMouse, params)
    cv2.imshow('dcm_image', dcm_copy[g])

    k = cv2.waitKey(1)
    if k == ord('q'):
        break

となります。いかがでしたか?

お疲れ様でした。


広告
HP Directplus -HP公式オンラインストア-

環境

  • windows10
  • python3.6.1
  • Anaconda custom(64-bit)
  • PyCharm2020.2(Communication Edition)

広告
上新電機 パソコン買取サービス
The post DICOM画像からjpg、png等の汎用画像に変更 first appeared on 診療放射線技師がPythonをはじめました。.]]>
413
DICOM画像のピクセル値を見てみる http://radiology-technologist.info/post-135 Sat, 24 Aug 2019 14:44:45 +0000 http://mcu03iphuk.m5.valueserver.jp/wordpress/?p=135 今回はPydicomを使ってDICOM画像のピクセ […]

The post DICOM画像のピクセル値を見てみる first appeared on 診療放射線技師がPythonをはじめました。.]]>
今回はPydicomを使ってDICOM画像のピクセルデータを見てみたいと思います。

ピクセルデータを表示してみる


ピクセルデータの表示からです。

import pydicom
from matplotlib import pyplot as plt
%matplotlib inline


ds = pydicom.dcmread('CT000060')
print(ds.pixel_array)


plt.imshow(ds.pixel_array,cmap=plt.cm.bone)
plt.show()

いかがでしょうか?

[[-2048 -2048 -2048 ... -2048 -2048 -2048]
 [-2048 -2048 -2048 ... -2048 -2048 -2048]

の部分がピクセルデータとなります。しかし’-2048’はFOVの領域外、

画像の左上3ピクセルのデータと右3ピクセルのデータの為

正しいデータかどうかちょっと分かりません。。。。。。


それでは、違う画像で試してみたいと思います。

放射線治療で用いられるチーズファントムの画像があったのでそれで見てみたいと思います。。

まず、image-Jでの計測結果を見てみたいと思います。

まず、image-Jでチーズファントムを開き

メニューバーの「Edit」⇒「Selection」⇒「Specify」を開き

画像座標(260,260)を左上頂点に横20ピクセル、高さ20ピクセルのデータを見てみたいと思います。

その領域の平均値22.067、標準偏差13.1、最小値―17、最大値55の結果が出ました。


それでは、pydicomを使って同じ領域のピクセルデータを見てみたいと思います。

import pydicom
from matplotlib import pyplot as plt
from matplotlib import patches as patches
%matplotlib inline

ds = pydicom.dcmread('cheeze')

fig,ax = plt.subplots(1)

rect = patches.Rectangle((260,260),20,20,linewidth=1,edgecolor='r',facecolor='none')

ax.add_patch(rect)
ax.imshow(ds.pixel_array,cmap=plt.cm.bone)
plt.show()

print("mean    " + str(ds.pixel_array[260:280,260:280].mean()))
print("std    " + str(ds.pixel_array[260:280,260:280].std()))
print("max     " + str(ds.pixel_array[260:280,260:280].max()))
print("min     " + str(ds.pixel_array[260:280,260:280].min()))

結果は平均値22.0675、標準偏差13.083、最小値-17、最大値55の結果ができました。

Image-Jの結果が

平均値22.067、標準偏差13.1、最小値―17、最大値55

同様の結果となりました。

pydicomでもきちんとピクセルデータが見れることが分かりました。


次回は、Pydicomで画像表示した際のWL,WWの設定をやってみたいと思います。


広告
上新電機 パソコン買取サービス
The post DICOM画像のピクセル値を見てみる first appeared on 診療放射線技師がPythonをはじめました。.]]>
135
DICOM画像を表示 http://radiology-technologist.info/post-112 Tue, 23 Jul 2019 11:16:29 +0000 http://mcu03iphuk.m5.valueserver.jp/wordpress/?p=112 今回はPydicomを使ってDICOM画像の表示を […]

The post DICOM画像を表示 first appeared on 診療放射線技師がPythonをはじめました。.]]>
今回はPydicomを使ってDICOM画像の表示をしてみたいと思います。

画像の表示

DICOM画像を表示する方法は4つの方法がPydicomにはサポートされています。

  • matplotlibを使って表示
  • Tkinterを使って表示
  • Pillowを使って表示
  • wxPythonを使って表示

とりあえず、matplotlibを使って表示させてみたいと思います。

import pydicom
from matplotlib import pyplot as plt
%matplotlib inline    #jupyter notebookでない場合は不要

ds = pydicom.dcmread('CT2.dcm')

plt.imshow(ds.pixel_array,cmap=plt.cm.bone)
plt.show()               #jupyter notebookでない場合は必要

plt.show()

はjupyter notebookを使用している場合は不要ですが,他のコード環境を使用している場合は必要となります。

どうでしょう?DICOM画像が表示できましたか?

この画像は、いつも見慣れたグレイスケールの画像ですが

plt.imshow(ds.pixel_array,cmap=plt.cm.bone)

boneの部分を変えることで

続いてはhotで設定してみました。

どうですか。アイソトープの画像みたくなりましたね。

この様に、cmapの設定を変更することで、カラースケールを変えることができます。

matplotlibのリファレンスページには約80種類のカラーマップがあり

colorExamplesのページにて確認することができます。

是非ともいろいろと試してみてください。



広告
上新電機 パソコン買取サービス
The post DICOM画像を表示 first appeared on 診療放射線技師がPythonをはじめました。.]]>
112
DICOMタグの取得 http://radiology-technologist.info/post-71 Sat, 20 Jul 2019 22:10:35 +0000 http://mcu03iphuk.m5.valueserver.jp/wordpress/?p=71 今回はPydicomを使ってDICOMタグの取得を […]

The post DICOMタグの取得 first appeared on 診療放射線技師がPythonをはじめました。.]]>
今回はPydicomを使ってDICOMタグの取得をやってみたいと思います。

下準備

まだ、よくわからないという方のために詳しく説明していきたいと思います。

ある程度分かっている方は下準備は飛ばしてください

まずDICOM画像が手元にない場合は、Image-JのHPからサンプル画像がダウンロードできます。

拡張子(ファイル名の最後の文字)がdcmの画像がDICOM画像です。

ダウンロードした画像は、 デスクトップに「pydicom」というフォルダを作って入れておいてください。

(フォルダ名は何でも構いませんが、日本語のフォルダ名はプログラム実行時にエラーになってしまいますので必ず半角英数にて付けてください。)

続いて、コマンドプロンプトを立ち上げます。

そこに

jupyter notebook

と打ち込みます。少し無反応な時間が経ったのち、webブラウザが立ち上がり下図のページが立ち上がります。

デスクトップをクリックし、pydicomのフォルダに移動します。

先ほど、ダウンロードした画像が入っていることが確認できます。

次に、このフォルダにコードを書いていくファイルを作成したいと思います。

画面右側上方に『New』というボタンがありますので

そちらをクリックし、『Python3』を選択します。

pythonコードのファイル名を決めます。

jupyterと書いてあるわきのUntitleをクリックします。

新しくい開いたダイアログに名前を付けて『Rename』をクリックします。

そうするとデスクトップにある『pydicom』のフォルダに

『先ほど付けた名前.ipynb』

のファイルが作成されていると思います。

ここまでできましたら、とりあえず下準備は完了です。


広告
デル株式会社

DICOMタグの表示

まず、簡単なところからDICOMタグを表示させてみましょう。

import pydicom

ds = pydicom.dcmread('CT2.dcm')

print(ds)

コードが書けたら

shift + enterキーを押します。

いかがでしょうか?

下図のようにDICOMタグが表示できたと思います。

例えば、他のファイルのタグを見たい場合は上記コードの

ds = pydicom.dcmread(‘CT2.dcm‘)

下線部分に、ファイル名を指定すればOKです。

また、他のフォルダにあるファイルを指定したい場合はフルパスを指定することで表示することができます。

また、DICOMタグは項目ごとに表示することもできます。

例えば、モダリティーを見たい場合は

import pydicom

ds = pydicom.dcmread('CT2.dcm')
md = ds.Modality

print(md)

で『CT』と表示させることもできます。

また、タグコードで指定することもできます。

コードは16進数に変換してあげる必要があるので、

16進数に変換する『0x』とコードの下2桁4桁を合わせて指定、それらコードを [ ] で囲み ます。

なので

モダリティーの場合は     [0x0008,0x0060]

患者名の場合は        [0x0010,0x0010]

を指定します。また、値だけど取得したい場合は

『.value』

を追加してあげます。指定しない場合はタグコードとタグ名、タグの値が返されます。

import pydicom


ds = pydicom.dcmread('CT2.dcm')


md = ds[0x08,0x60].value
print('ds[0x08,0x60].value   ⇒ '+   str(md))

nm=ds[0x10,0x10].value
print('ds[0x10,0x10].value   ⇒ '+   str(nm))

md = ds[0x08,0x60]
print('ds[0x08,0x60]         ⇒ '+   str(md))

nm=ds[0x10,0x10]
print('ds[0x10,0x10]         ⇒ '+   str(nm))

タグコードで指定する場合は、4桁で指定することができます!!

(画像が消えちゃっていたので追加したため背景色が変わっちゃっています・・・)

DICOMタグの取得は、 解像度を調べたり、撮影条件を調べたりといろいろと便利です。

検査の画像をシリーズで取得した場合、ファイル名がUIDでの表記になっていることがあり、その場合にファイル名を画像番号で名前変更したりと思った以上に使えますよ。


広告
上新電機 パソコン買取サービス
The post DICOMタグの取得 first appeared on 診療放射線技師がPythonをはじめました。.]]>
71