栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Python

【生物医学的前沿问题】在MRI扫描中自动识别胃肠病变组织的数学模型

Python 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

【生物医学的前沿问题】在MRI扫描中自动识别胃肠病变组织的数学模型

作者简介: 本文作者系大学统计学专业教师,多年从事统计学的教学科研工作,在随机过程、统计推 断、机器学习领域有深厚的理论积累与应用实践。个人主页

1. 问题描述

在2019年,据估计全球约有500万人被诊断为胃肠癌。这其中有半数患者接受为期1~6周、每天10—15分钟的化疗。在化疗过程中,使用X-射线的放射剂量照射肿瘤而尽量避开正常的胃肠器官。在MRI(磁共振成像)扫描中,为了靶向肿瘤位置而避开正常的胃肠器官,医生必须手工标出胃肠器官的位置。这是一个耗时费力的工作,并且使化疗过程延长15分至1小时,给患者增加了治疗的困难。


开发在MRI扫描中自动识别分割胃肠器官与癌症靶点的算法,将使化疗过程更快速、高效,减轻医生的负担和患者的困难。

2. 数据准备 2.1 数据描述

在这个问题里,我们要分割MRI图像的胃肠器官。图像是16-bit灰质png格式。
数据集的 class有3个不同的值:large bowel, small bowel, stomach.

print(clr.S+"--- train.csv ---"+clr.E)
train = pd.read_csv("../input/uw-madison-gi-tract-image-segmentation/train.csv")

print(clr.S+"shape:"+clr.E, train.shape)
print(clr.S+"Unique ID cases:"+clr.E, train["id"].nunique())
print(clr.S+"Missing Values Column:"+clr.E, train.isna().sum().index[-1])
print("t", clr.S+"with a total missing rows of:"+clr.E, train.isna().sum().values[-1])
print("t", clr.S+"% of missing rows:"+clr.E, 
      len(train[train["segmentation"].isna()==False]), "n")

print(clr.S+"Sample of train.csv:"+clr.E)
train.sample(5, random_state=26)

2.2 缺失值
# Show a dataframe of missing values
sns.displot(
    data=train.isna().melt(value_name="missing"),
    y="variable",
    hue="missing",
    multiple="fill",
    # Change aspect of the chart
    aspect=3,
    height=6,
    # Change colors
    palette=[my_colors[5], my_colors[2]], 
    legend=False)

plt.title("- [train.csv] %Perc Missing Values per variable -", size=18, weight="bold")
plt.xlabel("Total Percentage")
plt.ylabel("Dataframe Variable")
plt.legend(["Missing", "Not Missing"]);
plt.show();

print("n")

# Plot 2
plt.figure(figsize=(24,6))

cbar_kws = { 
    "ticks": [0, 1],
}

sns.heatmap(train.isna(), cmap=[my_colors[5], my_colors[2]], cbar_kws=cbar_kws)

plt.title("- [train.csv] Missing Values per observation -", size=18, weight="bold")
plt.xlabel("")
plt.ylabel("Observation")
plt.show();

3. 图像分析 3.1 读取图像
def read_image(path):
    '''Reads and converts the image.
    path: the full complete path to the .png file'''

    # Read image in a corresponding manner
    # convert int16 -> float32
    image = cv2.imread(path, cv2.IMREAD_UNCHANGED).astype('float32')
    # Scale to [0, 255]
    image = cv2.normalize(image, None, alpha = 0, beta = 255, 
                        norm_type = cv2.NORM_MINMAX, dtype = cv2.CV_32F)
    image = image.astype(np.uint8)
    
    return image

3.2 产生 masks 3.2.1 从分割到masks

分割由一列包括不同像素点和长度的数字组成。例如,
‘28094 3 28358 7 28623 9 28889 9 29155 9 29421 9 29687 9 29953 9 30219 9 30484 10 30750 10 31016 10 31282 10 31548 10 31814 10 32081 9 32347 8 32614 6’, 在这个数字列里,
28094是像素的开始点,3是长度。这样,我们可以计算每个分割的结束点

  • endpoint = startpoint + length
def mask_from_segmentation(segmentation, shape):
    '''Returns the mask corresponding to the inputed segmentation.
    segmentation: a list of start points and lengths in this order
    max_shape: the shape to be taken by the mask
    return:: a 2D mask'''

    # Get a list of numbers from the initial segmentation
    segm = np.asarray(segmentation.split(), dtype=int)

    # Get start point and length between points
    start_point = segm[0::2] - 1
    length_point = segm[1::2]

    # Compute the location of each endpoint
    end_point = start_point + length_point

    # Create an empty list mask the size of the original image
    # take onl
    case_mask = np.zeros(shape[0]*shape[1], dtype=np.uint8)

    # Change pixels from 0 to 1 that are within the segmentation
    for start, end in zip(start_point, end_point):
        case_mask[start:end] = 1

    case_mask = case_mask.reshape((shape[0], shape[1]))
    
    return case_mask
# Example
segmentation = '45601 5 45959 10 46319 12 46678 14 47037 16 47396 18 47756 18 48116 19 48477 18 48837 19 
                49198 19 49558 19 49919 19 50279 20 50639 20 50999 21 51359 21 51719 22 52079 22 52440 22 52800 22 53161 21 
                53523 20 53884 20 54245 19 54606 19 54967 18 55328 17 55689 16 56050 14 56412 12 56778 4 57855 7 58214 9 58573 12 
                58932 14 59292 15 59651 16 60011 17 60371 17 60731 17 61091 17 61451 17 61812 15 62172 15 62532 15 62892 14 
                63253 12 63613 12 63974 10 64335 7'

shape = (310, 360)

case_mask = mask_from_segmentation(segmentation, shape)
wandb_mask = []
wandb_mask.append(wandb.Image(case_mask))

plt.figure(figsize=(5, 5))
plt.title("Mask Example:")
plt.imshow(case_mask)
plt.axis("off")
plt.show();

3.2.2 full Mask for each ID

对于每个图像ID, 我们产生三层图像:

  • the first layer: large bowel
  • the second layer: small bowel
  • the third layer: stomach


由原始图像产生full masks, 这些masks提供有关健康组织的有价值信息。

def get_id_mask(ID, verbose=False):
    '''Returns a mask for each case ID. If no segmentation was found, the mask will be empty
    - meaning formed by only 0
    ID: the case ID from the train.csv file
    verbose: True if we want any prints
    return: segmentation mask'''

    # ~~~ Get the data ~~~
    # Get the portion of dataframe where we have ONLY the speciffied ID
    ID_data = train[train["id"]==ID].reset_index(drop=True)

    # Split the dataframe into 3 series of observations
    # each for one speciffic class - "large_bowel", "small_bowel", "stomach"
    observations = [ID_data.loc[k, :] for k in range(3)]


    # ~~~ Create the mask ~~~
    # Get the maximum height out of all observations
    # if max == 0 then no class has a segmentation
    # otherwise we keep the length of the mask
    max_height = np.max([obs.image_height for obs in observations])
    max_width = np.max([obs.image_width for obs in observations])

    # Get shape of the image
    # 3 channels of color/classes
    shape = (max_height, max_width, 3)

    # Create an empty mask with the shape of the image
    mask = np.zeros(shape, dtype=np.uint8)

    # If there is at least 1 segmentation found in the group of 3 classes
    if max_height != 0:
        for k, location in enumerate(["large_bowel", "small_bowel", "stomach"]):
            observation = observations[k]
            segmentation = observation.segmentation

            # If a segmentation is found
            # Append a new channel to the mask
            if pd.isnull(segmentation) == False:
                mask[..., k] = mask_from_segmentation(segmentation, shape)

    # If no segmentation was found skip
    elif max_segmentation == 0:
        mask = None
        if verbose:
            print("None of the classes have segmentation.")
            
    return mask
# Full Example

# Read image
path = '../input/uw-madison-gi-tract-image-segmentation/train/case131/case131_day0/scans/slice_0066_360_310_1.50_1.50.png'
img = read_image(path)

# Get mask
ID = "case131_day0_slice_0066"
mask = get_id_mask(ID, verbose=False)

plot_original_mask(img, mask, alpha=1)

3.3 保存masks 3.3.1 保存 3D masks

我们产生3D masks文件夹, 每个mask对应3层:

  • layer 1: large bowel segmentation
  • layer 2: small bowel segmentation
  • layer 3: stomach segmentation
# Create folder to save masks
os.mkdir("masks_png")

# Get a list of unique ids
unique_ids = train[train["segmentation"].isna()==False]["id"].unique()

for ID in tqdm(unique_ids):
    # Get the mask
    mask = get_id_mask(ID, verbose=False)
    # Write it in folder
    cv2.imwrite(f"masks_png/{ID}.png", mask)

# Save to zip file
shutil.make_archive('zip_masks3D', 'zip', 'masks_png')

# Delete the initial folder
shutil.rmtree('masks_png')
3.3.2 保存 2D masks

定义矩阵:

  • pixels of value 0: meaning no segmentation
  • pixels of value 1: meaning large bowel segmentation
  • pixels of value 2: meaning small bowel segmentation
  • pixels of value 3: meaning stomach segmentation

# Create folder to save masks
os.mkdir("masks2D_png")

# Get unique paths for the 3D masks (generated at step 3.1)
all_mask_paths = train[train["segmentation"].isna()==False]["mask_path"].unique()

for mask_path in tqdm(all_mask_paths):
    # Get file name
    name = mask_path.split("/")[-1]

    # Read mask
    mask = cv2.imread(mask_path)
    # Change masks from 3D to 2D
    mask = mask_3D_to_2D(mask)
    # Write it in folder
    cv2.imwrite(f"masks2D_png/{name}", mask)

# Save to zip file
shutil.make_archive('zip_masks2D', 'zip', 'masks2D_png')

# Delete the initial folder
shutil.rmtree('masks2D_png')

完

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/993993.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号