【2】Kaggle 医学影像数据读取

news/2024/11/14 12:51:18/文章来源:https://www.cnblogs.com/SXWisON/p/18370592

赛题名称:RSNA 2024 Lumbar Spine Degenerative Classification
中文:腰椎退行性病变分类

kaggle官网赛题链接:https://www.kaggle.com/competitions/rsna-2024-lumbar-spine-degenerative-classification/overview

文章安排


①、如何用python读取dcm/dicom文件
②、基于matplotlib可视化
③、绘制频率分布直方图
④、代码汇总

文件依赖


# requirements.txt
# Python version 3.11.8
torch==2.3.1
torchvision==0.18.1
matplotlib==3.8.4
pydicom==2.4.4
numpy==1.26.4
pip install -r requirements.txt

读取dicom图像并做预处理

概述

本文中采取pydicom包读取dicom文件,其关键代码格式为:

dcm_tensor = pydicom.dcmread(dicom_file)

注意数据集的路径,其在train_images文件下存放了每一患者的数据,对于每一患者包含三张MRI图像,每张MRI图像存放为一个文件夹。
需要注意的是,MRI图像为三维图像(dicom格式),一般习惯性将其每个切片分别保存为一个dcm文件,因此一张dicom图像将被存为一个文件夹【如下图】

我们可以采用如下路径访问该dicom文件:

"./train_images/4003253/702807833"

读取路径


为了读取dicom图像,我们需要写代码读取文件夹中的所有dcm文件

# dicom文件路径
dicom_dir = "./train_images/4003253/702807833"
# 保存所有dcm文件的路径
dicom_files = [os.path.join(dicom_dir, f) for f in os.listdir(dicom_dir) if f.endswith('.dcm')] 
  • os.listdir:返回dicom_dir路径下的所有文件
  • f.endswith('.dcm') :筛选所有dcm格式的文件
  • os.path.join: 将dcm文件名添加到dicom_dir之后
    示意:"./hello"+“1.dcm”->"./hello/1.dcm"

路径排序


这次的kaggle赛题所给的数据集中,文件名的迭代方式为:

1.dcm、2.dcm、...、9.dcm、10.dcm、11.dcm、...

这给我们带来了一定的麻烦,因为在os的文件名排序规则中,首先检索高位字母的ASCII码大小做排序,也就是说10.dcm将被认为是2.dcm前面的文件。
对此,本文采用正则表达式的方式,实现了依据文件名中数字大小排序。

def extract_number(filepath):# 获取文件名(包括扩展名)filename = os.path.basename(filepath)# 提取文件名中的数字部分,假设文件名以数字结尾,如 '1.dcm'match = re.search(r'(\d+)\.dcm$', filename)return int(match.group(1)) if match else float('inf')# 基于数字句柄排序
dicom_files.sort(key=extract_number)

该代码效果如下:

读取图像


为读取dicom图像,我们需要依次读取每一个dcm文件,并将其最终打包为3D tensor,下述代码实现了该功能:

# 创建空列表保存所有dcm文件
dcm_list= []# 迭代每一个文件
for dicom_file in dicom_files:# 读取文件dcm = pydicom.dcmread(dicom_file)# 将其转为numpy格式image_data = dcm.pixel_array.astype(np.float32)# 加入文件列表 dcm_list.append(image_data)# 将图片堆叠为3D张量
tensor_dcm = torch.stack([torch.tensor(image_data) for image_data in dcm_list])

数据预处理


常见的预处理方式有两种,归一化(Normalization)量化(Quantization)

  • 归一化:将数据缩放到某个标准范围内的过程。常见的归一化方法包括最小-最大归一化(Min-Max Normalization)和Z-score标准化(Z-score Normalization),前者将数据归一化至[0,1]范围,后者将数据转化为标准正态分布。本例中采用Min-Max方案。

  • 量化:量化是将数据的值域退化到离散值的过程。常用于减少存储和计算成本,尤其在神经网络模型中。量化通常将浮点数值转换为整数值。量化前一般先进行归一化。

归一化的实现如下:

def norm_tensor(tensor_dciom):# 查找图像的最大值和最小值vmin, vmax = tensor_dciom.min(), tensor_dciom.max()# 归一化tensor_dciom = (tensor_dciom - vmax ) / (max_val - vmin)return tensor_dciom

实现基于method句柄选择预处理方式:

if method == "norm":# 归一化tensor_dcm = norm_tensor(tensor_dcm)
elif method == "uint8":# 归一化tensor_dcm = norm_tensor(tensor_dcm)# 量化tensor_dcm = (tensor_dcm * 255).clamp(0, 255).to(torch.uint8)

绘图


由于dicom图像为三维数据,可视化时我们一般将其在z轴上分为多个切片依次可视化,本文采用的方式是,采用5*5网格可视化至多25个切片。

def show_dciom(tensor_dicom):# 查找图像的最大最小值vmin, vmax = tensor_dicom.min(), tensor_dicom.max()# 创建一个图形窗口fig, axes = plt.subplots(5, 5, figsize=(15, 15))  # 5x5 网格布局count = 0length = tensor_dicom.size()[0]for i in range(25):if count < length:count += 1else:return# 获取当前图像的坐标ax = axes[i // 5, i % 5]# 显示图片ax.imshow(tensor_dicom[i], cmap='gray') # , vmin=vmin, vmax=vmaxax.axis('off')  # 关闭坐标轴plt.tight_layout() # 避免重叠plt.title(f"Layer {i}")plt.show()

这里有一点需要比较注意,在ax.imshow()函数中,我们指定了vmin和vmax参数;这是因为当该参数未被指定时,imshow函数将会自动调整点的亮度,使值最大的点对应255亮度,值最小的点对应0亮度。鉴于相邻切片最大、最小像素值可能存在较大差异,这将使得相邻切片的图像亮度较异常,如下图:

这两张图的左上角区域实际上亮度相近,但从可视化图像来看,存在较大差异,这将对观察带来误解。

可视化频率分布直方图


可视化MRI图像的频率分布直方图在医学影像处理中有重要意义,主要包括以下几个方面:

  • 图像对比度分析:频率分布直方图可以显示MRI图像中不同灰度级别(或像素强度)的分布情况。通过分析直方图的形状和范围,可以了解图像的对比度。例如,直方图的分布范围较广表示图像对比度较高,能够更好地区分不同组织或结构。

  • 图像均衡化:通过直方图均衡化,可以改善图像的对比度,使得低对比度的区域更加清晰。均衡化过程通过重新分配图像中的像素值,使得直方图的分布更加均匀,从而增强图像的视觉效果。

  • 组织分割:频率分布直方图可以帮助确定适当的阈值,以进行图像分割。通过分析直方图,可以选择合适的阈值将不同组织或病变从背景中分离出来。

  • 图像质量评估:直方图分析可以揭示图像的质量问题,例如过暗或过亮的图像,或者图像噪声的影响。通过直方图的形态,可以评估图像是否需要进一步的处理或优化。

在绘制频率分布直方图前,需要先将三维向量展平,本文采用plt.hist函数绘制

def show_hist(tensor_dicom):# 将所有图片的像素值展平为一个一维数组pixel_values = tensor_dicom.numpy().flatten()# 绘制直方图plt.figure(figsize=(10, 6))plt.hist(pixel_values, bins=50, color='gray', edgecolor='black')plt.title('Histogram of All Pixel Values')plt.xlabel('Pixel Value')plt.ylabel('Frequency')plt.grid(True)plt.show()

直方图呈现如下分步,在val=0附近有一高峰,这是因为MRI图像中大部分区域并不存在人体组织,为空值0。
倘若除零以外的点过分集中在较小值(<100),那么很可能是因为MRI图像中出现了一个亮度极大的噪点,使得以该噪点亮度为最值归一化质量较差,对于这种情形,可以用99%分位数代替最大值,并将99%分位数归一化至亮度为200. (比起归一化至255,这将允许亮度最大1%的像素点亮度值有区分)。
本例中图像质量均较高,故不需要做特殊处理。

代码汇总


代码架构

主函数

# main.py
# Import custom utility functions
from utils import read_one_dicom, show_dciom, show_hist# Define the directory containing the DICOM images
dicom_dir = "./train_images/4003253/1054713880"# Read the DICOM image into a tensor with uint8 data type
tensor_dciom = read_one_dicom(dicom_dir, method="uint8")# Display the DICOM image slices in a 5x5 grid layout
show_dciom(tensor_dciom)# Plot the histogram of pixel values from the DICOM image slices
show_hist(tensor_dciom)# Convert the tensor to a NumPy array for further processing or inspection
np_img = tensor_dciom.numpy()

包文件

from .preprocess import read_one_dicomfrom .show import show_dciom
from .show import show_hist

读取&预处理

# preprocess.py
import numpy as np
import torch
import os
import re
import pydicom
from tqdm import tqdmdef norm_tensor(tensor_dciom):"""Normalize the image tensor to the range [0, 1].Args:tensor_dciom (torch.Tensor): Tensor containing image data.Returns:torch.Tensor: Normalized image tensor."""# Calculate the maximum and minimum values of the image tensorvmin, vmax = tensor_dciom.min(), tensor_dciom.max()# Normalize the image tensor to the range [0, 1]tensor_dciom = (tensor_dciom - vmin) / (vmax - vmin)return tensor_dciomdef extract_number(filepath):"""Extract the numeric part from the DICOM filename.Args:filepath (str): Path to the DICOM file.Returns:int: Extracted number from the filename. Returns float('inf') if not found."""# Get the filename (including extension)filename = os.path.basename(filepath)# Extract numeric part from filename, assuming filenames end with digits, e.g., '1.dcm'match = re.search(r'(\d+)\.dcm$', filename)return int(match.group(1)) if match else float('inf')def read_one_dicom(dicom_dir, method = "", bar_title = ""):"""Reads DICOM files from a directory and converts them into a PyTorch tensor.Args:dicom_dir (str): Directory containing DICOM files.method (str): Optional method to process the tensor ('norm' for normalization, 'uint8' for normalization and conversion to uint8).bar_title (str): Optional title for the progress bar.Returns:torch.Tensor: PyTorch tensor containing image data from DICOM files."""# Get all DICOM files and sort them based on numeric part of the filenamedicom_files = [os.path.join(dicom_dir, f) for f in os.listdir(dicom_dir) if f.endswith('.dcm')]    dicom_files.sort(key=extract_number)# Create an empty list to store image datadcm_list = []# Initialize tqdm progress barwith tqdm(total=len(dicom_files), desc='Processing DICOM files', unit='dcm', unit_scale=True, unit_divisor=1000000) as pbar:# Iterate over each DICOM file and read image datafor count, dicom_file in enumerate(dicom_files, start=1):# Read the DICOM filedcm = pydicom.dcmread(dicom_file)# Extract and convert image data to a NumPy arrayimage_data = dcm.pixel_array.astype(np.float32)# Add the image data to the listdcm_list.append(image_data)# Update progress bar descriptionpbar.set_description(bar_title + 'Reading')# Update progress barpbar.update(1)# Convert the list of image data to a PyTorch tensor and stack into a 3D tensortensor_dcm = torch.stack([torch.tensor(image_data) for image_data in dcm_list])if method == "norm":# Normalize the image tensortensor_dcm = norm_tensor(tensor_dcm)elif method == "uint8":# Normalize the image tensortensor_dcm = norm_tensor(tensor_dcm)# Scale the tensor values to the range [0, 255] and convert to uint8 typetensor_dcm = (tensor_dcm * 255).clamp(0, 255).to(torch.uint8)return tensor_dcm

可视化、绘制直方图

# show.py
import numpy as np
import torch
import matplotlib.pyplot as pltdef show_dciom(tensor_dicom):"""Display MRI image slices in a 5x5 grid layout.Parameters:tensor_dicom (torch.Tensor): Tensor containing MRI image slices, expected shape is (N, H, W),where N is the number of slices, and H and W are the height and width of the images."""# Calculate the minimum and maximum pixel values in the tensorvmin, vmax = tensor_dicom.min(), tensor_dicom.max()# Create a figure with a 5x5 grid layoutfig, axes = plt.subplots(5, 5, figsize=(15, 15))  # 5x5 grid layoutcount = 0length = tensor_dicom.size(0)for i in range(25):if count < length:count += 1else:return# Get the current subplot's axisax = axes[i // 5, i % 5]# Display the imageax.imshow(tensor_dicom[count - 1], cmap='gray', vmin=vmin, vmax=vmax)ax.axis('off')  # Hide the axisplt.tight_layout()  # Adjust layout to prevent overlapplt.title(f"Layer {i + 1}")  # Title indicating the last displayed sliceplt.show()def show_hist(tensor_dicom):"""Plot the histogram of pixel values for all MRI image slices.Parameters:tensor_dicom (torch.Tensor): Tensor containing MRI image slices, expected shape is (N, H, W)."""# Flatten all image pixel values into a single 1D arraypixel_values = tensor_dicom.numpy().flatten()# Plot the histogramplt.figure(figsize=(10, 6))plt.hist(pixel_values, bins=50, color='gray', edgecolor='black')plt.title('Histogram of All Pixel Values')plt.xlabel('Pixel Value')plt.ylabel('Frequency')plt.grid(True)plt.show()

下篇预告


讨论本题的解题方法

制作不易,请帮我点一个免费的赞,谢谢!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/784750.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

文章自然润色 API 数据接口

文章自然润色 API 数据接口 ai / 文本处理 基于 AI 的文章润色 专有模型 / 智能纠错。1. 产品功能基于自有专业模型进行 AI 智能润色 对原始内容进行智能纠错 高效的文本润色性能 全接口支持 HTTPS(TLS v1.0 / v1.1 / v1.2 / v1.3); 全面兼容 Apple ATS; 全国多节点 CDN 部…

知名开源工具被用于诈骗,作者无奈清空代码。。

开发者表示,因为自己的开源项目被诈骗份子使用,导致自己被跨省,所以永久删除本项目源代码。小道消息,知名开源下载工具 Aria 的开发者最近删除了整个 GitHub 仓库的源代码,并且在项目介绍文件中留下了这样一段话:开发者表示,因为自己的开源项目被诈骗份子使用,导致自己…

历年高校招生计划数据 API 数据接口

历年高校招生计划数据 API 数据接口 基础数据 / 高校招生,各高校历年招生计划数据,高校招生数据 / 历年计划。1. 产品功能支持历年高校招生计划数据查询; 包含各高校招生计划详细数据; 多维度查询条件支持; 毫秒级查询性能; 全接口支持 HTTPS(TLS v1.0 / v1.1 / v1.2 / …

多输入通道和多输出通道的卷积

假设我们有一个输入特征图,它具有3个输入通道(例如,一个彩色图像的RGB通道),并且我们想要使用一个包含4个卷积核的卷积层来产生4个输出通道。我们将计算中心位置 (2, 2) 的卷积值来展示卷积的过程。 1、输入特征图: 输入特征图具有3个输入通道,每个通道是一个3x3的矩阵。…

电脑自动更新怎么彻底关闭,你知道电脑自动更新怎么彻底关闭的办法吗

彻底关闭电脑自动更新的方法因操作系统而异,但以下是一些常见的解决方案,特别针对Windows 10系统: 一、使用系统设置关闭自动更新 点击屏幕左下角的“开始”按钮,选择“设置”(齿轮形状的图标)。在设置窗口中,找到并点击“更新和安全”选项。 在左侧菜单中选择“Windows…

GC终结标记 SuspendEE 是怎么回事

一:背景 1. 讲故事 写这篇是起源于训练营里有位朋友提到了一个问题,在 !t -special 输出中有一个 SuspendEE 字样,这个字样在 coreclr 中怎么弄的?输出如下:0:000> !t -special ThreadCount: 3 UnstartedThread: 0 BackgroundThread: 2 PendingThread: 0 Dead…

怎么一键清理电脑垃圾,清理垃圾的简单快捷的方法有哪些

一键清理电脑垃圾以及清理垃圾的简单快捷方法主要包括以下几种: 一、使用专业的电脑清理软件 优点:这些软件通常具有强大的扫描和清理能力,能够自动识别并删除系统中的垃圾文件、临时文件、无用注册表项等,同时提供一键清理功能,操作简便快捷。 操作步骤: 下载并安装专业…

ByteHouse案例实践:某销售数据平台如何基于OLAP大幅提升复杂查询效率?

ByteHouse是火山引擎推出的一款定位为OLAP的分析型数据库,基于ClickHouse进行架构升级和优化,在复杂查询层面拥有显著优势。更多技术交流、求职机会,欢迎关注字节跳动数据平台微信公众号,回复【1】进入官方交流群 在现如今激烈的市场竞争中,销售数据是企业下一步市场决策…

使用 OpenTelemetry (OTel) 实现 Elastic RUM (真实用户监控)

本文继续介绍 OpenTelemetry 与 Elastic Observability 的结合,详细讲解了如何使用 Docker Compose 或 Kubernetes 设置 OpenTelemetry 演示。 Elastic 真实用户监控(RUM)捕捉用户与网页浏览器的交互,并从性能角度提供有关“真实用户体验”的详细视图。 Elastic 的 RUM Age…

我们是如何测试数百个页面的

自动化测试是确保软件质量和提供良好用户体验的有效方式。在 Woovi,我们拥有数千个页面,用户与我们的第一次接触通常会通过这些展示我们产品的页面。因此,我们需要确保每个页面都能正常运行。每一个访问我们页面的用户都代表着一个新的潜在客户。 测试页面的挑战 Woovi 的页…

MBR30100CT-ASEMI低压降肖特基MBR30100CT

MBR30100CT-ASEMI低压降肖特基MBR30100CT编辑:ll MBR30100CT-ASEMI低压降肖特基MBR30100CT 型号:MBR30100CT 品牌:ASEMI 封装:TO-220 批号:最新 恢复时间:35ns 最大平均正向电流(IF):30A 最大循环峰值反向电压(VRRM):100V 最大正向电压(VF):0.70V~0.90V 工作温度…

浅谈HTML

html是一种标签语言,用来写前端页面的,通常结合CSS和js来写。 主要用于web开发,B/S架构的系统,所谓B/S其实也是一种特殊的C/S,只不过此时浏览器变成了客户端。 B/S架构:B是browser,S是server C/S架构:C是client,S是server **什么是 HTML?** HTML 是用来描述网页的一种语…