数日前、私は MRI スキャンを受けました。大きなチューブの中に入れられ、15 分間、私の周りの機械がブザー音やハム音、カチカチという音を発しました。検査の最後に、データが入った CD を受け取りました。このような状況で優秀な開発者はどうするでしょうか。もちろん、家に帰るとすぐにデータを調べ、 Pythonを使用してデータを抽出する方法を考え始めます。
CD には、多数の DLL、1 つの EXE、および Windows ビューア用の追加ファイルが含まれていましたが、これらは Linux では明らかに役に立ちません。肝心なのは、DICOM という名前のフォルダです。このフォルダには、8 つのフォルダに渡って一連の拡張子のないファイルが格納されていました。ChatGPT のおかげで、 DICOM はMRI、CT、X 線画像を保存するために通常使用される標準形式であることがわかりました。基本的に、これはグレースケール画像といくつかのメタデータを含むシンプルなパッケージ形式です。
次のコードを使用すると、ファイルを PNG 形式で簡単にエクスポートできます。
import os import pydicom from PIL import Image import sys def save_image(pixel_array, output_path): if pixel_array.dtype != 'uint8': pixel_array = ((pixel_array - pixel_array.min()) / (pixel_array.max() - pixel_array.min()) * 255).astype('uint8') img = Image.fromarray(pixel_array) img.save(output_path) print(f"Saved image to {output_path}") def process_dicom_directory(dicomdir_path, save_directory): if not os.path.exists(save_directory): os.makedirs(save_directory) dicomdir = pydicom.dcmread(dicomdir_path) for patient_record in dicomdir.patient_records: if 'PatientName' in patient_record: print(f"Patient: {patient_record.PatientName}") for study_record in patient_record.children: if 'StudyDate' in study_record: print(f"Study: {study_record.StudyDate}") for series_record in study_record.children: for image_record in series_record.children: if 'ReferencedFileID' in image_record: file_id = list(map(str, image_record.ReferencedFileID)) file_id_path = os.path.join(*file_id) print(f"Image: {file_id_path}") image_path = os.path.join(os.path.dirname(dicomdir_path), file_id_path) modified_filename = '_'.join(file_id) + '.png' image_save_path = os.path.join(save_directory, modified_filename) try: img_data = pydicom.dcmread(image_path) print("DICOM image loaded successfully.") save_image(img_data.pixel_array, image_save_path) except FileNotFoundError as e: print(f"Failed to read DICOM file at {image_path}: {e}") except Exception as e: print(f"An error occurred: {e}") if __name__ == "__main__": if len(sys.argv) < 3: print("Usage: python dicomsave.py <path_to_DICOMDIR> <path_to_save_images>") sys.exit(1) dicomdir_path = sys.argv[1] save_directory = sys.argv[2] process_dicom_directory(dicomdir_path, save_directory)
コードには 2 つのパラメータが必要です。1 つ目は DICOMDIR ファイルへのパスで、2 つ目は PNG 形式で画像が保存されるディレクトリです。
DICOM ファイルを読み取るには、 Pydicomライブラリを使用します。構造はpydicom.dcmread関数を使用して読み込まれ、そこからメタデータ (患者の名前など) と画像を含む研究を抽出できます。画像データもdcmreadで読み取ることができます。生データは、 save_image関数によって PNG に変換される、 pixelarrayフィールドを通じてアクセスできます。それほど複雑ではありません。
結果として、いくつかの正方形 (通常 512 x 512) のグレースケール画像が生成され、後で簡単に利用できるようになります。
なぜ、DICOM 形式を使用した私の冒険についてこの短いストーリーを書くことが重要だと思ったのでしょうか。プログラマーが医療データの処理と聞くと、それは大学や研究機関だけが扱える深刻なものだと考えるかもしれません (少なくとも私はそう思っていました)。ご覧のとおり、ここでは処理が非常に簡単で、ニューラル ネットワーク処理などに最適な、単純なグレースケール画像について話しているのです。
これによってどのような可能性が開けるか考えてみましょう。単純な腫瘍検出器のようなものに必要なのは、比較的単純な畳み込みネットワークと十分な数のサンプルだけです。実際には、このタスクは画像内の犬や猫を認識することとあまり変わりません。
これがいかに真実であるかを説明するために、例を挙げてみましょう。Kaggle で簡単に検索したところ、MRI データ (グレースケール画像) に基づいて脳腫瘍を分類したノートブックを見つけました。データセットでは、画像を 4 つのグループに分類しています。3 種類の脳腫瘍と、健康な脳の画像を含む 4 番目のカテゴリです。ネットワークのアーキテクチャは次のとおりです。
┏━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━┓ ┃ Layer (type) ┃ Output Shape ┃ Param # ┃ ┡━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━┩ │ conv2d (Conv2D) │ (None, 164, 164, 64) │ 1,664 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d (MaxPooling2D) │ (None, 54, 54, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_1 (Conv2D) │ (None, 50, 50, 64) │ 102,464 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_1 (MaxPooling2D) │ (None, 16, 16, 64) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_2 (Conv2D) │ (None, 13, 13, 128) │ 131,200 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_2 (MaxPooling2D) │ (None, 6, 6, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ conv2d_3 (Conv2D) │ (None, 3, 3, 128) │ 262,272 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ max_pooling2d_3 (MaxPooling2D) │ (None, 1, 1, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ flatten (Flatten) │ (None, 128) │ 0 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense (Dense) │ (None, 512) │ 66,048 │ ├─────────────────────────────────┼────────────────────────┼───────────────┤ │ dense_1 (Dense) │ (None, 4) │ 2,052 │ └─────────────────────────────────┴────────────────────────┴───────────────┘
ご覧のとおり、最初の数層は畳み込みと最大プーリング サンドイッチで構成されており、腫瘍のパターンを抽出し、その後に分類を実行する 2 つの密な層が続きます。これは、CIFAR-10 データセットを使用するTensorFlow の CNN サンプル コードで使用されているアーキテクチャとまったく同じです。これは私が最初に遭遇したニューラル ネットワークの 1 つだったので、よく覚えています。CIFAR -10データセットには、前述のように犬と猫の 2 つのクラスを含む 10 のクラスに分類された 60,000 枚の画像が含まれています。ネットワークのアーキテクチャは次のようになります。
Model: “sequential” _________________________________________________________________ Layer (type) Output Shape Param # ================================================================= conv2d (Conv2D) (None, 30, 30, 32) 896 _________________________________________________________________ max_pooling2d (MaxPooling2D) (None, 15, 15, 32) 0 _________________________________________________________________ conv2d_1 (Conv2D) (None, 13, 13, 64) 18496 _________________________________________________________________ max_pooling2d_1 (MaxPooling2 (None, 6, 6, 64) 0 _________________________________________________________________ conv2d_2 (Conv2D) (None, 4, 4, 64) 36928 _________________________________________________________________ flatten (Flatten) (None, 1024) 0 _________________________________________________________________ dense (Dense) (None, 64) 65600 _________________________________________________________________ dense_1 (Dense) (None, 10) 650 ================================================================= Total params: 122,570 Trainable params: 122,570 Non-trainable params: 0 _________________________________________________________________
明らかに、わずかに異なるパラメータを持つ同じアーキテクチャを扱っているので、MRI 画像で脳腫瘍を認識することは、写真で猫や犬を識別することよりはるかに難しいことではありません。さらに、前述の Kaggle ノートブックは、99% の有効性で腫瘍を分類できます。
私が指摘したいのは、特定のケースでは医療データを抽出して処理することがいかに簡単であるか、また、脳腫瘍の検出のように複雑に思える問題が、単純な従来のソリューションで非常に効果的に解決できることがよくあるということです。
したがって、私は皆さんに医療データから遠ざからないように勧めます。ご覧のとおり、私たちが扱っているのは比較的単純な形式であり、比較的単純で確立された技術 (畳み込みネットワーク、ビジョン トランスフォーマーなど) を使用することで、ここで大きな影響を達成できます。余暇に趣味としてオープン ソースのヘルスケア プロジェクトに参加し、使用するニューラル ネットワークを少しでも改善できれば、命を救うことができる可能性があります。