2016年3月12日 星期六

Hotstring優化:追求極致速度

AutoHotkey的最大優點就是簡易的語法,方便設定hotstring及hotkey。不過畢竟最後是CPU在跑,寫一行程式碼,不一定比三行程式碼還快,要讓程式跑得快,還是要以電腦的角度去思考。例如以最簡單的 ::btw::by the way 為例,trigger之後AutoHotkey會模擬鍵盤傳給Windows的code,Windows再送到active的文字編輯器,文字編輯器被接收到一個鍵會觸發/更新一些內建數值(例如目前是第幾個字元、總共幾個字元、儲存上個狀態以便Ctrl+Z可以復原等等),然後更新文字輸入區的畫面,讓使用者知道程式沒當掉依然有反應。在AutoHotkey的範圍內可做到幾點優化:
  • SetBatchLines -1
    • 預設設定下,AutoHotkey每執行一行程式碼,會短暫休息,再執行下一行,以避免讓CPU太操。但既然用AutoHotkey就是要效率,給他催下去!
  • SendMode, Input
    • 預設的Send是SendEvent模式,但SendInput在大部分的狀況下是Z>B,包括
      • 送字速度快
      • 因為有buffer,如果程式正在send字串時使用者也按下鍵盤,使用者按的鍵會在字串送完時接上,而不會中斷(最多buffer 5000個字元)
  • SetKeyDelay, -1, -1
    • 這個設定也是類似SetBatchLines,為了模擬正常人類按鍵,按下去會有一定duration,兩個按鍵間一定有一些delay,通常是遊戲才會偵測有沒有cheat
    • 雖然這個設定影響的是SendEvent,但有非常少數情況下AutoHotkey會自動從SendInput轉換成SendEvent或SendPlay,所以還是可以設定
  • 複製貼上法
    • 對於較大量的字串,例如某個procedure note的範本,用send會非常慢。比較直覺的方式是用剪貼簿當作媒介來複製、貼上

    • 註解掉的兩行是為了保留原本的剪貼簿內容,但在連續輸入的情況下不太需要
    • 有一說要搭配ClipWait,但日常小量字串應該都能順利運作,所以沒有使用
    • 實測比較SendInput和sendByClipboard,發現大概以7~10個字元為分界,更多的字元後者較快,較少的字元前者較快。
    • sendByClipboard的缺點:
      • 組合鍵(用到Ctrl, Alt, Shift, Win等)無法用(不過通常也不會很大量)
      • 單行定義的Hotstring會依據首字是否大寫/全部大寫,送出相對應的字串(首字大寫/全部大寫),sendByClipboard只能用笨方法來模擬。(注意後兩個搭配c選項)

  • 直接送字法
    • 利用剪貼簿畢竟多了一道程序,可以直接設定輸入框裡要顯示的文字。
    • Standard edit control可以搭配Edit library,測試會比剪貼簿方法還快,但缺點是全部文字整個更新,畫面可能會閃一下,或者捲軸的位置不一樣:

    • Scintilla control是SciTE、NotePad++所使用的,可以搭配Scintilla wrapper以及自己寫的RemoteScintilla,速度和standard edit差不多,優點是很多強大的功能。
      • 在自己的gui建立及操作Scintilla control要用Scintilla wrapper,但如果要access或modify其他process中的Scintilla control,則要用RemoteScintilla,利用_RemoteBuf library搭配Scintilla Documentation裡列出的messages(AutoHotkey的ControlGetText和ControlSetText只針對standard edit)。
      • 利用SCI_ADDTEXT,可以在游標處插入字串,而且游標會跳到加入字串的最後面,和Hotstring或是複製貼上的動作是一致的。

    2016年3月5日 星期六

    Automatic Lung CAD

    Low dose chest CT漸漸成為趨勢,幾乎是chest X-ray般的輻射劑量,卻提供超多細節,以及chest radiologist的工作量 T_____T

    本院chest CT檢查量就像物價一樣漲個不停,打起來最爽快的莫過於cancer follow up,爽點包括:
    • 已經有診斷、有治療,定期追蹤看有沒有復發、轉移,大部 分都是post-op changes, non-specific changes,以及一些雜魚tiny stable nodules。
    • 有前人打的報告,copy & paste再做些修改。
    • 之後會繼續來追蹤,有nodule沒看到,下次總會有機會再看到。
    • Multiple bilateral lung metastases最為#無痛,而且可以#秒殺,因為就已經是一堆nodules了啊!
    相反地,low dose chest CT可怕之處在於:
    • 通常是較健康的人(例如健檢),只有非常少、非常小的nodule
    • 因為是健康的人,氣吸很飽,肺變得很大,守備範圍加大…
    • 這個檢查要看細切影像(1mm),動輒兩三百張,非常耗神耗眼…
    • 萬一沒找到lesion,下次回診時可能已經真的有cancer了,雖然不一定有相關性,但被告的機率感覺比較高,#怕怕der
    • 因為low dose,soft tissue看不清楚,liver到底有沒有tumor只能瞎猜

    當LDCT成為事實,CAD就是義務!


    目前部內的架構是:
    • 做完CT → 細切傳至部內RAID(Osirix, iMac)
                 ↘ 粗切傳至醫院資訊室PACS

    為了自動化CAD,幻想中的流程是
    CT → 某台強大電腦運算CAD  ← user query
    • 可惜本人image processing很弱,只看paper無法自己拼成可用的程式
    • 也是有別人寫好的現成的matlab code,但效果沒有很好…

    於是規劃了複雜的流程
    CT → RAID → Workstation (CAD) → Orthanc server   ← user query

    細節如下:
    • 做CT,series name需要改為「LungCARE」,傳至部內RAID
    • Osirix設定auto-routing,有「LungCARE」的series會自動傳至#S牌工作站
    • 工作站設定「LungCARE」的study自動apply為MM Oncology,而且會在背景先做CAD
    • AutoHotkey程式,每天早上三點(所謂三更半夜四下無人)會執行
      • 登入
      • 篩選「昨天」的「LungCARE」
      • 打開study
      • Show lung CAD results
      • Accept all candidates(本來是要醫師自己到場accept或reject每一個candidate)
      • Complete and archive:result會存成一個新的series
    • 工作站設定result的series再自動re-routing到自己架的server(Orthanc)

    主要的考量包括:
    • S牌工作站
      • 可以找GGN,accuracy還算OK
      • 軟體介面是AutoHotkey可以access到的
      • 向工程師要到某種帳號,可以做進階設定
      • CAD可以pre-processing,不用現場等,前提是一定要命名為LungCARE
    • 其他工作站:
      • G牌:本部最舊的,很久沒更新;軟體只能一次處理一個case,無法批次處理;用linux系統,不熟…
      • P牌:CAD很弱...
      • T牌:介面無法用AutoHotkey抓,不能找GGN或是4mm以下的nodule
    • RAID轉運站
      • 每一台CT都會傳到RAID,而且Osirix可以設定auto-routing,就不再麻煩放射師要另外傳到工作站
      • 缺點是主機老舊,硬碟容量小,其實就是一個外接硬碟而已,還要常常手動更換,和「RAID」完全沾不上邊…
    • Orthanc server
      • 支援web介面查詢、瀏覽series、基本影像操作(亮度/對比)
      • 支援REST API,可以用http request配合json查詢、抓影像

    使用者端(chest radiologist們)則用另一個AutoHotkey程式,將下列動作綁在一個hotkey:
    • 抓RIS目前報告的病歷號
    • 用http request去查自架server是否有符合的result,有的話…
    • 在已開啟/新開啟的firefox或chrome打開連結,直接連到web viewer的頁面

    目前要再做一個server,針對非LDCT,可以丟一個病歷號過去,然後:
    • 從RAID拉影像(用dcm4che)
    • 修改series name為「LungCARE」(用ExifTool,或是出動Python)
    • 將dicom傳到工作站,等CAD
    • 登入工作站,accept candidates,export result
    • 回傳通知CAD已完成
    雖然很複雜,但目前能力只能做到這樣,希望以後有更好的流程~

    2015年12月18日 星期五

    Dynamic Hotstring實例

    之前在「自己寫武功祕笈:Radiologist的第一個Autohotkey」提到一點dynamic hotstring的使用,這篇再介紹幾個實用範例。

    Prerequisite

    • Regular expression:雖然看起來像火星文但真得很重要(自行覆頌三遍),可參「Regular Expression入門」
    • AutoHotkey_L:一般而言用Unicode 32-bit
    • Dynamic hotstring的library:AutoHotkey的論壇上可以找到幾種版本,我用的是比較多選項的這個版本
      • 下載Hotstring.ahk存到AutoHotkey主程式資料夾內的Lib資料夾,或者ahk檔資料夾內的Lib資料夾(詳見說明)
      • ahk檔開頭加上#include <Hotstring>
    • 自訂的hotstring是以函數方式執行,所以要放在auto-execute section內,簡單來說就是在第一個return之前的範圍

     範例1:日期

    • 輸入:8個數字,再加上一個分隔字元,例如20151218/
      輸出:2015/12/18


     範例2:檢查部位

    • 輸入:不定部位的字母代號,加上without/with contrast enhancement數字代號,以及一個結尾符號,例如b1=
      輸出:CT of the brain without contrast enhancement shows:

    範例3:lines and tubes for CXR

    • 輸入:數個字母代號,加上結尾符號,例如tgv=
      輸出:Placement of endotracheal tube, nasoenteric tube, central venous catheter(s).

    2015年12月11日 星期五

    醫病溝通

    根本就是平行世界。大部分時候只是醫生覺得病人懂了自己講的,病人也覺得懂了醫生講的,其實是各自腦補。

    今天第一次參加這種面對面溝通,雖然主要是針對一線的醫師,但也會做到procedure的敝科也無法置身事外。簡而言之就是常見的劇本:病人因為碰到一連串的事件不幸掛了,家屬不能接受,列出某些不滿意的事項(通常都是小缺失),質疑如果沒有這些小事件,是不是就不會有今天的結果。當然,「人好好的進醫院,怎麼會變這樣」的經典台詞也出現。幾個簡單心得如下:
    • 大部分人「不理性」, 更何況是剛遭遇那麼重大的事件。同樣的質疑點一提再提,不知道是不接受還是聽不進去,或是瞬間就忘了,一直鬼打牆似的對話重播。大概就是問了A,回答A,有可能還沒回答完又突然想到質疑B,回答B後換C,C回答後又回到A,講半天完全沒進度沒共識。
    • 大部分人無法被「告知」、無法「理解」、更不用說「記住」專業的知識。以前說什麼要「告知同意」,但病人/家屬絕對不可能理解100%,甚至可能是「-100%」,也就是完全誤解。再加上記憶力是有限的而且不可靠,事後醫生說他講過,但家屬覺得沒講過,各說各話,沒錄音根本沒證據,只能說還好簽同意書已經是routine。
    • 承上點,除了沒有專業的知識,有些完全錯誤的知識反而根深諦固,例如沒看過急性中風,質疑為什麼30分鐘前人是好的,見面時就發作,「哪有那麼巧的」;醫院冷氣那麼冷,害人身體變虛,害人感染;「再觀察」就是沒在治療,「保守治療」就是沒有盡力治療。不勝枚舉,只能說還好這些觀念在法庭上沒有什麼威脅…
    • 大部分人無法注意到「醫療上」會影響病人的因素,只能把注意力放在「醫療外」的枝微末節,但覺得是這些細節影響了預後。這部分大概是最難想像、最難掌握的,護理師不經意的一個動作(沒有蓋被子)、某位醫師(包括大P或菜鳥)隨口的病情評估(這一定好起來/好不起來),反而記得一清二楚,連那個人長得胖瘦高矮都記著,總之就是各種服務不周到。有些人希望醫生講越多越詳細越好,做個手術可能會死可能會植物人是基本的,各種替代治療不管對病人是好是壞都要知道;有些人反而不敢聽那麼多,聽越多越怕,覺得不吉利;反正怎樣都能怪。
    • 大部分人沒有benefit/risk的概念,不知道在不同狀況下會有不同的考慮,沒有標準答案,也不是SOP。可能遇到的說詞是:為什麼某個(侵入性)治療要「拖」緊急時才做,不能提早做,不能「預防」嗎?侵入性治療一定兼有benefit/risk,沒有適應症甚至有禁忌症的狀況下,risk1 > benefit1,整體效益可能benefit1 - risk1 - cost < 0;但在危急狀況下,不做治療的risk嚴重許多,不治療效益是 - BigRISK,治療的效益是benefit2 - risk2 - cost,所以有可能要治療。注意benefit1不一定等於benefit2,risk1也不一定等於risk2;而且,在危急狀況當下,很可能不治療或治療的效益都是負的,只能「兩害相權取其輕」。隨著各專科專業化,有時候連不同科醫生間也會吵起來,互相思考不周,只看到人家某種治療的benefit,卻沒去了解相關的risk,或者反之。
    行醫生涯,很難不會越來越保守,尤其是還很菜的時候… 大家常說的一句話是「還沒踢到鐵板,只是做得不夠多」。在踢到鐵板前,前輩們會叮嚀先穿好鐵鞋,雖然鐵鞋會造成某種程度的不便(例如別人覺得你效率低落),但為了防止踢到鐵板,確實是智慧的傳承,是一種自己人才知道的,benefit/risk之間巧妙的平衡。

    2015年9月7日 星期一

    Triangulation of mammography

    所謂的breast lesion的triangulation,是說MLO或CC其中一張看到lesion,但另一個view不確定在哪,則再照ML。如果(相對breast中軸)的距離,ML比MLO高,則lesion在medial side,反之則在lateral side。可參考這篇paper或是這張小小示意圖。

    以數學表示之:

    • 以nipple為原點之極座標系,假設lesion在(r, θ),θ = 0表示正上方,θ = π/2表示lateral,θ = -π/2表示medial。如果直角座標系則是(r sinθ, r cosθ)
    • 於是lesion在ML時高度為r cosθ,在CC時為r sinθ。事實上,對於某φ角度(原點及方向同θ)之造影,lesion高度為r cos(θ-φ)。
    • 對cos(θ-φ)作圖,θ from -π to π表示lesion從medial到lateral,φ from 0 to π/2表示從ML到CC,Z軸表示lesion高度,如圖。
    • 直接看contour plot,medial lesion也就是t from -π to 0的部分,在p from 0 to π/2也就是ML到CC,顏色變深也就是值變小;lateral lesion也就是t from 0 to π則相反。大致上正確。
    • 仔細想發現不對呀,就cos(θ-φ)而言,for any θ from -π to π,在φ from 0 to π/2並不一定是嚴格遞增或遞減,例如一個lesion在θ = π/8處,則ML排到CC的高度應該像這張圖。
    • 因此結論是:原始敘述並不完全正確,而且lesion在各個view間的關係並不在「直線」上,而是在cos曲線上。