实战:使用Faster R-CNN完成肺结节检测(LUNA16)(1)/LUNA16数据集初探

实战:使用Faster R-CNN完成肺结节检测(LUNA16)(1)/LUNA16数据集初探

LUNA16

LUNA16,全称Lung Nodule Analysis 16。该数据集来自另一个更大的数据集LIDC-IDRI,旨在推动更多计算机视觉领域的SOTA算法用于CAD领域。官方网站
比赛共分为两个部分

  1. 结节区域检测:确定每个疑似结节的区域,并给他们一个概率值
  2. 假阳性减少:对提取出的区域进行分类,类别为是或者不是一个真实结节

LUNA16数据集

数据集的由来

LUNA16由LIDC-IDRI数据集筛选而来,LIDC-IDRI它包括1018个低剂量的肺部CT影像。LUNA16排除了其中切片厚度大于2.5mm的影像,总共包含888张。其中结节区域由四个经验丰富的放射科医生标注得来,共有三个标记类别:非结节、结节<3 mm和结节>=3 mm。注释的参考标准为,至少包括3/4的放射科医生都标注了的且大于等于3mm的所有结节,经过筛选共剩下1186个待检测的结节区域。未包括在参考标准中的注释(非结节、结节<3 mm以及仅由1或2名放射科医生注释的结节)被称为不相关的发现。评估脚本(annotations\u excluded.csv)中提供了不相关结果的列表。

数据集的构成

  • subset0.zip~subset9.zip 包含所有CT图像的10个zip文件,每个病例的完整信息由两个文件组成,数据格式为“.mhd”,“.raw”。mhd文件存储ct的基本信息,raw文件为实际的ct影像图片
  • CSVFILES文件夹,包括5个文件:annotations.csv,candidates.csv,sampleSubmission.csv,annotations_excluded.csv,seriesuids.csv
  • annotations.csv:1186个肺结节信息,字段有seriesuid,coordX,coordY,coordZ,diameter_mm,用于’nodule detection’ track
  • candidates.csv:文件包含用于假阳性减少比赛所需的候选结节位置。一共551065条数据。其中,正例(class:1):1351条,其余都是负例(class:0)
  • sampleSubmission.csv:正确格式的提交文件示例,我们不提交,暂时没用。

完整的数据集由10个子集构成,可用于10fold交叉验证,格式为.rar文件,大小为50G左右。

LUNA16数据集

解压缩之后,有两种文件格式,每个病例的完整信息都由这两种文件存储。其中.mhd文件存储着ct的基本信息,.raw文件存储着实际的ct数据
mhd文件示例

ObjectType = Image
NDims = 3											 # 三维切片
BinaryData = True								 #二进制数据
BinaryDataByteOrderMSB = False
CompressedData = False
TransformMatrix = 1 0 0 0 1 0 0 0 1      							# 100,010,001 分别代表x,y,z
Offset = -198.10000600000001 -195 -335.209991  		# 原点坐标
CenterOfRotation = 0 0 0
AnatomicalOrientation = RAI
ElementSpacing = 0.7617189884185791 0.7617189884185791 2.5			# 像素间隔 x,y,z
DimSize = 512 512 121																				# 数据的大小 x,y,z
ElementType = MET_SHORT
ElementDataFile = 1.3.6.1.4.1.14519.5.2.1.6279.6001.105756658031515062000744821260.raw			# 数据存储的文件名

mhd已经交代了图像数据的信息,接下来对图像数据进行读取并可视化,这里主要用的Python中的SimpleITK库来打开一个.mhd格式的文件

def load_itk_image(filename): 
    itkimage = sitk.ReadImage(filename)           # 读取图像信息,一般读取格式为mhd文件与dicom文件
    numpyImage = sitk.GetArrayFromImage(itkimage) # 将读取出来的图像信息用像素值表示出来
    numpyOrigin = np.array(list(reversed(itkimage.GetOrigin())))   # 读取图像的原点信息,因为每张图像的不同,它的位置也与原点的距离不同
    numpySpacing = np.array(list(reversed(itkimage.GetSpacing())))  # 获取图像的尺度信息
    return numpyImage, numpyOrigin, numpySpacing
  • 可视化.mhd格式文件
def visual_mhd(np_image):
    # 查看第100张图像
    plt.figure()
    plt.imshow(np_image[100, :, :])
    # plt.imshow(np_image[100, :, :],cmap='gray') # 以灰度图显示
    plt.show()

实战:使用Faster R-CNN完成肺结节检测(LUNA16)(1)/LUNA16数据集初探

  • 读取图片对应的annotation信息,从.csv文件中读取candidates。为了能够正确打开并读取candidates,我们需要用到pyhton中csv模块。输入为candidates.csv文件,返回为二维列表
    实战:使用Faster R-CNN完成肺结节检测(LUNA16)(1)/LUNA16数据集初探

seriesuid:表示每个病例图像对应的文件名

filename = '/home/nin/LUNA16/CSVFILES/candidates.csv'
def readCSV(filename):
    lines = []
    with open(filename, "r") as f:
        csvreader = csv.reader(f)
        for line in csvreader:
            lines.append(line)
    return lines
  • 坐标转换

CT数据的坐标都是基于CT扫描仪的,坐标系单位为mm,每个病例的原点和体素间距都不一样;真实坐标系下,原点坐标统一为(0,0,0),体素(Voxel)间距为1。

结节的位置是CT扫坐标系(又称世界坐标系)下相对原点的mm值,需要将其转换到真实坐标轴位置。通过计算CT坐标系下结节位置在原点的相对位置,可以计算出真实坐标系下结节的位置,具体公式为:CT坐标系下的(结节位置-原点位置)/切片体素间距 = 真实坐标系下的(结节位置-(0,0,0))/1

def worldToVoxelCoord(worldCoord, origin, spacing):
    stretchedVoxelCoord = np.absolute(worldCoord - origin)
    voxelCoord = stretchedVoxelCoord / spacing
    return voxelCoord
# ------------------------------------------------------------
IN: cands[1]
>>> worldcoord = np.asarray([float(cand[3]),float(cand[2]),float(cand[1])])
>>> voxelCoord = worldToVoxelCoord(worldCoord, numpyOrigin, numpySpacing)
---------------------------------------------OUT---------------------------------------------
['1.3.6.1.4.1.14519.5.2.1.6279.6001.100225287222365663678666836860', '-56.08', '-67.85', '-311.92', '0']
array([-311.92,  -67.85,  -56.08])
array([  9.3159964 , 166.92507596, 186.44671875])
  • 提取candidates的patches并可视化
def show_nodules(ct_scan, nodules,Origin,Spacing,radius=20, pad=2, max_show_num=4):
    # radius是正方形边长一半,pad是边的宽度,max_show_num最大展示数
    show_index = []
    for idx in range(nodules.shape[0]): # lable是一个nx4维的数组,n是肺结节数目,4代表x,y,z,以及直径
        if idx < max_show_num:
            if abs(nodules[idx, 0]) + abs(nodules[idx, 1]) + abs(nodules[idx, 2]) + abs(nodules[idx, 3]) == 0:
                continue

            x, y, z = int((nodules[idx, 0]-Origin[0])/SP[0]), int((nodules[idx, 1]-Origin[1])/SP[1]), int((nodules[idx, 2]-Origin[2])/SP[2])   # 转换坐标
        print(x, y, z) #

        data = ct_scan[z]
        radius = int(nodules[idx, 3]/SP[0]/2)
        # pad = 2 * radius
        # 注意 y代表纵轴,x代表横轴
        data[max(0, y - radius):min(data.shape[0], y + radius),
        max(0, x - radius - pad):max(0, x - radius)] = 3000 # 竖线

        data[max(0, y - radius):min(data.shape[0], y + radius),
        min(data.shape[1], x + radius):min(data.shape[1], x + radius + pad)] = 3000 # 竖线

        data[max(0, y - radius - pad):max(0, y - radius),
        max(0, x - radius):min(data.shape[1], x + radius)] = 3000 # 横线

        data[min(data.shape[0], y + radius):min(data.shape[0], y + radius + pad),
        max(0, x - radius):min(data.shape[1], x + radius)] = 3000 # 横线

        if z in show_index: # 检查是否有结节在同一张切片,如果有,只显示一张
            continue
        show_index.append(z)
        plt.figure(idx)
        plt.imshow(data, cmap='gray')

    plt.show()

img_path = '/home/nin/LUNA16/subset0/1.3.6.1.4.1.14519.5.2.1.6279.6001.108197895896446896160048741492.mhd'
cand_path = '/home/nin/LUNA16/CSVFILES/annotations.csv'
cands = readCSV(cand_path)
cand = cands[24]
# show noudles
itkimage = sitk.ReadImage(img_path)  # 这部分给出了关于图像的信息,可以打印处理查看,这里就不在显示了
OR = itkimage.GetOrigin()
# print(OR)
SP = itkimage.GetSpacing()
# print(SP)
numpyImage = sitk.GetArrayFromImage(itkimage)  # 获取数据,自动从同名的.raw文件读取
b = np.array([[-100.5679445,67.26051683,-231.816619,6.440878725]])
show_nodules(numpyImage,b,OR,SP)

实战:使用Faster R-CNN完成肺结节检测(LUNA16)(1)/LUNA16数据集初探

参考文章
https://blog.csdn.net/zyc2017/article/details/84030903
https://blog.csdn.net/zsddragon/article/details/97793978

上一篇:Qt编写自定义控件51-可输入仪表盘


下一篇:Qt编写自定义控件4-旋转仪表盘