Dicom SR Parser For Sonography

趁著主任出國開會的空擋 (XD),把這個功能完成了個大概。

之所以會想做這個,其實也是想要省那一點點的時間,每次在打肚子 sono 報告時,總是會需要手工抄一些數據(雖然可能有人不認同這種報告樣式),例如左右兩邊 kidney 的長度、spleen 長度、prostate size …,其實這些量測性和計算性的資料會被 DICOM Structured Report (SR) 儲存起來,所以只要能解讀出這些欄位,就可以自動幫我填上這些資料。

其實目前報告系統中也有提供類似的功能,但那奇怪的結構實在不和我的胃口,只好「自己報告自己救」了。

選 Ruby 當開發語言

本來以為這方面應該是 python 的天下,可用的 package 像 pydicompynetdicom 似乎也都發展得很成熟,不過我比較熟的 ruby 也有相對應的方案 — ruby-dicom,而且把 network protocol 和 parser 包在一起,似乎更方便了。

SR 格式一大堆

因為肚子 sono 是最有機會碰到的,一開始先拿了幾份來解解看,了解一下 SR 的結構後,再把想要的資料 extract 出來,老實說目前記錄的資訊不如想像的多(當然相對也不那麼難解),以 upper abdomen 來說,通常只會有 CBD 直徑,兩側 Kidney 和 Spleen length 而已,如果有額外量測一些 cyst 還是其他沒有定義的東西,就不會寫到裡面去。

不過痛苦的地方在於各家的結構不一樣,Philips 和 GE 不同也就算了,連 GE 軟體版本不同也有差異是怎樣?!只好根據不同的版本去寫各自的 parser 了。

PACS Client

初步做好 Parser 後,就是要想辦法從 PACS 上去抓 SR 的 dcm 檔了,剛好也趁機了解了一下 PACS 的運作方式。一開始我以為可以用 C-GET 的 protocol 直接從 PACS 上下載資料,但這種協議似乎已經被主流 PACS 軟體所屏棄,連 ruby-dicom 的作者都坦言,自己從來沒用過這功能,不保證能運作。XD

下一個選項就是 C-MOVE 了。如果要用 C-MOVE,得先建立一台自己的 PACS,把自己的 ip, port, AE title 設定到上游 PACS 裡。Client 的 request 只是請上游 PACS 餵資料給自己的 PACS。不過難道我要自己這樣維護一台 PACS 嗎?在我有限資源的 virtual machine 裡再搞一個這個,實在不划算。

ruby-dicom 也有提供簡易型 server 工具,但如果要整合在自己的 app 裡面,似乎得想辦法讓它在接受到 request 時短暫的啟動,完成傳送後再把資料傳給 app,之後關閉。寫了封 email 去和 ruby-dicom 作者討論,或許在 app 裡開台微 server 可以用 Thread 的方式解決,然後再用自己的 file handler 來處理接收到的檔案,但如果同時有一堆人(雖然可能性很低)同時連線,跑在固定 port 的微 server 不就 GG。

幸好後來知道有 Web Access to DICOM Objects (WADO) 這種傳送資料的方式,用的也是我熟悉的 http,只要把從 PACS 上 query 回來的結果拼裝成 url 送出就 ok 了,前面的一堆麻煩完全一掃而空。

Sinatra App

之前只有接觸過 Ruby on Rails 這套 framework,但這個 project 根本完全不會用到 Object Relational Mapping (ORM), Model–view–controller (MVC) 的功能,有點在浪費資源又成效不彰的感覺。後來找到 Sinatra 這個輕量化的 framework 便拿來用了。

這個 web app 就只有提供 api 把 Dicom SR 的資料轉成 json 格式輸出,例如:

{
  "status":{
    "error":0,
    "message":""
  },
  "result":{
    "CBD":"4.8 mm",
    "spleen":"8.8 cm",
    "kidney":{
      "right":"10.6 cm",
      "left":"10.4 cm"
    }
  }
}

有了 json output, 就可以方便的和其他網頁做結合。不過 cross origin 的問題也困擾了我一陣子,雖然大家都推薦用 sinatra-cross_origin 這個 gem 來替 Sinatra 加入 CORS 的功能,但我試了半天 header 總是出不來,最後只好自己手動送 Access-Control-Allow-Origin 了。

用 AutoHotKey 整合到 SmartWonder

要整合外部的 api 進去 SmartWonder (我們的報告系統),目前找到比較理想的方式就是用 AutoHotKey 將 javascript inject 進去,不過最近也遇到奇怪的問題,不知道是 IE11 還是 SmartWonder 改版帶來的後遺症(後者機率較大),得先開一個新視窗將 javascript 跑完後再把資料存進原本的 SmartWonder 裡,雖然解決了這個問題可以為以後省下一些麻煩,但著實花了我好一陣子時間(而且拖著拖著主任都回國了 @[email protected])。

做這個功能估計大概打一份報告可以省下 10~15 秒吧!不過卻花了我一個多禮拜的時間,而且使用難度不低,估計大概也幫不到什麼人,就是個自 high 的 project 囉!換算一下,我未來要打 15,000 份的肚子 sono 才能把時間賺回來啊!@[email protected]

東西都放在 GitHub 上,有興趣的可以參考 vghks-dicom-sr (Sinatra App with SR parser),而 AutoHotKey 的部分可以參考 ahk-smartwonder 其中 MyScripts/lib/dicom-sr.ahkMyScripts/sono.ahk 的部分。