zoukankan      html  css  js  c++  java
  • 项目笔记《DeepLung:Deep 3D Dual Path Nets for Automated Pulmonary Nodule Detection and Classification》(三)(上)结果评估

    在(一)中,我将肺结节检测项目总结为三阶段,这里我要讲讲这个项目的第三阶段,至于第二阶段,由于数据增强部分的代码我始终看不大懂,先不讲。

    结果评估的程序在evaluationScript文件夹下,这个文件夹下的文件名比较烦,看的比较懵。

    annotations文件夹里面放的是结节标签文件,无关结节标签文件(是结节,但是不统计在内,也不作为非结节区域,就是评估的时候如果你检测到了它,既不算正确,也不算错误,略过),还有用户id文件。

    tool文件夹放的是读取csv文件的模块。

    frocwrtdetpepchluna16.py用来将输出的.npy格式的结果转化为.csv格式。

    noduleCADEvaluationLUNA16.py用来对.csv文件中的结果进行评估,得出froc曲线图。

    下面详细分析下代码,首先是frocwrtdetpepchluna16.py。

    def getcsv(detp, eps): #给定阈值和epoch
        for ep in eps:#针对每个epoch
            bboxpath = results_path + str(ep) + '/' #找到每个epoch的路径
            for detpthresh in detp:
                print 'ep', ep, 'detp', detpthresh
                f = open(bboxpath + 'predanno'+ str(detpthresh) + 'd3.csv', 'w') #根据阈值分别创建与之对应的文件
                fwriter = csv.writer(f)
                fwriter.writerow(firstline) #写入第一行,包括用户id,结节坐标x,y,z,结节概率p
                fnamelist = []
                for fname in os.listdir(bboxpath):
                    if fname.endswith('_pbb.npy'): #找到以_pbb.npy结尾的文件(输出的结节预测值),添加进文件列表
                        fnamelist.append(fname)
                      
                print(len(fnamelist))
                convert = functools.partial(convertcsv, bboxpath = bboxpath, detp = detpthresh) #这个函数对convertcsv函数进行修饰,其实就是预设定几个参数,不用再输入
                for fname in fnamelist:
                    print fname
                    rowlist = convert(fname) #将每一个_pbb.npy文件转换为csv文件  
                    for row in rowlist:
                        print row
                        fwriter.writerow(row)  

           
           #这段注释掉的是原来的代码,用来对那末多pbb.npy文件并行处理,事实证明,确实快了好几倍,实际运行应该用下面这段
           
           #map函数也是修饰函数,将fnamelist的元素并行送给convert处理
    #predannolist = p.map(functools.partial(convertcsv, bboxpath=bboxpath, detp=detpthresh), fnamelist) 
                                                                                               
                #for predanno in predannolist:                                         
                    # print predanno
                #    for row in predanno:
                        # print row
                #        fwriter.writerow(row)
                f.close()

    上面这段代码不是核心代码,这段的作用是对输出的结果文件调用convertcsv函数处理,结果就是每一个epoch都生成一个csv文件,存放80多个测试病例的预测结节位置及概率。

    那么接下来就说说比较核心的convertcsv函数

    def convertcsv(bboxfname, bboxpath, detp):#给定pbb.npy的文件名,pbb.npy的路径,阈值
        sliceim,origin,spacing,isflip = load_itk_image(datapath+bboxfname[:-8]+'.mhd')#加载原始数据
        origin = np.load(sideinfopath+bboxfname[:-8]+'_origin.npy', mmap_mode='r') #以下几行加载预处理后的坐标原点,分辨率,拓展box
        spacing = np.load(sideinfopath+bboxfname[:-8]+'_spacing.npy', mmap_mode='r')
        resolution = np.array([1, 1, 1])
        extendbox = np.load(sideinfopath+bboxfname[:-8]+'_extendbox.npy', mmap_mode='r')
        pbb = np.load(bboxpath+bboxfname, mmap_mode='r') #加载pbb.npy文件
        #print "load finished!"
        pbbold = np.array(pbb[pbb[:,0] > detp]) #根据阈值过滤掉概率低的
        pbbold = np.array(pbbold[pbbold[:,-1] > 3])  #根据半径过滤掉小于3mm的
        pbbold = pbbold[np.argsort(-pbbold[:,0])][:1000] #这条是我加上的,取概率值前1000的结节作为输出,不然直接进行nms耗时太长
        
        pbb = nms(pbbold, nmsthresh) #对输出的结节进行nms
        #print "after nms bboxs:",pbb.shape[0]
        # print len(pbb), pbb[0]
        # print bboxfname, pbbold.shape, pbb.shape, pbbold.shape
        pbb = np.array(pbb[:, :-1]) #去掉直径
        # print pbb[:, 0]
        pbb[:, 1:] = np.array(pbb[:, 1:] + np.expand_dims(extendbox[:,0], 1).T) #对输出加上拓展box的坐标,其实就是恢复为原来的坐标,我对这个拓展box深恶痛绝
        pbb[:, 1:] = np.array(pbb[:, 1:] * np.expand_dims(resolution, 1).T / np.expand_dims(spacing, 1).T) #将输出恢复为原来的分辨率,这样就对应了原始数据中的体素坐标
        if isflip:#如果有翻转的情况,将坐标翻转(我理解是这样的,原始数据有翻转的情况,但是label还是未翻转的label,那么将label翻转,所以模型的输出也是翻转的,现在要再翻转回去,与label对应)
            #拓展box与翻转这两个操作让我神烦 Mask
    = np.load(sideinfopath+bboxfname[:-8]+'_mask.npy', mmap_mode='r') pbb[:, 2] = Mask.shape[1] - pbb[:, 2] pbb[:, 3] = Mask.shape[2] - pbb[:, 3] pos = VoxelToWorldCoord(pbb[:, 1:], origin, spacing) #将输出转换为世界坐标 #print "voxel to world finished!" rowlist = [] # print pos.shape for nk in xrange(pos.shape[0]): # pos[nk, 2], pos[nk, 1], pos[nk, 0] #现在依次将文件名,z,y,x,概率(经过sigmoid处理)写入rowlist,每行都是一个输出结节 rowlist.append([bboxfname[:-8], pos[nk, 2], pos[nk, 1], pos[nk, 0], 1/(1+np.exp(-pbb[nk,0]))]) # print len(rowlist), len(rowlist[0]) return rowlist#bboxfname[:-8], pos[:K, 2], pos[:K, 1], pos[:K, 0], 1/(1+np.exp(-pbb[:K,0]))

    这段代码各种操作是真的繁琐,看着都头疼。这段主要完成的就是对输出结节应用阈值和nms,输出的结果再转换为与label一样的坐标体系,因为最后的准确与否全看与label是否一致。看一下打印的输出信息,这里用的是subset9为验证集,我只贴出了一个epoch的测试结果,阈值取为-0.125,这是sigmoid函数的输入,对应概率值0.4695。

    ep 1 detp -0.125
    write to/home/code/DeepLung/detector/results/new_arch/retrft969/val1/predanno-0.125pbb.csv

    至此,输出处理完毕,可以求取最后的FROC值了,离大功告成只差一步。

    不过,每个epoch都会对应一个csv结果文件,我们还要选取一个最好的结果,选取标准就是FROC值,这些都是如何做的呢,不妨看一下代码。

    def getfroc(detp, eps):
        maxfroc = 0
        maxep = 0
        for ep in eps: #对每个epoch分别处理
            bboxpath = results_path + str(ep) + '/'
            predannofnamalist = []
            for detpthresh in detp: #此处的detp就是阈值,只不过这里采用的是一个阈值列表,就我自己而言,我采用的阈值是-0.125,列表中只有一个元素
                predannofnamalist.append(bboxpath + 'predanno'+ str(detpthresh) + 'pbb.csv')
            #froclist = p.map(getfrocvalue, predannofnamalist)
            froclist = [getfrocvalue(predanno) for predanno in predannofnamalist] #调用getfrocvalue求取froc值
            if maxfroc < max(froclist):
                maxep = ep
                maxfroc = max(froclist)
            print froclistprint maxfroc, maxep

    接下来调用getfrocvalue函数,这个函数其实是调用了noduleCADEvaluationLUNA16.py,也就是核心模块,刚才的frocwrtdetpepchluna16.py只是做了一层处理,对输出进行筛选和变换,以用来进行最后的大决战。

    def getfrocvalue(results_filename):
        return noduleCADEvaluation(annotations_filename,annotations_excluded_filename,seriesuids_filename,results_filename,'./')

    可以看到,我们将标签文件,无关标签文件,用户id文件,以及最后的结果文件也就是之前的csv文件作为输入,输出会是一个标量值,也就是评分。

     对noduleCADEvaluationLUNA16.py的分析我想放到(三)(下)去讲,因为会比较长。

  • 相关阅读:
    利用Tomcat搭一个原型图服务器
    Linux 安装Nginx
    Linux 数据库安装
    一点点感慨
    文件锁-fcntl flock lockf
    Linux生成core文件、core文件路径设置
    信号量 互斥量 读写锁 条件变量
    二叉树遍历
    UNIX网络编程——常用服务器模型总结
    hash_map
  • 原文地址:https://www.cnblogs.com/wzyuan/p/9710678.html
Copyright © 2011-2022 走看看