所需知識
- FHIR Resource文件查看以及組成能力
 - 基本的程式能力
 - JSON資料格式
 
使用工具
- Weasis或RadiAnt DICOM Viewer (查看DICOM用)
 - Visual Studio Code (撰寫程式用)
 
程式語言
DICOM 架構
這邊使用TCIA,編號0522c0001的部分檔案做範例,點我下載
首先快速了解DICOM檔案的架構。
1個病人有多個Study(報告),Study內有多個Series(部位),Series當中有多個Instance(影像實例)。
以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 ConventionsA 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檔是同個Study
- DICOM欄位有三個UID,分別是
StudyInstanceUID、SeriesInstanceUID、SOPInstanceUID (InstanceUID)。 - 只要是相同
StudyInstanceUID的檔案,就代表是同個Study。 
使用程式讀取DICOM
此範例將示範如何取出DICOM檔案某個欄位標籤的數值。
- 創建資料夾
 - 用Visual Studio Code開啟資料夾
 - 新增一個新的
Terminal - 初始化專案
 
npm init -y- 安裝dicom-parser
 
npm i dicom-parser- 開寫程式碼
 
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官方文件以及下圖來看,ImagingStudy採用與DICOM一樣的架構來儲存資料(Study->Series->Instance)。
Mappings
官方文件也提供了DICOM轉換成FHIR ImagingStudy的Mappings,可以說是非常地友善。ImagingStudy Mappings使用此表格,我們就可以輕易地將DICOM轉成FHIR的格式。
DICOM轉成ImagingStudy
撰寫Data Types的Class
為方便使用及管理程式碼,首先要做的是幫 ImagingStudy 使用到的 Data Types 寫成 Class 以利組成 ImagingStudy。
- 創建
FHIRDataTypes.js檔案並把 Class 都寫在此檔案 - 以
Identifiertype為例 
注意 Identifier 底下還包含了幾種需要建成 Class 的 Type: - CodeableConcept - Period - Reference
//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:
轉換主程式
上面已經把會用到的Data Types轉成 Class了,現在開始撰寫轉換的程式。思路如下
- 讀取 DICOM 檔案
 - 搭配ImagingStudy Mappings對照 DICOM Tags 轉換到FHIR的欄位
 - 使用dicom-parser讀取數值
 - 利用剛剛寫的 Class 把數值填入對應的 Class
 - 把 ImagingStudy 組合起來
 
套件
- dicom-parser (解析dicom用)
 - flat
 - lodash
 - moment
 
npm install dicom-parser flat lodash moment整體專案完整程式碼
到專案主頁面會有 C# 、Python 版本的範例,以下為 node.js 範例
單檔案程式碼
如果覺得上面的專案比較複雜,可以看看這支程式碼都寫在一個檔案的範例
轉換結果
{
    "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"
                }
            ]
        }
    ]
}





