1. 程式人生 > >用Apache Hadoop和Apache Solr處理和索引醫學影象

用Apache Hadoop和Apache Solr處理和索引醫學影象

你還在為大規模影象管理感到頭疼嗎?讀下去,看看這個團隊是如何使用開源產品來更有效地索引和儲存高解析度醫學影象的。

時下,醫學影像迅速地成為了一種評估病人狀況,以及確定是否存在醫療條件的最好非侵入性方法。多數情況下,用來協助診斷的影像是構建現代醫學體系的第一步,而成像技術的進步也使我們能夠收集到更詳細的、解析度更高的2D、3D、4D以及顯微影象,從而幫助更快診斷和治療某些複雜的情況。

在現實生活中,人腦的高解析度顯微掃描大小可以達到恐怖的66TB。在一些3D模式的例子中(例如計算機斷層掃描),每幅影象大約4MB(2048×2048畫素)。就目前的情況來看,美國所有資源庫中有大約750PB的醫學影像,並且預計在2016年底能達到1EB的儲存量。基於這樣的事實,很顯然可以預感到醫療成像必將成為一個大資料問題。

目前,影象存檔和通訊系統(PACS)已經成為了醫學影像儲存和檔案檢索的行業標準,提供了專用的資料格式和物件。而在這篇文章中,我們分享了一個替代性的解決方案用來儲存和檢索醫學影像檔案,它利用一個Apache Hadoop叢集(CDH)提供高效能和具有成本效益的分散式影象處理,其優點包括: 

  • 讓醫學影像資料接近其他已經在使用的資料來源,例如基因組學、電子病歷、藥品、穿戴裝置資料等等,減少資料儲存和遷移成本  
  • 通過使用開源軟體和行業標準伺服器降低解決方案成本 
  • 允許在同一個叢集內結合其他形勢的資料做醫學影像分析  

本文將努力重現英特爾的工作,並將此作為可以在CDH叢集上利用Apache Hadoop和Apache Solr索引DICOM(Digital Imaging and Communications in Medicine)影象,並實現儲存、管理和檢索功能的佐證。

解決方案概述

下面就來關注一個簡單但重要的示例:儲存和檢索DICOM影象的能力,並且這個用例不依賴於其他現有的影像庫。醫院和成像設施將DICOM影象的副本傳送到本地與HIPPA相容的雲或託管伺服器叢集並存儲。在需要時,醫生或護理者應該能夠從連線到遠端叢集的本地機器上檢視、搜尋和檢索病人的DICOM影象,如圖1所示。

圖片描述

圖1

詳細說明

DICOM影象包含兩個部分:文字標題和二進位制影象。要確保影象是像圖2和圖3所示的那樣可儲存和可被搜尋,需使用下面幾個步驟。

圖片描述

圖2

圖片描述

圖3

要注意,圖2是從軟體開發人員的角度來分析,而圖3則是終端使用者和解決方案互動的角度。作為軟體開發人員我們必須親身經歷這個過程,來為終端使用者更好地驗證功能。

1、從DICOM影象中提取元資料。
2、HDFS中儲存原始的DICOM影象。
3、利用元資料生成一個索引檔案(也將被儲存在HDFS裡面)。
4、搜尋使用Hue介面來檢索影象。
5、下載圖片到本地系統並使用DICOM檢視器開啟檢視。

與高效能伺服器節點的成本相比,行業標準伺服器在效能和儲存上更具靈活性。我們的伺服器節點採用雙CPU結構的英特爾至強E5家族處理器,4TB×12(48TB)儲存和192GB記憶體。在必要時處理和儲存的需求可以通過橫向擴充套件來滿足——即增加叢集中的節點數。

系統設定和配置

以下是我們測試6節點CDH叢集和1個邊緣節點的關鍵指標。

圖片描述

圖片描述

軟體需求

為了測試這個解決方案,我們啟用Cloudera管理器中的CDH5.4服務:

  • HDFS

    • 儲存需要被索引的DICOM XML檔案
    • 輸出索引結果並存儲
  • Apache Solr (Solrcloud模式)

    • 通過SCHEMA.XML索引給定的DICOM XML檔案
  • Apache ZooKeeper

    • 在SolrCloud模式下使用ZooKeeper實現分散式索引
  • Hue(啟用搜索特性)

    • 在圖形介面檢視Solr的索引結果,也可以搜尋基於索引欄位的DICOM影象
  • Cloudera搜尋

    • 通過MapReduceIndexerTool做索引的離線批處理

資料集

我們的測試資料包含了從Visible Human Project下載的DICOM CT影象。

工作流

圖4描述了工作流路徑。

圖片描述

圖4

第1步:將所有的DICOM影象都儲存在本地資料夾,然後使用DCM工具包(DCMTK)與dcm2xml功能從影象中提取DICOM元資料,並將其以XML格式儲存。(參見附錄1中的例子。)

Example: dcm2xml <input-file-path> <output-file-path>
./dcm2xml source.dcm source.xml

在上面的例子中,要想執行dcm2xml功能,必須事先在本地機器上下載好DCMTK,然後在.bashrc中設定如下路徑。

Example: export DCMDICTPATH="/home/root/Dicom_indexing/dicom-script/dcmtk-3.6.0/s

第2步:將已轉換的DICOM影象匯入到HDFS的at/user/hadoop/input-dir和create/user/hadoop/output-dir中並存儲索引結果。

第3步:做ETL處理。這一步建議使用Morphlines配置檔案,然後按照要求解析和提取所需內容並同時為Solr建立可索引檔案。

第4步(參見下列過程):利用Solr中的schema.xml配置,從給定的XML檔案欄位中建立索引,同時還可以將MapReduceIndexerTool用於離線批處理索引。(見附件2)。

  • 首先,請確認Solr服務(以SolrCloud模式)在叢集上成功啟動並執行(訪問http://<your –solr-server-name>:8983/solr)。

  • 然後使用solrctl生成配置檔案,包括要索引的schema.xml欄位(使用 - ZK選項將提供ZooKeeper的主機名,還可以在Cloudera Manager’s ZooKeeper中發現這個資訊)。但是要注意IP地址的最後一項需要ZooKeeper埠,而且ZooKeeper主機IP會代替主機IP。

solrctl instancedir --zk hostip1,hostip2,hostip3:2181/solr --generate $HOME/solr_config
cd $HOME/solr_config/conf
  • 接下來需要在本地計算機中下載schema.xml檔案並進行編輯,包括要索引的所有欄位名,且欄位名稱要與XML索引檔案的名稱屬性相匹配。這個例子中,DICOM XML檔案僅需要索引成百上千個欄位中的10-15個欄位,同時還要記得將修改的schema.xml檔案在/ conf資料夾中更新(見附錄1檢視生成的DICOM XML檔案,見附錄2檢視定製的schema.xml檔案)。

  • 清理所有的收藏欄和現有的ZooKeeper例項目錄:

solrctl --zk hostip1,hostip2,hostip3:2181/solr collection --delete demo-collection >& /dev/null
solrctl --zk hostip1,hostip2,hostip3:2181/solr instancedir --delete demo-collection >& /dev/null
  • 上傳Solr配置到SolrCloud。
solrctl --zk hostip1,hostip2,hostip3:2181/solr instancedir --create <strong>demo-collection</strong>$HOME/solr_configs
  • 建立一個命名為demo-collection的Solr集合,-s2則表示該集合有兩個碎片。
solrctl --zk hostip1,hostip2,hostip3:2181/solr collection --create demo-collection -s 2 -r 1 -m 2
  • 建立一個空白目錄,並將MapReduceBatchIndexer結果寫到裡面。
hadoop fs -rm -f -skipTrash -r output-dir
hadoop fs -mkdir -p  output-dir
  • ETL再次處理,建議使用Morphlines配置檔案(見附錄3)。

  • 利用MapReduceIndexerTool索引資料並現場演示,而且${DICOM_WORKINGDIR}所在的位置還可以找到log4j.properties和morphlines.conf。

${HDFS_DICOM_OUTDIR} - Location of output dir folder on hdfs (ex: /user/hadoop/output-dir)
${HDFS_DICOM_INDIR} – Location of input dir folder on hdfs (ex: /user/hadoop/input-dir/)
  • Solr會在上面所建立的輸出目錄中儲存索引結果。

第5步:利用Hue檢視索引結果;DICOM影像URL可用於本地下載以供觀看。

測試

測試索引結果首先需要登入Hue介面(假設已獲得並啟用了Hue的搜尋功能。)

1、點選搜尋和導航>索引>演示系列>搜尋。下面是索引結果的預設檢視。

圖片描述

2、鍵入一位病人的姓名或身份證號,也可以是其他任何已編入索引的欄位資料。在這個例子中,下面截圖顯示的是一位病人的名字和其他標識。

圖片描述

3、當你展開一個單一結果時,可以看到如下圖所示的元資料欄位。

圖片描述

4、上面圖片中的DICOM URL點選無效,所以要使用Hue中的圖形控制元件建立一個良好的儀表板並新增一個可點選的URL。

圖片描述

5、點選DICOM的URL ,可以選擇將.dcm檔案下載到本地計算機。在該版本中我們將它下載到本地計算機中,並用名為MicroDicom Viewer的開源工具檢視。

圖片描述

6、使用MicroDicom Viewer檢視影象。

圖片描述

未來工作

我們計劃繼續開發這個參考架構,在此基礎上利用外掛提供更精簡的方法,併力爭做到允許使用者直接下載瀏覽器內的DICOM檔案。我們也將致力於更好的視覺化能力研究,來支援多個影象同時下載。

文章貢獻者:
KARTHIK Vadla,供職於Intel,Big Data Solutions Pathfinding Group部門軟體工程師。
Abhi Basu,供職於Intel,Data Solutions Pathfinding Group部門軟體架構師。
Monica Martinez-Canales,供職於Intel,Data Solutions Pathfinding Group部門首席工程師。

附錄1

<?xml version="1.0"?>
<file-format>
<meta-header xfer="1.2.840.10008.1.2.1" name="Little Endian Explicit">
<element tag="0002,0000" vr="UL" vm="1" len="4" name="FileMetaInformationGroupLength">216</element>
<element tag="0002,0001" vr="OB" vm="1" len="2" name="FileMetaInformationVersion" binary="hidden"></element>
<element tag="0002,0002" vr="UI" vm="1" len="28" name="MediaStorageSOPClassUID">1.2.840.10008.5.1.4.1.1.6.1</element>
<element tag="0002,0003" vr="UI" vm="1" len="58" name="MediaStorageSOPInstanceUID">1.2.826.0.1.3680043.2.307.111.48712655111.78571.301.34207</element>
<element tag="0002,0010" vr="UI" vm="1" len="22" name="TransferSyntaxUID">1.2.840.10008.1.2.4.70</element>
<element tag="0002,0012" vr="UI" vm="1" len="38" name="ImplementationClassUID">1.2.826.0.1.3680043.1.2.100.5.6.2.160</element>
<element tag="0002,0013" vr="SH" vm="1" len="16" name="ImplementationVersionName">DicomObjects.NET</element>
</meta-header>
<data-set xfer="1.2.840.10008.1.2.4.70" name="JPEG Lossless, Non-hierarchical, 1st Order Prediction">
<element tag="0008,0008" vr="CS" vm="2" len="16" name="ImageType">ORIGINAL\PRIMARY</element>
<element tag="0008,0012" vr="DA" vm="1" len="8" name="InstanceCreationDate">20091111</element>
<element tag="0008,0013" vr="TM" vm="1" len="10" name="InstanceCreationTime">164835.000</element>
<element tag="0008,0014" vr="UI" vm="1" len="30" name="InstanceCreatorUID">1.2.826.0.1.3680043.2.307.111</element>
<element tag="0008,0016" vr="UI" vm="1" len="28" name="SOPClassUID">1.2.840.10008.5.1.4.1.1.6.1</element>
<element tag="0008,0018" vr="UI" vm="1" len="58" name="SOPInstanceUID">1.2.826.0.1.3680043.2.307.111.48712655111.78571.301.34207</element>
<element tag="0008,0020" vr="DA" vm="1" len="8" name="StudyDate">20010215</element>
<element tag="0008,0023" vr="DA" vm="1" len="8" name="ContentDate">20010215</element>
<element tag="0008,0030" vr="TM" vm="0" len="0" name="StudyTime"></element>
<element tag="0008,0033" vr="TM" vm="1" len="10" name="ContentTime">093006.000</element>
<element tag="0008,0050" vr="SH" vm="0" len="0" name="AccessionNumber"></element>
<element tag="0008,0060" vr="CS" vm="1" len="2" name="Modality">US</element>
<element tag="0008,0070" vr="LO" vm="0" len="0" name="Manufacturer"></element>
<element tag="0008,0090" vr="PN" vm="0" len="0" name="ReferringPhysicianName"></element>
<element tag="0008,1030" vr="LO" vm="1" len="12" name="StudyDescription">CLR Standard</element>
<element tag="0008,2111" vr="ST" vm="1" len="66" name="DerivationDescription">From DSR by TomoVision's DICOMatic 2.0 rev-2e (conversion module)</element>
<element tag="0008,2124" vr="IS" vm="0" len="0" name="NumberOfStages"></element>
<element tag="0008,212a" vr="IS" vm="0" len="0" name="NumberOfViewsInStage"></element>
<element tag="0010,0010" vr="PN" vm="1" len="12" name="PatientName">BURRUS^NOLA</element>
<element tag="0010,0020" vr="LO" vm="1" len="6" name="PatientID">655111</element>
<element tag="0010,0030" vr="DA" vm="0" len="0" name="PatientBirthDate"></element>
<element tag="0010,0040" vr="CS" vm="0" len="0" name="PatientSex"></element>
<element tag="0018,0010" vr="LO" vm="0" len="0" name="ContrastBolusAgent"></element>
<element tag="0018,1030" vr="LO" vm="1" len="12" name="ProtocolName">CLR Standard</element>
<element tag="0018,5100" vr="CS" vm="0" len="0" name="PatientPosition"></element>
<sequence tag="0018,6011" vr="SQ" card="4" len="784" name="SequenceOfUltrasoundRegions">
………………………………………
</item>
</sequence>
</data-set>
</file-format>

附錄2

<field name="SOPInstanceUID" type="string" indexed="true" stored="true" required="true" multiValued="false" />
<field name="PatientID" type="string" indexed="true" stored="true" multiValued="false" /> 
<field name="StudyDescription" type="string" indexed="true" stored="true"/>
<field name="PatientName" type="string" indexed="true" stored="true" />
<field name="DicomUrl" type="string" stored="true"/>
<field name="ImageType" type="string" indexed="true" stored="true"/>
<field name="InstanceCreationDate" type="string" indexed="true" stored="true"/>
<field name="InstanceCreationTime" type="string" indexed="true" stored="true"/>
<field name="StudyDate" type="string" indexed="true" stored="true"/>
<field name="ContentDate" type="string" indexed="true" stored="true"/>
<field name="DerivationDescription" type="string" indexed="true" stored="true"/>
<field name="ProtocolName" type="string" indexed="true" stored="true"/>
Mention the unique key along with this
<uniqueKey><code>SOPInstanceUID</code></uniqueKey>
(Remove any previously existing unique key tag and replace with this tag.)

附錄3

SOLR_LOCATOR : {

#This is the name of the collection which we created with solrctl utility in our earlier steps
 collection : demo-collection  
#Zookeeper host names, you will find this information in Cloudera Manager at ZooKeeper service
zkHost : "hostip1:2181, hostip2:2181, hostip3:2181/solr" 

}
And include this specific XQuery inside the commands tag of morphlines
xquery {
    fragments : [
    {
        fragmentPath : "/"
        queryString : """
        for $data in /file-format/data-set
        return
        <record>
            <SOPInstanceUID>{$data/element[@name='SOPInstanceUID']}</SOPInstanceUID>
            <ImageType>{$data/element[@name='ImageType']}</ImageType>
            <InstanceCreationDate>{$data/element[@name='InstanceCreationDate']}</InstanceCreationDate>
            <InstanceCreationTime>{$data/element[@name='InstanceCreationTime']}</InstanceCreationTime>
            <StudyDate>{$data/element[@name='StudyDate']}</StudyDate>
            <ContentDate>{$data/element[@name='ContentDate']}</ContentDate>
            <DerivationDescription>{$data/element[@name='DerivationDescription']}</DerivationDescription>
            <ProtocolName>{$data/element[@name='ProtocolName']}</ProtocolName>
            <PatientID>{$data/element[@name='PatientID']}</PatientID>
            <PatientName>{$data/element[@name='PatientName']}</PatientName>
            <StudyDescription>{$data/element[@name='StudyDescription']}</StudyDescription>
            <DicomUrl>{$data/element[@name='DicomUrl']}</DicomUrl>
        </record>
        """
        }
        ]
    }
}