使用U-Net分割方法进行癌症诊断(教程翻译)

标签: AI 深度学习 图像处理
2017-07-04 20:00 837人阅读 评论(0)收藏举报
分类:
深度学习(4)

最近在做天池医疗AI大赛,看到一份不错的资料,想翻译一下做个记录,原链接点击打开链接

原作者JonathanMulholland

以下是翻译内容

在本教程中,我们将展示一种方法来根据患者的CT扫描图像来判断患者是否患有癌症或者可能在未来12个月内发展成癌症患者。

我们将训练一个网络来分割出潜在的癌性结节,然后使用这些分割出的结点的特征来对CT扫描病人未来12个月的病情诊断进行预测。

教程代码位于点击打开链接

依赖和工具

  • numpy

  • scikit-image

  • scikit-learn

  • keras(tensorflow backend)

  • matplotlib

  • pydicom

  • SimpleITK

注意:Keras训练允许使用多种backend。我们选择安装支持GPU的tensorflow作为Keras的backend。(译者注:keras可以选择tensorflow或者theano作为keras的backend,因为keras的框架有基于tensorflow的版本和theano的版本)。

为了识别具有结节的区域,我们将使用U-Net风格的卷积网络,这个网络主要用于做分割。点击打开链接

我们的网络代码是基于MarkoJocic在Kaggle论坛上为UltrasoundNerve Segmentation挑战发布的教程。点击打开链接

我们用于预测癌症诊断的图像是(CT)扫描图像。CT扫描中结节的外观暗含了患者患有癌症的可能性的信息,我们将需要通过训练标记好的结节的样例来训练U-net神经网络,再通过训练好的网络来寻找结节。我们不采用手动标记图像,而选择LungNodule Analysis 2016 (LUNA2016) challenge中提供的标记好了结节位置的CT图像。我们将首先使用LUNA数据集为我们的U-Net生成适当的训练集。我们将使用这些训练集来训练我们的有监督分割器。

LUNA2016数据构建训练集

我们将使用annotations.csv中给出的结节位置,并从每个病人扫描中提取包含最大结节的三个横切片。将根据annotations.csv中给出的结节尺寸为这些切片创建掩码。该文件的输出将是每个病人扫描的两个文件:一组图像和一组相应的结节掩模。来自LUNA2016挑战的数据可以在这里找到点击打开链接

首先我们导入必要的工具,并在病人的CT扫描中找到最大的结节。一些患者在annotations.csv中有多个结节。我们使用名为df_node的DataFrame(pandas中的数据结构)来跟踪病例编号和节点信息。这些节点信息是后缀为.mhd文件中定义的坐标系的(x,y,z)坐标(mm)。

以下代码片段来自LUNA_mask_extraction.py:

import SimpleITK as sitk
import numpy as np
import csv
from glob import glob
import pandas as pdfile_list=glob(luna_subset_path+"*.mhd")#####################
#
# Helper function to get rows in data frame associated
# with each file
def get_filename(case):global file_listfor f in file_list:if case in f:return(f)
#
# The locations of the nodes
df_node = pd.read_csv(luna_path+"annotations.csv")
df_node["file"] = df_node["seriesuid"].apply(get_filename)
df_node = df_node.dropna()#####
#
# Looping over the image files
#
fcount = 0
for img_file in file_list:print "Getting mask for image file %s" % img_file.replace(luna_subset_path,"")mini_df = df_node[df_node["file"]==img_file] #get all nodules associate with fileif len(mini_df)>0:       # some files may not have a nodule--skipping those biggest_node = np.argsort(mini_df["diameter_mm"].values)[-1]   # just using the biggest nodenode_x = mini_df["coordX"].values[biggest_node]node_y = mini_df["coordY"].values[biggest_node]node_z = mini_df["coordZ"].values[biggest_node]diam = mini_df["diameter_mm"].values[biggest_node]


在mhd文件中获取结节位置

结节的位置相对于由CT扫描仪定义的以毫米为单位的坐标系。图像数据数由不同长度的大小为512×512的array构成。为了将体素位置转换为世界坐标系,我们需要知道[0,0,0]体素的真实坐标和体素的间距(mm)。

为了找到一个结节的体素坐标,根据其真实的位置,我们使用itk图像对象的GetOrigin()和GetSpacing()方法:

itk_img = sitk.ReadImage(img_file) 
img_array = sitk.GetArrayFromImage(itk_img) # indexes are z,y,x (notice the ordering)
center = np.array([node_x,node_y,node_z])   # nodule center
origin = np.array(itk_img.GetOrigin())      # x,y,z  Origin in world coordinates (mm)
spacing = np.array(itk_img.GetSpacing())    # spacing of voxels in world coor. (mm)
v_center =np.rint((center-origin)/spacing)  # nodule center in voxel space (still x,y,z ordering)

结节的中心位于img_array的v_center[2]切片中。我们将节点信息传递给make_mask()函数,并复制生成的掩码给v_center[2]切片图像以及其上方和下方的切片图像。

i = 0
for i_z in range(int(v_center[2])-1,int(v_center[2])+2):mask = make_mask(center,diam,i_z*spacing[2]+origin[2],width,height,spacing,origin)masks[i] = maskimgs[i] = matrix2int16(img_array[i_z])i+=1
np.save(output_path+"images_%d.npy" % (fcount) ,imgs)
np.save(output_path+"masks_%d.npy" % (fcount) ,masks)

值得注意的是,在make_mask()函数中,掩码坐标必须与array的坐标的顺序相匹配。x和y排序被翻转。请参阅下面代码中的最后一行的下一个代码:

    def make_mask(center,diam,z,width,height,spacing,origin):...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.0return(mask)

我们应该从每次扫描收集更多的切片吗?

由于结节位置是根据球体定义的,并且结节是不规则形状的,球体边缘附近的切片可能不含结节组织。使用这样的切片可能产生假阳性导致污染训练集。对于这个分割任务,可能存在一个最优的包含的切片的数量。但是为了简单起见,我们只取3张切片,只选择最大的靠近结节中心的切片。

检查以确保结节mask如我们所预期:

import matplotlib.pyplot as pltimgs = np.load(output_path+'images_0.npy')
masks = np.load(output_path+'masks_0.npy')
for i in range(len(imgs)):print "image %d" % ifig,ax = plt.subplots(2,2,figsize=[8,8])ax[0,0].imshow(imgs[i],cmap='gray')ax[0,1].imshow(masks[i],cmap='gray')ax[1,0].imshow(imgs[i]*masks[i],cmap='gray')plt.show()raw_input("hit enter to cont : ")

结节的中心位于img_array的v_center[2]切片中。我们将节点信息传递给make_mask()函数,并复制生成的掩码给v_center[2]切片图像以及其上方和下方的切片图像。

左上角的图像是扫描切片。 右上角的图像是节点掩码(mask)。左下角的图像是掩码切片,突出显示了结节。

结节的近景图像

提取肺部感兴趣区域以缩小我们的结节搜索


节点掩码似乎已经被正确构建出来了。下一步是提取图像中的肺部区域。我们需要为此步骤导入一些skimage图像处理模块。总体策略是阈值化图像以区分出图像中的各个区域,然后识别哪些区域是肺部。肺与周围组织有较高的对比,因此阈值相当简单。我们使用一些临时的策略来从图像中消除非肺部区域,这些策略不适用于所有数据集。

导入库

from skimage import morphology
from skimage import measure
from sklearn.cluster import KMeans
from skimage.transform import resize

这些步骤可以在LUNA_segment_lung_ROI.py中找到

数组被加载为dtype= np.float64的格式,因为在Sklearn中的KMeans对于这种精度的数据输入存在一个bug。

我们将逐步介绍使用img提取肺部ROI的步骤,img是我们通过LUNA2016的数据提取成的切片集合中的一张512×512的切片,它看起来大概类似于下图:


二值化

我们的第一步是标准化像素值并查看亮度分布

img = imgs_to_process[i]
#Standardize the pixel values
mean = np.mean(img)
std = np.std(img)
img = img-mean
img = img/std
plt.hist(img.flatten(),bins=200)

-1.5附近的下溢峰值是图像的黑色扫描仪外部部分。0.0左右的峰值是背景和肺内部,1.0至2.0的大团块是非肺组织和骨骼。该直方图的结构在整个数据集中是不同的。下面显示了两个数据集的典型图像。左边的图与img类似,在灰色的圆形区域中存在相同的黑色背景。右边的图像中不存在黑色背景,因此它们的像素值直方图差别很大。

我们必须确保我们设置的阈值能够通过像素值区分肺和密度更高的组织。 为此,我们将最小值的像素重置为图像中心附近的平均像素值,并使用k = 2执行kmeans聚类。 效果似乎不错。

[html] view plaincopy
  1. middle = img[100:400,100:400]
  2. mean = np.mean(middle)
  3. max = np.max(img)
  4. min = np.min(img)
  5. #move the underflow bins
  6. img[img==max]=mean
  7. img[img==min]=mean
  8. kmeans = KMeans(n_clusters=2).fit(np.reshape(middle,[np.prod(middle.shape),1]))
  9. centers = sorted(kmeans.cluster_centers_.flatten())
  10. threshold = np.mean(centers)
  11. thresh_img = np.where(img<threshold,1.0,0.0)  # threshold the image

这种方法很好地分离了了两种类型的图像区域,并消除了左侧的黑色光晕

腐蚀和膨胀

然后,我们使用腐蚀和膨胀来填平(消除)由不透明射线造成的黑色肺部区域,然后根据每个区域的边界框大小选择区域。 初始的区域集合大概如下图所示

[html] view plaincopy
  1. eroded = morphology.erosion(thresh_img,np.ones([4,4]))
  2. dilation = morphology.dilation(eroded,np.ones([10,10]))
  3. labels = measure.label(dilation)
  4. label_vals = np.unique(labels)
  5. plt.imshow(labels)

切割非ROI区域

根据经验确定每个区域边界框的切割,对于LUNA数据似乎效果很好,但可能不具有普遍性

[html] view plaincopy
  1. abels = measure.label(dilation)
  2. label_vals = np.unique(labels)
  3. regions = measure.regionprops(labels)
  4. good_labels = []
  5. for prop in regions:
  6. B = prop.bbox
  7. if B[2]-B[0]<475 and B[3]-B[1]<475 and B[0]>40 and B[2]<472:
  8. good_labels.append(prop.label)
  9. mask = np.ndarray([512,512],dtype=np.int8)
  10. mask[:] = 0
  11. #
  12. #  The mask here is the mask for the lungs--not the nodes
  13. #  After just the lungs are left, we do another large dilation
  14. #  in order to fill in and out the lung mask
  15. #
  16. for N in good_labels:
  17. mask = mask + np.where(labels==N,1,0)
  18. mask = morphology.dilation(mask,np.ones([10,10])) # one last dilation
  19. plt.imshow(mask,cmap='gray')

使用ROI掩膜(mask)

LUNA_segment_lung_ROI.py中的下一步是将肺部的ROI掩码应用于每个图像,裁剪到肺ROI的边界方框,然后将生成的图像调整大小为512×512。

[html] view plaincopy
  1. masks = np.load(working_path+"lungmask_0.py")
  2. imgs = np.load(working_path+"images_0.py")
  3. imgs = masks*imgs

裁剪到边界,并调整大小为512×512。

然后我们将对像素进行归一化。 这是因为掩码将图像中的非ROI区域设置为0,并且该操作对像素值分布不敏感。 为了解决这个问题,我们计算掩膜区域的平均值和标准差,并将背景的像素值(现在为零)设置为到像素分布中的较低值(-1.2 * stdev,这是经验选择的)。(译者注:个人理解是之前的 操作使得非ROI区域,即背景区域的像素值为0,这样不合适,因此我们将背景区域设置为一个比较低的像素值,这样既能使背景区域像素值非0,也保证了它足够小)

最终结果是一系列可以作为训练样本的肺部图片。

这些图像和相应的修剪和重新调整的掩码被随机打乱传递到一个numpy数组文件中,该数组文件的维数为[<num_images>,1,512,512]。 由于U-net使用多通道,所以前面的1是必须的。

[html] view plaincopy
  1. #
  2. #  Writing out images and masks as 1 channel arrays for input into network
  3. #
  4. final_images = np.ndarray([num_images,1,512,512],dtype=np.float32)
  5. final_masks = np.ndarray([num_images,1,512,512],dtype=np.float32)
  6. for i in range(num_images):
  7. final_images[i,0] = out_images[i]
  8. final_masks[i,0] = out_nodemasks[i]
  9. rand_i = np.random.choice(range(num_images),size=num_images,replace=False)
  10. test_i = int(0.2*num_images)
  11. np.save(working_path+"trainImages.npy",final_images[rand_i[test_i:]])
  12. np.save(working_path+"trainMasks.npy",final_masks[rand_i[test_i:]])
  13. np.save(working_path+"testImages.npy",final_images[rand_i[:test_i]])
  14. np.save(working_path+"testMasks.npy",final_masks[rand_i[:test_i]])

您可以通过查看肺部掩膜与和原始文件来验证ROI分割的效果:

[html] view plaincopy
  1. imgs = np.load(working path+'images_0.npy')
  2. lungmask = np.load(working_path+'lungmask_0.npy')
  3. for i in range(len(imgs)):
  4. print "image %d" % i
  5. fig,ax = plt.subplots(2,2,figsize=[8,8])
  6. ax[0,0].imshow(imgs[i],cmap='gray')
  7. ax[0,1].imshow(lungmask[i],cmap='gray')
  8. ax[1,0].imshow(imgs[i]*lungmask[i],cmap='gray')
  9. plt.show()
  10. raw_input("hit enter to cont : ")

Dice系数作为分割的成本函数

我们将要使用的网络是教程开端提到的U-net,使用的是keras框架来构建。 损失函数是Dice系数,链接点击打开链接

比较了预测和实际的节点掩膜。

以下代码片段全部取自LUNA_train_unet.py

损失函数如下:

[html] view plaincopy
  1. smooth = 1.
  2. # Tensorflow version for the model
  3. def dice_coef(y_true, y_pred):
  4. y_true_f = K.flatten(y_true)
  5. y_pred_f = K.flatten(y_pred)
  6. intersection = K.sum(y_true_f * y_pred_f)
  7. return (2. * intersection + smooth) / (K.sum(y_true_f) + K.sum(y_pred_f) + smooth)
  8. def dice_coef_loss(y_true, y_pred):
  9. return -dice_coef(y_true, y_pred)

该损失函数类似于用于评估该网络最初编写的超声神经分割挑战的度量(再次参见本教程开头的链接)。

载入分割器

函数调用命令

[html] view plaincopy
  1. model = get_unet()
  2. model_checkpoint = ModelCheckpoint('unet.hdf5', monitor='loss', save_best_only=True)

将编译并返回模型,并告诉keras在checkpoints保存模型的权重。 如果您想从以前的训练结果加载最佳权重,或使用本教程回购中包含的权重,请使用下面这行命令加载权重文件

[html] view plaincopy
  1. model.load_weights('unet.hdf5')

训练分割器

从命令行调用LUNA_train_unet.py系统将尝试从当前目录加载一个unet.hdf5文件,并在脚本中根据下面这行命令设置的参数进行训练并测试。

[html] view plaincopy
  1. model.fit(imgs_train, imgs_mask_train, batch_size=2, nb_epoch=20,
  2. verbose=1, shuffle=True,callbacks=[model_checkpoint])

测试代码如下

[html] view plaincopy
  1. num_test = len(imgs_test)
  2. imgs_mask_test = np.ndarray([num_test,1,512,512],dtype=np.float32)
  3. for i in range(num_test):
  4. imgs_mask_test[i] = model.predict([imgs_test[i:i+1]], verbose=0)[0]
  5. np.save('masksTestPredicted.npy', imgs_mask_test)
  6. mean = 0.0
  7. for i in range(num_test):
  8. mean+=dice_coef_np(imgs_mask_test_true[i,0], imgs_mask_test[i,0])
  9. mean/=num_test
  10. print("Mean Dice Coeff : ",mean)

model.predict()函数操作一次可以测试多个样本,但因为我们可以快速重载GPU,我们循环测试各个样本。

本教程的最终结果是通过GPU机器,使用TitanX训练得到的。 对于家用GPU计算基准测试,个人设置了GTX970,我们可以在大约一个小时内运行20个epoch,训练集大小为320,批量大小为2。 我们开始约3个小时的训练后,我们就开始得到相对合理的结节掩膜预测结果,损失值大约0.3 。

这里给出了从患者扫描获取的三个切片的一个分割示例。 完整的圆是来自LUNA annotationc.csv文件的“真实”节点掩膜,红色是分割器的预测节点区域。 原始图像在右上方给出。

训练一个识别癌症的分类器

现在,我们已经准备好开始使用我们上一节中的图像分割来训练分类器以生成特征。
The Data Science Bowl训练数据集必须被放到分割器进行分割,这可以通过重用用于处理LUNA数据的代码来完成。 但是存在两点区别:
首先,DSB数据采用dicom格式,可以使用pydicom模块进行读取

[html] view plaincopy
  1. import dicom
  2. dc = dicom.read_file(filename)
  3. img = dc.pixel_array

其次,为了在扫描中定位节点,扫描的每一层都必须通过分割器运行,因此每个层也必须进行图像处理以对ROI进行掩膜运算。 这是一个非常耗时的过程。

基于结节特征的简单分类器

我们首先描述结节图的一些特征,并将它们放入特征向量中,这些特征向量可以用于分类。 我们选取特征并不详尽,它的目的在于说明如何发现可用于识别结节所在区域的度量。

我们希望您可以添加一些新功能,探索卷积模型,直接从感兴趣的区域提取特征。 我们已经包括了一些关于图像中的平均尺寸,形态和位置的特征,用于建模。

[html] view plaincopy
  1. def getRegionMetricRow(fname = "nodules.npy"):
  2. seg = np.load(fname)
  3. nslices = seg.shape[0]
  4. #metrics
  5. totalArea = 0.
  6. avgArea = 0.
  7. maxArea = 0.
  8. avgEcc = 0.
  9. avgEquivlentDiameter = 0.
  10. stdEquivlentDiameter = 0.
  11. weightedX = 0.
  12. weightedY = 0.
  13. numNodes = 0.
  14. numNodesperSlice = 0.
  15. # do not allow any nodes to be larger than 10% of the pixels to eliminate background regions
  16. maxAllowedArea = 0.10 * 512 * 512
  17. areas = []
  18. eqDiameters = []
  19. for slicen in range(nslices):
  20. regions = getRegionFromMap(seg[slicen,0,:,:])
  21. for region in regions:
  22. if region.area > maxAllowedArea:
  23. continue
  24. totalArea += region.area
  25. areas.append(region.area)
  26. avgEcc += region.eccentricity
  27. avgEquivlentDiameter += region.equivalent_diameter
  28. eqDiameters.append(region.equivalent_diameter)
  29. weightedX += region.centroid[0]*region.area
  30. weightedY += region.centroid[1]*region.area
  31. numNodes += 1
  32. weightedX = weightedX / totalArea
  33. weightedY = weightedY / totalArea
  34. avgArea = totalArea / numNodes
  35. avgEcc = avgEcc / numNodes
  36. avgEquivlentDiameter = avgEquivlentDiameter / numNodes
  37. stdEquivlentDiameter = np.std(eqDiameters)
  38. maxArea = max(areas)
  39. numNodesperSlice = numNodes*1. / nslices
  40. return np.array([avgArea,maxArea,avgEcc,avgEquivlentDiameter,\
  41. stdEquivlentDiameter, weightedX, weightedY, numNodes, numNodesperSlice])
  42. def getRegionFromMap(slice_npy):
  43. thr = np.where(slice_npy > np.mean(slice_npy),0.,1.0)
  44. label_image = label(thr)
  45. labels = label_image.astype(int)
  46. regions = regionprops(labels)
  47. return regions
  48. import pickle
  49. def createFeatureDataset(nodfiles=None):
  50. if nodfiles == None:
  51. noddir = "/home/jmulholland/NLST_nodules/"
  52. nodfiles = glob(noddir +"*npy")
  53. # dict with mapping between truth and
  54. truthdata = pickle.load(open("/home/sander/truthdict.pkl",'r'))
  55. numfeatures = 9
  56. feature_array = np.zeros((len(nodfiles),numfeatures))
  57. truth_metric = np.zeros((len(nodfiles)))
  58. for i,nodfile in enumerate(nodfiles):
  59. patID = nodfile.split("_")[2]
  60. truth_metric[i] = truthdata[int(patID)]
  61. feature_array[i] = getRegionMetricRow(nodfile)
  62. np.save("dataY.npy", truth_metric)
  63. np.save("dataX.npy", feature_array)

一旦我们创建了特征向量,我们将它们加载到一些简单的分类模型中,看看实验结果。 我们选择随机森林和XGBoost基于我们的特征工程创建一些模型。

[html] view plaincopy
  1. from sklearn import cross_validation
  2. from sklearn.cross_validation import StratifiedKFold as KFold
  3. from sklearn.metrics import classification_report
  4. from sklearn.ensemble import RandomForestClassifier as RF
  5. import xgboost as xgb
  6. X = np.load("dataX.npy")
  7. Y = np.load("dataY.npy")
  8. kf = KFold(Y, n_folds=3)
  9. y_pred = Y * 0
  10. for train, test in kf:
  11. X_train, X_test, y_train, y_test = X[train,:], X[test,:], Y[train], Y[test]
  12. clf = RF(n_estimators=100, n_jobs=3)
  13. clf.fit(X_train, y_train)
  14. y_pred[test] = clf.predict(X_test)
  15. print classification_report(Y, y_pred, target_names=["No Cancer", "Cancer"])
  16. print("logloss",logloss(Y, y_pred))
  17. # All Cancer
  18. print "Predicting all positive"
  19. y_pred = np.ones(Y.shape)
  20. print classification_report(Y, y_pred, target_names=["No Cancer", "Cancer"])
  21. print("logloss",logloss(Y, y_pred))
  22. # No Cancer
  23. print "Predicting all negative"
  24. y_pred = Y*0
  25. print classification_report(Y, y_pred, target_names=["No Cancer", "Cancer"])
  26. print("logloss",logloss(Y, y_pred))
  27. # try XGBoost
  28. print ("XGBoost")
  29. kf = KFold(Y, n_folds=3)
  30. y_pred = Y * 0
  31. for train, test in kf:
  32. X_train, X_test, y_train, y_test = X[train,:], X[test,:], Y[train], Y[test]
  33. clf = xgb.XGBClassifier(objective="binary:logistic")
  34. clf.fit(X_train, y_train)
  35. y_pred[test] = clf.predict(X_test)
  36. print classification_report(Y, y_pred, target_names=["No Cancer", "Cancer"])
  37. print("logloss",logloss(Y, y_pred))

结果显示了建模的效果

[html] view plaincopy
  1. Random Forest precision recall f1-score support
  2. No Cancer 0.77 0.95 0.85 219 Cancer 0.08 0.02 0.03 64
  3. avg / total 0.61 0.73 0.66 283
  4. ('logloss', 9.1534198755740697) Predicting all positive precision recall f1-score support
  5. No Cancer 0.00 0.00 0.00 219 Cancer 0.23 1.00 0.37 64
  6. avg / total 0.05 0.23 0.08 283
  7. ('logloss', 26.728505803260379) Predicting all negative precision recall f1-score support
  8. No Cancer 0.77 1.00 0.87 219 Cancer 0.00 0.00 0.00 64
  9. avg / total 0.60 0.77 0.68 283
  10. ('logloss', 7.8108893613932242) XGBoost precision recall f1-score support
  11. No Cancer 0.77 0.92 0.84 219 Cancer 0.18 0.06 0.09 64
  12. avg / total 0.64 0.72 0.67 283
  13. ('logloss', 9.5195722669850706)

我们比较随机森林,XGBoost的结果,以及预测患有癌症和预测不患有癌症的两个模型的结果。
结果:

[html] view plaincopy
  1. Random Forest
  2. precision    recall  f1-score   support
  3. No Cancer       0.81      0.97      0.88       463
  4. Cancer       0.13      0.02      0.03       107
  5. avg / total       0.68      0.79      0.72       570
  6. ('logloss', 7.150150893624649)
  7. XGBoost
  8. precision    recall  f1-score   support
  9. No Cancer       0.83      0.86      0.84       463
  10. Cancer       0.27      0.21      0.24       107
  11. avg / total       0.72      0.74      0.73       570
  12. ('logloss', 8.9074570257718957)
  13. Predicting all positive
  14. precision    recall  f1-score   support
  15. No Cancer       0.00      0.00      0.00       463
  16. Cancer       0.19      1.00      0.32       107
  17. avg / total       0.04      0.19      0.06       570
  18. ('logloss', 28.055831025357818)
  19. Predicting all negative
  20. precision    recall  f1-score   support
  21. No Cancer       0.81      1.00      0.90       463
  22. Cancer       0.00      0.00      0.00       107
  23. avg / total       0.66      0.81      0.73       570
  24. ('logloss', 6.4835948671148085)

下一步如何改进? 我们为您提供了一个解决这个问题的框架,将基于深度学习的分割方法与旧的计算机视觉方法结合在一起。 根据这些内容您可以通过更多数据或其他预处理方法来改进u-net模型。 分类片段可以由另一个卷积网络替代,我们普遍将图像作为独立的2D切片来处理,或许您可以考虑更多地利用结节的3d性质。

使用U-Net分割方法进行癌症诊断相关推荐

  1. 视杯和视盘分割及分类方法对青光眼诊断的探讨

    摘要: 在过去几年中,视网膜眼底图像的使用已增加用于诊断视网膜疾病. 青光眼是一种导致眼睛视神经受损的疾病,导致视力下降. 一旦确诊,该病不能完全治疗,但及时检测可进一步控制青光眼的效果. 通常通过分 ...

  2. 【paper 1】2022 一种用于脑内出血血肿体积检测的鲁棒性深度学习分割方法-英文

    A Robust Deep Learning Segmentation Method for Hematoma Volumetric Detection in Intracerebral Hemorr ...

  3. 基于曲率的体素聚类的三维激光雷达点云实时鲁棒分割方法

    点云PCL免费知识星球,点云论文速读. 文章:Curved-Voxel Clustering for Accurate Segmentation of 3D LiDAR Point Clouds wi ...

  4. 基于轮廓调整的SOTA实例分割方法,速度达32.3fps | CVPR 2020

    作者 | VincentLee 来源 | 晓飞的算法工程笔记 介绍 实例分割是许多计算机视觉任务中的重要手段,目前大多数的算法都采用在给定的bbox中进行pixel-wise分割的方法.受snake算 ...

  5. 一种基于分层聚合的3D实例分割方法(ICCV 2021)

    Hierarchical Aggregation for 3D Instance Segmentation (ICCV 2021) 代码地址:https://github.com/hustvl/HAI ...

  6. CVPR2021|深度感知镜面分割方法(RGBD数据)

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 作者丨paopaoslam 来源丨 泡泡机器人SLAM 编辑丨3D视觉工坊 标题:Depth-Awar ...

  7. 自动驾驶领域:一种实时高精度的城市道路场景语义分割方法

    点击上方"3D视觉工坊",选择"星标" 干货第一时间送达 论文下载:https://arxiv.org/pdf/2003.08736.pdf 简介: 近年来,深 ...

  8. 干货丨计算机视觉必读:图像分类、定位、检测,语义分割和实例分割方法梳理(经典长文,值得收藏)

    文章来源:新智元 作者:张皓 [导读]本文作者来自南京大学计算机系机器学习与数据挖掘所(LAMDA),本文直观系统地梳理了深度学习在计算机视觉领域四大基本任务中的应用,包括图像分类.定位.检测.语义分 ...

  9. 基于连通域字符分割的流程_基于改进连通域算法的车牌字符分割方法

    基于改进连通域算法的车牌字符分割方法 朱亚萍,邱锦山,杨成忠 [摘 要] 摘要:为了解决车牌字符分割中不连通汉字分割.粘连字符分割以及 竖直边框干扰等问题,提出了一种基于改进连通域算法的车牌字符分割方 ...

  10. 基于像素聚类的分割方法基于slic的方法_博士论文摘要 | 张荣春:数码影像与TLS点云数据融合提取地质结构面方法研究...

    <测绘学报> 构建与学术的桥梁 拉近与权威的距离 数码影像与TLS点云数据融合提取地质结构面方法研究 张荣春1,2 1.南京邮电大学地理与生物信息学院, 江苏 南京 210023;2.河海 ...

最新文章

  1. Android 插件化原理学习 —— Hook 机制之动态代理
  2. jQuery 五角星评分
  3. mpiBlast安装详解以及使用说明
  4. centos查看网络速率_CentOS 7查看网络带宽使用情况
  5. HTML5中input标签有用的新属性
  6. 女性寒性体质注意事项
  7. MooseFs的使用与操作
  8. 下面不属于使用工具的潜在收益的是哪个_电子商务概论习题集
  9. python将PDF文件转化为图片
  10. 从“一时红”到“持久火”,“网红”农产品如何越向“长红”?
  11. 政府转型迎来大数据时代 打破部门“信息孤岛”
  12. FS68001 全新原装 SOP8 无线充电供电IC 无线充电芯片 无线输电芯片5W方案
  13. 炎炎夏日 已过 ,博客 从新开始
  14. 拼多多怎么设置不包邮?怎么看评价?
  15. Java Bean 转 Map 的巨坑,注意了!!!
  16. 怎么阻止流氓软件在电脑里自动安装软件?
  17. 完善三维全栈产品布局 的卢深视用三维全栈技术点亮2020全球智博会
  18. 图片聚类——k-means算法的python实现
  19. WC2019 冬眠记
  20. Swi-Prolog 数值约束

热门文章

  1. java中构造器的调用顺序
  2. 凸优化第四章凸优化问题 4.1 优化问题
  3. VC Ping IP的类
  4. Python Web开发 Django框架下开发一个博客
  5. hibernate的flush()、refresh()、clear()针对一级缓存的操作的区别
  6. 别被数据分析师骗了!用数据说谎的三种办法
  7. Java专业人员必备工具 优缺点解析中文版
  8. 功率 dbm 和 mw 的换算
  9. [原]工欲善其事,必先利其器,记一次处理Oracle Listener挂掉的处理过程
  10. 秒懂C#通过Emit动态生成代码