###### tags: `DICOM`
# DICOM To FHIR ImagingStudy - Node.js
# 所需知識
- FHIR Resource文件查看以及組成能力
- 基本的程式能力
- JSON資料格式
# 使用工具
- [Weasis](https://nroduit.github.io/en/getting-started/#try-weasis-now)或[RadiAnt DICOM Viewer](https://www.radiantviewer.com/download/?src=mbst&f=setup) (查看DICOM用)
- [Visual Studio Code](https://code.visualstudio.com/Download) (撰寫程式用)
# 程式語言
- [Node.js](https://nodejs.org/en/download/)
# DICOM 架構
這邊使用TCIA,編號`0522c0001`的部分檔案做範例,[點我下載](https://yrouka-my.sharepoint.com/:u:/g/personal/a5566qq123_my365_com_tw/Eejy-0hUEOtPoL7IJ7l-rtwB-0xUOKPOfMJRPQBi9xKXCA?e=SVzR8f)
首先快速了解DICOM檔案的架構。
1個病人有多個Study(報告),Study內有多個Series(部位),Series當中有多個Instance(影像實例)。

以JSON來看大概會長這樣
```json=
{
"study": {
"series" : [
{
"instance": [
]
}
]
}
}
```
如果太過抽象,可以透過DICOM Viewer開啟提供的範例檔。
這裡使用Weasis開啟其中一個資料中所有的檔案。

- 藍色,以日期為標題框住的範圍為Study
- 綠色,單一個正方形預覽圖為Series
- 紅色,於正方形左下角之數字為此Series含有的Instance的數量
# DICOM欄位資訊
我們可以透過Weasis上的按鈕,來查看DICOM的欄位資訊,打開後請記得切換到`All DICOM attributes`。

圖中可以看到我們常用的`病人名稱`欄位,由以下幾項組成:
- (0010,0010) -> 欄位標籤,解析DICOM時會用到
> 補充: [DICOM PS3.6 2021e - Data Dictionary 5 Conventions](https://dicom.nema.org/medical/dicom/current/output/chtml/part06/chapter_5.html)
A Data Element Tag is represented as (gggg,eeee), where gggg equates to the Group Number and eeee equates to the Element Number within that Group.
- [PN] -> 欄位資料型態
- PatientName -> 欄位名稱
- 0552c0001 -> 數值

如果想要快速查詢`欄位標籤`可以使用以下網站輔助:
- [DICOM Library](https://www.dicomlibrary.com/dicom/dicom-tags/)
- [DICOM Standard Browser](https://dicom.innolitics.com/ciods)
## 如何知道DICOM檔是同個Study
- DICOM欄位有三個UID,分別是`StudyInstanceUID`、`SeriesInstanceUID`、`SOPInstanceUID (InstanceUID)`。
- 只要是**相同**`StudyInstanceUID`的檔案,就代表是同個Study。
# 使用程式讀取DICOM
此範例將示範如何取出DICOM檔案某個欄位標籤的數值。
- 創建資料夾
- 用Visual Studio Code開啟資料夾
- 新增一個新的`Terminal`
- 初始化專案
```
npm init -y
```
- 安裝dicom-parser
```
npm i dicom-parser
```
- 開寫程式碼
```javascript=
const fs = require('fs');
const dicomParser = require('dicom-parser');
//讀取檔案
let fileBuffer = fs.readFileSync("./1.3.6.1.4.1.14519.5.2.1.5099.8010.199920086920823171706454903251/1.3.6.1.4.1.14519.5.2.1.5099.8010.260151978148957514594497217760/000001.dcm");
//將讀取後的Buffer轉成Uint8Array
let uint8FileBuffer = new Uint8Array(fileBuffer);
//使用DICOM Parser讀取DICOM檔
let dataSet = dicomParser.parseDicom(uint8FileBuffer);
//取出 Patient Name 0010,0010的數值
let patientName = dataSet.string("x00100010");
//印出取得的數值
console.log("Patient Name: " + patientName);
```
# FHIR ImagingStudy
以上大致了解了如何讀取DICOM後,現在來了解FHIR ImagingStudy。
從[FHIR ImagingStudy官方文件](https://www.hl7.org/fhir/imagingstudy.html)以及下圖來看,ImagingStudy採用與DICOM一樣的架構來儲存資料(Study->Series->Instance)。

## Mappings
官方文件也提供了DICOM轉換成FHIR ImagingStudy的Mappings,可以說是非常地友善。
[ImagingStudy Mappings](https://www.hl7.org/fhir/imagingstudy-mappings.html)
使用此表格,我們就可以輕易地將DICOM轉成FHIR的格式。

# DICOM轉成ImagingStudy
## 撰寫Data Types的Class
為方便使用及管理程式碼,首先要做的是幫 ImagingStudy 使用到的 Data Types 寫成 Class 以利組成 ImagingStudy。
- 創建`FHIRDataTypes.js`檔案並把 Class 都寫在此檔案
- 以`Identifier` type為例

:::warning
注意 Identifier 底下還包含了幾種需要建成 Class 的 Type:
- CodeableConcept
- Period
- Reference
:::
```javascript=
//Coding
class Coding {
constructor() {
this.system = undefined;
this.version = undefined;
this.code = undefined;
this.display = undefined;
this.userSelected = undefined;
}
}
//CodeableConcept
class CodeableConcept {
constructor() {
this.Coding = undefined;
this.text = undefined;
}
}
//Period
class Period {
constructor() {
this.start = undefined;
this.end = undefined;
}
}
//Reference
class Reference {
constructor() {
this.reference = undefined; //(Literal reference, Relative, internal or absolute URL)(string)
this.type = undefined; //string
this.identifier = undefined;
this.display = undefined;
}
}
//Identifier
class Identifier {
constructor() {
this.use = undefined;
this.type = new CodeableConcept();
this.system = undefined;
this.value = undefined;
this.period = new Period();
}
}
```
最後在一步一步把 ImagingStudy 的 Class 都寫完。
完整的FHIR Data Types Class: [Github 點我看程式碼](https://github.com/chinlin-example/dicom-to-fhir/blob/62df074ccc7bfe1702d0069ffebc4d6393dad744/nodejs/src/FHIRDataTypes.js)
# 轉換主程式
上面已經把會用到的Data Types轉成 Class了,現在開始撰寫轉換的程式。
思路如下
- 讀取 DICOM 檔案
- 搭配[ImagingStudy Mappings](https://www.hl7.org/fhir/imagingstudy-mappings.html)對照 DICOM Tags 轉換到FHIR的欄位
- 使用dicom-parser讀取數值
- 利用剛剛寫的 Class 把數值填入對應的 Class
- 把 ImagingStudy 組合起來
## 套件
- dicom-parser (解析dicom用)
- flat
- lodash
- moment
```bash=
npm install dicom-parser flat lodash moment
```
完整程式碼: [Github 點我看程式碼](https://github.com/chinlin-example/dicom-to-fhir/blob/62df074ccc7bfe1702d0069ffebc4d6393dad744/nodejs/src/DICOM2FHIRImagingStudy.js)
## 轉換結果
```json=
{
"resourceType": "ImagingStudy",
"id": "1.2.826.0.1.3680043.8.1055.1.20111102150758591.92402465.76095170",
"identifier": [
{
"use": "official",
"system": "urn:dicom:uid",
"value": "urn:oid:1.2.826.0.1.3680043.8.1055.1.20111102150758591.92402465.76095170"
}
],
"status": "unknown",
"subject": {
"reference": "Patient/0",
"type": "Patient",
"identifier": {
"use": "usual",
"value": "0"
}
},
"started": "2006-10-12T01:02:58.000Z",
"description": "CT1 abdomen",
"series": [
{
"uid": "1.2.826.0.1.3680043.8.1055.1.20111102150758591.96842950.07877442",
"number": 6168,
"modality": {
"system": "http://dicom.nema.org/resources/ontology/DCM",
"code": "CT"
},
"description": "ARTERIELLE",
"instance": [
{
"uid": "1.2.826.0.1.3680043.8.1055.1.20111102150758591.03296050.69180943",
"sopClass": {
"system": "urn:ietf:rfc:3986",
"code": "urn:oid:1.2.840.10008.5.1.4.1.1.2"
},
"number": 1,
"title": "ORIGINAL\\PRIMARY\\AXIAL\\HELIX"
}
]
}
]
}
```
# Support Me
文件創作花費了很多心血製作,如果你覺得很有幫助
不妨贊助我一下喝杯咖啡唄,[Support Me](https://portaly.cc/Li070/support)