マイブログ リスト

医療言語処理講座

2019年1月18日金曜日

Pyゼミ2.02 PyQt5によるDICOM画像表示とページング

複数のDICOM画像をボタンをクリックしてページングしてみよう


keyword: PyQt5,DICOM,ページング


PyQt5のボタンの作り方とボタンをクリックした時の処理の仕方


前回(Pyゼミ2.01)はGUI上にDICOM画像を表示するだけでした。
今回は表示した画像の下に2つのボタン,NextボタンとPreviousボタンを付けます。

Nextボタンをクリックすると次のDICOM画像が表示され,
Previousボタンをクリックすると前のDICOM画像を表示します。

2つのボタンをQPushButtonクラスを使って次のように作成します。
        nextBtn = QPushButton("Next")          
        prevBtn = QPushButton("Previous")    

次にボタンをクリックした時(シグナルの発生)に行う処理(スロット)をclicked.connect関数を使って次のように記述します。
        nextBtn.clicked.connect(self.nextButtonClicked)            
        prevBtn.clicked.connect(self.prevButtonClicked)
nextBtnボタンがクリックされると,self.nextButtonClicked関数が呼びだされ,この関数に次の画像を読み込み,ウインドウに画像を表示するプログラムを記述します。


nextButtonClicked関数の処理


次の画像を表示するため画像ファイル番号fnoを一つインクリメントします。
        self.fno += 1

インクリメントしたfnoの値が画像枚数を超えたら,0にセットして元に戻す処理をしています。
        if self.fno == len(self.dcmfnm):
            self.fno = 0

DICOM画像のファイル名が保存されたリストdcmfnmのfno番目のDICOM画像を読み込み一旦PNGファイル(tmp.png)に保存します,
        self.readDicom2png(self.dirnm + self.dcmfnm[self.fno], 'tmp.png')

PNGファイルをQPixmapクラスでpixmapに読み込み,setPixmapクラスでラベルlblimgにセットすることで画像が更新されます。
        pixmap = QPixmap('tmp.png')
        self.lblimg.setPixmap(pixmap)
また,ファイル名も更新します。
        self.lblfnm.setText( "  File Name  : " + self.dcmfnm[ self.fno ] )


前回,GUIでDICOM画像を表示させるプログラムと比べて注意する点は以下のとおりです。
・ファイル名を表示するラベルlblfnmがself.lblfnmに変更しています。
・画像を表示するラベルlblimgがself.lblimgに変更しています。
これはself.を付けてインスタンス変数にすることで,クラス内で変数を共有することができます。
したがって,他の関数から値を変更したりすることができます。



プログラムを入力して実行してみましょう。



import sys,os import numpy as np import cv2 import pydicom from PyQt5.QtCore import * from PyQt5.QtGui import * from PyQt5.QtWidgets import * from PyQt5.Qt import * class DicomPageButton(QWidget): def __init__(self, dirname): super().__init__() self.dcmfnm = os.listdir(dirname) # ディレクトリ内のDICOMファイル名 self.dirnm = dirname self.fno = 0 # 表示するファイル番号,最初は0番 print("画像枚数:",len(self.dcmfnm)) self.initUI() def initUI(self): #ファイル名を表示するラベルの作成 self.lblfnm = QLabel(" File Name : " + self.dcmfnm[self.fno]) fnmbox = QVBoxLayout() fnmbox.addWidget(self.lblfnm) #画像を読み込みラベルにセットする self.readDicom2png(self.dirnm + self.dcmfnm[self.fno], './tmp.png') pixmap = QPixmap('./tmp.png') self.lblimg = QLabel() self.lblimg.setPixmap(pixmap) #画像表示レイアウトの設定 imgbox = QVBoxLayout() imgbox.addWidget(self.lblimg) # NextとPreviousボタンの作成 nextBtn = QPushButton("Next") prevBtn = QPushButton("Previous") nextBtn.clicked.connect(self.nextButtonClicked) prevBtn.clicked.connect(self.prevButtonClicked) hbox = QHBoxLayout() #ボタンを配置するための水平ボックス hbox.addWidget(prevBtn) hbox.addWidget(nextBtn) # 各パーツを配置する垂直なボックスを作成 mainVbox = QVBoxLayout() mainVbox.addLayout(fnmbox) mainVbox.addLayout(imgbox) mainVbox.addLayout(hbox) # 垂直ボックスをウィンドウにレイアウトする self.setLayout(mainVbox) self.setWindowTitle('DICOM Paging') self.show() def nextButtonClicked(self): #Nextボタンをクリックした時の処理 self.fno += 1 if self.fno == len(self.dcmfnm): self.fno = 0 print("Push Next Button \t#", self.fno, "file:",self.dcmfnm[self.fno]) self.readDicom2png(self.dirnm + self.dcmfnm[self.fno], 'tmp.png') self.pixmap = QPixmap('tmp.png') self.lblimg.setPixmap(self.pixmap) self.lblfnm.setText(" File Name : " + self.dcmfnm[self.fno])
def prevButtonClicked(self): #Priviousボタンをクリックした時の処理 self.fno -= 1 if self.fno == -1: self.fno = len(self.dcmfnm)-1 print("Push Previous Button \t#", self.fno, "file:",self.dcmfnm[self.fno]) self.readDicom2png(self.dirnm + self.dcmfnm[self.fno], 'tmp.png') self.pixmap = QPixmap('tmp.png') self.lblimg.setPixmap(self.pixmap) self.lblfnm.setText(" File Name : " + self.dcmfnm[self.fno]) # DICOM画像読み込んでウィンドニング後PNGに保存 def readDicom2png(self, dcmfnm, tmpfnm): ds = pydicom.read_file(dcmfnm) #DICOM画像を読み込む wc = ds.WindowCenter #ウィンドウセンター値を代入 ww = ds.WindowWidth #ウィンドウ幅を代入 img = ds.pixel_array #画素値を代入 #表示画素値の最大と最小を計算する max = wc + ww / 2 min = wc - ww / 2 #ウインドニング処理 img = 255 * (img - min)/(max - min) #最大と最小画素値を0から255に変換 img[img > 255] = 255 #255より大きい画素値は255に変換 img[img < 0 ] = 0 #0より小さい画素値は0に変換 img = img.astype(np.uint8) cv2.imwrite(tmpfnm, img)
if __name__ == '__main__': app = QApplication(sys.argv) dirname = "../dcmdir1/" #DICOM画像の入ったディレクトリ ex = DicomPageButton(dirname) sys.exit(app.exec_())



実行結果


実行するとウィンドウが現れ,画像の下のボタンをクリックすると次の画像が表示されます。

ターミナルにはクリックスされたボタンの種類,ファイル番号とファイル名が表示されます。

$ python␣dicomPageButton.py⏎
画像枚数: 8
Push Next Button  # 1 file: Chest02
Push Next Button  # 2 file: Chest01
Push Next Button  # 3 file: Brain01






プログラムの説明


main関数

変数dirnameにDICOM画像が保存されたディレクトリを指定します。
これを引数にDicomPageButtonクラスのインスタンスを生成しています。

イニシャライザ__init__()関数

画像表示に必要な値の初期化を行っています。
DICOM画像の入ったディレクトリ(dirname)内のDICOMファイル名を読み込んでリストself.dcmfnmに代入しています。
        self.dcmfnm = os.listdir(dirname) 
DICOMファイル名も画像読み込み時に使用するので,self.dirnmに代入します。
        self.dirnm  = dirname
表示する画像番号self.fnoを初期化します。

initUI()関数

2つのラベルと2つのボタンを作成しています。
lblfnmは,DICOM画像のファイル名を表示するためのラベルです。
lblimgは,DICOM画像表示のためのラベルです。
nextBtnは,次の画像を表示するボタンです。
prevBtnは,前の画像を表示するボタンです。

各ラベルとボタンをレイアウトするBoxを作成しています。
fnmboxは,ラベルlblfnmをレイアウトします。
imgboxは,ラベルlblimgをレイアウトします。
hboxは,ボタンnextBtnとprevBtnを水平にレイアウトします。
mainVBoxは,上記2つのboxを垂直にレイアウトします。

最後にウィンドウに出力する処理を行います。
        self.setLayout(mainVbox)    
        self.setWindowTitle('DICOM Paging')    
        self.show()


nextButtonClicked()関数

self.dcmfnmの中の次のDICOM画像を読み込み,self.lblimgに表示する処理を行います。表示したDICOMファイル名を更新します.


prevButtonClicked()関数

self.dcmfnmの中のひとつ前のDICOM画像を読み込み,self.lblimgに表示する処理を行います。
表示したDICOMファイル名を更新します.

readDicom2png()関数

DICOM画像を読み込み,PNGファイルに出力します(Pyゼミ1.02参照)。



課 題


1)指定した画像番号のDICOMファイルとファイル名の更新の以下の4行が,iniUI関数の最初とnextButtonCliched関数とprevButtonClicked関数の3か所に記述されています.これをdisplayImageという関数にまとめなさい.
self.readDicom2png(self.dirnm + self.dcmfnm[self.fno], 'tmp.png') self.pixmap = QPixmap('tmp.png') self.lblimg.setPixmap(self.pixmap) self.lblfnm.setText(" File Name : " + self.dcmfnm[self.fno])

2)画像の表示順序がImage Positionに従っていません.頭部から腹部に向かってImage Positiondeソートして表示するように修正しなさい.(Pyゼミ1.04,dicomSort.pyを参照しなさい)



0 件のコメント:

コメントを投稿