LIDC-IDRI肺結節Dicom數據集解析與總結


一、數據源

訓練數據源為LIDC-IDRI,該數據集由胸部醫學圖像文件(如CT、X光片)和對應的診斷結果病變標注組成。該數據是由美國國家癌癥研究所(National Cancer Institute)發起收集的,目的是為了研究高危人群早期癌癥檢測。

該數據集中,共收錄了1018個研究實例。對于每個實例中的圖像,都由4位經驗豐富的胸部放射科醫師進行兩階段的診斷標注。在第一階段,每位醫師分別獨立診斷并標注病患位置,其中會標注三中類別:1) >=3mm的結節, 2) <3mm的結節, 3) >=3mm的非結節。在隨后的第二階段中,各位醫師都分別獨立的復審其他三位醫師的標注,并給出自己最終的診斷結果。這樣的兩階段標注可以在避免forced consensus的前提下,盡可能完整的標注所有結果。

文件位置LIDC-IDRI -> lidc-idri nodule counts (6-23-2015).xlsx


文件位置LIDC-IDRI -> tcia-diagnosis-data-2012-04-20.xls

二、圖像文件格式

1. 圖像Dicom格式

圖像文件為Dicom格式,是醫療圖像的標準格式,其中除了圖像像素外,還有一些輔助的元數據如圖像類型、圖像時間等信息。
一張CT圖像有 512x512 個像素點,在dicom文件中每個像素由2字節表示,所以每張圖片約512KB大小。圖像中每個像素都是整數,專業名稱為 Hounsfield scale 或 CT Number,是描述物質的放射密度的量化值(參考Wikipedia)。下表為常見物質的HU值。

由于圖片為單通道,畫圖渲染出來為黑白圖,放射密度越高的位置越亮。

除了像素圖以外,元數據中有一些其它主要Tag (參考DICOM的常用Tag分類和說明)

在LIDC-IDRI上面可以直接網頁上搜索圖像數據信息,通過Dicom里面的tag可以對比上述tag描述,我們在實際過程中只取上述tag使用,其他tag暫時不管:

2. HDF文件格式

在之后的數據處理中可能還會用到hdf格式的數據,下面介紹一下hdf文件格式:

HDF是用于存儲和分發科學數據的一種自我描述、多對象文件格式。HDF是由美國國家超級計算應用中心(NCSA)創建的,以滿足不同群體的科學家在不同工程項目領域之需要。HDF可以表示出科學數據存儲和分布的許多必要條件。HDF被設計為:

  • 自述性:對于一個HDF文件里的每一個數據對象,有關于該數據的綜合信息(元數據)。在沒有任何外部信息的情況下,HDF允許應用程序解釋HDF文件的結構和內容。
  • 通用性:許多數據類型都可以被嵌入在一個HDF文件里。例如,通過使用合適的HDF數據結構,符號、數字和圖形數據可以同時存儲在一個HDF文件里。
  • 靈活性:HDF允許用戶把相關的數據對象組合在一起,放到一個分層結構中,向數據對象添加描述和標簽。它還允許用戶把科學數據放到多個HDF文件里。
  • 擴展性:HDF極易容納將來新增加的數據模式,容易與其他標準格式兼容。
  • 跨平臺性:HDF是一個與平臺無關的文件格式。HDF文件無需任何轉換就可以在不同平臺上使用。

除了NCSA在用它,我想還有國家衛星氣象中心因為國家衛星氣象中心提供了一個中文版的翻譯HDF5.0 使用簡介 - 國家衛星氣象中心!
注:hdf具體介紹詳見下述鏈接:
關于HDF文件的一點概述(HDF4,HDF5)
HDF5 小試——高大上的多對象文件格式

三、診斷標注

診斷標注以XML格式提供,目前已有XML parser (DeepLearning:medical_image/src/data/annotation.py)

  • XML文件頭部ResponseHeader:
<?xml version="1.0" encoding="UTF-8"?>
<LidcReadMessage uid="1.3.6.1.4.1.14519.5.2.1.6279.6001.1308168927148.0" xmlns="http://www.nih.gov">
 <ResponseHeader>
  <Version>1.7</Version>
  <MessageId>1152727</MessageId>
  <DateRequest>2006-06-05</DateRequest>
  <TimeRequest>17:08:37</TimeRequest>
  <RequestingSite>removed</RequestingSite>
  <ServicingSite>removed</ServicingSite>
  <TaskDescription>Second unblinded read</TaskDescription>
  <CtImageFile>removed</CtImageFile>
  <SeriesInstanceUid>1.3.6.1.4.1.14519.5.2.1.6279.6001.340202188094259402036602717327</SeriesInstanceUid>
  <StudyInstanceUID>1.3.6.1.4.1.14519.5.2.1.6279.6001.584233139051825667176600857752</StudyInstanceUID>
  <DateService>2006-06-05</DateService>
  <TimeService>17:08:37</TimeService>
  <ResponseDescription>1 - Reading complete</ResponseDescription>
  <ResponseComments>Merged, reader anonymized, unblinded responses</ResponseComments>
 </ResponseHeader>
  • XML文件ReadingSession (<nonNodule> 標注 ):
<readingSession>
  <annotationVersion>3.12</annotationVersion>
  <servicingRadiologistID>anonymous</servicingRadiologistID>
  <unblindedReadNodule>
   <noduleID>16448</noduleID>
   <roi>
    <imageZposition>-181.0</imageZposition>
    <imageSOP_UID>1.3.6.1.4.1.14519.5.2.1.6279.6001.156388264492529846911221229696</imageSOP_UID>
    <inclusion>TRUE</inclusion>
    <edgeMap><xCoord>61</xCoord><yCoord>198</yCoord></edgeMap>
   </roi>
  </unblindedReadNodule>
  <nonNodule>
   <nonNoduleID>16446</nonNoduleID>
   <imageZposition>-187.0</imageZposition>
   <imageSOP_UID>1.3.6.1.4.1.14519.5.2.1.6279.6001.270634182491671360557327079123</imageSOP_UID>
   <locus><xCoord>373</xCoord><yCoord>225</yCoord>
   </locus>
  </nonNodule>
 </readingSession>
  • XML文件ReadingSession (<noduleID>標注 ):
<readingSession>
  <annotationVersion>3.12</annotationVersion>
  <servicingRadiologistID>anonymous</servicingRadiologistID>
  <unblindedReadNodule>
   <noduleID>12468</noduleID>
   <roi>
    <imageZposition>-70.0</imageZposition>
    <imageSOP_UID>1.3.6.1.4.1.14519.5.2.1.6279.6001.350028248790971951374528707491</imageSOP_UID>
    <inclusion>TRUE</inclusion>
    <edgeMap><xCoord>398</xCoord><yCoord>252</yCoord></edgeMap>
   </roi>
  </unblindedReadNodule>
  <unblindedReadNodule>
   <noduleID>12453</noduleID>
   <characteristics>
    <subtlety>3</subtlety>
    <internalStructure>1</internalStructure>
    <calcification>6</calcification>
    <sphericity>2</sphericity>
    <margin>4</margin>
    <lobulation>1</lobulation>
    <spiculation>1</spiculation>
    <texture>5</texture>
    <malignancy>1</malignancy>
   </characteristics>
   <roi>
    <imageZposition>-250.0</imageZposition>
    <imageSOP_UID>1.3.6.1.4.1.14519.5.2.1.6279.6001.100791367364990316173567441168</imageSOP_UID>
    <inclusion>TRUE</inclusion>
    <edgeMap><xCoord>49</xCoord><yCoord>316</yCoord></edgeMap>
    <edgeMap><xCoord>50</xCoord><yCoord>315</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>314</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>313</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>312</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>311</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>310</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>309</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>308</yCoord></edgeMap>
    <edgeMap><xCoord>51</xCoord><yCoord>307</yCoord></edgeMap>
    <edgeMap><xCoord>50</xCoord><yCoord>306</yCoord></edgeMap>
    <edgeMap><xCoord>49</xCoord><yCoord>305</yCoord></edgeMap>
    <edgeMap><xCoord>48</xCoord><yCoord>306</yCoord></edgeMap>
    <edgeMap><xCoord>48</xCoord><yCoord>307</yCoord></edgeMap>
    <edgeMap><xCoord>49</xCoord><yCoord>308</yCoord></edgeMap>
    <edgeMap><xCoord>48</xCoord><yCoord>309</yCoord></edgeMap>
    <edgeMap><xCoord>48</xCoord><yCoord>310</yCoord></edgeMap>
    <edgeMap><xCoord>48</xCoord><yCoord>311</yCoord></edgeMap>
    <edgeMap><xCoord>49</xCoord><yCoord>312</yCoord></edgeMap>
    <edgeMap><xCoord>48</xCoord><yCoord>313</yCoord></edgeMap>
    <edgeMap><xCoord>49</xCoord><yCoord>314</yCoord></edgeMap>
    <edgeMap><xCoord>48</xCoord><yCoord>315</yCoord></edgeMap>
    <edgeMap><xCoord>49</xCoord><yCoord>316</yCoord></edgeMap>
   </roi>
  </unblindedReadNodule>
  <unblindedReadNodule>
   <noduleID>12461</noduleID>
   <roi>
    <imageZposition>-253.0</imageZposition>
    <imageSOP_UID>1.3.6.1.4.1.14519.5.2.1.6279.6001.150739457477763063347777523734</imageSOP_UID>
    <inclusion>TRUE</inclusion>
    <edgeMap><xCoord>202</xCoord><yCoord>398</yCoord></edgeMap>
   </roi>
  </unblindedReadNodule>
  <nonNodule>
   <nonNoduleID>12471</nonNoduleID>
   <imageZposition>-199.0</imageZposition>
   <imageSOP_UID>1.3.6.1.4.1.14519.5.2.1.6279.6001.267363953457812811093234965009</imageSOP_UID>
   <locus><xCoord>372</xCoord><yCoord>153</yCoord>
   </locus>
  </nonNodule>
 </readingSession>

四、數據處理代碼分析

1. nodule_structs.py

【基本結構】

  • NoduleCharstics
  • NoduleRoi
  • Nodule(所有結節類型的基類)

【結節分類 -> 普通結節,小結節,非結節】

  • NormalNodule
  • SmallNodule
  • NonNodule

【結節標注】

  • RadAnnotation

【結節頭】

  • AnnotationHeader

【讀取XML信息】

  • IdriReadMessage

2. annotation.py(部分)

  • 【分析XML】
def parse(xml_filename):
    logging.info("Parsing %s" % xml_filename)
    annotations = []
    # ET is the library we use to parse xml data
    tree = etree.parse(xml_filename)
    root = tree.getroot()
    # header = parse_header(root)
    # readingSession-> holds radiologist's annotation info
    for read_session in root.findall('nih:readingSession', NS):
        # to hold each radiologists annotation
        # i.e. readingSession in xml file
        rad_annotation = RadAnnotation()
        rad_annotation.version = \
          read_session.find('nih:annotationVersion', NS).text
        rad_annotation.id = \
          read_session.find('nih:servicingRadiologistID', NS).text
    
        # nodules
        nodule_nodes = read_session.findall('nih:unblindedReadNodule', NS)
        for node in nodule_nodes:
            nodule = parse_nodule(node)
            if nodule.is_small:
              rad_annotation.small_nodules.append(nodule)
            else:
              rad_annotation.nodules.append(nodule)
        
        # non-nodules
        non_nodule = read_session.findall('nih:nonNodule', NS)
        for node in non_nodule:
            nodule = parse_non_nodule(node)
            rad_annotation.non_nodules.append(nodule)
        annotations.append(rad_annotation)
    return annotations
  • 【分析結節頭】
 def parse_header(root):
     header = AnnotationHeader()
     print root.findall('nih:*', NS)
     resp_hdr = root.findall('nih:ResponseHeader', NS)[0]
     header.version = resp_hdr.find('nih:Version', NS).text
     header.message_id = resp_hdr.find('nih:MessageId', NS).text
     header.date_request = resp_hdr.find('nih:DateRequest', NS).text
     header.time_request = resp_hdr.find('nih:TimeRequest', NS).text
     header.task_desc = resp_hdr.find('nih:TaskDescription', NS).text
     header.series_instance_uid = resp_hdr.find('nih:SeriesInstanceUid', NS).text
     date_service = resp_hdr.find('nih:DateService', NS)
     if date_service is not None:
         header.date_service = date_service.text
     time_service = resp_hdr.find('nih:TimeService', NS)
     if time_service is not None:
         header.time_service = time_service.text
     header.study_instance_uid = resp_hdr.find('nih:StudyInstanceUID', NS).text
     return header
  • 【分析結節,非結節】
    具體詳見源代碼

3. dicom_set.py(部分)

CT圖像有三種,分別是CT,CR,DX,這個也可以查看LIDC-IDRI中的Modality,如下圖所示:

【基類DcmImage】

【三種子類 -> CTImage, CRImage, RXImage】

  • CTImage
  • CRImage
  • DXImage

4. lidc_xml_parser.py(部分)

主要就是分析XML文件,提取所有需要用到的信息,詳見源代碼。

5. prepare_data.py(部分)

【判斷指定位置是否有結節】

【One-hot編碼】

【處理結節】

五、數據特征分析

由于數據具有很多不同的特征,有的對后期訓練比較有利,有的卻不利于后期的訓練,需要進行各種預處理之類的。
【有利點】

  • CT圖像的連續性(等間隔多次斷層掃描,可以更有利于對結節的判別)
  • 三維CT圖像(不同軸切方向掃描)
  • 進行預處理(如去噪,增強,平滑等處理)

【不利點】

  • 數據不平衡,不對稱(帶標注的數據點所占的比例太低,不到1/1000),可以考慮對positive進行旋轉,增加數據源,對negtive進行削減,只取部分等;
  • 圖像的模糊性(灰度模糊性,幾何模糊性,和不確定性等),存在偽影(被測者自主或者不自主的運動所致)
  • 原始圖像的格式采用的是Dicom格式,需要進行各種預處理才能拿來用,前期比較麻煩,如果處理好了,就相對比較好用了

【數據切割方法】

  • 順序切割
    切割方法:CT圖片大小為 512x512,按32個像素為步長,截取 64x64 大小的塊,每張原始圖可切出225張訓練圖。
    標注方法:若64x64圖片中,存在結節的中心點,則標注為True,否則標注為False。
  • 僅取標注點
    由于在原始標注中描述了所有的結節和非結節,他們在數量上相當,所以只采集所有有標注的圖片來進行識別。
    切割方法:讀取結節的坐標,以結節中心點為切割中心點截取64x64大小的圖片。
    標注方法:若圖像為結節點,則標注為True,否則標注為False。
  • 標注圖+其他非標注切割
    由于僅取標注點的圖片來訓練,無法讓分類器識別影像邊緣位置,會造成大量誤判,因此再重新調整數據集,將有標注的圖和沒有標注的圖都加入訓練集。
    切割方法:對于所有標注的點,采用 #僅取標注點# 的方法截取。對于其它所有沒有標注的方格,按 #順序切割# 的方式獲得。
    標注方法:將數據分為4類:1) >=3mm結節, 2) <3mm結節, 3) >=3mm非結節, 4) 無標注。有原始標注的圖片會組成1, 2, 3類,其它順序截取的圖片會構成第4類。

注:
【Data Imbalance】
  對于醫療圖像來說,大部分圖像是正常的,非標記的數據只占非常小的比例。以本例中,標記數據與非標記數據的比例至少為 1:1000 以上。在先驗概率如此小的情況下,按照正確率而言,即使將所有數據都判定為正常非病患,正確率也可以高達99.9%以上。然而對于病患診斷,不能漏診則是重中之重。因此,需要調整數據集的分布,放大Positive數據的比例,減小Negative數據的比例,讓分類器可以有效識別positive。

在實驗中,按以下方法調整數據集:

  • 旋轉有標記的圖片,每次旋轉27度,每次旋轉后形成一張新的圖片。這樣所有有原始標記的圖片則擴大了10倍。
  • 所有沒有原始標記的圖片,只取2%,剩下則全部舍棄。

Loss Function:
對于醫療診斷而言,漏診的代價更大,即將positive判定為negative的代價是更大的更嚴重的錯誤。
判定錯誤的代價定義為:

六、短期總結

1. 知識儲備

  • 做出一個checklist,逐步完善
  • 將自己學的,做的的東西寫出來,總結成文,發出去
  • 能夠將自己掌握的知識講出來,并且讓別人能夠聽懂

2.經驗積累(項目)

  • 對于簡單的問題,能夠穩定到一個比較好的結果,比較高的準確率
  • 對于沒有接觸過的項目,遇到新問題時,能夠快速定位,及時解決(比如服務器出現問題,能夠半小時定位,三小時解決恢復服務)

Reference:

[1] LIDC-IDRI 官網
[2] Lung Image Database Consortium (LIDC) Nodule Size Report
[3] 肺部CT圖像分析及特征提取研究
[4] 肺結節CT圖像特征提取及SVM分類方法研究
[5] Computer-aided classification of lung nodules on computed tomography images via deep learning technique (paper)
[6] Computerized Detection of Lung Nodules in Thin-Section CT Images by use of Selective Enhancement Filters and an Automated Rule-Based Classifier (paper)
[7] DICOM的常用Tag分類和說明
[8] 肺結節CT征象及顯示
[9] CT影像的識別
[10] 基于分類技術的肺部CT圖像識別
[11] 關于HDF文件的一點概述(HDF4,HDF5)
[12] HDF5 小試——高大上的多對象文件格式
[13] 基于ct影像孤立性肺結節識別與分析
[14] 肺結節的影像診斷和鑒別診斷 (ppt)
[15] 肺部結節的CT鑒別診斷 (ppt)


(注:感謝您的閱讀,希望本文對您有所幫助。如果覺得不錯歡迎分享轉載,但請先點擊 這里 獲取授權。本文由 版權印 提供保護,禁止任何形式的未授權違規轉載,謝謝!)

最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。

推薦閱讀更多精彩內容