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プログラムを組んでいる時、マウスを使って何かをしたいという事はとても多いと思います。そこで、今回はそのマウスコントロール(マウスイベント)を紹介したいと思います。
マウスイベントの種類としては、
の3つのイベントがあります。それぞれ
と名前が付けられています。イベントを呼び出すコードは
fig.canvas.mpl_connect('イベント名', イベントが発生した時の動作を記した関数)
で呼び出します。よって、マウスをクリックした時、クリックを離した時、マウスホイールを回した時のイベント呼び出しは以下となります。
#クリック時 fig.canvas.mpl_connect('button_press_event', on_button_press) #クリックを離した時 fig.canvas.mpl_connect('button_release_event', on_button_release) #マウスホイールを回した時 fig.canvas.mpl_connect('scroll_event', wheel_scroll)
マウスには右クリック、マウスホイールクリック、左クリックの3種類があります。
OpenCvの場合は、それぞれが独立していますが、matplotlibでは、一つのイベントとしてまとめられています。
ではどのようにして区別しているかというと、左クリックをした時は”1”、マウスホイールを押した時は”2”、右クリックを押した時は”3”とevent.buttonという変数に番号が返されます。
なので、関数内に下の様なコードを記すことで機能を分けることができます。
def on_button_press(event): # マウスクリック if event.button == 1: ~~~ ~~~ if event.button == 2: ~~~ ~~~ if event.button == 3: ~~~ ~~~
それでは、簡単なコードを紹介します。
左クリックを押すころで背景色を”青”、マウスホイールを押すことで”白”、右クリックを押すことで”赤”に変えるプログラムを紹介します。
# -- coding utf-8 -- import matplotlib.pyplot as plt def on_button_press(event): if event.button == 1: print(event) fig.patch.set_facecolor('blue') if event.button == 2: print(event) fig.patch.set_facecolor('white') if event.button == 3: print(event) fig.patch.set_facecolor('red') fig.canvas.draw() fig = plt.figure() fig.canvas.mpl_connect('button_press_event', on_button_press) plt.show()
上記コードを実行すると以下のようになります。
eventの情報を表示したものになります。中にはダブルクリックの情報もありますので、ダブルクリックしたら背景色が”緑色”になるコードを追加してみたいと思います。(下記コード10行~13行目)
※dbclickではなく、dblclickです。lが入ることに注意してくださいね。
# -- coding utf-8 -- import matplotlib.pyplot as plt def on_button_press(event): if event.button == 1: print(event) fig.patch.set_facecolor('blue') if event.button == 1 and event.dblclick == True: print(event) fig.patch.set_facecolor('green') if event.button == 2: print(event) fig.patch.set_facecolor('white') if event.button == 3: print(event) fig.patch.set_facecolor('red') fig.canvas.draw() fig = plt.figure() fig.canvas.mpl_connect('button_press_event', on_button_press) plt.show()
いかがでしょうか?ダブルクリックの時の動作も指定することができました。
次に、マウスホイールを回した時の処理について書いていきたいと思います。
マウスホイールのイベントは以下で呼び出します。
fig.canvas.mpl_connect('scroll_event', wheel_scroll)
マウスホイールを回した時の関数ないに、eventで返される値を表示するコードを24~25行目に追加します。
# -- coding utf-8 -- import matplotlib.pyplot as plt def on_button_press(event): if event.button == 1: print(event) fig.patch.set_facecolor('blue') if event.button == 1 and event.dblclick == True: print(event) fig.patch.set_facecolor('green') if event.button == 2: print(event) fig.patch.set_facecolor('white') if event.button == 3: print(event) fig.patch.set_facecolor('red') fig.canvas.draw() def wheel_scroll(event): print(event) fig = plt.figure() fig.canvas.mpl_connect('button_press_event', on_button_press) fig.canvas.mpl_connect('scroll_event', wheel_scroll) plt.show()
マウスホイールを回した時の動作はクリック時の時と同じevent.buttonの変数に”up”、”down”で返ってきいることが分かります。
サンプルコードです。
マウスホイールを回した時に背景色が変わるプログラムを作成します。
まずは、カラーリストを作成します。(下記コード28行目)
続いてcolの変数を宣言し(下記コード46行目)、その変数の番号によってリストにある色を背景色として指定します。(下記コード41行目)
colの変数のglobal化をお忘れなく(下記コード25行目)忘れてしまうと、マウスホイールを回しても変数が0でリセットされてしまいます。
後は、マウスホイールを回すことでcolの変数を変えるコードを書きます。(下記コード30~38行目)32~33行目、37~38行目はcolの変数がリストの要素数を超えた場合にリストの最初と、最後に戻すためのものです。
# -- coding utf-8 -- import matplotlib.pyplot as plt def on_button_press(event): if event.button == 1: print(event) fig.patch.set_facecolor('blue') if event.button == 1 and event.dblclick == True: print(event) fig.patch.set_facecolor('green') if event.button == 2: print(event) fig.patch.set_facecolor('white') if event.button == 3: print(event) fig.patch.set_facecolor('red') fig.canvas.draw() def wheel_scroll(event): global col print(event) colorlist = ["r", "g", "b", "c", "m", "y", "k", "w"] if event.button == "up": col -= 1 if col == -1: col = 7 if event.button == "down": col += 1 if col == 7: col = 0 print(col) fig.patch.set_facecolor(colorlist[col]) fig.canvas.draw() fig = plt.figure() col = 0 fig.canvas.mpl_connect('button_press_event', on_button_press) fig.canvas.mpl_connect('scroll_event', wheel_scroll) plt.show()
いかがでしたか?マウスホイールを回すことで背景色が変わるプログラムはできましたか?
今回、クリックを離した時の動作の説明は省略させていただきましたが、クリックした時と同様ですので試してみてください。
マウスコントロールは、画像上でroiを設定したり、WW,WLの変更、マウスホイールは画像を切り替えてみたりと業務上でも常に使っている機能です。
この機能を設定できるようになればプログラムの幅も広がり楽しくなること間違いなしです。
是非、使ってみてください。
次回は、matplotlibで何かのキーを押した時のイベントをやってみたいと思います。
http://radiology-technologist.info/post-1847
まず、「MRI画像をスライダーを使ってウインドウ調整」からマウスホイールのイベントを持ってきます。
def onMouse(event, x, y, flag, params): if event == cv2.EVENT_MOUSEWHEEL: # ホイールを回したときの動作 print('マウスホイール' + str(g)) if flag > 0: #もしflagが正の数だったら画像番号gを1引く g -= 1 # -= とはgの数から1を引いた数をiに代入すること elif flag < 0: #もしflagが負の数だったら画像番号gを1足す g += 1 # += とはgの数から1を引いた数をgに代入すること if g <= 0: #もし、gが0より小さかったら g = 0 #iは0 elif g >= len(filenames)-1: #もし、gが要素数以上だったら g = len(filenames)-1 #要素数より1少ない数に
これをまず、前回のコードに貼り付けていきます。
なお、前回変数はiを用いていましたがfor文でiを使っているので明確に区別するため今回はgという変数を指定しています。
import fileselect as fs #ファイルパス取得のモジュールをインポート import numpy as np import pydicom import math import copy import cv2 def onMouse(event, x, y, flag, params): if event == cv2.EVENT_MOUSEWHEEL: # ホイールを回したときの動作 print('マウスホイール' + str(g)) if flag > 0: g -= 1 elif flag < 0: g += 1 if g <= 0: g = 0 elif g >= len(filenames)-1: g = len(filenames)-1 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
以前は、while分を用いて画像表示をずっとループさせることで対処していました。しかし、そのループの中でww,wlの値を取得し、ルックアップテーブルの更新、それを画像配列に適応といった作業をしており非常に非効率的ですので、そこを修正していきたと思います。
まず、ルックアップテーブルの作成から画像配列への適応まではww,wlのスライダーを変更した時のみやればいい作業なので49~61行目までを24行目にある関数make_LUTの関数に入れてしまいましょう。passの一文は削除してください。
ちょっと話が脱線しますが、初めの表に以下の一文を追加しておいてください。
#coding: UTF-8
これが無いとprintのコードで日本語が使えません。。。。
# 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): 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 def make_LUT(val): 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_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: cv2.imshow('dcm_image', dcm_copy[0]) k = cv2.waitKey(1) if k == ord('q'): break
これで、機能の分散はできました。
しかし、このままでは画像表示ができません。DICOM画像は画素値がjpegやping画像のように256階調で収まっていないので、それをwwの範囲を256階調に当てはめていく必要があります。その処理が前回のコードでは画像表示の直前にあったのですが、今回はmake_LUTの関数の中に入っていますので、画像選択の後に一度make_LUTの関数を走らなければなりません。
関数を呼び出す場所はwhile文の前で、トラックバーの設定が終わった60行目にしましょう。
make_LUTの関数はトラックバーを変更した時の関数でトラックバーの値を引数としてmake_LUTに渡します。しかし、make_LUTの関数内ではcv2.getTrackbarPosで値を取得しているので正直、この引数はあまり意味がありません。
なので、引数は適当に数字の’0’を入れておきます。
60行目に以下の一文を挿入してください。
make_LUT(0)
これで、ルックアップテーブルの作成と、それを適応した画像配列への適応への流れを作る事ができました。
ここで作成した画像配列を表示すればいいことになります。
しかし、ここでまた問題があります。
make_LUTの関数内ではlookup_tblとdcm_copyの配列を使います。しかし、make_LUTの関数は引数を一つしか受けないという事です。
先ほど60行目に記入した関数を指定する場合は、いくらでも引数を指定できます。しかし、今回はスライダーの設定(58~59行目)と共有しています。そちらの方は引数を幾つも指定することはできません。
その為、関数外の変数、配列を使えるようにしなくてはいけません。
以下を見てください。
左側のコードでは、関数内で配列を5倍していますが、関数から戻って来ると値が元に戻ってしまっています。
関数内で更新したものは関数内でしか適応されません。(retuenで返せば別です)
右側のコードを見てください。関数内で’global 配列名(変数名)’とすることで引数として渡さなくても関数外の配列(変数)を使うことができるようになります。また、関数内で処理した結果も関数外に反映されます。
これを用いて画像表示用の配列を指定してあげましょう。
関数make_LUTの中で使いたい配列はlookup_tblとdcm_copyですので
24行目に
global lookup_tbl,dcm_copy
を追加します。この一文で配列lookup_tblとdcm_copyを使うことができるようになり、更新した結果も関数外で利用することができるようになりました。
いかがでしょうか?これで画像表示までできました。スライダーもきちんと機能していますよね?
しかし、まだマウスホイールで画像変更はできていません、次にそれを付け加えていきたいと思います。
まず、マウスイベント内で使っている変数gの初期化をしておかなくてはなりません。画像は選択した初めの画像を表示したいので0としておきましょう。
while分の中で宣言してしまうと常に0が代入されてしまうのでwhile文の直前で宣言しておきましょう
g = 0
の一文をwhile文の前に入れておきます。
そして、マウスホイール動かした際の動作は、画像を切り替えて表示する事なのでマウスイベントを呼び出す関数はwhile文の中に書きます。
そして、マウスイベントに引き渡す変数はparamsの中にまとめて指定します。
今回はgという変数しかないので
(詳しくは過去の記事「マウスを使って画像を切り替える」を見てください。)
params = g
そしてマウスイベントを呼び出す
cv2.setMouseCallback(‘dcm_image’, onMouse, params)
最後に、マウスイベント関数内にparamsの中にgという変数が入っていますよ!とマウスイベントの中で示してあげる必要があります。
また、このマウスイベントで変更したgは関数外で使用しますのでグローバル化しておきます。
global g
そしてその後に
g = params
を書いてあげましょう。
いよいよ完成しました。
動かしてみましょう。
あれ、画像が変わらない。。。。
忘れていました。
cv2.imshow(‘dcm_image’, dcm_copy[0])
dcm_copy[0] を dcm_copy[g]
0 ⇒ g
に変更をお忘れなく。
お疲れ様でした。
# 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 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
次回はトラックバーで画像調整したものをjpgやpngといった汎用画像に保存する方法をやっていきたいと思います。
ただ、今回切り替えていく画像はDICOM画像ではなくてピング形式や、ビットマップ等の一般的な画像ファイルとします。
右クリック(左クリック)した時の動作や、マウス中央のマウスホイールをクルクルっとした時の動作をマウスイベントと言います。
今回は、そのマウスイベントを使って画像を切り替えていくことをやろうと思っているのですが、Image-Jなどの画像ビューアではマウスホイールで画像切り替えを行っていると思いますので、それを真似してコードを作成していきたいと思います。
まずは、ざっくりと手順を。
といった流れでやっていきます。
マウスイベントは、関数を作って指定していきますので、まずは関数のおさらいです。関数は
def ○○(引数1 ,引数2):
で指定していきます。なお、関数内のコードは字を下げて記載していきます。(Tabキーで文字を下げていきますが、プログラム作成用のソフトを使う場合、大抵初めの一行目を下げれば、それ以降も自動で字下げをやってもらえます。)
それでは、本題のマウスイベントに入ります。関数名はどんな名前でも構わないのですが、マウス動作なのでonMouseとしてみましょう。
def onMouse(event,x,y,flag,param):
def onMouse(event,x,y,flag,param):
マウスイベントには指定しなければならない引数がいくつかあります。ここで引数の説明をします。
引数のeventとは、マウスの操作を指します。
クリックを押した時なのか、クリックを押しながら動かした時なのか、クリックを上げた時なのか、マウスホイールを動かした時なのか等さまざまな指定ができます。
等、いろんなイベントがありますが今回は、マウスホイールの動作なので
cv2.EVENT_MOUSEWHEEL:を使います。
(なお、マウスイベントはOpenCVの動作になりますので、OpenCVをインポートするのを忘れないでくださいね。)
なので、プログラム的に
「もし、マウスホイールを回したら」という構文を作ります。なので
# coding: UTF-8 def onMouse(event, x, y, flag, params): if event == cv2.EVENT_MOUSEWHEEL:
イベントが起こった画像上の座標を指します。
今回は意味はありません。
flagとは、イベントと同時に起こした動作です。
例えば、shiftキーを押しながらとか、ctrlキーを押しながらとか言ったことです。
今回のマウスホイールの場合は、数値が入ってきます。
マウスホイールを回した場合手前に回すか、奥に回すかによって正の数、もしくは負の数が返されます。値も、使っているマウスによってさまざまです。
ちなみに、今回はflagで受けた数値は0以上か、0以下で画像を送るか、戻すかの判断します。
paramとは、event,x,y,flag以外で処理に使いたい値をひっくるめて指定できます。
今回は、画像表示した際のウインドウ名や、ファイルを格納した変数を渡すのに使います。使い方として関数の次の行に以下のように指定します。
i,filenames = params
といった感じです。 (i,filenames は以後のコードの中で使う変数なので今は気にしないでください。)
i,filenamesという変数はparamsの中に入っていますよという意味です。
そいれでは、関数を書いていきたいと思います。
前回の記事で書きましたが、複数の画像をダイアログを使って選択した際、変数にその画像のパスが保存されます。そのパスを一個一個取り出して画像表示をしていかなくてはいけません。
1個目のパスを取り出して 画像を表示した次は、2個目のパスを取り出して表示、次は3個目のパスを取り出して表示と順番にやっていく。
i番目の画像パスを取り出したいときは
filenames[i]といった書き方になると記載しました。(filenamesはファイルパスを入れた変数名です。)
なので、マウスホイールを動かした時にiが変わる処理を書きます。
マウスホイールが動いた時には引数のflagの引数に正、もしくは負の数が入ってきますので
もし、正の数であったら画像を送る(画像番号がiだったらi+1の画像表示)負の数であったら画像を戻す( 画像番号がiだったらi-1の画像表示)
# coding: UTF-8 def onMouse(event, x, y, flag, params): i,filenames= params if event == cv2.EVENT_MOUSEWHEEL: # ホイールを回したときの動作 if flag > 0: #もしflagが正の数だったら画像番号iを1引く i -= 1 # -= とはiの数から1を引いた数をiに代入すること # -=が気に入らなかったらi = i-1でもOK elif flag < 0: #もしflagが負の数だったら画像番号iを1足す i += 1 # += とはiの数から1を引いた数をiに代入すること # +=が気に入らなかったらi = i+1でもOK
13行目
if i < 0: #もし、iが0より小さかったら
となっていたら文字化けしていますので ”<” 部分を ”<” と直してください。
これで、画像番号を示す変数iを変える処理は完成です。簡単でしょ?
しかし、このままではiが負になったり、iが変数の要素(選択したファイル数)以上の数になった際には画像表示できませんとエラーになってしまいます。
今度は、その処理を書いていきます。
内容は、
もしiがマイナスになるようだったらiは0のまま。
もしiが要素数以上になるようだったらiはそのまま
といった処理を付け加えておきます。
# coding: UTF-8 def onMouse(event, x, y, flag, params): wname, img ,i,filenames= params if event == cv2.EVENT_MOUSEWHEEL: # ホイールを回したときの動作 if flag > 0: #もしflagが正の数だったら画像番号iを1引く i -= 1 # -= とはiの数から1を引いた数をiに代入すること elif flag < 0: #もしflagが負の数だったら画像番号iを1足す i += 1 # += とはiの数から1を引いた数をiに代入すること if i < 0: #もし、iが0より小さかったら i = 0 #iは0 if i > len(filenames)-1: #もし、iが要素数以上だったら i = len(filenames)-1 #要素数より1少ない数に
ここでlen(filenames)が出てきましたが、lenとは要素の数を返す関数となるのでファイル選択した数となります。
len(filenames)-1 となっているのはプログラム上、数は0からカウントするからです。(人間は1から数えるので-1しないとずれてしまいます。)
次に、今回は画像表示を何度も繰り返す動作になるので画像表示のコード自体を関数と作成してしまいましょう。
今回はDICOM画像ではなく、一般的な画像形式(png,bitmap,jpg)の画像を表示していきます。(DICOM画像だと、WW,WLの設定もコードに入れなければならない為、簡素化のために一般的画像形式としました。)
使うモジュールはマウスイベントでOpenCVを使っているので、同じOpenCVで開こうと思います。
画像を開く際はまず、画像を変数内に読み込みます。
img = cv2.imread(filenames[i])
そして、画像表示のウインドウ名を決めなければなりません。
今回は、window名なので変数名wnameとしてそこにimgと名付けます。
wname = “img”
最後に、画像表示のコードを付けます。コードは以下となり、引数は
cv2.imshow(ウインドウ名,画像のパス)
となるので
cv2.imshow(wname, img)
となります。
これで画像表示ができるかといえばできません。よく忘れてしまうのですが画像表示後をいつまで続けるのか?といった指定をしなければいけません。忘れてしまうと画像が一瞬開いてすぐに閉じてしまいます。見た目には画像が表示されたことに気が付かず、おかしいな?と思ってしまいます。私はこれに気が付かず苦労しました。
cv2.waitKey()
上記処理は、何かキーが押されるまで待ちます。
cv2.destroyAllWindows()
何か押されたらすべてのウインドウを閉じる処理となります。
def imageshow(filenames,i): img = cv2.imread(filenames[i]) wname = "img" cv2.imshow(wname, img) print(str(i) + " image " + filenames[i]) #確認用。コンソールに変数の番号とイメージパスを表示 cv2.setMouseCallback(wname, onMouse, [i, filenames]) cv2.waitKey() cv2.destroyAllWindows()
マウスイベントと、画像表示、ダイアログを使ってファイルを選択の関数を一つのファイルに入れ、それらを繋げていきましょう。
順番はどうでもいいですが、まずマウスイベントのファイルを入れ、ファイルセレクト、その後に、画像表示の関数を入れていきましょう。
その際、OpenCV、tkinterのimportを忘れないでくださいね。
# coding: UTF-8 import cv2 import tkinter from tkinter import filedialog as tkFileDialog def onMouse(event, x, y, flag, params): i, filenames= params if event == cv2.EVENT_MOUSEWHEEL: # ホイールを回したときの動作 if flag > 0: #もしflagが正の数だったら画像番号iを1引く i -= 1 # -= とはiの数から1を引いた数をiに代入すること elif flag < 0: #もしflagが負の数だったら画像番号iを1足す i += 1 # += とはiの数から1を引いた数をiに代入すること if i <= 0: #もし、iが0より小さかったら i = 0 #iは0 elif i >= len(filenames)-1: #もし、iが要素数以上だったら i = len(filenames)-1 #要素数より1少ない数に imageshow(filenames, i) def fileselect(): root = tkinter.Tk() root.withdraw() fTyp = [('', '*')] iDir = 'C:/Desktop' filenames = tkFileDialog.askopenfilenames(filetypes=fTyp, initialdir=iDir) return filenames # 選択ファイルの絶対パスを返します。 def imageshow(filenames,i): img = cv2.imread(filenames[i]) wname = "img" cv2.imshow(wname, img) print(str(i) + " image " + filenames[i]) cv2.waitKey() cv2.destroyAllWindows()
上記の様になります。ただ、これだけではプログラムは動かないのでそれらを関連付けてあげましょう。
プログラムの流れは、
となります。
filenamesという変数に選択した画像ファイルのパスを入れたいので
初めは
filenames = fileselect()
とし、ダイアログを使ってファイルを選択する関数(fileselect)を呼び出し、returnで返されたリストをfilenamesの変数にいれます。
今度は、そのリストを画像表示(imageshow)の関数で画像表示しましょう。
imageshowの関数にリスト(filenames)を渡してそのリストの中から画像表示をさせます。その際、はじめに表示する画像はリストに入っている初めの画像にしたいので変数iを0に設定します。
i=0
imageshow(filenames,i)
これで、画像表示がでます。
はじめに、マウスホイールの関数を書きましたがそれだけでは、マウスイベントは動きません。
動かすにはマウスイベントを呼び出すコードが必要となります。
マウスイベントを呼び出すにはcv2.setMouseCallbackという関数を使います。
windowとはどのウインドウでのマウスイベントなのかを指定します。
wnameです。
onMouseはどの関数名を呼び出すかという事です。
onMouse関数のparamsの渡す引数を指定します。複数ある場合は[]で指定します。
今回はiとfilenamesを渡しますので[i,filenames]となります。
マウスイベントは、画像が表示されてから閉じられるまでの間になるので上記コードの39行目になります。
cv2.setMouseCallback(wname, onMouse, [i, filenames])
のコードを挿入して終了となります。
# coding: UTF-8 import cv2 import tkinter from tkinter import filedialog as tkFileDialog def onMouse(event, x, y, flag, params): i, filenames= params if event == cv2.EVENT_MOUSEWHEEL: # ホイールを回したときの動作 if flag > 0: #もしflagが正の数だったら画像番号iを1引く i -= 1 # -= とはiの数から1を引いた数をiに代入すること elif flag < 0: #もしflagが負の数だったら画像番号iを1足す i += 1 # += とはiの数から1を引いた数をiに代入すること if i <= 0: #もし、iが0より小さかったら i = 0 #iは0 elif i >= len(filenames)-1: #もし、iが要素数以上だったら i = len(filenames)-1 #要素数より1少ない数に imageshow(filenames, i) def fileselect(): root = tkinter.Tk() root.withdraw() fTyp = [('', '*')] iDir = 'C:/Desktop' filenames = tkFileDialog.askopenfilenames(filetypes=fTyp, initialdir=iDir) return filenames # 選択ファイルの絶対パスを返します。 def imageshow(filenames,i): img = cv2.imread(filenames[i]) wname = "img" cv2.imshow(wname, img) print(str(i) + " image " + filenames[i]) cv2.setMouseCallback(wname, onMouse, [i, filenames]) cv2.waitKey() cv2.destroyAllWindows() filenames = fileselect() i=0 imageshow(filenames,i)
いかがでしょうか?
きちんと動きましたか?