Databricksで医療画像データDICOMを活用する方法（2/2）

株式会社NTTデータ [Data & Intelligence事業部](https://enterprise-aiiot.nttdata.com/) の [nttd-saitouyun](https://qiita.com/nttd-saitouyun) です。
本記事は長文になってしまったため、２回に分けてお伝えします。

# はじめに
私は[Databricks](https://enterprise-aiiot.nttdata.com/service/databricks)のエバンジェリストとして活動しております。
その活動の中でお客様のデータ活用の課題や要望をお伺いすることが多いのですが、「DICOMデータを活用したい」とご要望をいただいた際に、私の不勉強でお役立てできなかったため、今回は勉強も兼ねて、**「DICOM」と「DICOMをDatabricksで活用する方法」** について調べた結果をご紹介します。

[前編](https://qiita.com/nttd-saitouyun/items/1cdcb2225c58f7a4256c)では、「DICOM」と DICOMデータをPythonで扱うライブラリである「PyDicom」の基本操作と大規模環境における課題を説明しました。
本記事では、大規模環境における課題を解決する「DICOMをDatabricksで活用する方法」をご説明します。


# DICOMをDatabricksで活用する方法
それでは、[Databricks ソリューションアクセラレータ](https://www.databricks.com/jp/solutions/accelerators)で公開されているDICOMデータの活用方法を見ていきましょう。

## Databricksソリューションアクセラレータとは
[公式ページ](https://www.databricks.com/jp/solutions/accelerators)には以下の説明があります。
>Databricks ソリューションアクセラレータは、さまざまな業界・業種に共通の主要なユースケースに対応する、フル機能の Notebook やベストプラクティスを含む目的に特化したガイドです。

分析のプラットフォームだけでなく、**分析のナレッジまで提供しているのはDatabricksの大きな強み**だと言えます。

## databricks.pixels Solution Accelerator
今回は「[databricks.pixels Solution Accelerator](https://github.com/databricks-industry-solutions/pixels)」を活用します。アクセスするとGithubに遷移し、以下のページが表示されます。
このソリューションを活用することで、**DICOMのメタデータをSQLで分析する**ことができるようになることがわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/e4bb89b6-9c67-e9fc-9856-1d28d82ca6a7.png)

## databricks.pixels
`databricks.pixels`は今回活用するライブラリです。以下の特徴があります。
- DICOMデータを、**SQLでアクセス可能なメタデータ、サムネイルに変換できる**。
- 画像処理を複数サーバで分散処理することで、**何百万もの画像ファイルを処理できる**。
- Delta Lake & Delta Engineを分析に利用できる。つまり、**ACIDトランザクションやタイムトラベルができる**。

## リポジトリへの追加
Gitに公開されているコードを活用するには、[Databricks Workspace](https://qiita.com/taka_yayoi/items/78bf647c40a906d90db0)のリポジトリに登録します。GithubのURL を入力して、「リポジトリの作成」をクリックしてください。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/e6809aac-409b-c898-b4ff-2d71ae2f064f.png)
 - URL : https://github.com/databricks-industry-solutions/pixels

追加したリポジトリには、以下の3つのノートブックが含まれています。内容は後述します。
- 00-setup
- 01-dcm-demo
- 02-dcm-browser

## クラスタの作成
デフォルト値でクラスタを作成してください。私が実行したときは以下のランタイム、構成を使用しました。
- Databricks Runtimeのバージョン：11.3 LTS (includes Apache Spark 3.3.0, Scala 2.12)
- クラスタの構成
    - インスタンスタイプ：i3.xlarge（4vCPU、30.5GBメモリ）
    - ドライバーノード：1台
    - ワーカーノード：2～8台（クラスタ全体で、8～32vCPU、61～244GBメモリ）

クラスタの設定について、より詳細な情報が知りたい方は以下の記事を参照ください。

https://qiita.com/taka_yayoi/items/8d951b660cd87c6c5f18


## 前準備(00-setup)
前準備のノートブックで、後述する２つのノートブックから呼び出されます。よって、単体で実行する必要はありません。次の処理を実施しています。

- ライブラリのインストールを実施
    - pydicom、s3fs、python-gdcm のインストールを実施。<br><br>
- インプットパラメータの定義
    -  1.0 Path to directory tree containing files. /dbfs or s3:// supported<br>DICOMデータの格納先のパス。S3のURLかDBFSのパスを指定する。「s3://hls-eng-data-public/dicom/ddsm/」がデフォルト値で、サンプルデータが格納されている。<br><br>
    -  2.0 Catalog Schema Table to store object metadata into<br>DICOMデータから抽出したメタデータやサムネイルを保存するテーブルを指定する。デフォルト値は「hive_metastore.pixels_solacc.object_catalog」となる。<br><br>
    -  3.0 Update mode on object metadata table<br>メタデータの更新方法の指定。「overwrite（上書き）」「append（追加）」から選択する。デフォルト値は「overwrite（上書き）」となる。<br>
これらの定義により、ノートブックの上部に以下のようなインプットウィジェットが表示されるようになります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/a4d01563-604d-58dc-aa3d-003325a79322.png)

 - スキーマの初期化
     - インプットパラメータの2.0で指定されたスキーマが存在しない場合は作成する。


## DICOMデータの分析（01-dcm-demo）
いよいよ本処理です。10426個のサンプルファイルに対して、DICOMデータからメタデータの抽出とサムネイル画像の生成を行い、データをテーブル化します。その後、SQLを用いていくつかの分析を行います。

私が実行したときはデータをテーブル化するところまでに約40分かかりました。よって、**1000ファイルの処理に約4分**のスピード感です。お手元の環境とぜひ比較して見てください！

それでは、細かい処理を見ていきましょう。

### データの読み込み
ライブラリをインポートして、ファイルを読み込みます。`table`と`path`変数にはインプットパラメータの値が格納されています。
```python:ライブラリのインポート
from databricks.pixels import Catalog
from databricks.pixels.dicom import DicomMetaExtractor, DicomThumbnailExtractor # The Dicom transformers
```

```python:サンプルファイルの読み込み
catalog = Catalog(spark, table=table)
catalog_df = catalog.catalog(path=path)
```

作成されたデータフレームの構造は以下の通りです。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/757d2fa4-12cc-ee31-73aa-3792c529b74a.png" width="500">

各カラムの意味は以下はREADMEに記載されているER図を見るとわかります。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/bdb3037b-9c51-2e6e-550c-01762bd348c6.png)


データの例も以下に示します。
|rowId|path|modificationTime|length|relative_path|local_path|extension|path_tags|
|:----|:----|:----|:----|:----|:----|:----|:----|
|6157|s3://hls-eng-data-public/dicom/ddsm/normals/patient6419/6419.LEFT_CC.dcm|2022-11-16T17:15:13.000+0000|5155662|s3://hls-eng-data-public/dicom/ddsm/normals/patient6419/6419.LEFT_CC.dcm|s3://hls-eng-data-public/dicom/ddsm/normals/patient6419/6419.LEFT_CC.dcm|dcm|["patient6419","6419","LEFT","CC","dcm"]|
|7075|s3://hls-eng-data-public/dicom/ddsm/cancers/patient8189/8189.LEFT_CC.dcm|2022-11-16T17:05:44.000+0000|4899574|s3://hls-eng-data-public/dicom/ddsm/cancers/patient8189/8189.LEFT_CC.dcm|s3://hls-eng-data-public/dicom/ddsm/cancers/patient8189/8189.LEFT_CC.dcm|dcm|["patient8189","8189","LEFT","CC","dcm"]|
|7947|s3://hls-eng-data-public/dicom/ddsm/benigns/patient9669/9669.RIGHT_MLO.dcm|2022-11-16T16:56:28.000+0000|4672102|s3://hls-eng-data-public/dicom/ddsm/benigns/patient9669/9669.RIGHT_MLO.dcm|s3://hls-eng-data-public/dicom/ddsm/benigns/patient9669/9669.RIGHT_MLO.dcm|dcm|["patient9669","9669","RIGHT","MLO","dcm"]|
|8917|s3://hls-eng-data-public/dicom/ddsm/benigns/patient8186/8186.RIGHT_MLO.dcm|2022-11-16T16:54:42.000+0000|4403956|s3://hls-eng-data-public/dicom/ddsm/benigns/patient8186/8186.RIGHT_MLO.dcm|s3://hls-eng-data-public/dicom/ddsm/benigns/patient8186/8186.RIGHT_MLO.dcm|dcm|["patient8186","8186","RIGHT","MLO","dcm"]|
| ... | ... | ... | ... | ... | ... | ... | ... |


### DICOMデータからメタデータを抽出
先ほど作成したcatalog_dfからDICOMデータを読み込み、メタデータを抽出します。内部では、PyDicom と gdcmを使用してファイルを解析しています。メタデータは、`meta`カラムにJSON文字列として格納されます。
```python
meta_df = DicomMetaExtractor(catalog).transform(catalog_df)
```
作成されたデータフレームの構造は以下の通りです。`is_anon`と`meta`というカラムが追加されていることがわかります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/bb598422-6ba5-f6fe-d1e3-34edc46d5cfd.png" width="500">

`is_anon`と`meta`のデータの例も以下に示します。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/de85d4d2-87e5-0e84-5da4-d9631a5bc766.png)



### DICOMデータからサムネイルを抽出
先ほど作成したmeta_dfからDICOMから画像データを読み込み、メタデータとともにインラインでサムネイル(約45KB) を保存します。
```python
thumbnail_df = DicomThumbnailExtractor().transform(meta_df)
```

作成されたデータフレームの構造は以下の通りです。`thumnail`というSTRUCT型のカラムが追加されていることがわかります。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/d995efad-fcf3-31a8-9d09-05eb687b4b8c.png" width="500">

この`thumnail`カラムの中には、`origin`、`height`、`width`、・・・というカラムがネストされていますが、Apache Sparkの [Image data source](https://spark.apache.org/docs/latest/ml-datasource.html) という画像をテーブルとして処理、格納する機能が使われています。

`thumnail`のデータの例も以下に示します。テーブルの中に画像が保存されていますね。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/0c183e9b-932d-4dd2-9f1d-a179b1c930c5.png)


### テーブルとして保存
これまで作成してきた`thumbnail_df`をDeltaテーブルとして保存します。
```python
catalog.save(thumbnail_df, mode=write_mode)
```
<br>

テーブルにクエリを実行して見ましょう。
`${c.table}`には、インプットパラメータ０２の設定値である`hive_metastore.pixels_solacc.object_catalog`が格納されています。
※`%sql`は、DatabricksノートブックでSQLを実行する際に指定するマジックコマンドです。
```sql
%sql describe ${c.table}
```
コードを実行すると以下のようにスキーマ情報が出力され、テーブルとして定義されていることがわかります。データエクスプローラからも参照できるようになっています。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/0e14e9b8-4ae2-88fe-54c7-999ee9ec05df.png)

以下のようにselect文を実行すると`thumbnail_df`と同じ内容を取得できます。
```sql
%sql select * from ${c.table}
```

<br>
<hr>
ここまでの手順で、Amazon S3に格納されていたDICOMファイルをテーブル化することができました。細かく見てきたので長く感じたかもしれませんが、実はたったの7行でこの作業を実現しています。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/62702fd1-f98e-e125-9b2f-f1c8313f26f7.png)
<hr>


### ファイルのメタデータ分析
このノートブックにはいくつかテーブルを分析するためのクエリが記載されているのご紹介します。

まずは、簡単なところから。10426という結果が返ってくるため、全てのサンプルデータがテーブルとして保存されていることがわかります。
```sql
%sql select count(*) from ${c.table}
```
<br>

もう少し複雑な集計です。サンプルデータ中のDICOMデータの統計値を算出しています。
```sql
%sql
with x as (
  select
  format_number(count(DISTINCT meta:['00100010'].Value[0].Alphabetic),0) as patient_count,
  format_number(count(1),0) num_dicoms,
  format_number(sum(length) /(1024*1024*1024), 1) as total_size_in_gb,
  format_number(avg(length), 0) avg_size_in_bytes
  from ${c.table} t
  where extension = 'dcm'
)
select patient_count, num_dicoms, total_size_in_gb, avg_size_in_bytes from x
```
結果は以下の通りです。患者数、DICOMデータのファイル数、ファイルの合計サイズが出力されました。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/743ab8a0-6098-3749-dc1f-335768c7b80a.png)

<br>

さらに複雑なパターンです。`meta`カラムに格納されているJSONデータからデータを抽出しています。
ちなみに、キーに使っている８桁の数字の意味は[こちら](https://dicom.innolitics.com/ciods)から調べることができます。
例えば、`Patient's Name`属性は[ここ](https://dicom.innolitics.com/ciods/ct-image/patient/00100010)を見ると、Tag(0010,0010)で定義されているので、`00100010`を指定していることがわかります。
```sql
%sql
SELECT
    rowid,
    meta:['00100010'].Value[0].Alphabetic patient_name, 
    meta:['00082218'].Value[0]['00080104'].Value[0] `Anatomic Region Sequence Attribute decoded`,
    meta:['0008103E'].Value[0] `Series Description Attribute`,
    meta:['00081030'].Value[0] `Study Description Attribute`,
    meta:`00540220`.Value[0].`00080104`.Value[0] `projection`, -- backticks work for numeric keys
    split(meta:`00081030`.`Value`[0],'_')[0] `Label`,
    split(meta:`00081030`.`Value`[0],'_')[1] `Instance`
FROM ${c.table}
```
結果は以下の通りです。JSONからテーブル形式でデータを見ることができます。だいぶデータが整理されたように見えますね！
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/abeabc9f-becf-95ec-192a-18e152d36921.png)
**Databricksでは、ノートブックで表示したデータに対して、シームレスにデータの可視化やデータプロファイリングを行うこともできます。** 上記のテーブルに対して、各`label`に対して患者数がどのくらいいるのかをグラフにして見ました（ここはオリジナルです）。この後、機械学習を行う際に学習データの偏りを調べたりするのに便利です。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/5aa5fc99-2375-f296-1ba8-c78f627ea6b4.png)


## DICOMブラウザ（02-dcm-browser）
作成したテーブルを活用して、DICOMブラウザを作っています。驚きです。
ノートブックを実行すると以下のように画像の一覧が表示されます。左側にあるボタンを押すと画像の絞り込みも行えます。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/283e26cf-5046-ec19-2714-fffdc1453fb4.png)
画像をクリックすると拡大も行えます。
<img src="https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3118781/f0a9d2b7-c661-9e1a-d963-04a807a52776.png" width="250">

データフレームをアプリ化するには、Streamlitを使うことが多いように感じますが、ノートブックだけでもここまでできるのは正直驚きました。

<br>

# 参考
調査の中で見つけたDICOMデータをApache Sparkで扱うためのライブラリを参考としてご紹介します。

## spark-tk
https://github.com/trustedanalytics/spark-tk

SPARK + AI Summit 2020（現在のDATA + AI Summit）で「[A Predictive Analytics Workflow on DICOM Images using Apache Spark](https://www.databricks.com/session/a-predictive-analytics-workflow-on-dicom-images-using-apache-spark)」というタイトルで事例が公開されています。
事例があることもあり、当初はこのライブラリを使用しようと考えましたが、リリースが2017年で止まっていることと、相当古いpipを使わないとREADMEの通りにインストールできないことが分かったため、調査対象から外しました。

## spark-dicom
https://github.com/abzoobabd/spark-dicom

こちらは、Gitには公開されていますが、正式にリリースされていないようなので調査対象外にしています。



# おわりに
いかがでしたでしょうか？
本記事では以下の３つのポイントを中心にご紹介しました。
- DICOMデータに関する知識
- 非構造化データをDatabricksで活用する方法
- Databricksソリューションアクセラレータによるデータ分析の加速

特に**非構造化データを大規模に処理することができる点、分析ナレッジがソリューションアクセラレータとして公開されている点**はDatabricksの大きな特徴だと思います。
データの量や種類が日々増えている昨今の状況で、データ分析を効率的に進めるため、Databricksを活用しましょう！！

# 仲間募集中！
NTTデータ [Data&Intelligence事業部](https://enterprise-aiiot.nttdata.com/) では、以下の職種を募集しています。

<details>
<summary>
1. 「クラウド技術を活用したデータ分析プラットフォームの開発・構築(ITアーキテクト/クラウドエンジニア)」の募集
</summary>
クラウド/プラットフォーム技術の知見に基づき、ITアーキテクトまたはPMとして、DWH、BI、ETL領域における、ソリューション開発の推進や、コンサルティング工程のシステムグランドデザイン策定時におけるアーキテクト観点からの検討を行う人材を募集しています。

https://nttdata.jposting.net/u/job.phtml?job_code=808

</details>

<details>
<summary>
2. AI／データ活用を実践する「クラウド・ソリューションアーキテクト」の募集
</summary>
AI／データ活用を実践する「クラウド・ソリューションアーキテクト」として、クラウド先進テクノロジーを積極活用し、お客様のビジネス価値創出活動を実践。AI／データ活用の基本構想立案コンサルティングからクラウドプラットフォーム提供・活用を支援しています。お客様のAI・データ活用を支援するクラウド・ソリューション提案、アーキテクチャ設計・構築・継続活用支援（フルマネージドサービス提供）、および最新クラウドサービスに関する調査・検証で、クラウド分析基盤ソリューションのメニュー拡充を実施する人材を募集します。

https://nttdata.jposting.net/u/job.phtml?job_code=807

</details>

<br>
<br>
また、取り扱う主なソリューションについては、以下のページも参照ください。

# ソリューション紹介
<details>
<summary>Trusted Data Foundation について</summary>
～データ資産を分析活用するための環境をオールインワンで提供するソリューション～
最新のクラウド技術を採用して弊社が独自に設計したリファレンスアーキテクチャ（Datalake+DWH+AI/BI）を顧客要件に合わせてカスタマイズして提供します。
可視化、機械学習、DeepLearningなどデータ資産を分析活用するための環境がオールインワンで用意されており、これまでとは別次元の量と質のデータを用いてアジリティ高くDX推進を実現できます。

https://enterprise-aiiot.nttdata.com/tdf/

</details>