跳至主要內容
技術

心電圖存成 DICOM 之後,要用什麼打開?

心電圖存成 DICOM 之後,要用什麼打開?
ECG 心電圖小常識 第 3 / 4 篇 ,前往系列總覽

本篇是「ECG 心電圖小常識」系列的第 3 / 4 篇。你可以從系列總覽開始閱讀,也可以直接接著看本文。

起因

原本只是要確認某個匯出的心電圖 DICOM 檔裡,那段診斷文字是不是正確的,但我用手邊的 DICOM viewer 打開,畫面一片空白。檔案其實沒壞,是工具不對 XD

DICOM 不是「一種」檔案

副檔名都是 .dcm,裡面可能是完全不同的東西。決定它是什麼的是 SOP Class UID

SOP Class UID這是什麼資料長什麼樣
1.2.840.10008.5.1.4.1.1.7Secondary Capture像素。通常是報告 PDF 轉成的圖
1.2.840.10008.5.1.4.1.1.9.1.112-Lead ECG Waveform沒有任何像素,只有一連串電壓取樣值

12-Lead ECG Waveform 的 PixelData 是空的,內容躺在 WaveformSequence 裡:12 個導程,每個導程數千個 16-bit 整數。

「12 導程」是哪 12 個、為什麼身上只接 10 條線,見本系列的為什麼心電圖要用 10 條導線?

大部分 DICOM viewer 是為影像設計的(CT、MR、X 光)。丟一個沒有像素的檔案給它,得到空白畫面是預期行為,不是缺陷。

Weasis

Weasis 把波形當一等公民,作者 Nicolas Roduit,EPL 2.0 / Apache 2.0 雙授權。

確認它有獨立的波形模組,而不只是宣稱支援 —— 列出 OSGi bundle:

for j in $(find ~/.weasis/cache-*/ -name "bundle.jar"); do
  unzip -p "$j" META-INF/MANIFEST.MF | awk '/^Bundle-SymbolicName:/{print $2}'
done | sort -u | grep dicom
weasis-dicom-codec
weasis-dicom-explorer
weasis-dicom-rt
weasis-dicom-sr
weasis-dicom-viewer2d
weasis-dicom-viewer3d
weasis-dicom-wave      ← 波形模組

weasis-dicom-wave 是獨立 bundle,不是塞進影像檢視器的附加功能。

3 分鐘快速上手

裝與開

brew install --cask weasis
open -a Weasis your-ecg.dcm

也可以把檔案拖進視窗,或用 File → Open。

畫面長這樣

Weasis v4.7.2 開啟 12 導程 ECG DICOM:左為 DICOM Explorer,中央為波形,右側 Measurements 面板列出 Annotations

以下是 v4.7.2 實際畫面上的元件:

  • 左側 DICOM Explorer — 載入的資料照 Patient / Study / Series 階層列出,縮圖標示為 ECG。上方有 Search tags 可搜欄位。
  • 中央波形區 — 截圖的版面是 4 欄 × 3 列(I/aVR/V1/V4、II/aVL/V2/V5、III/aVF/V3/V6),最下方一條 II 導程的 rhythm strip。游標移到某導程時,上方會顯示該導程的 Minimum / Maximum 電壓。
  • 右側 Measurements 面板 — 分成 Markers(游標量測結果)與 Annotations(Tag / Value 表格)兩區。

頂部三個下拉

控制項畫面上的值作用
Zoom 第一個下拉auto mm/sX 軸走紙速度
Zoom 第二個下拉auto mm/mVY 軸增益
Display format4x2.5 Seconds with rhythm導程版面配置

4x2.5 Seconds with rhythm 指 4 欄、每欄 2.5 秒,外加一條 rhythm strip。這和本系列第一篇談的 6x2 格式是不同的版面慣例,下拉裡可以切換。

另外還有一個獨立的 Zoom: 100% 滑桿,等比縮放不改變上面兩個刻度。

Annotations 面板裡有什麼

這是這次真正要看的地方。表格內容直接來自 DICOM 的 WaveformAnnotationSequence

TagValue
Filter Low Frequency0.05 Hz
Filter High Frequency150.0 Hz
Notch Filter Frequency60.0 Hz
TextHeart rate (<60 bpm)…
Ventricular Heart Rate62.0 {H.B.}/min
PR Interval167.0 ms
QRS Duration127.0 ms
QT Interval418.0 ms
QTc Interval428.0 ms
P Axis / QRS Axis / T Axis56.0 / 74.0 / 44.0 deg

Text 那列就是 (0070,0006) UnformattedTextValue —— 判讀文字。其餘幾列則是各自帶 ConceptNameCodeSequence 的數值型 annotation。

什麼時候用 GUI,什麼時候用程式

這次的任務是「確認一段文字精確等於什麼」。GUI 做得到,但表格會截斷(截圖裡就顯示成 Heart rate (<60 bpm)Q…),而且看不出不可見字元。

三行 Python 更適合:

import pydicom
ds = pydicom.dcmread("your-ecg.dcm")
for item in ds.WaveformAnnotationSequence:
    if "UnformattedTextValue" in item:
        print(repr(item.UnformattedTextValue))

repr() 而不是 print()。這次要抓的是文字裡混進 HTML 跳脫字元(&lt; 而不是 <),這種差異在 GUI 上、甚至在一般 print 輸出裡都容易看漏,repr() 會連跳脫字元和 \r\n 一起攤開。

適用場景:

要驗什麼用什麼
波形長相、雜訊、間期量測Weasis
某欄位的值精確是什麼pydicom + repr()
檔案格式合不合規(SOP Class、必填欄位)pydicom

兩者驗的東西不同,不能互相取代。

截圖前的去識別化

要把畫面放進文件或簡報前,先把病患欄位換掉:

ds = pydicom.dcmread(src)
ds.PatientName = "DOE^JOHN"
ds.PatientID = "DEMO-0001"
ds.save_as(dst)

Weasis 的 DICOM Explorer 和分頁標籤都會顯示 Patient ID,截圖時很容易連帶入鏡。

另外,測試用的 .dcm 如果放在 git repo 目錄下,記得確認 .gitignore 有蓋到 —— *.dcm 適合預設 ignore。

重點整理

  • .dcm 副檔名不代表同一種檔案,先看 SOP Class UID
  • 影像用的 viewer 打不開波形,是設計範圍問題不是缺陷
  • Weasis 有獨立的 weasis-dicom-wave 模組,判讀文字顯示在 Measurements → Annotations 的 Text
  • 檢視波形用 GUI,驗證欄位值用 pydicom

留言討論

esc
輸入關鍵字搜尋文章...
查看收藏 →