1. 程式人生 > >將自己的dcm資料製作成LUNA16資料集提供資料樣式。

將自己的dcm資料製作成LUNA16資料集提供資料樣式。

1.先說下luna資料樣式。一個CT序列在LUNA16資料集主要是由一個mhd檔案一個raw檔案以及一個或多個csv檔案(以一個為例不做那麼多區分(3mm以下不做處理等))。以下為LUNA16資料集的csv格式:

seriesuid,coordX,coordY,coordZ,class
1.3.6.1.4.1.14519.5.2.1.6279.6001.100225287222365663678666836860,68.42,-74.48,-288.7,0

2.再說下我得到資料樣式。一個CT序列的所有dcm檔案以及對應的csv檔案。csv格式:

UID,FileName,StudyInstanceUID,SeriesInstanceUID,SOPInstanceUID,FractureType,RibPosition,Annotation,CoordX,CoordY
20181213115819943,0XeuArlv0F0u0FG20wTxArGp1ZCtyXlp0v4u0V4vyXlu0rOp1E4uyXCp1wG31Flp0wet0ret0FTu0Few0rlzArGw0wG50reuAr0v1wZpDl11ee==,0E4vyXT2yXO21FC30F0zyXOp1rcu1F03yXct0FTu0Fex0FKu1Fewee==,0E4wyXlvyXcp0Flt1v4zyXlp1o431FOz0E4w0ret0reuArlu0r0t0FG41F0w1FZt0rl40wcu1ee=,0E4wyXlvyXcp0Flt1v4zyXlp1o431FOz0E4w0ret0reuArlu0r0t0FG41F0w1FZt0rl40wc3ACe=,無錯位,4,L,415;404;401;409;430;433;435;427;415;,233;250;268;289;287;270;249;233;233;

FileName,StudyInstanceUID,SeriesInstanceUID,SOPInstanceUID 是經過加密的。解下密就ok了這裡就不多敘述。

3.將一個CT序列的所有dcm檔案轉換成一個mhd檔案和一個raw檔案

參考部落格 https://blog.csdn.net/zhuang19951231/article/details/79488591  就ok。貼下程式碼如下:

import cv2
import os
import pydicom
import numpy
import SimpleITK

# 路徑和列表宣告
PathDicom = "E:/DcmData/xlc/Fracture_data/Me/3004291153/3307885/"  # 與python檔案同一個目錄下的資料夾,儲存dicom檔案
SaveRawDicom = "E:/DcmData/xlc/Fracture_data/mhd_raw/"  # 與python檔案同一個目錄下的資料夾,用來儲存mhd檔案和raw檔案
lstFilesDCM = []
# for root, dirs, files in os.walk(PathDicom):
#     for name in files:
#         print(os.path.join(root, name))
#     for name in dirs:
#         print(os.path.join(root, name))

# 將PathDicom資料夾下的dicom檔案地址讀取到lstFilesDCM中
for dirName, subdirList, fileList in os.walk(PathDicom):
    for filename in fileList:
        if ".dcm" in filename.lower():  # 判斷檔案是否為dicom檔案
            print(filename)
            lstFilesDCM.append(os.path.join(dirName, filename))  # 加入到列表中

# 第一步:將第一張圖片作為參考圖片,並認為所有圖片具有相同維度
RefDs = pydicom.read_file(lstFilesDCM[0])  # 讀取第一張dicom圖片
print(RefDs.SOPInstanceUID)
# 第二步:得到dicom圖片所組成3D圖片的維度
ConstPixelDims = (int(RefDs.Rows), int(RefDs.Columns), len(lstFilesDCM))  # ConstPixelDims是一個元組

# 第三步:得到x方向和y方向的Spacing並得到z方向的層厚
ConstPixelSpacing = (float(RefDs.PixelSpacing[0]), float(RefDs.PixelSpacing[1]), float(RefDs.SliceThickness))

# 第四步:得到影象的原點
Origin = RefDs.ImagePositionPatient

# 根據維度建立一個numpy的三維陣列,並將元素型別設為:pixel_array.dtype
ArrayDicom = numpy.zeros(ConstPixelDims, dtype=RefDs.pixel_array.dtype)  # array is a numpy array

# 第五步:遍歷所有的dicom檔案,讀取影象資料,存放在numpy陣列中
i = 0
for filenameDCM in lstFilesDCM:
    ds = pydicom.read_file(filenameDCM)
    ArrayDicom[:, :, lstFilesDCM.index(filenameDCM)] = ds.pixel_array
    #cv2.imwrite("out_" + str(i) + ".png", ArrayDicom[:, :, lstFilesDCM.index(filenameDCM)])
    i += 1

# 第六步:對numpy陣列進行轉置,即把座標軸(x,y,z)變換為(z,y,x),這樣是dicom儲存檔案的格式,即第一個維度為z軸便於圖片堆疊
ArrayDicom = numpy.transpose(ArrayDicom, (2, 0, 1))

# 第七步:將現在的numpy陣列通過SimpleITK轉化為mhd和raw檔案
sitk_img = SimpleITK.GetImageFromArray(ArrayDicom, isVector=False)
sitk_img.SetSpacing(ConstPixelSpacing)
sitk_img.SetOrigin(Origin)
SimpleITK.WriteImage(sitk_img, os.path.join(SaveRawDicom, "3307885" + ".mhd"))

比較困惑的就是ArrayDicom = numpy.transpose(ArrayDicom, (2, 0, 1)) 這步,後來研究了下發現luna資料集裡也是這樣的。還有就是生成兩個檔案時,名字跟luna資料集的名字不一樣。這裡後面再說。

4.自己資料集的csv對應成LUNA16資料集的csv格式。

4.1 分別更改每個dcm檔案的檔名為SOPInstanceUID欄位,程式碼如下:

import os
import pydicom
PathDicom = "E:/DcmData/xlc/Fracture_data/Me/3004276169/3302845/"
def getSubPaths(dir):
    list = []
    # 判斷路徑是否存在
    if (os.path.exists(dir)):
        # 獲取該目錄下的所有檔案或資料夾目錄
        files = os.listdir(dir)
        for file in files:
            # 得到該檔案下所有目錄的路徑
            m = os.path.join(dir, file)
            print(m)
            mp=os.path.splitext(file)[0] #獲取檔名字首,[-1]為字尾。
            print(mp)
            if ".dcm" in file.lower():
                RefDs = pydicom.read_file(m)
                filename = RefDs.SOPInstanceUID
                os.rename(m, os.path.join(dir, filename + ".DCM"))

    #return list
getSubPaths(PathDicom)

4.2 怎麼建立CSV並寫入資料

參考 https://blog.csdn.net/waple_0820/article/details/70049953 有兩種(csv和pandas.to_csv),最終選擇pandas.to_csv另一種麻煩。演示程式碼如下:

import pandas as pd

#任意的多組列表
a = [1,2,3]
b = [4,5,6]
c = [7,8,9]
d = [10,11,12]
e = [13,14,15]

#字典中的key值即為csv中列名
dataframe = pd.DataFrame({'seriesuid':a,'coordX':b,'coordY':c,'coordZ':d,'class':e})

#將DataFrame儲存為csv,index表示是否顯示行名,default=True
dataframe.to_csv("test.csv",index=False,sep=',')

4.3 由於CSV資料轉為LUNA16資料集中資料的樣式。

import pandas as pd
import os
import pydicom
import csv
import numpy as np
#任意的多組列表
seriesuid = []
coordX = []
coordY = []
coordZ = []
DX = []
DY = []
cl = []
candidates = r'E:/DcmData/xlc/Fracture_data/Me/3004276169/3302845/RibFracture.dec'
PathDicom = "E:/DcmData/xlc/Fracture_data/Me/3004276169/3302845/"


##pandas多個引數分割不出
# candidatesList = pd.read_csv(candidates)
# for type in candidatesList['SOPInstanceUID'],candidatesList['FractureType'],candidatesList['CoordX'],candidatesList['CoordY']:
#     sum=type[0].split('/n')
#     print(sum[0])
#     m = os.path.join(PathDicom, type+'.DCM') #標記的dcm檔案
#     RefDs = pydicom.read_file(m)
#     coordZ.append(RefDs.ImagePositionPatient[2])

# #使用csv,發現dec用不了,還是用pandas
# def readCSV(filename):
#     lines = []
#     with open(filename, "r") as f:
#         csvreader = csv.reader(f)
#         for line in csvreader:
#             lines.append(line)
#     return lines
# candidatesList = readCSV(candidates)
# for cand in candidatesList:
#     print(cand)


##pandas
candidatesList = pd.read_csv(candidates)
print(len(candidatesList))
for i in range(len(candidatesList)):
    m = os.path.join(PathDicom, candidatesList.loc[i][5]+'.DCM')
    RefDs = pydicom.read_file(m)
    coordZ.append(RefDs.ImagePositionPatient[2])

    seriesuid.append(RefDs.SeriesInstanceUID)

    deslist = np.array(['正常', '隱匿型', '無錯位', '有錯位', '有骨痂', '畸形癒合'])
    typelist = np.zeros(6)
    for j in range(6):
        if candidatesList.loc[i][6] == deslist[j]:
            cl.append(j)
            break

    X = candidatesList.loc[i][9].split(';')
    Y = candidatesList.loc[i][10].split(';')
    ax = []
    ay = []
    for xi in range(len(X)-1):
        ax.append(X[xi])
    for yi in range(len(Y)-1):
        ay.append(Y[yi])
    ax = list(map(float, ax))
    ay = list(map(float, ay))
    minx = np.min(ax)*RefDs.PixelSpacing[0]+RefDs.ImagePositionPatient[0]
    maxx = np.max(ax)*RefDs.PixelSpacing[0]+RefDs.ImagePositionPatient[0]
    miny = np.min(ay)*RefDs.PixelSpacing[1]+RefDs.ImagePositionPatient[1]
    maxy = np.max(ay)*RefDs.PixelSpacing[1]+RefDs.ImagePositionPatient[1]
    coordX.append(minx)
    coordY.append(miny)
    DX.append(maxx-minx)
    DY.append(maxy-miny)
print(len(seriesuid),len(coordX),len(coordY),len(coordZ),len(DX),len(DY),len(cl))

#字典中的key值即為csv中列名(放一起它的順序很亂,只能一個一個往後面插入)
dataframe = pd.DataFrame({'seriesuid':seriesuid})
dataframe['coordX'] = coordX
dataframe['coordY'] = coordY
dataframe['coordZ'] = coordZ
dataframe['DistanceX_mm'] = DX
dataframe['DistanceY_mm'] = DY
dataframe['class'] = cl
print (dataframe)
#將DataFrame儲存為csv,index表示是否顯示行名,default=True
dataframe.to_csv("test.csv",index=False,sep=',')

轉化之後的格式如下:

seriesuid,coordX,coordY,coordZ,DistanceX_mm,DistanceY_mm,class
1.3.12.2.1107.5.1.4.75751.30000018110301585335900183214,112.599609375,-160.556640625,-436.5,23.5078125,38.71875,2

4.4多個csv合併

參考 https://blog.csdn.net/qq_16949707/article/details/76099310  

程式碼如下:

import pandas as pd
import os
import glob
csv_files = glob.glob('E:/DcmData/xlc/Fracture_data/Me/*.csv')
df = df = pd.DataFrame(columns=['seriesuid', 'coordX', 'coordY', 'coordZ', 'DistanceX_mm','DistanceY_mm','class'])
for csv in csv_files:
    df = pd.merge(df,pd.read_csv(csv),how='outer')
    os.remove(csv)
df_to_save = pd.DataFrame(df,columns=['seriesuid', 'coordX', 'coordY', 'coordZ', 'DistanceX_mm','DistanceY_mm','class'])
df_to_save.to_csv('annotations.csv',index=False)

執行程式這樣就大功告成了。