DICOM画像からjpg、png等の汎用画像に変更

OpenCV

前回、OpenCVを用いてマウスホイールを使って画像を切り替えるといった記事を書きました。しかし、そのままではつまらないので、DICOM画像をjpeg,pngといった汎用画像として保存する方法を記載したいと思います。


手順

手順としては、

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

の手順となります。

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

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

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

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


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

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

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

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

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

if event == cv2.EVENT_RBUTTONDOWN:

でできます。

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

インデントの位置は

if event == cv2.EVENT_MOUSEWHEEL:

と同じ位置にします。


保存先の指定は?

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

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

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

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

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


# -*- 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画像と同じ名前で保存する方法としましょう。

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

.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

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

お疲れ様でした。


環境

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

タイトルとURLをコピーしました