使用Yolov7进行LUNA16的肺结节检测

1、图像预处理

1.1 从mhd文件中获取CT图像,保存3张切片


def normalizePlanes(npzarray):
    maxHU = 400
    minHU = -1000
    npzarray = (npzarray - minHU)/(maxHU - minHU)
    npzarray[npzarray>1] = 1
    npzarray[npzarray<0] = 0
    npzarray *= 255

    return (npzarray.astype(int))


def make_mask(center,diam,z,width,height,spacing,origin):
    '''
    '''
    mask = np.zeros([height,width])  #匹配图像

    #定义结节所在的体素范围
    v_center = (center-origin)/spacing
    v_diam = int(diam/spacing[0]+5)
    v_xmin = np.max([0,int(v_center[0]-v_diam)-5])
    v_xmax = np.min([width-1,int(v_center[0]+v_diam)+5])
    v_ymin = np.max([0,int(v_center[1]-v_diam)-5])
    v_ymax = np.min([height-1,int(v_center[1]+v_diam)+5])

    v_xrange = range(v_xmin,v_xmax+1)
    v_yrange = range(v_ymin,v_ymax+1)

    x_data = [x*spacing[0]+origin[0] for x in range(width)]
    y_data = [x*spacing[1]+origin[1] for x in range(height)]

    #结节周围全都填充1
    for v_x in v_xrange:
        for v_y in v_yrange:
            p_x = spacing[0]*v_x + origin[0]
            p_y = spacing[1]*v_y + origin[1]
            if np.linalg.norm(center-np.array([p_x,p_y,z]))<=diam:
                mask[int((p_y-origin[1])/spacing[1]),int((p_x-origin[0])/spacing[0])] = 1.0
    
    return (mask)


luna_path = "E:/LUNA16/src/subset0/"
output_path = "E:/VSCode/lung/npy"
file_list = glob("E:\LUNA16\src\subset0/" + "*.mhd")

# 获取数据中的每一行
def get_filename(case):
    global file_list
    for f in file_list:
        if case in f:
            return (f)


# 节点的位置
df_node = pd.read_csv('E:\LUNA16\src/annotations.csv')
df_node["file"] = df_node["seriesuid"].apply(get_filename)
# df_node: 文件的路径  example:E:\LUNA16\src\subset0\1.3.6.1.4.1.14519.5.2.1……
df_node = df_node.dropna()

# 循环遍历图像文件
#fcount = 0
for fcount, img_file in enumerate(tqdm(file_list)):
    print("Getting mask for image file %s" % img_file.replace(luna_path, ""))
    mini_df = df_node[df_node["file"] == img_file]  # 得到所有结节
    if(len(mini_df) > 0):    #跳过没有结节的文件

        #读取数据
        itk_img = sitk.ReadImage(img_file)
        img_array = sitk.GetArrayFromImage(itk_img)   #(z,y,x)
        num_z,height,width = img_array.shape    #heightXwidth constitute the transverse plane  
        origin = np.array(itk_img.GetOrigin())  #世界坐标系下的x,y,z(mm)
        spacing = np.array(itk_img.GetSpacing()) #世界坐标中的体素间隔(mm)

        #遍历所有节点
        for node_idx,cur_row in mini_df.iterrows():
            node_x = cur_row["coordX"]
            node_y = cur_row["coordY"]
            node_z = cur_row["coordZ"]
            diam = cur_row["diameter_mm"]

            #保留三个切片
            imgs = np.ndarray([3,height,width],dtype=np.float32)
            masks = np.ndarray([3,height,width],dtype=np.uint8)
            center = np.array([node_x,node_y,node_z])  #结点中心
            v_center = np.rint((center-origin)/spacing) #体素坐标系的结点中心(x,y,z)
            for i,i_z in enumerate(np.arange(int(v_center[2])-1,int(v_center[2])+2).clip(0,num_z-1)):   #clip防止超出z
                mask = make_mask(center,diam,i_z*spacing[2]+origin[2],width,height,spacing,origin)
                masks[i] = mask
                #imgs[i] = img_array[i_z]
                imgs[i] = normalizePlanes(img_array[i_z])
            
            np.save(os.path.join(output_path,"images_%04d_%04d.npy" % (fcount, node_idx)),imgs)
            np.save(os.path.join(output_path,"masks_%04d_%04d.npy" % (fcount, node_idx)),masks)

1.2  获取肺实质

for img_file in file_list:
    imgs_to_process = np.load(img_file).astype(np.float64) 
    print("on image",img_file)
    for i in range(len(imgs_to_process)):
        img = imgs_to_process[i]

        #标准化
        mean = np.mean(img)   #均值
        std = np.std(img)    #标准差
        img = (img-mean)/std

        #寻找肺部附近的平均像素,以重新调整过度曝光的图像
        middle = img[100:400,100:400]
        
        #使用Kmeans算法将前景(放射性不透明组织)和背景(放射性透明组织,即肺部)分离。
        #仅在图像中心进行此操作,以尽可能避免图像的非组织部分。
        kmeans = KMeans(n_clusters=2,n_init=10).fit(np.reshape(middle,[np.prod(middle.shape),1]))
        #np.prod 计算给定数组中所有元素的乘积
        #np.reshape 低维变高维  .flatten() 高维变1维
        centers = sorted(kmeans.cluster_centers_.flatten())
        threshold = np.mean(centers)
        thresh_img = np.where(img<threshold,1.0,0.0)  #阈值化图像,二值化处理

        #腐蚀和膨胀
        eroded = morphology.erosion(thresh_img,np.ones([4,4]))
        dilation = morphology.dilation(eroded,np.ones([10,10]))

        labels = measure.label(dilation)#对二值图像进行标记,标记连通区域
        label_vals = np.unique(labels)  #获取标记值的唯一值,即标记的数量
        regions = measure.regionprops(labels) #标记区域
        good_labels = []
        for prop in regions:
            B = prop.bbox   #边界框
            if B[2]-B[0]<475 and B[3]-B[1]<475 and B[0]>40 and B[2]<472:
                good_labels.append(prop.label)
            #np.ndarray 创建多维数组
        mask = np.ndarray([512,512],dtype=np.int8)
        mask[:] = 0  #肺部mask

        for N in good_labels:
            mask = mask + np.where(labels==N,1,0)
        mask = morphology.dilation(mask,np.ones([10,10]))
        imgs_to_process[i] = mask

    np.save(img_file.replace("images","lungmask"),imgs_to_process)

1.3  获取xml和jpg文件

        通过遍历只含结节图像nodule_annos.xls,该文件在另一篇文章中生成。点此跳转

       这里也可以通过遍历官方文件annotations.csv获得

        获取结节图像的结节信息生成xml文件,生成xml文件的方法参考了这位博主的方法,可以去学习,生成不了也可以去下载该博主共享的数据集,点此跳转

        获取名称匹配lungmask图像获得jpg图像

count = 0
for i in range(len(img_list)):
    origin_img = np.load(img_list[i])
    lung_mask = np.load(lungmask_list[i])
    for j in range(len(origin_img)):
        img = origin_img[j]
        lung_img = lung_mask[j]
        segment = img * lung_img
        segment = Image.fromarray(segment)
        segment = segment.convert('RGB')
        segment.save("E:\VSCode\lung/" + '{:04d}.jpg'.format(count+1))
        count = count + 1

2、模型

YOLOV7

 3、训练

2023.4.14更

 

当然后续继续训练也是有很大的提升的,模型没有完全收敛

2023.5.15 更


有小伙伴私信问我说xml中的filename和图片名称对不上,具体的原因是因为我删掉了一部分肺实质分割不太好的分割图,所以导致最后出现不匹配的原因。
不过labelimg查看结节位置都是对的,训练起来也没有太大的影响

这里用无删减的数据集训练了一下

训练结果

检测结果

  • 8
    点赞
  • 47
    收藏
    觉得还不错? 一键收藏
  • 31
    评论
LUNA16是一个肺结节检测和分类的数据集,主要用于肺结节的研究和算法评估。在使用pytorch搭建分类网络进行疑似肺结节分类之前,需要进行样本集的生成。 生成样本集的步骤如下: 1. 安装cuda11.4和cudnn8.2.4,这是为了支持GPU加速计算。 2. 创建一个虚拟环境并安装Python 3.8。 3. 使用pip安装pytorch和相关的库,可以采用命令"pip install torch torchvision torchaudio --extra-index-url https://download.pytorch.org/whl/cu114"安装。 4. 下载nnDetection项目,并将LUNA16的数据文件夹存放在项目的"Task016_Luna/raw"目录下。 5. 运行预处理脚本"prepare.py",该脚本将LUNA16原始的.raw格式数据转换为.nii.gz格式,并根据annotations.csv中提供的结节信息构造结节的标签信息和mask数据。 6. 进行尺度归一化操作,确保样本的尺度一致。 以上是使用pytorch搭建LUNA16肺结节分类网络的准备工作,接下来可以根据需要进行深度学习模型的训练和评估。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* [nnDetection复现Luna16 附模型](https://blog.csdn.net/qq_29304033/article/details/128140704)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] - *3* [实战:使用Pytorch搭建分类网络(肺结节假阳性剔除)](https://blog.csdn.net/qq_24739717/article/details/101034728)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 31
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值