マイブログ リスト

医療言語処理講座

2019年9月27日金曜日

Pyゼミ1.06 DICOM CT画像をLUTで疑似カラーを付けてみる

CTのグレー画像をLUTを使ってカラー画像にしてみる。


keyword: DICOM, ImageJ, LUT(Lookup table)


CTやMR画像などはグレー画像で色がありません。
これに色を付けるには1つの画素値を赤(R),緑(G)と青(B)の3つの色に分ける必要があります。
グレーの画素値(0から255)をRGBに分解する対応表をLookup table(LUT)と呼びます。

ImageJのLUTでCT画像に色を付ける


画像処理ツールのImageJにはたくさんのLUTが揃っています。

ImageJのImage/Lookup TablesにたくさんのLUTを見ることができます。


これを使ってImageJでグレー画像に疑似カラーの色を付けることができます。
オリジナルのDICOM画像を表示します。
ImageJによるDICOM画像表示

 この画像に「16 Color」のLUTを使ってカラー化してみます。
LUT処理後のDICOM画像
このように,脳実質が緑,脳室が青,頭蓋骨が白,周囲の脂肪が赤く表示されます。グレーの画素値の違いを色を付けることで見やすくなります。

ImageJのLUTはImageJがインストールされたディレクトリImageの下のlutsのディレクトリの中に .lut ファイルとして存在します。


ImageJのLUTファイルを読み込む


プログラムreadImageJLut.pyは初めにLUTを入れるリストのためにnumpyとLUTを表示するためにmatplotlibをインポートします。

LUTファイル名を変数lutfnmに代入します。

LUTがファイルをOpenしてdtにLUTのデータを”rb”で,バイナリデータとして読み込みます。
 f = open(lutfnm, "rb")
 dt = f.read()
 f.close()

dtにはRGBの値が1次元のバイナリデータとして格納されているため整数型のnumpyのリストに変換します。
for文を使ってdtの要素を一つずつ取り出しリストrgbに入れます。
 rgb     = np.array( [ i for i in dt ] )

リストrgbにはRの成分256個,Gの成分56個,Bの成分256個が1次元に格納されているので,reshapeメソッドを使って,3x256の2次元リストに変換しています。
これをR,G,Bの各成分のリストr,g,bに代入します。
 r, g, b = np.reshape( rgb, (3,256) )

これで各画素値に対応するRGB成分がリストr,g,bに格納されました。
グレーの画素値がvのとき,各RGB成分は r[v], g[v], b[v]として取り出すことができます。

readImageJLut.py

import numpy as np
from matplotlib import pyplot as plt

lutfnm = "D:\Dropbox\Temporary\ImageJ\ij152-win-java8\ImageJ\luts\\16_Colors.lut"

f = open(lutfnm, "rb")
dt = f.read()
f.close()

print(">1>要素数:", len(dt), ",型:",type(dt))

rgb     = np.array([i for i in dt]) # [r0・・・r255 g0・・・g255 b0・・・b255]
r, g, b = np.reshape(rgb,(3,256))   #  赤,緑と青の成分をリストr,gとbに分解
print(">2>",len(r),len(g),len(b))

# 画素値(Gray)とRGB成分の表示
print("LUT\nGray\tRed\tGreen\tBlue")
for i in range(256):
    print(i,"\t", r[i],"\t", g[i],"\t", b[i])

# LUT値のグラフ表示
x = np.linspace(0,255,256)  # 0〜255を256分割する
plt.plot(x, r, color='red')
plt.plot(x, g, color='green')
plt.plot(x, b, color='blue')
plt.show()



 プログラムを実行してみよう


プログラムを実行するとはじめに次の2行が表示されます。

>1>要素数: 768 ,型: <class 'bytes'>
>2> 256 256 256


要素数は768と表示されています。これは256xRGBになります。
そしてImageJのLUTはバイナリデータでファイルに格納されているためバイト型になっています。

次にバイト型が整数型のリストに変換され,LUTが表示されます。

LUT
Gray    Red     Green   Blue
0        0       0       0
1        0       0       0
2        0       0       0
3        0       0       0
4        0       0       0
5        0       0       0
6        0       0       0
7        0       0       0
8        0       0       0
9        0       0       0
10       0       0       0
11       0       0       0
12       0       0       0
13       0       0       0
14       0       0       0
15       0       0       0
16       1       1       171
17       1       1       171
18       1       1       171
19       1       1       171
 ・・・・・・・・・
236      222     180     222
237      222     180     222
238      222     180     222
239      222     180     222
240      255     255     255
241      255     255     255
242      255     255     255
243      255     255     255
244      255     255     255
245      255     255     255
246      255     255     255
247      255     255     255
248      255     255     255
249      255     255     255
250      255     255     255
251      255     255     255
252      255     255     255
253      255     255     255
254      255     255     255
255      255     255     255


これは左の列のGrayの値の画素値がRGB成分に分解されるときの値を示しています。
この表がLUTの実体です。

最後にLUTの各値をプロットしたグラフが現れます。
16_Color.lutのカラー成分のグラフ
グラフは横軸がGrayの画素値,縦軸がRGBそれぞれの成分の値になります。
Grayが15以下ではRGBすべて0なので黒で表示され,240以上ではRGBすべて255なので白で表示されます。
Grayが16以上239以下ではRGBそれぞれの割合で混合され様々な色を出力することができます。


CT画像をLUTを使ってカラー画像にする


プログラムimageLUT.pyはDICOM画像を読み込むのためのpydicomモジュールと画像保存のためのcv2モジュールをインポートします。

プログラムの前半はLUTファイルを読み込みrgb各成分をリストr,g,bに読み込みます。
今回はLUTに”BRGBCMYW.lut”を使いました。

DICOM画像を読み込み変数dに代入します。
ウィンドウニング情報を変数wwとwcに,画像の高さと幅をrowとcolに,画像情報をimgに代入します。

DICOM画像のウィンドニング処理した後に,カラースケールを画像中に表示するために255~0の帯を画像に入れます。
 for y in range(256):            #カラースケース
     for x in range(50):         #スケールの幅
         img[350-y, x] = y       #画像中にスケールを入れる。
1行目のfor y in range(256):は,range(256)により変数yに0から255の値が入ります。
yは画素値でもあり,カラースケールの帯の縦方向の表示位置でもあります。
次のfor x in range(50):は,スケールの帯の幅を50にします。
最後に img[350-y, x] = y は,画像img中の350-y,xの位置に画素値yを代入します。
カラースケールの入った画像をグレー画像のまま”original.png”のファイル名で保存します。
ここで350は画像の上縁から350画素の位置からカラースケールが始まることを示します。

次にLUTを使って画像imgをRGBの各成分に分けるために,画素値がすべて0のrimg,gimg,bimgを作成します。
 rimg = np.zeros((row, col))

次に二重のfor文を使って,x,yの画素の値img[y,x]をリストr,g,bを使って各成分の値に変換して画像rimg,gimg,bimgに代入します。
 rimg[y, x] = r[int(img[y, x])]

Opencvのmergeメソッド使ってRGB成分の画像をマージしてカラー画像cimgを作成します。
 cimg = cv2.merge((rimg, gimg, bimg)) 

pyplotを使ってコンソールに表示します。
pyplotでコンソールに表示された疑似カラー画像
最後にOpneCV を使ってLUT処理後の画像を"tmp.png"として保存します。

OpneCVの処理系はカラー画像をBGRとの順で管理しているため他のPILなどと扱いが異なるため注意が必要です。
BGRのままで画像を保存する次のようになります。

BGR順の画像で正しく色が表示されていません。
全く異なる画像になってしまいます。

OpneCVで画像を保存するとき,その直前でRGBをBGRに変換して保存します。
BGR→RBG変換にはいくつかの方法があります。
ひとつはmergeするときに順番をBGRとしてカラー画像を作成する方法です。
 cimg = cv2.merge((bimg, gimg, rimg))
先のmergeとはrimgとbimgが反対になっています。

もう一つの方法はリストの技を使う方法です。
 cimg = cimg[:, :, ::-1]
既に存在するRGBG画像をBGR画像に変換するのに便利です。



imageLUT.py

import numpy as np
from matplotlib import pyplot as plt
import pydicom
import cv2

lutfnm = "ImageJインストールディレクトリ\ImageJ\luts\BRGBCMYW.lut"
#lutfnm = "ImageJインストールディレクトリ\ImageJ\luts\\16_Colors.lut"
#lutfnm = "ImageJインストールディレクトリ\ImageJ\luts\Red_Hot.lut"

f = open(lutfnm, "rb")
dt = f.read()
f.close()

print(">1>", len(dt), type(dt))

rgb     = np.array([i for i in dt]) # [r0・・・r255 g0・・・g255 b0・・・b255]
r, g, b = np.reshape(rgb,(3,256))   #  [[r0・・・r255] [g0・・・g255] [b0・・・b255]]

x = np.linspace(0,255,256)  # 0〜255を256分割する
plt.plot(x, r, color='red')
plt.plot(x, g, color='green')
plt.plot(x, b, color='blue')
plt.show()

# DICOM画像を読み込む
d = pydicom.dcmread('../dcmdir1/Brain01')

wc = d.WindowCenter     #ウィンドウセンターの取得
ww = d.WindowWidth      #ウィンドウ幅の取得
row= d.Rows             #画像の行数の取得
col= d.Columns          #画像の列数を取得
img = d.pixel_array     #画像を取得

#DICOM画像のウィンドニング
max = wc + ww / 2         #表示最大画素値
min = wc - ww / 2         #表示最小画素値
img = 255*(img-min)/(max-min)
img[img > 255] =255
img[img < 0] = 0

# カラースケールを画像に入れる
for y in range(256):            #カラースケース
    for x in range(50):         #スケールの幅
        img[350-y, x] = y       #画像中にスケールを入れる。

cv2.imwrite("original.png", img)

#LUTを使ってR,G,Bのチャンネルを作成する。
rimg = np.zeros((row, col))     #Rチャンネルの初期化
gimg = np.zeros((row, col))     #Gチャンネルの初期化
bimg = np.zeros((row, col))     #Bチャンネルの初期化
for y in range(row):
    for x in range(col):
        #LUTで画像を各チャンネルに変換
        rimg[y, x] = r[int(img[y, x])]
        gimg[y, x] = g[int(img[y, x])]
        bimg[y, x] = b[int(img[y, x])]
      
cimg = cv2.merge((rimg, gimg, bimg)) # RGB
plt.imshow(cimg)
plt.show()

#OpenCV は画像のチャンネル順を BGR として扱うためBGRの順ででマージする。
#cimg = cv2.merge((bimg, gimg, rimg)) # BGR
cimg = cimg[:, :, ::-1]               # BGR->RGB変換
cv2.imwrite("temp.png", cimg)



プログラムを実行してみる


LUTには”BRGBCMYW.lut”を用いています。
実行するとLUTのグラフとLUT処理した画像がコンソールに現れます。
カレントディレクトリにカラースケールを入れたグレー画像original.pngとLUT処理された画像temp.pngが保存されます。
LUT処理前のグレー画像
左にカラースケール(まだグレーですが)が入っています。

LUT(BRGBCMYW.lut)処理後の画像
左のカラースケールに色がついている

ImageJはたくさんのLUTを提供しています。是非いろいろ試してみてください。
3つほどカラースケールを下に示しました。
目的に応じて使い分けるとよいでしょう。



おわりに


今回はLUTの処理について,ImageJのLUTファイルを読み込むところから説明をしました。
様々な処理結果を見やすくすにはLUTを使った疑似カラー表示が適している場合が多いと思います。
LUTによる表示は実験結果の効果的な表現に便利です。



2019年9月13日金曜日

医用画像使ったPythonゼミナールを始めまーす


修正履歴:各Pyゼミのリンクを作成しました。また,一部採番が重複していたので修正しました。2019.09.13

年も明けて2019年

ブログを立ち上げてかなり放置していたので,そろそろなんかやってみようかとPCに向かっているところです。
放置している間にBloggerもだいぶ変わった。
医療情報に関連するテーマで少し持続するテーマ,”Pythonで医用画像AI”を考えてみました。

人工知能の流行もやや一段落

人工知能の流行も一時よりはだいぶ熱が冷めた感じですが,医療においてはこれからでしょう。
研究室では,これまでJavaを使ってAIを作ってきましたが,あるきっかけらPythonに移行することにしました。
それれは医療画像規格であるDICOM(Digital Imaging and COmmunication in Medicine)が非常に簡単にPythonで扱えるからでした。

本学,北海道情報大学医療情報学部ではJavaプログラミングが1年生(診療情報管理専攻)で必修科目です。
プログラミングの実習中の学生との雑談でpython勉強中です,という学生もいたりして,彼らにも画像処理結果が見れるので良いテーマでしょう。

PythonでAIやってみる?

本研究室の3,4年生のゼミナールでこれまで勉強してきた内容や作成物を整理して,1年生でもわかるようにブログの中で公開してみようかと考えたわけです。

もちろんここらでちょっと自分もAIに触れてみようと思っている人や学生などを対象に連載を組んでみたいと思っています。読んでもらいたいターゲットとしては

  • python勉強したいけど興味ある分野が見つからない人々
  • とりあえず人工知能に触れてみた大学生,高校生,ひょっとしたら小中高生
  • 修士のテーマにしたいけどどうする,という大学院生
  • 画像がいっぱいあって解析してみたい診療放射線技師や医師など医療スタッフ
  • 会社でAI推進の指示があったが何から手を付けて良いかわからない社会人
  • ネットで色々調べたけど具体的にAIとDICOMをどうくっつけるか迷える人々

環境構築編,DICOM編,GUI編とAI編をはじめます

具体的には以下の内容で進めていきたいと考えています。どこから始めても良いように並行して公開していきます。

0 環境構築編

本ゼミナールではUbuntu上で開発することを前提に進めまていますが,Windows上でのAI開発環境構築やノートPC上でのDual boot環境構築をまとめました。

Pyゼミ0.01 Pythonで医用画像のAIを試すための環境構築 
              
Pyゼミ0.02 Windows10AIしたい
              
Pyゼミ0.03 MSI ノートPCPS42 8RC-009JP)にAI環境を構築する
             
Pyゼミ0.04 MSI P65Dual bootにしてみた

Pyゼミ0.05 備忘録 Ubuntu18.04にTensorFlowとChainerをインストールする 

Pyゼミ0.06 備忘録 Ubuntu20.04にTensorFlowとPyTorchをインストールする New 20221019

Ⅰ DICOM編

医用画像は標準化が進み,DICOM(Digital Imaging and COmmunication in Medicine)が一般的に用いられています。Pydicomを使った簡単な画像の表示,ウィンドニングと呼ばれる最適画像濃度変換処理とJPEG,PNG画像変換や画像ポジションによるソート,画像拡張について紹介します。

Pyゼミ1.01 DICOM画像表示
              
Pyゼミ1.02 DICOMからJPEGなどへの変換
              
Pyゼミ1.03 DICOMのタグ情報を読む
              
Pyゼミ1.04 DICOMのタグ情報(ImagePosition)で画像ファイルをソートする
              
Pyゼミ1.05 DICOM JPEG圧縮画像を解凍する

Pyゼミ1.06 DICOM CT画像をLUTで疑似カラーを付けてみる


Pyゼミ1.07 CT画像にDICOM Color LUTを使ってヒートマップ画像などを重ねてFusion画像を作ってみる

Pyゼミ1.0 番外編 DICOM RDSRを読む 20220502

Pyゼミ1.08 PET画像を読む  20220506 


Ⅱ GUI(Graphical User Interface)編

PyQt5を使ったGUIプログラムを紹介します。
DICOM画像表示,複数DICOM画像のページングとマウスにより任意画像の抽出,DICOM画像上から任意のROIの抽出など画像加工プログラムを紹介します。

Pyゼミ2.01 PyQt5によるDICOM画像表示.画像ファイル渡し
              
Pyゼミ2.02 PyQt5によるDICOM画像表示とページング
              
Pyゼミ2.03 PyQt5によるDICOM画像のセグメンテーション
              
Pyゼミ2.04 PyQt5によるDICOM画像の拡大表示と保存

Pyゼミ2.05 PyQt5によるラジオボタンとチェックボックス new2020.02.16

             

Ⅲ 人工知能編

人工知能のフレームワークであるChainerを使います。
手書き数字認識MNISTのデータから医用画像データへの拡張方法,pythonを使ったChainerの訓練プログラムをGPU対応にする方法などを紹介します。

Pyゼミ3.01 手書き認識のニューラルネットワーク
              
Pyゼミ3.02 手書き画像からDICOM画像へ
              
Pyゼミ3.03 DICOM画像を訓練用とテスト用に分けてみる
              
Pyゼミ3.04 手書き文字認識のニューラルネットワークをDICOMに拡張する
              
Pyゼミ3.05 MNISTのネットワークをAlexnetに対応させる
              
Pyゼミ3.06 ChainerTrainer Extensionを使ってみる
              
Pyゼミ3.07 ChainerGPUを使って学習してみる



プログラムの方針

プログラムのサンプルを探すのであればGitHubに大量のソースコードが保存されています。しかし,初心者がチャレンジしてするには敷居が高いように思います。
そこでPyゼミでは次の点に留意してプログラムを作成しています。

  • 100行程度の長さで,アルゴリズムを容易に理解できる
  • ひとつのプログラム内でアルゴリズムが完結している
  • 実行した結果が複雑ではなく容易に理解が得られる


おわりに

本研究室のゼミナールだけでなく,外部の他の勉強会やセミナーにも参加してみて,周囲の人たちがいろいろな問題をもっていることが分かってきました。
おおよそ図書などで人工知能が使えるようになっても,最初にデータをどのように加工してAIに与えるべきなのか,つまりMNISTのデータで上手くいくのは分かったけどその次どうしたらいいの...という問題にすこしでも参考になればと考えています。

ちょっとお願い

なお本ソースコードの使用にあたって,何らかの具都合や不利益が生じたとしても本研究室で責任を負いかねます.どうぞご了承ください。
本ゼミナールで配布するDICOM画像は私のCT全身像です。実験以外の利用はしないようお願いいたします。
また,ご意見などありましたらメッセージをいただけますでしょうか。

2019年8月26日月曜日

Pyゼミ0.01 Pythonで医用画像のAIを試すための環境構築

それではPythonゼミナールをはじめます。


Keyword:Python, VMware Workstation Player, Anaconda, Chainer, ImageJ

Python開発環境の作成

最初は開発環境の作成です。ゼミでは特に統合開発環境は使わずにテキストエディタでプログラムを入力・保存して,ターミナル(端末)上で実行するようなシンプルな環境で勉強しています。

今回の目標は,次の通りです。

  • 仮想環境(ここではVMware)でUbuntuを起動する。
  • UbuntuにAnaconda,ChainerとImageJをインストールする。
  • テキストエディタgeditを開発しやすいように設定変更する。
  • 医療画像を扱える環境を作成する。


ハードウェアの環境


  • CPU,Corei3以上
  • メモリ,4GB以上,できれば8GB以上が望ましい
  • ストレージ,20GB以上の空き領域
  • GPU,動作条件には入れませんが,連載の中ではGPU対応について取り上げます。


---------------------------------------------------

PythonとChainerのインストール手順を示します.


①仮想環境VMwareのダウンロードとインストール

ゼミでは基本的に手持ちのWindowsノートPCを使います。
Windowsに仮想環境構築のためVMware Workstation Playerをインストールします。
VMwareのホームページからダウンロードしてインストールします。

②Ubuntuダウンロード

UbuntuのLTS(Long Time Support)の最新版は現在Ubuntu18.04ですが,ネット上の情報が多いUbuntu16.04を使います。
UbuntuのホームページからUbuntu16.04のisoイメージをダウンロードします。

③仮想環境にUbuntuをインストール

VMware Workstation Player上でUbuntu16.04をインストールします。
VMware Workstation Playerを起動し,Ubuntuのisoイメージを選択し仮想環境を作ります。インターネット上に方法がたくさん掲載されいるので参考にしましょう。

④Anacondaのダウンロードとインストール

VMware Workstation Player上でUbuntuを起動して,anacondaをインストールします。
 ancondaには開発に必要なPythonのパッケージがたくさん入っています。
Anacondaのホームページからファイルをダウンロードしてください。

ターミナル(ダウンロードフォルダ内で右クリックし,リストから「端末を開く」を選択)から以下のコマンドを実行します。

$bash␣Anaconda3-4.4.0-Linux-x86_64.sh⏎

($はプロンプト,␣はスペース,⏎はEnterキーです。Anacondaのバージョンにより数字「3-4.4.0」が異なります)

⑤Chainerのインストール

人工知能フレームワークのChainerをインストールします。
ここではバージョン2.0を指定してインストールします。指定しなければ最新版がインストールされます。

$pip␣install␣chainer==”2.0.0”⏎


⑥画像解析ビューワのImageJ

ImageJのホームページから画像解析ビューワImageJのファイルをダウンロードしてインストールします。
DICOM画像の表示やメタ情報を確認することができます。
また,Pythonのプログラムで処理した画像の確認にも利用できます。

これでとりあえずの環境がととのいました.

⑦確認してみよう

1)pythonを起動します。

ターミナルからpythonの入力するとプロンプト>>>が現れ,対話モードでの入力が可能な状態です。
$python⏎
Python 3.6.4 |Anaconda custom (64-bit)| (default, Dec 21 2017, 21:42:08)
[GCC 7.2.0] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

pythonを起動するとバージョンが表示されます。

2)簡単な演算

四則演算です.

>>> 1 + 2
3
>>> 2 - 3
-1
>>> 3 * 4
12
>>> 4 / 5
0.8


Pythonは整数の割り算で割り切れない場合,値は浮動小数点になります。割り算の結果を整数で得たい場合は // を使います.

>>> 5 / 2
2.5
>>> 5 // 2
2

3)NumPyの配列の生成

はじめにNumPyのライブラリをnpという名前でインポートします.

>>> import numpy as np


0から9までの範囲(range(10))の要素をもつ,浮動小数点(dtype = np.float32)の配列xを作成して表示します.

>>> x = np.array(range(10), dtype = np.float32)
>>> x
array([ 0.,  1.,  2.,  3.,  4.,  5.,  6.,  7.,  8.,  9.], dtype = float32)

4)Chainerの確認

Chainerをインポートしてバージョンを確認してみます。ゼミでは2.0を基本にしていますが,それ以降のバージョンでも問題なく動作するはずです。

>>> import chainer
>>> chainer.__version__
'2.1.0'


モジュールのインポート

次にChainerの3つのモジュールをインポートして簡単な演算の確認をしてみます。

>>> from chainer import Variable
>>> import chainer.functions as F
>>> import chainer.links as L


Variable,functionsとlinksを使って簡単ネットワークを作ってみる

VariableはChainerのネットワークに渡す変数を定義します。xに4つの値を[1,2,3,4]を代入します。

>>> x = Variable(np.array([[1,2,3,4]],dtype=np.float32))
>>> x
variable([[ 1.,  2.,  3.,  4.]])

次にlinksパッケージのLiner関数を用いて入力が4ノード,出力が3ノードのネットワークを作ってみます.

>>> l = L.Linear(4,3)


このネットワークの重みWとバイアスbの初期値を見てみましょう.

>>> l.W.data
array([[-0.35292298, -0.23537879, -0.09752855,  0.19314146],
       [-1.20308304, -0.10927824, -0.37381825,  0.24644093],
       [ 0.31677744, -0.83313662,  0.28393224,  0.09073655]], dtype=float32)
>>> l.b.data
array([ 0.,  0.,  0.], dtype=float32)
重みWは3行4列の行列で乱数により初期化されているのがわかります.バイアスは出力ノードに関連するので3つの初期値0が表示されています.

このネットワークに入力xを与えて,出力hを見てみましょう。
>>> h = l(x)
>>> h
variable([[-0.34370032, -1.55733061, -0.13475287]])

variable型で3つの出力を得ることができました。
重みWとバイアスbをもつネットワークは次のように計算して出力を求めます。
           h = W x + b 
ここでhの出力の検算してみましょう。
ネットワークlの重みの1行目はl.W.data[0]で得ることができます。
>>> l.W.data[0]
array([-0.35292298, -0.23537879, -0.09752855,  0.19314146], dtype=float32)

これに内積のメソッドdot()を使って次のように計算し見ます。
>>> l.W.data[0].dot(np.array([1,2,3,4],dtype=np.float32))+l.b.data[0]
-0.34370035

確かに,hの出力のゼロ番目の要素の値をほぼ同じ値になりました。
同様に1番目,2番めの要素の値は次のようになり,chainerを使った計算とほぼ同じ出力が得られました。
>>> l.W.data[1].dot(np.array([1,2,3,4],dtype=np.float32))+l.b.data[1]
-1.5573305
>>> l.W.data[2].dot(np.array([1,2,3,4],dtype=np.float32))+l.b.data[2]
-0.13475287

最後に出力hは負の値ですが,これをsoftomax関数を使って確率分布に変換します。
>>> y = F.softmax(h)
>>> y
variable([[ 0.3953355 ,  0.11746077,  0.48720363]])

chainerの動作の確認ができました。
またchainerのモジュールを使うことにより簡単にネットワークを作ることができることもわかりました。

5)Anacondaの確認

condaコマンドを使ってAnacondaのバージョンおよびPythonのバージョンやその他のライブラリのバージョンが確認できます。

$conda␣list⏎
# packages in environment at /home/uesemi/anaconda3:
#
# Name                    Version                   Build  Channel
_license                  1.1                      py36_1 
alabaster                 0.7.10           py36h306e16b_0 
anaconda                  custom           py36hbbc8b67_0 
 ・・・・・
python                    3.6.4                hc3d631a_0 
 ・・・・・・
zeromq                    4.2.2                hb0b69da_1 
zict                      0.1.2            py36ha0d441b_0 
zlib                      1.2.11               ha838bed_2   


⑧Pythonの終了

>>>Ctl + D



テキストエディタの設定

これからテキストエディタのgeditを使ってpythonのプログラムを作成していきますが,最初にテキストエディタのgeditの設定を行います。

geditの起動

geditの起動には空のテキストファイルを作成し,ダブルクリックして起動するのが簡単でしょう。
プログラムを作成するディレクトリ上で右クリックし「新しいドキュメント/空のドキュメント」を作成します。
ファイル上で右クリックしてリストから「名前の変更」を選択し,プログラムの名前に変更します(例えばreadFile.pyなど)

ファイルをダブルクリックするとgeditが起動してファイルを開きます。

設定ダイアログの表示

geditのメニューバーの「編集/設定」を選択します。


表示設定

最初の「表示」タブです。
行番号を表示する」にチェックを入れます。
Pythonのプログラム実行時のエラーに行番号が表示されるので,行番号を有効にしておくと作業効率が上がります。


エディター設定

次に「エディター」タブです。
タブの代わりにスペースを挿入する」にチェックを入れます。
Pythonはインデントによりfor文やif文の有効範囲(ブロック)を明示しているのでタブは非常に重要です。しかしタブとスペースが混在したりするとインデントのエラーが出力されます。ここではタブをスペース4文字に置き換えるようにします。

自動インデントを有効にする」にチェックを入れます。
プログラム入力中,文の最後でenterキーを打つと,前の行の先頭と同じ位置にカーソルが来ますので便利です。


フォントと色設定

「フォントと色」ではカラースキームを選択することができます。
好みに合わせて選択するとよいでしょう。私は「クラシック」を選択して利用しています。



まとめ

これでPythonと人工知能のフレームワークChainerを使って,プログラムを作成する準備ができました。このほかに必要なモジュールについてはそのつど説明します。
次回はDICOM画像を表示するプログラム(Pyゼミ1.01)を作成しましょう。


2019年7月7日日曜日

Pyゼミ1.05 DICOM JPEG圧縮画像を解凍する

外部コマンドを使ってDICOM圧縮画像を解凍する


Keyword: DICOM JPEG圧縮、解凍、外部コマンド、DCMTK


先日、妻が半月板損傷の手術で入院しました。
術後撮影したMRI画像をCDで病院からいただき、講義・実習用に使おうと考えていました。

ところがDICOM画像はJPEG圧縮されていました。
そこで今回は画像を解凍するプログラムを紹介します。
注目する機能は次の2つを紹介します。

  • DICOM JPEG圧縮画像を解凍するDCMTKの紹介
  • DCMTKの解凍コマンドをpythonのプログラム内から呼び出す方法


DICOM Tool Kit


DICOM Tool kitは圧縮・解凍だけなく様々な機能がコマンドとして提供されています。
詳細な情報はここに書かれています。

特徴としてはWindows、LinuxやMacOSで利用可能です。
もちろんここではUbuntuで利用することを前提にします。

DCMTKのインストール


1行で簡単にインストールすることができます。

$ sudo apt install dcmtk



DICOM JPEG画像解凍コマンド dcmpjpeg


解凍にdcmpjpegを使います。
DCMTKをインストール後にコマンドを打ち込んでみましょう

下記のように使い方とオプションが表示されます。

$ dcmdjpeg
$dcmtk: dcmdjpeg v3.6.2 2017-07-14 $

dcmdjpeg: Decode JPEG-compressed DICOM file
usage: dcmdjpeg [options] dcmfile-in dcmfile-out

parameters:
  dcmfile-in                     DICOM input filename to be converted
  dcmfile-out                    DICOM output filename
(続く)

オプションの内容については割愛しましたが、ここでは”-u”を使います。

意味は” disable support for new VRs, convert to OB”とあり、
「新しいVR(Value Representation、値表現)のサポートを無効にし、OB(Other Byte String 8bit)に変換する」
となります。。

対象とするDICOM画像により微妙にオプションを調整あるいは付けない選択をする必要があります。

テスト画像の準備


JPEG圧縮した画像のサンプルがJIRA(日本画像医療システム工業会)のホームページにDICOM画像ファイルがあります。

解凍テストのために、MR_JPG_IR6a.dcmとMR_JPG_IR87a.dcmをダウンロードします。

画像ファイルをcompDicomというディレクトリを作成しその中に保存します。

解凍後の画像を保存するディレクトリdecompを作成しておきます。

準備完了です。


プログラム


プログラムはmain関数の中ではじめに圧縮画像が保存されたディレクトリと解凍した画像を保存するディレクトリをそれぞれ変数dcmdirとdstdirにそれぞれに代入します。

dcmdirディレクトリに保存されている画像の一覧をlistdirメソッドを使って配列(リスト)flistに代入します。

次にfor文を使ってflistに入っている画像を1画像づつfnameに代入して、decompjpeg関数を使って解凍処理を行います。

解凍舌画像をpydicomを使って読み込み、患者氏名などの基本情報をprintします。

decompjpeg関数は圧縮された画像ファイル名fname_iと解凍後のファイル名fname_oを引数に持ちます。

はじめに、解凍するためのdcmtkのコマンド”dcmjpeg”とオプションの"-u"をそれぞれ変数cmdとoptに代入します。

実行するコマンドラインを変数cmdlineに作成します。
このcmdlineをsubprocessライブラリのcheck_callメソッドに渡します。
このメソッドはcmdlineの実行がうまく行った場合、0を返します。

以上がプログラムの説明です。



dcmdecomp.py

import os, sys
import subprocess
import pydicom  # pydicom 1.0

def decompjpeg(fname_i, fname_o):       # DCMTKを用いて圧縮画像を解凍する
    cmd = "dcmdjpeg"        # DCMTKのコマンド
    opt = "-u"              # オプション

    cmdline = cmd + " " + opt + " " + fname_i + " " + fname_o

    #外部プログラム dcmd -f   を起動
    proc = subprocess.check_call( cmdline, shell=True)

    if proc == 0:
        print("解凍成功:", proc, fname_o)
    else:
        print("\n\n解凍失敗:", proc, fname_o)
  
    return proc


if __name__=='__main__':  
    dcmdir = "./compDicom/"
    dstdir = "./decomp/"   # Distnation directory
    print("compressed Dicom image dir    = ", dcmdir)
    print("decompressed image dir = ", dstdir)
      
    flist = os.listdir(dcmdir)  #DICOM元画像のファイルリスト作成
  
    for fname in flist:
        decompjpeg( dcmdir + fname, dstdir + fname )     #画像解凍処理
              
        ds = pydicom.read_file(dstdir + fname)
        print("\nPatinet ID         : ", ds.PatientID)
        print("Study Date         : ", ds.StudyDate)
        print("Study Instance UID : ", ds.StudyInstanceUID)
        print("SOP instance UID   : ", ds.SOPInstanceUID)


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



$ python dcmdecomp.py
compressed Dicom image dir    =  ./compDicom/
decompressed image dir =  ./decomp/

解凍成功: 0 ./decomp/MR_JPG_IR6a.dcm
Patinet ID         :  999999-004
Study Date         :  20041012
Study Instance UID :  1.2.392.200036.8120.100.20041012.1123100.2001
SOP instance UID   :  1.2.392.200036.8120.100.20041012.1123100.2001002010

解凍成功: 0 ./decomp/MR_JPG_IR87a.dcm
Patinet ID         :  999999-004
Study Date         :  20041012
Study Instance UID :  1.2.392.200036.8120.100.20041012.1123100.2001
SOP instance UID   :  1.2.392.200036.8120.100.20041012.1123100.2001002010


実行後、decompディレクトリに解凍画像が保存されています。
compDicomディレクトリにある画像は129kbですが、compディレクトリには206kbになり容量が増えています。


おわりに


DICOM JPEG圧縮画像を解凍するdcmdjepgコマンドを紹介しました。
DICOM Tool Kitは簡単にインストールできるのでとても便利です。

また、外部コマンドをpythonから起動する方法を紹介しました。
コンソールからコマンドを打ち込むように記述すればよく、他の外部コマンドにも応用可能です。




2019年6月23日日曜日

Pyゼミ0.04 MSI P65をDual bootにしてみた

ノートPCデュアルブート

PS42が上手くいくならP65でも上手くいくはず?


Keyword: MSI P65-8RE-015JP、Dual Boot、Ubuntu18.04、Windows10

前回Pyゼミ0.03でMSI社のノートPC PS42のデュアルブートインストール記を書きました。
当初Ubuntu16.04でかなり苦戦しましたが,
Ubuntu18.04に変えるとかなりあっさりとインストールできたので,今回は上位機種のP65-8RE-015JPにトライしました。

P65-8RE-015JPのスペック
  • CPU   Core i7 8750H 2.2GHz/6コア
  • Memory 16GB
  • SSD   512GB
  • GPU   GeForce GTX 1060 Max-Q Design
  • Weight  1.88㎏   


前回PS42のマニュアルはできているので,この通り作業すればいいよと学生への課題として与えました。

しかし。。。



Ubuntu18.04がインストールできない


これまで何度かインストールに使ってきたインストール用USBでUbuntu18.04が上手く起動しません。
BIOSの設定を確認しながら何度か挑戦してもだめ。。。
18.04はあっさりあきらめてUbuntu16.04をインストールすることに変更。

週末の土曜日の午後。
PS42の上位機種なのに18.04がインストールできないことに違和感を持ちながら作業へ。

16.04のインストールが終了し,いつの持て順でCUIモードに移行してNVIDIAのドライバーのインストールへ
しかし,今度はまったくドライバーがインストールできない状況に。。。

何度か試している途中に,”do-release-upgrade”を使ってアップグレードするようにメッセージが表示されていることに気づきました。
ここで転機が。。。


これしかないね。


もう手がないな。

do-release-upgradeやってみようかということでメッセージの指示に従い。
まず,Ubuntu16.04のアップグレードとアップデートを実行。

$ sudo apt upgrade

$ sudo apt update

次に,Ubuntu18.04へのアップグレードを実行。

$ sudo do-release-upgrade

約1時間弱かかってダウンロード,途中すべてyesと入力。
その後,文字バケバケの何かを選択する画面が表示され,とりあえず選択されているディフォルトで続行。
(続行かどうかも文字が化けてて分からない)
とりあえず順調に進んでいるようなので,既に4時間以上だったので放置して帰ることに。


上手くいってる。


月曜日の朝。
P65の真っ黒な画面の最後に[y/n]と評されている。
何も考えずにyと入力。
再起動がはじまり,Ubuntu18.04起動完了。

土曜の作業はまったく上手くいっていないかったのでダメかもともっていましたが,インストールされていました。
よかった,よっかた。




残作業を無事終了


PS42へのUbuntu18.04のインストールに従い以下の作業を行いました。
参照:Pyゼミ0.03

1)NVIDIAドライバーのインストール

①CUIモードで作業するため,ランレベルを3に設定します。


$ sudo telinit 3

②Ctl + Alt + F1 でCUIモードに移行します。


③とりあえずアップデートを行います。

$ sudo apt-get upgrade
$ sudo apt-get update

④レポジトリの追加をします。

$ sudo add-apt-repository ppa:xorg-edgers/ppa -y
$ sudo apt-get update

⑤インストール可能なNVIDIAドライバの一覧を検索します。

$ apt-cache search "^nvidia-[0-9]{3}$"

⑥一覧の中から最新のドライバ「nvidia-390」を選び,インストールします。

$ sudo apt-get install -y nvidia-390

⑦インストールにしばらく時間がかかります。インストール終了後、再起動します。

$ reboot

⑧再起動後,ターミナルで次のコマンドを入力してNVIDIAのドライバーが正しくインストールされたことを確認します。


「GeForce GTX 106...」と表示され、GPUを認識していることが確認できます。

$ nvidia-smi
Tue Jun 18 16:42:35 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.116                Driver Version: 390.116                   |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 106...  Off  | 00000000:01:00.0 Off |                  N/A |
| N/A   47C    P3    17W /  N/A |    218MiB /  6078MiB |      0%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1393      G   /usr/lib/xorg/Xorg                           121MiB |
|    0      1600      G   /usr/bin/gnome-shell                          95MiB |
+-----------------------------------------------------------------------------+


2)CUDAのインストール

①CUDA9.1をインストールします。

$ sudo apt install nvidia-cuda-toolkit nvidia-driver-390

②再起動後にCUDAが認識されているか確認します。

$ nvcc –V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation>
Built on Fri_Nov__3_21:07:56_CDT_2017
Cuda compilation tools, release 9.1, V9.1.85

3)cuDNNのインストール

NVIDIAからcudnnのファイルをダウンローします。
 ファイル名:cudnn-9.0-linux-x64-v7.4.2.24.tgz

①Ubuntuのホームディレクトリにファイルを置き,解凍します。cudaディレクトリが作成されます。

$ tar -zxf cudnn-9.0-linux-x64-v7.4.2.24.tgz

②cudaディレクトリに移動し,ファイルを所定の場所にコピーします。

$ cd cuda
$ sudo cp lib64/libcudnn* /usr/lib/cuda/lib64/
$ sudo cp include/cudnn.h /usr/lib/cuda/include/

③~/.bashrcファイルをテキストエディタで開き,最後の行に下記の内容を追加します。

$ gedit ~/.bashrc
ファイルの最後の行に次の2行を追加します。
# cuDNN
export PATH=/usr/lib/cuda/bin:${PATH}
export LD_LIBRARY_PATH=/usr/lib/cuda/lib64:${LD_LIBRARY_PATH}

4)Ancondaのインストール

Anaconda DistributionからLinuxのPython3.Xをダウンロードします。
ファイル名:Anaconda3-2019.03-Linux-x86_64.sh

①Anacondaをインストールします。

$ bash Anaconda3-2019.03-Linux-x86_64.sh
※インストール中すべてyesと入力

②pythonを起動して確認します。PythonとAnacondaのバージョンが表示されます。

$ python
Python 3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:09:58)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

5)Chainerのインストール

①CUDA9.1用のcupyのインストールします。

$ pip install cupy-cuda91

②chainerをインストールします

$ pip install chainer

③Pythonを起動して,Chainerとcupyが正しくインストールされたことを確認します。

$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import chainer
>>> chainer.__version__
'5.4.0'
>>>
>>> import cupy
>>> cupy.__version__
'6.0.0'

6)Opencvのインストール

①pipのアップグレードとOpencvのインストールを行います。

$ pip install –upgrade pip
$ pip install opencv-python

②Pythonを起動し,Opnecvのバージョンを確認します。

$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.1.0'

7)Pydicomのインストール

①pipでpydicomをインストールします。

$ pip install  pydicom

②Pythonを起動してpydicomのバージョンを確認します。

$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydicom
>>> pydicom.__version__
'1.2.2'

8)DICOM Tool kitのインストール

①DICOM Tool Kitをインストールします。

$ sudo apt install dcmtk

②インストールを確認します。

・DICOM画像の解凍を行うdcmdjpegコマンドのバージョンを確認します。
$ dcmdjpeg --version
$dcmtk: dcmdjpeg v3.6.2 2017-07-14 $

dcmdjpeg: Decode JPEG-compressed DICOM file

Host type: Debian
Character encoding: UTF-8

External libraries used:
- ZLIB, Version 1.2.11
- IJG, Version 6b  27-Mar-1998 (modified)





ということで無事終了。







2019年6月12日水曜日

Pyゼミ0.02 Windows10でAIしたい

Windows10でSpiderやJupiter Noteを使ってAIやってみる。


Keyword:Windows10, WinPython, Spider、Jupiter Note、Chainer、Python

ゼミナールではUnix上で開発や実験をしていましが,やはり初学者が最初に取り掛かるには少し敷居が高いかもしれません。

本学の実習室のOSはWindowsなので,学内で簡単にPythonやAIを試せる環境を構築できないか検討していたところ,WinPythonを見つけました。

WinPythonはPythonによる開発環境を提供してくれます。もちろん必要なライブラリも簡単にインストールして利用可能です。すばらしい。

また,USBメモリに入れて持ち運ぶことができます。ますます素晴らしい。

私が試したところではWindows7ではPython開発環境のSpyderなど起動できませんでした。おすすめはWindows10になります。


ダウンロードしてUSBメモリに保存する


すでにAI開発環境をインストールしたWinPython(約880MB)をダウンロードします。


ダウンロードしたファイルを解凍します。

解凍後のフォルダをUSBメモリに保存して,他のPCに挿して実行できます。

とても便利です。

Winディレクトリに中に以下のディレクトリがあります。

  • 1.01readDicom~3.04dicomNN...Pythonのソースコードと関連するファイルが保存されています。
  • dcomdir1...テスト用のDICOM画像が保存されています。
  • dicmdir2...訓練・テスト用のDICOM画像が4つの部位(Head,Chest,AbdomenとLung)に分けて保存されています。
  • WinPython63bit3.7 / WPy-3710...Pythonの開発環境用のプログラムが保存されています。



USBメモリから起動する


WinPythonには人気のあるPython開発環境が実装されています。

WinPython63bit3.7 / WPy-3710の中には以下のようなプログラムがあります。



人気のSpiderやJupter NoteをつかってPythonで開発がすぐできます。


必要なライブラリをあらかじめインストールしました。


Pyゼミナールで使う以下のライブラリをインストールしてあります。
  • Anaconda(numpy 1.14.6,PyQt5など) 
  • Chainer 5.2.0
  • OpneCV 4.0.0
  • pydicom 1.2.2

Pyゼミナールで提供するソースコードを使ってすぐに実験可能です。


DICOM画像を表示してみる


PyQtで作成したGUIを使ってDICOM画像を表示します。

例えばSpyderを使って,2.01PyQt dicomDisplayのdicomDisplay.pyを開いて実行ボタンをクリックすると画像が表示できます。


DICOM画像の部位を分類のタスクを学習しテストする


頭部,胸部,腹部と肺野の4つの部位を学習し,分類のテストを行います。

dcmdir2ディレクトリ内の画像を使って,3.04dicomNNディレクトリ内のdicomNNtrain.pyを使って部位分類の訓練を行います。

学習したモデルを使い,dicomNNtest.pyでテストをおこないます。

テスト結果の平均正答率がコンソールに表示されます。

また,推論結果がresultディレクトリにJPEG画像が保存されます。

JPEG画像ファイル名は,推論した部位名から始まるファイル名で保存されています。


まとめ


Windows10でのAI環境をWinPython使って作成しました。

簡単な実験が行え,USBメモリに入れて持ち運べるので便利です。

私はオープンキャンパスに使っています。



2019年4月28日日曜日

Pyゼミ0.03 MSI ノートPC(PS42 8RC-009JP)にAI環境を構築する

MSI ノートPC、PS42 8RC-009JPをUbuntuとWindowsのDual BootでAI開発環境も入れる


Keyword:MSI PS42,Dual boot,Windows 10, Ubuntu18.04, Geforce GTX1050,Chainer


2019.06.17 NVIDIAドライバーのインストールの誤記を修正しました。



出張中にもGPU使って簡単な実験できる環境がほしいと、GPU搭載のノートPCを探していました。
価格は20万円、質量は2キロ超を覚悟していましたが、たまたまツクモに寄って見たノートPCがこれでした。



スペックは次の通りです。
 CPU  Corei7-8550U
 メモリ 8GB
 SSD  500GB
 GPU  Geforce GTX1050(4GB)
 質量  1.18Kg


GTX1050のメモリが4GB搭載しているのが嬉しいです。
価格が132,800円(税抜き)、コストパフォーマンス高いと思います。


Ubuntu 16.04のインストール失敗


これまでLinuxでのAI開発環境はUbuntu 16.04を基本にマニュアル化されていました。

今回はインストールに3週間ハマりました。問題がいろいろ発生しました。
・インストール時Wifiが認識しない。
 →有線ネットワークで対応しました(有線LAN→USB変換)
・GPUが認識しない
 →強引にNVIDIAのドライバーを入れてnvidia-smiコマンドでGPUを認識しているように見えましたが、GTX1050と表示されません。
 デバイスを正しく認識してくれませんでした。

結局16.04のインストールは諦めました。


Ubuntu 18.04のインストール成功


18.04を試してみるとなんの問題もなくWfiが接続できました。
また、16.04で試した内容が役に立ち、スキマ時間を使いながら2日でインストールとセットアップが完成しました。


Ubuntu18.04をPS42にインストールする

インストールの手順を紹介します。
1)Ubntuインストール用のUSBメモリの準備
2)ノートPCのSSDの領域の縮小
3)USBメモリからUbuntuのインストールとDual bootの確認
4)NVIDIAドライバのインストール
5)CUDAのインストール
6)cuDNNのインストール
7)Ancondaのインストール
8)Chainerのインストール
9)Opencvのインストール
10)Pydicomのインストール
11)DICOM Tool Kitのインストール
12)Windowsの時刻同期の設定変更
13)Dropboxのインストール(オプション)



1)Ubntuインストール用のUSBメモリの準備

USBメモリにUbuntu18.04のisoイメージを入れてインストール用USBを作成します。
それぞれのリンク先を参考にしてください。
①Ubuntu 16.04 LTSのisoイメージをここからダウンロードします。
https://www.ubuntulinux.jp/News/ubuntu1804-ja-remix
②ツールを使ってインストール用USBにisoイメージファイルを焼きます。
https://linuxfan.info/make-linux-install-usb-on-windows

2)ノートPCのSSDの領域の縮小

・500GBはCドライブとDドライブにそれぞれ半分ずつ割り振られています。
・Cドライブの約120GをUbuntu用に確保してインストールします。
・DドライブはWindowsのデータ領域(Dropboxで共有)に使用しました。


手順
①Windowsファイルマネージャーの「PC」のアイコンを右クリックして,「管理」を選択します。

②「記憶域/ディスクの管理」をクリックします。

③Cドライブを選択し,右クリックして「ボリュームの縮小」を選択します。

④「縮小する領域サイズ(MB)」に「120000」(MB)と入力して,「縮小」ボタンをクリックします。
→Cドライブに約120GBの「未割当」領域ができます。

⑤「コンピュータの管理」を閉じ,コンピュータをシャットダウンします。

3)USBメモリからUbuntuのインストール

3-1)BIOSの設定を行います。

①PC起動時にドラゴンが現れたら「DELETE」キーを連打し,BIOSを起動します。

②「Security / Secure Boot / Secure」を「Disabled」に変更します。

③ (1)「Boot / Fast Boot」を「Disabled」に,(2)「Boot / Boot mode select」を「UEFI」に,(3)「Boot / FIXED BOOT ORDER Priorities」を「USB Hard Drive」に変更します。

④「Save & Exit」で「Save Change and Reset」を選択して設定を保存して終了します。(Windowsも終了して電源オフの状態にします)

3-2)Ubuntuのインストール

①上記1)で作成したUSBメモリをPCに挿入して電源を入れると,Ubuntuの起動・インストールのメニューが現れます。「Try Ubuntu without Installing」を選択します。

②Ubuntuが起動,ネットワークの接続(Wifi)を確認し,ディスクトップ上の左上にあるインストールアイコン「Ubuntu 18.04LTSのインストール」をダブルクリックします。

③「ようこそ」画面で「日本語」を選択し,「続ける」ボタンをクリックします。

④「Ubuntuのインストール準備」で「Ubuntuのインストール中のアップデートをダウンロードする」と「グラフィック,Wifi機器,・・・」の2つにチェックを入れて,「続ける」ボタンをクリックします。

⑤「インストールの種類」は「それ以外」を選択し,「続ける」ボタンをクリックすします。

⑥上記2)④で作成した120GBの未割当領域(空き領域)をクリックし,左下の「+」をクリックして,パーティション編集を行います。
・はじめにスワップ領域8GB を作成ます。パラメータは以下の通りです。
    サイズ           8000 MB
    新しいパーティションタイプ 論理パーテション
     新しいパーティションの場所 この領域の終点
    利用方法          スワップ領域



・次に残りの空き領域をクリックして,「+」ボタンをクリックしてインストール領域を確保します。
    サイズ           残りのサイズ 127977MB
    新しいパーティションタイプ 基本パーテション
    新しいパーティションの場所 この領域の始点
    利用方法          ext4ジャーナリングファイルシステム
    マウントポイント       /



⑦「インストール」ボタンをクリックしてインストールを開始します。
⑧インストールが終了したら,PCをシャットダウンすて,USBメモリを抜きます。

3-3)デュアルブートの確認

①PC起動時にドラゴンが現れたら「DELETE」キーを打ちBIOSを起動します。

②「Boot / UEFI Hard Disk Drive BBS Priorities/Boot Option #1」を「ubuntu」に設定します。(こうしないとOSの選択メニューが現れないでWindowsが起動する)



③「Save & Exit / Save Changes and Reset」で設定を保存して終了します。

④OSの起動メニューが現れ,「Ubuntu」を選択するとUbuntuが起動する。正常にUbuntuが起動できることを確認します。

⑤Ubuntuを再起動すします。

⑥OSの起動メニューが現れ,「Windows Boot Manager」を選択し,正常にWindows10が起動することを確認します。

4)NVIDIAドライバのインストール

・NIVIDIAのGPUボードを認識して利用するために必須なドライバーです。

参考 「環境構築 - Ubuntu 16.04 に NVIDIA ドライバをインストールする方法について」

①Ubuntuを起動します。
CUIモードで作業するため,ランレベルを3に設定します。
$ sudo telinit 3

②Ctl + Alt + F1 でCUIモードに移行します。

③Unbuntuのアップデートを行います。
$ sudo apt-get upgrade
$ sudo apt-get update

④レポジトリの追加をします。
$ sudo add-apt-repository ppa:xorg-edgers/ppa -y
$ sudo apt-get update

⑤インストール可能なNVIDIAドライバの一覧を検索します。
$ apt-cache search "^nvidia-[0-9]{3}$"

⑥一覧の中から最新のドライバ「nvidia-390」(20190416現在)を選び,インストールします。
$ sudo apt-get install -y nvidia-390

⑦インストールにしばらく時間がかかります。インストール終了後、再起動します。
$ reboot

⑧再起動後,ターミナルで次のコマンドを入力してNVIDIAのドライバーが正しくインストールされたことを確認します。
$ nvidia-smi
Wed Apr 10 16:01:46 2019       
+-----------------------------------------------------------------------------+
| NVIDIA-SMI 390.116                Driver Version: 384.130                   |
|-------------------------------+----------------------+----------------------+
| GPU  Name        Persistence-M| Bus-Id        Disp.A | Volatile Uncorr. ECC |
| Fan  Temp  Perf  Pwr:Usage/Cap|         Memory-Usage | GPU-Util  Compute M. |
|===============================+======================+======================|
|   0  GeForce GTX 105...  Off  | 00000000:03:00.0 Off |                  N/A |
| N/A   53C    P0    N/A /  N/A |    332MiB /  4041MiB |      6%      Default |
+-------------------------------+----------------------+----------------------+
                                                                               
+-----------------------------------------------------------------------------+
| Processes:                                                       GPU Memory |
|  GPU       PID   Type   Process name                             Usage      |
|=============================================================================|
|    0      1019      G   /usr/lib/xorg/Xorg                           195MiB |
|    0      1962      G   compiz                                       136MiB |
+-----------------------------------------------------------------------------+

5)CUDAのインストール

・CUDAはNVIDIA社の並列コンピューティングアーキテクチャです。

参考:Ubuntu18.04にNvidia Driver, CUDA, cuDNNを入れる

①CUDA9.1をインストールします。
$ sudo apt install nvidia-cuda-toolkit nvidia-driver-390


②再起動後にCUDAが認識されているか確認します。
$ nvcc –V
nvcc: NVIDIA (R) Cuda compiler driver
Copyright (c) 2005-2017 NVIDIA Corporation>
Built on Fri_Nov__3_21:07:56_CDT_2017
Cuda compilation tools, release 9.1, V9.1.85

6)cuDNNのインストール

・NVIDIAが公開しているDeep Learning用のライブラリです。
NVIDIAからcudnnのファイルをダウンローします。
 ファイル名:cudnn-9.0-linux-x64-v7.4.2.24.tgz

①Ubuntuのホームディレクトリにファイルを置き,解凍します。cudaディレクトリが作成されます。
$ tar -zxf cudnn-9.0-linux-x64-v7.4.2.24.tgz

②cudaディレクトリに移動し,ファイルを所定の場所にコピーします。
$ cd cuda
$ sudo cp lib64/libcudnn* /usr/lib/cuda/lib64/
$ sudo cp include/cudnn.h /usr/lib/cuda/include/

③~/.bashrcファイルをテキストエディタで開き,最後の行に下記の内容を追加します。
$ gedit ~/.bashrc

ファイルの最後の行に次の2行を追加します。
# cuDNN
export PATH=/usr/lib/cuda/bin:${PATH}
export LD_LIBRARY_PATH=/usr/lib/cuda/lib64:${LD_LIBRARY_PATH}


7)Ancondaのインストール

・Python本体とよく利用されるライブラリをセットにしたPythonパッケージです。
・高度な数値計算やデータ分析、機械学習などが簡単にできるようになります。

参考 「Ubuntuを初めて触るド素人がAnacondaを入れてみた


Anaconda DistributionからLinuxのPython3.Xをダウンロードします。
ファイル名:Anaconda3-2019.03-Linux-x86_64.sh

①Anacondaをインストールします。
$ bash Anaconda3-2019.03-Linux-x86_64.sh

※インストール中すべてyesと入力

②pythonを起動して確認します。PythonとAnacondaのバージョンが表示されます。
$ python
Python 3.6.1 |Anaconda 4.4.0 (64-bit)| (default, May 11 2017, 13:09:58)
[GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>

③Anacondaのライブラリを確認します。リストが長いので結果は割愛します。
$ conda list


8)Chainerのインストール

・日本製の深層学習フレームワークです。
・ニューラルネットワークをPythonで柔軟に記述し、学習させることができます。
①CUDA9.0用のcupyのインストールします。
$ pip install cupy-cuda90

②chainerをインストールします
$ pip install chainer

③Pythonを起動して,Chainerとcupyが正しくインストールされたことを確認します。
$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import chainer
>>> chainer.__version__
'5.4.0'
>>> 
>>> import cupy
>>> cupy.__version__
'5.4.0'


Version 5.4.0がインストールされています。


9)Opencvのインストール

・画像処理に関するさまざまな関数を備えたライブラリです。

①pipのアップグレードとOpencvのインストールを行います。
$ pip install –upgrade pip
$ pip install opencv-python

②Pythonを起動し,Opnecvのバージョンを確認します。
$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import cv2
>>> cv2.__version__
'4.1.0'


10)Pydicomのインストール

PydicomはDICOM画像を扱うのに必須のライブラリです。

①pipでpydicomをインストールします。
$ pip install  pydicom

②Pythonを起動してpydicomのバージョンを確認します。
$ python
Python 3.7.3 (default, Mar 27 2019, 22:11:17)
[GCC 7.3.0] :: Anaconda, Inc. on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pydicom
>>> pydicom.__version__
'1.2.2'


11)DICOM Tool kitのインストール

DICOM Tool kitはJPEG圧縮されたDICOM画像を解凍するときなどに使うDICOM関連のツール類です。

①DICOM Tool Kitをインストールします。
$ sudo apt install dcmtk

②インストールを確認します。
・DICOM画像の解凍を行うdcmdjpegコマンドのバージョンを確認します。
$ dcmdjpeg --version
$dcmtk: dcmdjpeg v3.6.2 2017-07-14 $

dcmdjpeg: Decode JPEG-compressed DICOM file

Host type: Debian
Character encoding: UTF-8

External libraries used:
- ZLIB, Version 1.2.11
- IJG, Version 6b  27-Mar-1998 (modified)

表示できたらOKです。

12)Windowsの時刻同期の設定変更

・Linuxとのデュアルブートの環境では,Linux終了後Windowsを起動すると時間がずれる現象が発生します(日本の場合は9時間ずれます)。詳細は参考をご覧ください。

参考:【Windows10】起動時にWindows Time Serviceを起動させる。


①Windows Timeサービスを「自動 (遅延開始)」に設定します。
・ディスクトップの左下の[スタート]をクリックしてプログラムの中から[Windows管理ツール/サービス]を選択します。
・一覧の中の[Windows Time]を右クリックして[プロパティ]を選択します。



・スタートアップの種類を[自動(遅延開始)]を選択し,[適用]をクリックして終了します。



②タスクスケジューラの「SynchronizeTime」タスクを「無効」にします。
・ディスクトップの左下の[スタート]をクリックしてプログラムの中から[Windows管理ツール/タスクスケジューラ]を選択します。
・[タスクスケジューラライブラリ / Microsoft / Windows / Time Synchronization]を選択します。


・[SynchronizeTime]を右クリックして[無効]を選択して終了します。

これで,Linux終了後にWindowsを起動しても時刻がずれることはなくなるはずです。これまで手動での時刻の修正は必要なくなります。

13)Dropboxのインストール(オプション)

開発環境を他のPC(main PCなど)と共有するのにとても便利なツールです。無償で2GB使えます。WindowsはもちろんLinuxやAndroidでも利用可能です。
ここでは,UbuntuにDropboxをインストールして利用する方法を紹介します。

①Dropboxのアカウントを作成する
 あらかじめDropboxのアカウントを作成しておきましょう

参考:Dropbox Basic (無料版) で使える容量は?プラン別の容量と無料で容量を増やす方法

②Dropboxをインストールする
 ターミナルから以下のコマンドを入力します。
$ cd ~ && wget -O - "https://www.dropbox.com/download?plat=lnx.x86_64" | tar xzf -

③Dropboxを起動する。
 ターミナルから以下のコマンドを入力します。
$ ~/.dropbox-dist/dropboxd

【注意】他のPCにあるファイルを編集状態で,自PCで同じファイルを起動し編集,保存すると不整合が発生します。Dropboxは「競合ファイル」として自動的に新しいファイルを別名で他のPCに保存しますが,基本的に同期がかかっているディレクトリのファイルは作業終了後、必ず閉じるように注意しましょう。


おわりに


今回,MSIのノートPCをWindowsとLinuxのDual boot環境を作成しました。
当初Ubuntu16.04で試みていましたが,過去の成功がじゃまして無駄な時間を使ってしまいました。
しかし,失敗の中にも効率的にインストール方法が見つけることができ,そのおかげでUbuntu18.04ではスムースにインストールすることができました。

Googleが提供するCollaboratoryなどクラウドでのAI開発環境など様々な環境がありますが,PCの中のローカルな環境にAIを構築することにこだわるのは医療情報を扱う場合,閉ざされた環境で実験,開発する必要があるからです。

今回,GPU搭載の1.18㎏のノートPCにAI環境を構築できたことで,いつでも開発・テストができる環境を得たことは大きいです。(もちろんデータは自分のサンプル画像しか入っていませんが)

2019年2月25日月曜日

Pyゼミ3.07 ChainerでGPUを使って学習してみる

GPUによる高速な学習を試してみる。


keyword: Chainer, GPU, cupy


大量のデータを使って学習するにはGPU(Graphics Processing Uni)は必須です。
CPUの演算より約10倍も早くなります。
いろいろパラメータやデータなどを変えて実験するにはGPUはなくてはならないハードウェアです。

これまで作成したAIのプログラム(Pyゼミ3.05など)の修正点について説明します。
GPUを使うための環境の構築については説明しません。
環境の構築はインターネット上にたくさんの情報がりますので,OS,CUDAやその他のドライバーなどのバージョンを確認しながら構築してください。


前回Pyゼミ3.05のプログラムをGPU対応にする


ChainerのプログラムをGPU対応するには次の3点の修正を加えます。

  1. numpyの代わりにcupyを使う
  2. modelをGPUに対応させる
  3. UpdaterをGPUに対応させる

これだけです。それぞれについて具体的に説明します。

1. numpyの代わりにcupyを使う

cupyはGPUで演算させるためのライブラリで,行列演算などを効率的に行うnumpyと互換性を持っています。
つまり,numpyで記述しているプログラムをcupyに書き換えればよいわけです。

numpyは次のようにnpという名前でインポートしています。
    import numpy as np

また,GPUの使用,未使用をスイッチするために,uses_device変数を初期化します。
未使用のときはー1を,使用時は0以上の値(通常は0)を設定します。
    uses_device = 0     # GPU 0〜、CPU -1

さて,GPU使用時にnumpyをcupyに書き換える方法ですが,
次のように,GPU使用時(>=0)に,cupyをcpとしてインポートし,
npにcpを代入するだけで,プログラムはGPU用に書き換えられました。
    if uses_device >= 0:
        import cupy as cp
        np = cp


2. modelをGPUに対応させる

ニューラルネットワークのモデルをいつも通りに生成します。
    model = Alexnet(nlbl)

このモデルをGPUに対応させます。
GPU使用時(>=0)には以下の行を実行し,モデルがGPUを使って演算できるようにします。
    if uses_device >= 0:
      chainer.cuda.get_device_from_id(0).use()
      chainer.cuda.check_cuda_available()
      model.to_gpu()


3. UpdaterをGPUに対応させる


パラメータ更新を担当するUpdaterをGPUに対応させます。
引数にdevice=uses_deviceを追加するだけです。
    updater  = training.StandardUpdater(t_iterator, self.opt, device=uses_device) 

また,Pyゼミ3.05のプログラム中のEvaluatorも同様に引数にdevice=uses_deviceを追加します。
    trainer.extend(extensions.Evaluator(v_iterator, self.model, device=uses_device))

以上,3つの追加修正によりGPUに対応したプログラムになります。


プログラムを入力して実行してみよう


このプログラムはPy3.05が基本です。これに3つの修正が加わっています。

dicomAlexnetTrainGPU.py

import os, sys
import numpy as np
import cv2
import chainer
from chainer import Function, report, training, utils, Variable
from chainer import datasets, iterators, optimizers, serializers
from chainer import Link, Chain, ChainList
import chainer.functions as F
import chainer.links as L
from chainer.datasets import tuple_dataset
from chainer.training import extensions
from chainer.datasets import split_dataset_random #new
import pydicom

uses_device = 0     # GPU 0〜、CPU -1

if uses_device >= 0:
    import cupy as cp
    np = cp

def constractTrainData( fname , resz ):
    image = []
    label = []
    for i, line in enumerate(open(fname, 'r')):
        data = line.split(",")        
        print("Train",i, data[0], "Label#",data[1])
        img = readDicom2png(data[0], "tmp.png")
        if resz > 0:                        
            img = cv2.resize(img,(resz, resz))      
        img = img.astype(np.float32)  
        img = (img - 128)/128           #画素値0-255を-1.0から+1.0に変換
        image.append([img])
              
        t = np.array(int(data[1]), dtype=np.int32)
        label.append(t)
              
    train = tuple_dataset.TupleDataset(image, label)      
    return train
  
def readDicom2png(dcmfnm, tmpfnm):
    ds  = pydicom.read_file(dcmfnm)    
    wc  = ds.WindowCenter              
    ww  = ds.WindowWidth                        
    img = ds.pixel_array                        
    max = wc + ww / 2                    
    min = wc - ww / 2    
    img = 255 * (img - min)/(max - min)  
    img[img > 255] = 255                
    img[img < 0  ] = 0                  
    img = img.astype(np.uint8)
    cv2.imwrite(tmpfnm, img)
    return img  

def getLabelName(fname):
    lblnm = []
    for line in open(fname, 'r'):
        data = line.split(",")
        lblnm.append(data[1].strip())
        #lblnm[int(data[0])]= data[1]  
    print("LabelName:",lblnm)            
    return len(lblnm), lblnm
      
class Alexnet(Chain):  ### AlexNet ###
    def __init__(self, n_label, n_channel=1):
        super(Alexnet, self).__init__(
            conv1 = L.Convolution2D(n_channel, 96, 11, stride=4),
            conv2 = L.Convolution2D(96, 256, 5, pad=2),
            conv3 = L.Convolution2D(256, 384, 3, pad=1),
            conv4 = L.Convolution2D(384, 384, 3, pad=1),
            conv5 = L.Convolution2D(384, 256, 3, pad=1),
            fc6 = L.Linear(None, 4096),
            fc7 = L.Linear(4096, 4096),
            fc8 = L.Linear(4096, n_label),
        )

    def __call__(self, x):
        h = F.max_pooling_2d(F.local_response_normalization(
            F.relu(self.conv1(x))), 3, stride=2)
        h = F.max_pooling_2d(F.local_response_normalization(
            F.relu(self.conv2(h))), 3, stride=2)
        h = F.relu(self.conv3(h))
        h = F.relu(self.conv4(h))
        h = F.max_pooling_2d(F.relu(self.conv5(h)), 2, stride=2)
        h = F.dropout(F.relu(self.fc6(h)))
        h = F.dropout(F.relu(self.fc7(h)))
        return self.fc8(h)

class Classifier():
    def __init__(self, mdlnm, nlbl):
        model = Alexnet(nlbl)
        
        if uses_device >= 0:  # GPUを使う
            chainer.cuda.get_device_from_id(0).use()
            chainer.cuda.check_cuda_available()
            model.to_gpu() # GPU用データ形式に変換

        self.model = L.Classifier(model,lossfun=F.softmax_cross_entropy)
        self.opt   = optimizers.Adam()
        self.opt.setup(self.model)      
        self.modelName = mdlnm      
          
    def doTrain(self, nLbl, train, batch_size, n_epoch, modelName):
        trn, vld = split_dataset_random(train, int(len(train)*0.8), seed=0)      
        t_iterator = iterators.SerialIterator(trn, batch_size, shuffle=True)
        v_iterator = iterators.SerialIterator(vld, batch_size, repeat=False, shuffle=False)
        updater  = training.StandardUpdater(t_iterator, self.opt, device=uses_device) #GPU
        trainer  = training.Trainer(updater, (n_epoch, 'epoch'), out='result')

        trainer.extend(extensions.Evaluator(v_iterator, self.model, device=uses_device))  #GPU            
        trainer.extend(extensions.LogReport())
        trainer.extend(extensions.PrintReport(['epoch', 'main/loss', 'main/accuracy', 'validation/main/accuracy']))
        trainer.extend(extensions.ProgressBar())
        trainer.extend(extensions.PlotReport(['main/loss', 'validation/main/loss'], x_key='epoch', file_name='loss.png'))
        trainer.extend(extensions.PlotReport(['main/accuracy', 'validation/main/accuracy'], x_key='epoch', file_name='accuracy.png'))
      
        trainer.run()
              
        print("学習モデル",self.modelName,"を保存します... .   .")
        chainer.serializers.save_hdf5( self.modelName, self.model )
        print("\t... .  .学習モデルを保存しました")

if __name__=='__main__':
    trainfnm = "./TrainTestList/TrainData.0.3.csv"
    labelfnm = "./TrainTestList/labelName.csv"
    resize   = 128       #画像リサイズ 0のときそのままの画素サイズ
    modelnm  = "Alexnet.0.3.hdf5"  #モデル保存名  
    batch_size = 10  # バッチサイズ
    n_epoch    = 30  # エポック数
  
    nLbl, lblName = getLabelName(labelfnm)
    train = constractTrainData(trainfnm, resize)
    print("\ntrain\n")
    print("画像データ数:",len(train),"画像/ラベル:",len(train[0]))
    print("画像Chanel数:",len(train[0][0]),"高さ:",len(train[0][0][0]),"幅:",len(train[0][0][0][0]))
  
    dl = Classifier(modelnm, nLbl)
    dl.doTrain(nLbl, train, batch_size, n_epoch, modelnm)



実行結果


プログラムは,実効時間を計測するためtimeコマンドを付けて実行します。

プログラムはラベルファイルを読み込みラベル名を表示し,DICOM画像を読み込みます。

学習がはじまり30回のエポック終了後に実行時間が表示されます。
timeコマンドは,プログラムの起動から終了までの時間をrealの値で示しています。
わたしのGefoece GTX 1060では0m29.636sでした。約30秒でした。
(実行環境:OS Ubuntu 16.04 LTS,CPU Core i5-6400 2.7GHz, メモリ 8GB,GPU GeForce GTX 1060 6GB)

$ time␣python␣dicomAlexnetTrainGPU.py⏎
LabelName: ['Lung', 'Abdomen', 'Chest', 'Head']
Train 0 ../dcmdir2/Lung/W5305890 Label# 0
Train 1 ../dcmdir2/Lung/K5303593 Label# 0
Train 2 ../dcmdir2/Lung/R5305640 Label# 0
Train 5 ../dcmdir2/Lung/D5304468 Label# 0
     ・・・・・
Train 139 ../dcmdir2/Head/G5221187 Label# 3
Train 140 ../dcmdir2/Head/Q5221671 Label# 3
Train 141 ../dcmdir2/Head/E5221093 Label# 3

train

画像データ数: 142 画像/ラベル: 2
画像Chanel数: 1 高さ: 128 幅: 128
epoch       main/loss   main/accuracy  validation/main/accuracy
1           1.8203      0.283333       0.481481                
2           1.06403     0.545455       0.725926                
3           0.741141    0.581818       0.759259                
4           0.735347    0.7            0.725926                
5           0.639062    0.581818       0.825926
         ・・・・・
28          0.00639354  1              1                        
29          0.0178032   0.990909       1                        
30          0.0548964   0.981818       0.933333                
学習モデル Alexnet.0.3.hdf5 を保存します... .   .
 ... .  .学習モデルを保存しました

real 0m29.636s
user 0m31.316s
sys 0m17.240s


GPU使用・未使用の演算時間の比較


GPU使用時では約30秒の実行時間でしたが,
uses_device = -1 にして,GPUを使用しないで実行時間を計測すると
6分18秒でした。
その差は約12倍です。
明らかに実行速度が向上しています。


real 6m18.688s
user 9m16.004s
sys 8m6.400s



GPUの稼働状況はNVIDIAのツールで確認できます。
引数はハイフン,エルです。

$ nvidia-smi␣-l⏎




中段の右にGPU-Utilが96%と表示され,GPUがフル稼働しているのが分かります。


まとめ


今回はGPUに対応したプログラムの作り方について説明しました。
次の3つについて修正するだけで簡単にGPUが使えることがわかりました。
  1. numpyの代わりにcupyを使う
  2. modelをGPUに対応させる
  3. UpdaterをGPUに対応させる
まだ,GPU未導入の方はぜひ導入を検討するとよいでしょう。

また,使っていない古い読影端末やワークステーションにGPU搭載しているものがあります。
とりあえずこれを流用する方法もあると思います。