ホーム » MONAI » MONAI 0.7 : tutorials : モジュール – 医用画像のロード

MONAI 0.7 : tutorials : モジュール – 医用画像のロード

MONAI 0.7 : tutorials : モジュール – 医用画像のロード (翻訳/解説)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 10/21/2021 (0.7.0)

* 本ページは、MONAI の以下のドキュメントを翻訳した上で適宜、補足説明したものです:

* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。

 

クラスキャット 人工知能 研究開発支援サービス 無料 Web セミナー開催中

◆ クラスキャットは人工知能・テレワークに関する各種サービスを提供しております。お気軽にご相談ください :

◆ 人工知能とビジネスをテーマに WEB セミナーを定期的に開催しています。スケジュール
  • お住まいの地域に関係なく Web ブラウザからご参加頂けます。事前登録 が必要ですのでご注意ください。
  • ウェビナー運用には弊社製品「ClassCat® Webinar」を利用しています。

お問合せ : 本件に関するお問い合わせ先は下記までお願いいたします。

株式会社クラスキャット セールス・マーケティング本部 セールス・インフォメーション
E-Mail:sales-info@classcat.com  ;  WebSite: https://www.classcat.com/  ;  Facebook

 

MONAI 0.7 : tutorials : モジュール – 医用画像のロード

このノートブックは MONAI で医用画像の様々な形式を簡単にロードして多くの追加の操作を実行する方法を紹介します。

このノートブックは MONAI で医用画像の様々な形式を簡単にロードして多くの追加の操作を実行する方法を説明します。

 

環境のセットアップ

!python -c "import monai" || pip install -q "monai-weekly[itk, pillow]"

 

インポートのセットアップ

# Copyright 2020 MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

# Copyright 2020 MONAI Consortium
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#     http://www.apache.org/licenses/LICENSE-2.0
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.

import os
import shutil
import numpy as np
import itk
from PIL import Image
import tempfile
from monai.data import ITKReader, PILReader
from monai.transforms import (
    LoadImage, LoadImaged, EnsureChannelFirstd,
    Resized, EnsureTyped, Compose
)
from monai.config import print_config

print_config()
MONAI version: 0.6.0+22.g027947bf.dirty
Numpy version: 1.21.0
Pytorch version: 1.9.0
MONAI flags: HAS_EXT = False, USE_COMPILED = False
MONAI rev id: 027947bf91ff0dfac94f472ed1855cd49e3feb8d

Optional dependencies:
Pytorch Ignite version: 0.4.5
Nibabel version: 3.2.1
scikit-image version: 0.18.2
Pillow version: 8.2.0
Tensorboard version: 2.4.1
gdown version: 3.13.0
TorchVision version: 0.10.0
tqdm version: 4.61.1
lmdb version: 1.2.1
psutil version: 5.8.0
pandas version: 1.1.5
einops version: 0.3.0

For details about installing the optional dependencies, please visit:
    https://docs.monai.io/en/latest/installation.html#installing-the-recommended-dependencies

 

デフォルトの画像リーダーで Nifti 画像をロードする

MONAI はサポートされる suffix と下の順序に基づいてリーダーを自動的に選択します :

  • このローダを呼び出すとき実行時にユーザ指定されたリーダー。
  • リストの最新のものから最初のものへ登録されたリーダー。
  • デフォルトのリーダー : (nii, nii.gz -> NibabelReader), (png, jpg, bmp -> PILReader), (npz, npy -> NumpyReader), (others -> ITKReader).

 

サンプル画像を生成する

# generate 3D test images
tempdir = tempfile.mkdtemp()
test_image = np.random.rand(64, 128, 96)
filename = os.path.join(tempdir, "test_image.nii.gz")
itk_np_view = itk.image_view_from_array(test_image)
itk.imwrite(itk_np_view, filename)

 

画像ファイルをロードする

data, meta = LoadImage()(filename)
print(f"image data shape:{data.shape}")
print(f"meta data:{meta}")
image data shape:(96, 128, 64)
meta data:{'sizeof_hdr': array(348, dtype=int32), 'extents': array(0, dtype=int32), 'session_error': array(0, dtype=int16), 'dim_info': array(0, dtype=uint8), 'dim': array([  3,  96, 128,  64,   1,   1,   1,   1], dtype=int16), 'intent_p1': array(0., dtype=float32), 'intent_p2': array(0., dtype=float32), 'intent_p3': array(0., dtype=float32), 'intent_code': array(0, dtype=int16), 'datatype': array(64, dtype=int16), 'bitpix': array(64, dtype=int16), 'slice_start': array(0, dtype=int16), 'pixdim': array([1., 1., 1., 1., 0., 0., 0., 0.], dtype=float32), 'vox_offset': array(0., dtype=float32), 'scl_slope': array(nan, dtype=float32), 'scl_inter': array(nan, dtype=float32), 'slice_end': array(0, dtype=int16), 'slice_code': array(0, dtype=uint8), 'xyzt_units': array(2, dtype=uint8), 'cal_max': array(0., dtype=float32), 'cal_min': array(0., dtype=float32), 'slice_duration': array(0., dtype=float32), 'toffset': array(0., dtype=float32), 'glmax': array(0, dtype=int32), 'glmin': array(0, dtype=int32), 'qform_code': array(1, dtype=int16), 'sform_code': array(1, dtype=int16), 'quatern_b': array(0., dtype=float32), 'quatern_c': array(0., dtype=float32), 'quatern_d': array(1., dtype=float32), 'qoffset_x': array(-0., dtype=float32), 'qoffset_y': array(-0., dtype=float32), 'qoffset_z': array(0., dtype=float32), 'srow_x': array([-1.,  0.,  0., -0.], dtype=float32), 'srow_y': array([ 0., -1.,  0., -0.], dtype=float32), 'srow_z': array([0., 0., 1., 0.], dtype=float32), 'affine': array([[-1.,  0.,  0., -0.],
       [ 0., -1.,  0., -0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'original_affine': array([[-1.,  0.,  0., -0.],
       [ 0., -1.,  0., -0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'as_closest_canonical': False, 'spatial_shape': array([ 96, 128,  64], dtype=int16), 'original_channel_dim': 'no_channel', 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image.nii.gz'}

 

Nifti 画像のリストをロードしてそれらをマルチチャネル画像としてスタックする

ファイルのリストをロードし、それらを一緒にスタックして最初の次元として新しい次元を追加します。

そしてスタックされた結果を表わすために最初の画像のメタデータを使用します。

 

幾つかの画像サンプルを生成する

filenames = ["test_image.nii.gz", "test_image2.nii.gz", "test_image3.nii.gz"]
for i, name in enumerate(filenames):
    filenames[i] = os.path.join(tempdir, name)
    itk_np_view = itk.image_view_from_array(test_image)
    itk.imwrite(itk_np_view, filenames[i])

 

画像をロードする

出力データ shape は (3, 96, 128, 64) であることに注意してください、3 チャネル画像を示しています。

data, meta = LoadImage()(filenames)

print(f"image data shape:{data.shape}")
print(f"meta data:{meta}")
image data shape:(3, 96, 128, 64)
meta data:{'sizeof_hdr': array(348, dtype=int32), 'extents': array(0, dtype=int32), 'session_error': array(0, dtype=int16), 'dim_info': array(0, dtype=uint8), 'dim': array([  3,  96, 128,  64,   1,   1,   1,   1], dtype=int16), 'intent_p1': array(0., dtype=float32), 'intent_p2': array(0., dtype=float32), 'intent_p3': array(0., dtype=float32), 'intent_code': array(0, dtype=int16), 'datatype': array(64, dtype=int16), 'bitpix': array(64, dtype=int16), 'slice_start': array(0, dtype=int16), 'pixdim': array([1., 1., 1., 1., 0., 0., 0., 0.], dtype=float32), 'vox_offset': array(0., dtype=float32), 'scl_slope': array(nan, dtype=float32), 'scl_inter': array(nan, dtype=float32), 'slice_end': array(0, dtype=int16), 'slice_code': array(0, dtype=uint8), 'xyzt_units': array(2, dtype=uint8), 'cal_max': array(0., dtype=float32), 'cal_min': array(0., dtype=float32), 'slice_duration': array(0., dtype=float32), 'toffset': array(0., dtype=float32), 'glmax': array(0, dtype=int32), 'glmin': array(0, dtype=int32), 'qform_code': array(1, dtype=int16), 'sform_code': array(1, dtype=int16), 'quatern_b': array(0., dtype=float32), 'quatern_c': array(0., dtype=float32), 'quatern_d': array(1., dtype=float32), 'qoffset_x': array(-0., dtype=float32), 'qoffset_y': array(-0., dtype=float32), 'qoffset_z': array(0., dtype=float32), 'srow_x': array([-1.,  0.,  0., -0.], dtype=float32), 'srow_y': array([ 0., -1.,  0., -0.], dtype=float32), 'srow_z': array([0., 0., 1., 0.], dtype=float32), 'affine': array([[-1.,  0.,  0., -0.],
       [ 0., -1.,  0., -0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'original_affine': array([[-1.,  0.,  0., -0.],
       [ 0., -1.,  0., -0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'as_closest_canonical': False, 'spatial_shape': array([ 96, 128,  64], dtype=int16), 'original_channel_dim': 0, 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image.nii.gz'}

 

DICOM 形式の 3D 画像のロード

サンプル画像を生成する

filename = os.path.join(tempdir, "test_image.dcm")
dcm_image = np.random.randint(256, size=(64, 128, 96)).astype(np.uint8)
itk_np_view = itk.image_view_from_array(dcm_image)
itk.imwrite(itk_np_view, filename)

Ref.

!pip install pydicom
from pydicom import dcmread
from pydicom.data import get_testdata_file
ds = dcmread(filename)
 print(type(ds.PixelData), len(ds.PixelData), ds.PixelData[:2])
<class 'bytes'> 786432 b'\x97\xb3'
arr = ds.pixel_array
print(type(arr), arr.shape, arr.dtype)
<class 'numpy.ndarray'> (64, 128, 96) uint8
plt.imshow(arr[:,:,0])

 

画像のロード

data, meta = LoadImage()(filename)

print(f"image data shape:{data.shape}")
print(f"meta data:{meta}")
image data shape:(96, 128, 64)
meta data:{'0008|0016': '1.2.840.10008.5.1.4.1.1.7.2', '0008|0018': '1.2.826.0.1.3680043.2.1125.1.49144329026051408197861589261117151', '0008|0020': '20210722', '0008|0030': '170636.794924 ', '0008|0050': '', '0008|0060': 'OT', '0008|0090': '', '0010|0010': '', '0010|0020': '', '0010|0030': '', '0010|0040': '', '0020|000d': '1.2.826.0.1.3680043.2.1125.1.55082693700512159103862591206822067', '0020|000e': '1.2.826.0.1.3680043.2.1125.1.88286845929415088271370520342732963', '0020|0010': '', '0020|0011': '', '0020|0013': '', '0020|0052': '1.2.826.0.1.3680043.2.1125.1.54872246537810654209027218359324264', '0028|0002': '1', '0028|0004': 'MONOCHROME2 ', '0028|0008': '64', '0028|0009': '(5200,9230)', '0028|0010': '128', '0028|0011': '96', '0028|0100': '8', '0028|0101': '8', '0028|0102': '7', '0028|0103': '0', '0028|1052': '0 ', '0028|1053': '1 ', '0028|1054': 'US', 'spacing': array([1., 1., 1.]), 'original_affine': array([[-1.,  0.,  0.,  0.],
       [ 0., -1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'affine': array([[-1.,  0.,  0.,  0.],
       [ 0., -1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'spatial_shape': array([ 96, 128,  64]), 'original_channel_dim': 'no_channel', 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image.dcm'}

 

DICOM 画像のリストをロードしてそれらをマルチチャネル画像としてスタックする

ファイルのリストをロードし、それらを一緒にスタックして最初の次元として新しい次元を追加します。

そしてスタックされた結果を表わすために最初の画像のメタデータを使用します。

 

幾つかの画像サンプルを生成する

filenames = ["test_image-1.dcm", "test_image-2.dcm", "test_image-3.dcm"]
for i, name in enumerate(filenames):
    filenames[i] = os.path.join(tempdir, name)
    dcm_image = np.random.randint(256, size=(64, 128, 96)).astype(np.uint8)
    itk_np_view = itk.image_view_from_array(dcm_image)
    itk.imwrite(itk_np_view, filenames[i])

 

画像をロードする

data, meta = LoadImage()(filenames)

print(f"image data shape:{data.shape}")
print(f"meta data:{meta}")
image data shape:(3, 96, 128, 64)
meta data:{'0008|0016': '1.2.840.10008.5.1.4.1.1.7.2', '0008|0018': '1.2.826.0.1.3680043.2.1125.1.70459687821230247148357643462536357', '0008|0020': '20210722', '0008|0030': '170636.830318 ', '0008|0050': '', '0008|0060': 'OT', '0008|0090': '', '0010|0010': '', '0010|0020': '', '0010|0030': '', '0010|0040': '', '0020|000d': '1.2.826.0.1.3680043.2.1125.1.29026801319907695346385092515779097', '0020|000e': '1.2.826.0.1.3680043.2.1125.1.48166663359438709592920991059259631', '0020|0010': '', '0020|0011': '', '0020|0013': '', '0020|0052': '1.2.826.0.1.3680043.2.1125.1.52318595437965660783667062205792724', '0028|0002': '1', '0028|0004': 'MONOCHROME2 ', '0028|0008': '64', '0028|0009': '(5200,9230)', '0028|0010': '128', '0028|0011': '96', '0028|0100': '8', '0028|0101': '8', '0028|0102': '7', '0028|0103': '0', '0028|1052': '0 ', '0028|1053': '1 ', '0028|1054': 'US', 'spacing': array([1., 1., 1.]), 'original_affine': array([[-1.,  0.,  0.,  0.],
       [ 0., -1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'affine': array([[-1.,  0.,  0.,  0.],
       [ 0., -1.,  0.,  0.],
       [ 0.,  0.,  1.,  0.],
       [ 0.,  0.,  0.,  1.]]), 'spatial_shape': array([ 96, 128,  64]), 'original_channel_dim': 0, 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image-1.dcm'}

 

PNG 形式で 2D 画像をロードする

画像を生成する

test_image = np.random.randint(0, 256, size=[128, 256])
filename = os.path.join(tempdir, "test_image.png")
Image.fromarray(test_image.astype("uint8")).save(filename)

 

画像をロードする

data, meta = LoadImage()(filename)

print(f"image data shape:{data.shape}")
print(f"meta data:{meta}")
image data shape:(256, 128)
meta data:{'format': 'PNG', 'mode': 'L', 'width': 256, 'height': 128, 'spatial_shape': array([256, 128]), 'original_channel_dim': 'no_channel', 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image.png'}

 

指定した画像リーダで画像をロードする

そして画像リーダーのための追加パラメータを設定できます、例えば、ITKReader のために c_order_axis_indexing=True を設定すると、このパラメータは後で ITK read() 関数に渡されます。

loader = LoadImage()
loader.register(ITKReader())
data, meta = loader(filename)

print(f"image data shape:{data.shape}")
print(f"meta data:{meta}")
image data shape:(256, 128)
meta data:{'spacing': array([1., 1.]), 'original_affine': array([[-1.,  0.,  0.],
       [ 0., -1.,  0.],
       [ 0.,  0.,  1.]]), 'affine': array([[-1.,  0.,  0.],
       [ 0., -1.,  0.],
       [ 0.,  0.,  1.]]), 'spatial_shape': array([256, 128]), 'original_channel_dim': 'no_channel', 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image.png'}

 

画像をロードして追加の操作を実行する

幾つかの画像リーダーはファイルから画像を読んだ後の追加の操作をサポートできます。

例えば、PILReader のためにコンバータを設定できます : PILReader(converter=lambda image: image.convert(“LA”))

loader = LoadImage(PILReader(converter=lambda image: image.convert("LA")))
data, meta = loader(filename)

print(f"image data shape:{data.shape}")
print(f"meta data:{meta}")
image data shape:(256, 128, 2)
meta data:{'format': None, 'mode': 'LA', 'width': 256, 'height': 128, 'spatial_shape': array([256, 128]), 'original_channel_dim': -1, 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image.png'}

 

LoadImage を他の変換と組み合わせる

変換チェインを構築するために LoadImage を他の変換と接続することは非常に簡単です。

transform = Compose([
    LoadImaged(keys="image"),
    EnsureChannelFirstd(keys="image"),
    Resized(keys="image", spatial_size=[64, 64]),
    EnsureTyped("image"),
])
test_data = {"image": filename}
result = transform(test_data)
print(f"image data shape:{result['image'].shape}")
print(f"meta data:{result['image_meta_dict']}")
image data shape:torch.Size([1, 64, 64])
meta data:{'format': 'PNG', 'mode': 'L', 'width': 256, 'height': 128, 'spatial_shape': array([256, 128]), 'original_channel_dim': 'no_channel', 'filename_or_obj': '/var/folders/6f/fdkl7m0x7sz3nj_t7p3ccgz00000gp/T/tmpq2arvyex/test_image.png'}

 

データディレクトリのクリーンアップ

一時ディレクトリが使用された場合にはディレクトリを削除します。

shutil.rmtree(tempdir)
 

以上



ClassCat® Chatbot

人工知能開発支援

◆ クラスキャットは 人工知能研究開発支援 サービスを提供しています :
  • テクニカルコンサルティングサービス
  • 実証実験 (プロトタイプ構築)
  • アプリケーションへの実装
  • 人工知能研修サービス
◆ お問合せ先 ◆
クラスキャット
セールス・インフォメーション
E-Mail:sales-info@classcat.com

カテゴリー