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
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了这里就不多叙述。
参考博客 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.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)
运行程序这样就大功告成了。
PS2019年7月17日.在实际使用时第三点存在错误的地方,修改后的代码已经在这篇博客https://blog.csdn.net/qq_36401512/article/details/85336072中给出.