『医学影像』基于Unet+++实现脊柱MRI定位(上)

本文介绍基于Unet+++实现脊柱MRI定位的项目。因手动选择锥体截面耗时易错,项目将3D数据映射为2D,用深度学习定位L3水平中间轴向切片。处理数据集为PNG格式,定义网络、数据读取类,经训练和验证,测试集平均定位误差为4.0。

☞☞☞AI 智能聊天, 问答助手, AI 智能搜索, 免费无限量使用 DeepSeek R1 模型☜☜☜

『医学影像』基于unet+++实现脊柱mri定位(上) - 创想鸟

基于Unet+++实现脊柱MRI定位-(上)

目前AIstudio已经有许多基于Unet的分割项目,本项目主要介绍分割网络的另外的应用场景,希望对大家的研究有所启发。

0.研究动机

在医学领域,经常需要分析患某种疾病后身体脂肪含量的变化,一般通过选择某个锥体的截面来估计全身的脂肪含量。 常规的方法是通过手动从几百张影像中选择需要的切片(一般为L3),这种方法即耗时又枯燥,稍不注意还容易出错。

在选择到目标切片后,随后进行手动分割,然后使用相关公式估计全身的脂肪含量。

切片选择相关的研究大部分都是在3D数据上对所有的锥体进行标注,但是这个任务中不需要其他的锥体的具体位置,而且3D数据对设备的要求更高。

因此,目前的一个解决方案是通过将三维数据映射使用MLP映射到二维,然后使用深度学习进行定位。

一个经典的解决方案如下

『医学影像』基于Unet+++实现脊柱MRI定位(上) - 创想鸟        

1.项目介绍

计算机断层扫描(CT)成像广泛用于研究身体成分,即肌肉和脂肪组织的比例,应用于营养或化疗剂量设计等领域。

特别是,来自固定位置的轴向CT切片通常用于身体成分分析。然而,如果手动进行,从数百张切片中手动选择是非常繁琐的操作。

本项目的目的是从全身或部分身体扫描体积中自动找到L3水平的中间轴向切片。

2.数据集介绍

使用公开数据集—磁共振图像脊柱结构多类别三维自动分割数据集,该数据集是一个分割数据集,数据格式是nii.gz。分割磁共振T2腰椎矢状位,加背景一共20类。

椎体有S、L5、L4、L3、L2、L1、T12、T11、T10、T9,椎间盘有L5/S, L4/L5, L3/L4, L2/L3, L1/L2, T12/L1, T11/T12, T10/T11, T9/T10

我们对该数据集进行二次处理,包括MLP,剪裁等,建立自己的实验数据集。

『医学影像』基于Unet+++实现脊柱MRI定位(上) - 创想鸟        

3.代码实现

3.1 解压数据并导入常用库

In [ ]

# 数据集解压#!unzip  -o data/data81211/train.zip -d /home/aistudio/work/

   In [ ]

#安装 nii处理工具  SimpleITK 和分割工具paddleSeg!pip install SimpleITK!pip install paddleseg!pip install nibabel

   In [ ]

#导入常用库import osimport randomimport numpy as npimport matplotlib.pyplot as pltfrom random import shuffleimport cv2import paddlefrom PIL import Imageimport shutilimport reimport globimport reimport SimpleITK as sitk

   

3.2 将数据处理为PNG格式

使用分割的思路来解决定位问题,多次实验将目标位置宽度设置为7个像素效果最佳。

slices的选择与窗宽窗位需要自己根据数据调整

In [ ]

from PIL import Imagedef read_intensity(path):    sitkImage = sitk.ReadImage(path)    intensityWindowingFilter = sitk.IntensityWindowingImageFilter()    #转换成0到255之间    intensityWindowingFilter.SetOutputMaximum(255)    intensityWindowingFilter.SetOutputMinimum(0)    if 'mask' not in path:        #调窗宽窗位        intensityWindowingFilter.SetWindowMaximum(1900)        intensityWindowingFilter.SetWindowMinimum(-300)    sitkImage = intensityWindowingFilter.Execute(sitkImage)    return sitkImagefilename = r'data//Data_L3Location//'if  not os.path.exists(filename):    os.mkdir(filename)path_ ='work/train/MR/*.nii.gz'dcm_list_ = glob.glob(path_)s_s = 4 # 开始slices位置s_e = 6 # 结束slices位置idx = 0for i,_ in enumerate(dcm_list_):    item = dcm_list_[i]    NUM = re.findall("d+",item)[0]    print(i,idx)    path_mri  ='work/train/MR/Case' + str(NUM) + '.nii.gz'    path_mask ='work/train/Mask/mask_case' + str(NUM) + '.nii.gz'    mri = read_intensity(path_mri)    mask = read_intensity(path_mask)    npdata = sitk.GetArrayFromImage(mri)    npmask = sitk.GetArrayFromImage(mask)    npdata = cv2.flip(np.transpose(npdata[:,:,:],(1,2,0)),0)    npmask = cv2.flip(np.transpose(npmask[:,:,:],(1,2,0)),0)    h,w = np.max(npdata[:,:,s_s:s_e],2).shape    if h<768 or w<696:        continue    else:        scale = 0.3        npdata[:,:int(scale*npdata.shape[1]),:] = 0        npdata[:,int((1-scale)*npdata.shape[1]):,:] = 0        npdata_max = np.max(npdata[:,:,s_s:s_e],2) # 最大值压缩        npdata_mean = np.mean(npdata[:,:,s_s:s_e],2) # 均值压缩        npdata_mix = 0.5*(npdata_max+npdata_mean) # 混合压缩        npmask_ = np.max(npmask[:,:,s_s:s_e],2)        npmask_13 = npmask_.copy()        npmask_14 = npmask_.copy()        # 13 / 14  L3        npmask_13[npmask_ != 13] = 0        npmask_14[npmask_ != 14] = 0        npmask_13[npmask_13 == 13] = 255        npmask_14[npmask_14 == 14] = 255        mid_13 = np.where(np.max(npmask_13,1) == 255)[0].mean() # 获取13的中间行索引        mid_14 = np.where(np.max(npmask_14,1) == 255)[0].mean() # 获取14的中间行索引        mid_index = int((mid_13+mid_14)*0.5) # 获取 L3锥体的中间行索引        # 对数据进行截断        npdata_max = npdata_max[:768,184:696] # 880/2 = 440 - 256 = 184 目的是取中间的512列        npdata_mix = npdata_mix[:768,184:696] # 880/2 = 440 - 256 = 184 目的是取中间的512列        npdata_mean = npdata_mean[:768,184:696] # 880/2 = 440 - 256 = 184 目的是取中间的512列        mask = np.zeros_like(npdata_max)        mask[mid_index-3:mid_index+3,int(scale*mask.shape[1]):int((1-scale)*mask.shape[1])] = 255 # 标注 L3锥体的中间位置                # 对数据两侧进行切除处理        img_ma = Image.fromarray(np.uint8(npdata_max))        img_mi = Image.fromarray(np.uint8(npdata_mix))        img_me = Image.fromarray(np.uint8(npdata_mean))        img_la = Image.fromarray(np.uint8(mask))        img_ma.save(filename+'max_'+str(idx) +'.png')        img_mi.save(filename+'mix_'+str(idx) +'.png')        img_me.save(filename+'mean_'+str(idx) +'.png')        img_la.save(filename+'label_'+str(idx) +'.png')        idx = idx+1

   

3.3 定义数据读取类

训练集与测试集比例为8:2In [ ]

import paddlefrom paddle.io import Datasetimport paddleseg.transforms as Timport matplotlib.image as mpimg # mpimg 用于读取图片import numpy as np# 重写数据读取类class MRILocationDataset(Dataset):    def __init__(self,mode = 'train',transform =None):               label_path_ ='data/Data_L3Location/label_*.png'        self.png_list_ = glob.glob(label_path_)        self.transforms = transform        self.mode = mode        # 选择前80%训练,后20%测试        if self.mode == 'train':            self.png_list_ = self.png_list_[:int(0.8*len(self.png_list_))]        else:            self.png_list_ = self.png_list_[int(0.8*len(self.png_list_)):]    def __getitem__(self, index):        item = self.png_list_[index]        mask = mpimg.imread(item) # 读取和代码处于同一目录下的 lena.png        mix_ = mpimg.imread(item.replace('label','mix'))         max_ = mpimg.imread(item.replace('label','max'))         mean_ = mpimg.imread(item.replace('label','mean'))         mask = np.expand_dims(mask, axis=0)        mix_ = np.expand_dims(mix_, axis=0)        max_ = np.expand_dims(max_, axis=0)        mean_ = np.expand_dims(mean_, axis=0)        data = np.concatenate((mix_,max_,mean_),axis=0)        if self.transforms:            data ,mask= self.transforms(data,mask)                    return data ,mask    def __len__(self):        return len(self.png_list_)

   In [ ]

# 预览数据dataset = MRILocationDataset(mode='train')print('=============train dataset=============')imga, imgb = dataset[4]print(imga.shape,imgb.shape)imga = imga[0]*255imga = Image.fromarray(imga)#当要保存的图片为灰度图像时,灰度图像的 numpy 尺度是 [1, h, w]。需要将 [1, h, w] 改变为 [h, w]imgb = np.squeeze(imgb)plt.figure(figsize=(12, 6))plt.subplot(1,2,1),plt.xticks([]),plt.yticks([]),plt.imshow(imga)plt.subplot(1,2,2),plt.xticks([]),plt.yticks([]),plt.imshow(imgb)plt.show()

       

=============train dataset=============(3, 768, 512) (1, 768, 512)

       

               

3.4 定义unet+++网络

简介

UNet的发展

2006年Hinton大神提出了一种encoder-decoder结构,当时这个encoder-decoder结构提出的主要作用并不是分割,而是压缩图像和去噪声。输入是一幅图,经过下采样的编码,得到一串比原先图像更小的特征,相当于压缩,然后再经过一个解码,理想状况就是能还原到原来的图像。而在2015,基于此拓扑结构的FCN和UNet相继提出,其中UNet的对称结构简单易懂,效果还好,就成为了许多网络改进的范本之一。

医真AI+开放平台 医真AI+开放平台

医真AI+ 医学AI开放平台

医真AI+开放平台 52 查看详情 医真AI+开放平台

来源

ICASSP 2020 paper 《UNet 3+: A full-scale connected unet for medical image segmentation》

设计特点

全尺度连接:

为了弥补UNet和UNet++不能精确分割图像中器官的位置和边界,UNet3+中每一个解码器都结合了全部编码器的特征,这些不同尺度的特征能够获取细粒度的细节和粗粒度的语义。UNet 3+中的每一个解码器层都融合了来自编码器中的小尺度和同尺度的特征图,以及来自解码器的da尺度的特征图,这些特征图捕获了全尺度下的细粒度语义和粗粒度语义。下图表明了第三层解码器的特征图如何构造

『医学影像』基于Unet+++实现脊柱MRI定位(上) - 创想鸟                        

全尺度监督:

在UNet++中,已经实现了深度监督。它对生成的全分辨率特征图进行操作,即 X0,1 、X0,2、 X0,3 、X0,4后面加一个1×1的卷积核,相当于监督每个分支的UNet的输出。与UNet++对每个嵌套的子网络进行监督不同的是,在UNet3+中每一个解码器模块都有一个输出,与ground truth进行比较计算loss,从而实现全尺度的监督

『医学影像』基于Unet+++实现脊柱MRI定位(上) - 创想鸟                        

分类引导模块:

为了防止非器官图像的过度分割,和提高模型的分割精度,作者通过添加一个额外的分类任务来预测输入图像是否有器官,从而实现更精准的分割。具体就是利用最丰富的语义信息,分类结果可以进一步指导每一个切分侧边输出两个步骤。首先,在argmax函数的帮助下,将二维张量转化为{0,1}的单个输出,表示有/没有目标。随后将单个分类输出与侧分割输出相乘。由于二值分类任务的简单性,该模块通过优化二值交叉熵损失函数,轻松获得准确的分类结果,实现了对非目标图像过分割的指导。

『医学影像』基于Unet+++实现脊柱MRI定位(上) - 创想鸟                        

(图源知乎:玖零猴,侵删)

网络结构

与UNet和UNet++相比,UNet3+结合了多尺度特征,重新设计了跳跃连接,并利用多尺度的深度监督,UNet3+提供更少的参数,但可以产生更准确的位置感知和边界增强的分割图

『医学影像』基于Unet+++实现脊柱MRI定位(上) - 创想鸟                

说明

pytorch版本中有UNet3+、用到了深度监督的UNet3+以及分类指导模块的UNet3+,都以在unet.py中转为paddle的版本。具体介绍还是请移步知乎:UNet3+(UNet+++)论文解读

参考项目 https://aistudio.baidu.com/aistudio/projectdetail/1555546In [ ]

import paddleimport paddle.nn as nnimport paddle.nn.functional as Ffrom paddle.nn import initializerdef init_weights(init_type='kaiming'):    if init_type == 'normal':        return paddle.framework.ParamAttr(initializer=paddle.nn.initializer.Normal())    elif init_type == 'xavier':        return paddle.framework.ParamAttr(initializer=paddle.nn.initializer.XavierNormal())    elif init_type == 'kaiming':        return paddle.framework.ParamAttr(initializer=paddle.nn.initializer.KaimingNormal)    else:        raise NotImplementedError('initialization method [%s] is not implemented' % init_type)class unetConv2(nn.Layer):    def __init__(self, in_size, out_size, is_batchnorm, n=2, ks=3, stride=1, padding=1):        super(unetConv2, self).__init__()        self.n = n        self.ks = ks        self.stride = stride        self.padding = padding        s = stride        p = padding        if is_batchnorm:            for i in range(1, n + 1):                conv = nn.Sequential(nn.Conv2D(in_size, out_size, ks, s, p),                                     nn.BatchNorm(out_size),                                     nn.ReLU(), )                setattr(self, 'conv%d' % i, conv)                in_size = out_size        else:            for i in range(1, n + 1):                conv = nn.Sequential(nn.Conv2D(in_size, out_size, ks, s, p),                                     nn.ReLU(), )                setattr(self, 'conv%d' % i, conv)                in_size = out_size        # initialise the blocks        for m in self.children():            m.weight_attr=init_weights(init_type='kaiming')            m.bias_attr=init_weights(init_type='kaiming')    def forward(self, inputs):        x = inputs        for i in range(1, self.n + 1):            conv = getattr(self, 'conv%d' % i)            x = conv(x)        return x'''    UNet 3+'''class UNet_3Plus(nn.Layer):    def __init__(self, in_channels=3, n_classes=1, is_deconv=True, is_batchnorm=True, end_sigmoid=True):        super(UNet_3Plus, self).__init__()        self.is_deconv = is_deconv        self.in_channels = in_channels        self.is_batchnorm = is_batchnorm        self.end_sigmoid = end_sigmoid        filters = [16, 32, 64, 128, 256]        ## -------------Encoder--------------        self.conv1 = unetConv2(self.in_channels, filters[0], self.is_batchnorm)        self.maxpool1 = nn.MaxPool2D(kernel_size=2)        self.conv2 = unetConv2(filters[0], filters[1], self.is_batchnorm)        self.maxpool2 = nn.MaxPool2D(kernel_size=2)        self.conv3 = unetConv2(filters[1], filters[2], self.is_batchnorm)        self.maxpool3 = nn.MaxPool2D(kernel_size=2)        self.conv4 = unetConv2(filters[2], filters[3], self.is_batchnorm)        self.maxpool4 = nn.MaxPool2D(kernel_size=2)        self.conv5 = unetConv2(filters[3], filters[4], self.is_batchnorm)        ## -------------Decoder--------------        self.CatChannels = filters[0]        self.CatBlocks = 5        self.UpChannels = self.CatChannels * self.CatBlocks        '''stage 4d'''        # h2->320*320, hd4->40*40, Pooling 8 times        self.h2_PT_hd4 = nn.MaxPool2D(8, 8, ceil_mode=True)        self.h2_PT_hd4_conv = nn.Conv2D(filters[0], self.CatChannels, 3, padding=1)        self.h2_PT_hd4_bn = nn.BatchNorm(self.CatChannels)        self.h2_PT_hd4_relu = nn.ReLU()        # h2->160*160, hd4->40*40, Pooling 4 times        self.h2_PT_hd4 = nn.MaxPool2D(4, 4, ceil_mode=True)        self.h2_PT_hd4_conv = nn.Conv2D(filters[1], self.CatChannels, 3, padding=1)        self.h2_PT_hd4_bn = nn.BatchNorm(self.CatChannels)        self.h2_PT_hd4_relu = nn.ReLU()        # h3->80*80, hd4->40*40, Pooling 2 times        self.h3_PT_hd4 = nn.MaxPool2D(2, 2, ceil_mode=True)        self.h3_PT_hd4_conv = nn.Conv2D(filters[2], self.CatChannels, 3, padding=1)        self.h3_PT_hd4_bn = nn.BatchNorm(self.CatChannels)        self.h3_PT_hd4_relu = nn.ReLU()        # h4->40*40, hd4->40*40, Concatenation        self.h4_Cat_hd4_conv = nn.Conv2D(filters[3], self.CatChannels, 3, padding=1)        self.h4_Cat_hd4_bn = nn.BatchNorm(self.CatChannels)        self.h4_Cat_hd4_relu = nn.ReLU()        # hd5->20*20, hd4->40*40, Upsample 2 times        self.hd5_UT_hd4 = nn.Upsample(scale_factor=2, mode='bilinear')  # 14*14        self.hd5_UT_hd4_conv = nn.Conv2D(filters[4], self.CatChannels, 3, padding=1)        self.hd5_UT_hd4_bn = nn.BatchNorm(self.CatChannels)        self.hd5_UT_hd4_relu = nn.ReLU()        # fusion(h2_PT_hd4, h2_PT_hd4, h3_PT_hd4, h4_Cat_hd4, hd5_UT_hd4)        self.conv4d_1 = nn.Conv2D(self.UpChannels, self.UpChannels, 3, padding=1)  # 16        self.bn4d_1 = nn.BatchNorm(self.UpChannels)        self.relu4d_1 = nn.ReLU()        '''stage 3d'''        # h2->320*320, hd3->80*80, Pooling 4 times        self.h2_PT_hd3 = nn.MaxPool2D(4, 4, ceil_mode=True)        self.h2_PT_hd3_conv = nn.Conv2D(filters[0], self.CatChannels, 3, padding=1)        self.h2_PT_hd3_bn = nn.BatchNorm(self.CatChannels)        self.h2_PT_hd3_relu = nn.ReLU()        # h2->160*160, hd3->80*80, Pooling 2 times        self.h2_PT_hd3 = nn.MaxPool2D(2, 2, ceil_mode=True)        self.h2_PT_hd3_conv = nn.Conv2D(filters[1], self.CatChannels, 3, padding=1)        self.h2_PT_hd3_bn = nn.BatchNorm(self.CatChannels)        self.h2_PT_hd3_relu = nn.ReLU()        # h3->80*80, hd3->80*80, Concatenation        self.h3_Cat_hd3_conv = nn.Conv2D(filters[2], self.CatChannels, 3, padding=1)        self.h3_Cat_hd3_bn = nn.BatchNorm(self.CatChannels)        self.h3_Cat_hd3_relu = nn.ReLU()        # hd4->40*40, hd4->80*80, Upsample 2 times        self.hd4_UT_hd3 = nn.Upsample(scale_factor=2, mode='bilinear')  # 14*14        self.hd4_UT_hd3_conv = nn.Conv2D(self.UpChannels, self.CatChannels, 3, padding=1)        self.hd4_UT_hd3_bn = nn.BatchNorm(self.CatChannels)        self.hd4_UT_hd3_relu = nn.ReLU()        # hd5->20*20, hd4->80*80, Upsample 4 times        self.hd5_UT_hd3 = nn.Upsample(scale_factor=4, mode='bilinear')  # 14*14        self.hd5_UT_hd3_conv = nn.Conv2D(filters[4], self.CatChannels, 3, padding=1)        self.hd5_UT_hd3_bn = nn.BatchNorm(self.CatChannels)        self.hd5_UT_hd3_relu = nn.ReLU()        # fusion(h2_PT_hd3, h2_PT_hd3, h3_Cat_hd3, hd4_UT_hd3, hd5_UT_hd3)        self.conv3d_1 = nn.Conv2D(self.UpChannels, self.UpChannels, 3, padding=1)  # 16        self.bn3d_1 = nn.BatchNorm(self.UpChannels)        self.relu3d_1 = nn.ReLU()        '''stage 2d '''        # h2->320*320, hd2->160*160, Pooling 2 times        self.h2_PT_hd2 = nn.MaxPool2D(2, 2, ceil_mode=True)        self.h2_PT_hd2_conv = nn.Conv2D(filters[0], self.CatChannels, 3, padding=1)        self.h2_PT_hd2_bn = nn.BatchNorm(self.CatChannels)        self.h2_PT_hd2_relu = nn.ReLU()        # h2->160*160, hd2->160*160, Concatenation        self.h2_Cat_hd2_conv = nn.Conv2D(filters[1], self.CatChannels, 3, padding=1)        self.h2_Cat_hd2_bn = nn.BatchNorm(self.CatChannels)        self.h2_Cat_hd2_relu = nn.ReLU()        # hd3->80*80, hd2->160*160, Upsample 2 times        self.hd3_UT_hd2 = nn.Upsample(scale_factor=2, mode='bilinear')  # 14*14        self.hd3_UT_hd2_conv = nn.Conv2D(self.UpChannels, self.CatChannels, 3, padding=1)        self.hd3_UT_hd2_bn = nn.BatchNorm(self.CatChannels)        self.hd3_UT_hd2_relu = nn.ReLU()        # hd4->40*40, hd2->160*160, Upsample 4 times        self.hd4_UT_hd2 = nn.Upsample(scale_factor=4, mode='bilinear')  # 14*14        self.hd4_UT_hd2_conv = nn.Conv2D(self.UpChannels, self.CatChannels, 3, padding=1)        self.hd4_UT_hd2_bn = nn.BatchNorm(self.CatChannels)        self.hd4_UT_hd2_relu = nn.ReLU()        # hd5->20*20, hd2->160*160, Upsample 8 times        self.hd5_UT_hd2 = nn.Upsample(scale_factor=8, mode='bilinear')  # 14*14        self.hd5_UT_hd2_conv = nn.Conv2D(filters[4], self.CatChannels, 3, padding=1)        self.hd5_UT_hd2_bn = nn.BatchNorm(self.CatChannels)        self.hd5_UT_hd2_relu = nn.ReLU()        # fusion(h2_PT_hd2, h2_Cat_hd2, hd3_UT_hd2, hd4_UT_hd2, hd5_UT_hd2)        self.Conv2D_1 = nn.Conv2D(self.UpChannels, self.UpChannels, 3, padding=1)  # 16        self.bn2d_1 = nn.BatchNorm(self.UpChannels)        self.relu2d_1 = nn.ReLU()        '''stage 1d'''        # h2->320*320, hd1->320*320, Concatenation        self.h2_Cat_hd1_conv = nn.Conv2D(filters[0], self.CatChannels, 3, padding=1)        self.h2_Cat_hd1_bn = nn.BatchNorm(self.CatChannels)        self.h2_Cat_hd1_relu = nn.ReLU()        # hd2->160*160, hd1->320*320, Upsample 2 times        self.hd2_UT_hd1 = nn.Upsample(scale_factor=2, mode='bilinear')  # 14*14        self.hd2_UT_hd1_conv = nn.Conv2D(self.UpChannels, self.CatChannels, 3, padding=1)        self.hd2_UT_hd1_bn = nn.BatchNorm(self.CatChannels)        self.hd2_UT_hd1_relu = nn.ReLU()        # hd3->80*80, hd1->320*320, Upsample 4 times        self.hd3_UT_hd1 = nn.Upsample(scale_factor=4, mode='bilinear')  # 14*14        self.hd3_UT_hd1_conv = nn.Conv2D(self.UpChannels, self.CatChannels, 3, padding=1)        self.hd3_UT_hd1_bn = nn.BatchNorm(self.CatChannels)        self.hd3_UT_hd1_relu = nn.ReLU()        # hd4->40*40, hd1->320*320, Upsample 8 times        self.hd4_UT_hd1 = nn.Upsample(scale_factor=8, mode='bilinear')  # 14*14        self.hd4_UT_hd1_conv = nn.Conv2D(self.UpChannels, self.CatChannels, 3, padding=1)        self.hd4_UT_hd1_bn = nn.BatchNorm(self.CatChannels)        self.hd4_UT_hd1_relu = nn.ReLU()        # hd5->20*20, hd1->320*320, Upsample 16 times        self.hd5_UT_hd1 = nn.Upsample(scale_factor=16, mode='bilinear')  # 14*14        self.hd5_UT_hd1_conv = nn.Conv2D(filters[4], self.CatChannels, 3, padding=1)        self.hd5_UT_hd1_bn = nn.BatchNorm(self.CatChannels)        self.hd5_UT_hd1_relu = nn.ReLU()        # fusion(h2_Cat_hd1, hd2_UT_hd1, hd3_UT_hd1, hd4_UT_hd1, hd5_UT_hd1)        self.conv1d_1 = nn.Conv2D(self.UpChannels, self.UpChannels, 3, padding=1)  # 16        self.bn1d_1 = nn.BatchNorm(self.UpChannels)        self.relu1d_1 = nn.ReLU()        # output        self.outconv1 = nn.Conv2D(self.UpChannels, n_classes, 3, padding=1)        # initialise weights        for m in self.sublayers ():            if isinstance(m, nn.Conv2D):                m.weight_attr = init_weights(init_type='kaiming')                m.bias_attr = init_weights(init_type='kaiming')            elif isinstance(m, nn.BatchNorm):                m.param_attr =init_weights(init_type='kaiming')                m.bias_attr = init_weights(init_type='kaiming')    def forward(self, inputs):        ## -------------Encoder-------------        h2 = self.conv1(inputs)  # h2->320*320*64        h2 = self.maxpool1(h2)        h2 = self.conv2(h2)  # h2->160*160*128        h3 = self.maxpool2(h2)        h3 = self.conv3(h3)  # h3->80*80*256        h4 = self.maxpool3(h3)        h4 = self.conv4(h4)  # h4->40*40*512        h5 = self.maxpool4(h4)        hd5 = self.conv5(h5)  # h5->20*20*1024        ## -------------Decoder-------------        h2_PT_hd4 = self.h2_PT_hd4_relu(self.h2_PT_hd4_bn(self.h2_PT_hd4_conv(self.h2_PT_hd4(h2))))        h2_PT_hd4 = self.h2_PT_hd4_relu(self.h2_PT_hd4_bn(self.h2_PT_hd4_conv(self.h2_PT_hd4(h2))))        h3_PT_hd4 = self.h3_PT_hd4_relu(self.h3_PT_hd4_bn(self.h3_PT_hd4_conv(self.h3_PT_hd4(h3))))        h4_Cat_hd4 = self.h4_Cat_hd4_relu(self.h4_Cat_hd4_bn(self.h4_Cat_hd4_conv(h4)))        hd5_UT_hd4 = self.hd5_UT_hd4_relu(self.hd5_UT_hd4_bn(self.hd5_UT_hd4_conv(self.hd5_UT_hd4(hd5))))        hd4 = self.relu4d_1(self.bn4d_1(self.conv4d_1(            paddle.concat([h2_PT_hd4, h2_PT_hd4, h3_PT_hd4, h4_Cat_hd4, hd5_UT_hd4], 1)))) # hd4->40*40*UpChannels        h2_PT_hd3 = self.h2_PT_hd3_relu(self.h2_PT_hd3_bn(self.h2_PT_hd3_conv(self.h2_PT_hd3(h2))))        h2_PT_hd3 = self.h2_PT_hd3_relu(self.h2_PT_hd3_bn(self.h2_PT_hd3_conv(self.h2_PT_hd3(h2))))        h3_Cat_hd3 = self.h3_Cat_hd3_relu(self.h3_Cat_hd3_bn(self.h3_Cat_hd3_conv(h3)))        hd4_UT_hd3 = self.hd4_UT_hd3_relu(self.hd4_UT_hd3_bn(self.hd4_UT_hd3_conv(self.hd4_UT_hd3(hd4))))        hd5_UT_hd3 = self.hd5_UT_hd3_relu(self.hd5_UT_hd3_bn(self.hd5_UT_hd3_conv(self.hd5_UT_hd3(hd5))))        hd3 = self.relu3d_1(self.bn3d_1(self.conv3d_1(            paddle.concat([h2_PT_hd3, h2_PT_hd3, h3_Cat_hd3, hd4_UT_hd3, hd5_UT_hd3], 1)))) # hd3->80*80*UpChannels        h2_PT_hd2 = self.h2_PT_hd2_relu(self.h2_PT_hd2_bn(self.h2_PT_hd2_conv(self.h2_PT_hd2(h2))))        h2_Cat_hd2 = self.h2_Cat_hd2_relu(self.h2_Cat_hd2_bn(self.h2_Cat_hd2_conv(h2)))        hd3_UT_hd2 = self.hd3_UT_hd2_relu(self.hd3_UT_hd2_bn(self.hd3_UT_hd2_conv(self.hd3_UT_hd2(hd3))))        hd4_UT_hd2 = self.hd4_UT_hd2_relu(self.hd4_UT_hd2_bn(self.hd4_UT_hd2_conv(self.hd4_UT_hd2(hd4))))        hd5_UT_hd2 = self.hd5_UT_hd2_relu(self.hd5_UT_hd2_bn(self.hd5_UT_hd2_conv(self.hd5_UT_hd2(hd5))))        hd2 = self.relu2d_1(self.bn2d_1(self.Conv2D_1(            paddle.concat([h2_PT_hd2, h2_Cat_hd2, hd3_UT_hd2, hd4_UT_hd2, hd5_UT_hd2], 1)))) # hd2->160*160*UpChannels        h2_Cat_hd1 = self.h2_Cat_hd1_relu(self.h2_Cat_hd1_bn(self.h2_Cat_hd1_conv(h2)))        hd2_UT_hd1 = self.hd2_UT_hd1_relu(self.hd2_UT_hd1_bn(self.hd2_UT_hd1_conv(self.hd2_UT_hd1(hd2))))        hd3_UT_hd1 = self.hd3_UT_hd1_relu(self.hd3_UT_hd1_bn(self.hd3_UT_hd1_conv(self.hd3_UT_hd1(hd3))))        hd4_UT_hd1 = self.hd4_UT_hd1_relu(self.hd4_UT_hd1_bn(self.hd4_UT_hd1_conv(self.hd4_UT_hd1(hd4))))        hd5_UT_hd1 = self.hd5_UT_hd1_relu(self.hd5_UT_hd1_bn(self.hd5_UT_hd1_conv(self.hd5_UT_hd1(hd5))))        hd1 = self.relu1d_1(self.bn1d_1(self.conv1d_1(            paddle.concat([h2_Cat_hd1, hd2_UT_hd1, hd3_UT_hd1, hd4_UT_hd1, hd5_UT_hd1], 1)))) # hd1->320*320*UpChannels        d1 = self.outconv1(hd1)  # d1->320*320*n_classes        if self.end_sigmoid:            out = F.sigmoid(d1)        else:            out = d1        return out

   In [ ]

# 模型可视化import numpyimport paddleunet3p = UNet_3Plus(in_channels=3, n_classes=1)model = paddle.Model(unet3p)model.summary((2,3, 768, 512))

       

--------------------------------------------------------------------------- Layer (type)       Input Shape          Output Shape         Param #    ===========================================================================   Conv2D-2      [[2, 3, 768, 512]]   [2, 16, 768, 512]         448        BatchNorm-1   [[2, 16, 768, 512]]   [2, 16, 768, 512]         64           ReLU-1      [[2, 16, 768, 512]]   [2, 16, 768, 512]          0          Conv2D-3     [[2, 16, 768, 512]]   [2, 16, 768, 512]        2,320       BatchNorm-2   [[2, 16, 768, 512]]   [2, 16, 768, 512]         64           ReLU-2      [[2, 16, 768, 512]]   [2, 16, 768, 512]          0         unetConv2-2    [[2, 3, 768, 512]]   [2, 16, 768, 512]          0         MaxPool2D-1   [[2, 16, 768, 512]]   [2, 16, 384, 256]          0          Conv2D-4     [[2, 16, 384, 256]]   [2, 32, 384, 256]        4,640       BatchNorm-3   [[2, 32, 384, 256]]   [2, 32, 384, 256]         128          ReLU-3      [[2, 32, 384, 256]]   [2, 32, 384, 256]          0          Conv2D-5     [[2, 32, 384, 256]]   [2, 32, 384, 256]        9,248       BatchNorm-4   [[2, 32, 384, 256]]   [2, 32, 384, 256]         128          ReLU-4      [[2, 32, 384, 256]]   [2, 32, 384, 256]          0         unetConv2-3   [[2, 16, 384, 256]]   [2, 32, 384, 256]          0         MaxPool2D-2   [[2, 32, 384, 256]]   [2, 32, 192, 128]          0          Conv2D-6     [[2, 32, 192, 128]]   [2, 64, 192, 128]       18,496       BatchNorm-5   [[2, 64, 192, 128]]   [2, 64, 192, 128]         256          ReLU-5      [[2, 64, 192, 128]]   [2, 64, 192, 128]          0          Conv2D-7     [[2, 64, 192, 128]]   [2, 64, 192, 128]       36,928       BatchNorm-6   [[2, 64, 192, 128]]   [2, 64, 192, 128]         256          ReLU-6      [[2, 64, 192, 128]]   [2, 64, 192, 128]          0         unetConv2-4   [[2, 32, 192, 128]]   [2, 64, 192, 128]          0         MaxPool2D-3   [[2, 64, 192, 128]]    [2, 64, 96, 64]           0          Conv2D-8      [[2, 64, 96, 64]]     [2, 128, 96, 64]       73,856       BatchNorm-7    [[2, 128, 96, 64]]    [2, 128, 96, 64]         512          ReLU-7       [[2, 128, 96, 64]]    [2, 128, 96, 64]          0          Conv2D-9      [[2, 128, 96, 64]]    [2, 128, 96, 64]       147,584      BatchNorm-8    [[2, 128, 96, 64]]    [2, 128, 96, 64]         512          ReLU-8       [[2, 128, 96, 64]]    [2, 128, 96, 64]          0         unetConv2-5    [[2, 64, 96, 64]]     [2, 128, 96, 64]          0         MaxPool2D-4    [[2, 128, 96, 64]]    [2, 128, 48, 32]          0          Conv2D-10     [[2, 128, 48, 32]]    [2, 256, 48, 32]       295,168      BatchNorm-9    [[2, 256, 48, 32]]    [2, 256, 48, 32]        1,024         ReLU-9       [[2, 256, 48, 32]]    [2, 256, 48, 32]          0          Conv2D-11     [[2, 256, 48, 32]]    [2, 256, 48, 32]       590,080     BatchNorm-10    [[2, 256, 48, 32]]    [2, 256, 48, 32]        1,024         ReLU-10      [[2, 256, 48, 32]]    [2, 256, 48, 32]          0         unetConv2-6    [[2, 128, 48, 32]]    [2, 256, 48, 32]          0         MaxPool2D-5   [[2, 16, 768, 512]]    [2, 16, 96, 64]           0          Conv2D-12     [[2, 16, 96, 64]]     [2, 16, 96, 64]         2,320      BatchNorm-11    [[2, 16, 96, 64]]     [2, 16, 96, 64]          64           ReLU-11      [[2, 16, 96, 64]]     [2, 16, 96, 64]           0         MaxPool2D-6   [[2, 32, 384, 256]]    [2, 32, 96, 64]           0          Conv2D-13     [[2, 32, 96, 64]]     [2, 16, 96, 64]         4,624      BatchNorm-12    [[2, 16, 96, 64]]     [2, 16, 96, 64]          64           ReLU-12      [[2, 16, 96, 64]]     [2, 16, 96, 64]           0         MaxPool2D-7   [[2, 64, 192, 128]]    [2, 64, 96, 64]           0          Conv2D-14     [[2, 64, 96, 64]]     [2, 16, 96, 64]         9,232      BatchNorm-13    [[2, 16, 96, 64]]     [2, 16, 96, 64]          64           ReLU-13      [[2, 16, 96, 64]]     [2, 16, 96, 64]           0          Conv2D-15     [[2, 128, 96, 64]]    [2, 16, 96, 64]        18,448      BatchNorm-14    [[2, 16, 96, 64]]     [2, 16, 96, 64]          64           ReLU-14      [[2, 16, 96, 64]]     [2, 16, 96, 64]           0         Upsample-1     [[2, 256, 48, 32]]    [2, 256, 96, 64]          0          Conv2D-16     [[2, 256, 96, 64]]    [2, 16, 96, 64]        36,880      BatchNorm-15    [[2, 16, 96, 64]]     [2, 16, 96, 64]          64           ReLU-15      [[2, 16, 96, 64]]     [2, 16, 96, 64]           0          Conv2D-17     [[2, 80, 96, 64]]     [2, 80, 96, 64]        57,680      BatchNorm-16    [[2, 80, 96, 64]]     [2, 80, 96, 64]          320          ReLU-16      [[2, 80, 96, 64]]     [2, 80, 96, 64]           0         MaxPool2D-8   [[2, 16, 768, 512]]   [2, 16, 192, 128]          0          Conv2D-18    [[2, 16, 192, 128]]   [2, 16, 192, 128]        2,320      BatchNorm-17   [[2, 16, 192, 128]]   [2, 16, 192, 128]         64           ReLU-17     [[2, 16, 192, 128]]   [2, 16, 192, 128]          0         MaxPool2D-9   [[2, 32, 384, 256]]   [2, 32, 192, 128]          0          Conv2D-19    [[2, 32, 192, 128]]   [2, 16, 192, 128]        4,624      BatchNorm-18   [[2, 16, 192, 128]]   [2, 16, 192, 128]         64           ReLU-18     [[2, 16, 192, 128]]   [2, 16, 192, 128]          0          Conv2D-20    [[2, 64, 192, 128]]   [2, 16, 192, 128]        9,232      BatchNorm-19   [[2, 16, 192, 128]]   [2, 16, 192, 128]         64           ReLU-19     [[2, 16, 192, 128]]   [2, 16, 192, 128]          0         Upsample-2     [[2, 80, 96, 64]]    [2, 80, 192, 128]          0          Conv2D-21    [[2, 80, 192, 128]]   [2, 16, 192, 128]       11,536      BatchNorm-20   [[2, 16, 192, 128]]   [2, 16, 192, 128]         64           ReLU-20     [[2, 16, 192, 128]]   [2, 16, 192, 128]          0         Upsample-3     [[2, 256, 48, 32]]   [2, 256, 192, 128]         0          Conv2D-22    [[2, 256, 192, 128]]  [2, 16, 192, 128]       36,880      BatchNorm-21   [[2, 16, 192, 128]]   [2, 16, 192, 128]         64           ReLU-21     [[2, 16, 192, 128]]   [2, 16, 192, 128]          0          Conv2D-23    [[2, 80, 192, 128]]   [2, 80, 192, 128]       57,680      BatchNorm-22   [[2, 80, 192, 128]]   [2, 80, 192, 128]         320          ReLU-22     [[2, 80, 192, 128]]   [2, 80, 192, 128]          0        MaxPool2D-10   [[2, 16, 768, 512]]   [2, 16, 384, 256]          0          Conv2D-24    [[2, 16, 384, 256]]   [2, 16, 384, 256]        2,320      BatchNorm-23   [[2, 16, 384, 256]]   [2, 16, 384, 256]         64           ReLU-23     [[2, 16, 384, 256]]   [2, 16, 384, 256]          0          Conv2D-25    [[2, 32, 384, 256]]   [2, 16, 384, 256]        4,624      BatchNorm-24   [[2, 16, 384, 256]]   [2, 16, 384, 256]         64           ReLU-24     [[2, 16, 384, 256]]   [2, 16, 384, 256]          0         Upsample-4    [[2, 80, 192, 128]]   [2, 80, 384, 256]          0          Conv2D-26    [[2, 80, 384, 256]]   [2, 16, 384, 256]       11,536      BatchNorm-25   [[2, 16, 384, 256]]   [2, 16, 384, 256]         64           ReLU-25     [[2, 16, 384, 256]]   [2, 16, 384, 256]          0         Upsample-5     [[2, 80, 96, 64]]    [2, 80, 384, 256]          0          Conv2D-27    [[2, 80, 384, 256]]   [2, 16, 384, 256]       11,536      BatchNorm-26   [[2, 16, 384, 256]]   [2, 16, 384, 256]         64           ReLU-26     [[2, 16, 384, 256]]   [2, 16, 384, 256]          0         Upsample-6     [[2, 256, 48, 32]]   [2, 256, 384, 256]         0          Conv2D-28    [[2, 256, 384, 256]]  [2, 16, 384, 256]       36,880      BatchNorm-27   [[2, 16, 384, 256]]   [2, 16, 384, 256]         64           ReLU-27     [[2, 16, 384, 256]]   [2, 16, 384, 256]          0          Conv2D-29    [[2, 80, 384, 256]]   [2, 80, 384, 256]       57,680      BatchNorm-28   [[2, 80, 384, 256]]   [2, 80, 384, 256]         320          ReLU-28     [[2, 80, 384, 256]]   [2, 80, 384, 256]          0          Conv2D-30    [[2, 16, 768, 512]]   [2, 16, 768, 512]        2,320      BatchNorm-29   [[2, 16, 768, 512]]   [2, 16, 768, 512]         64           ReLU-29     [[2, 16, 768, 512]]   [2, 16, 768, 512]          0         Upsample-7    [[2, 80, 384, 256]]   [2, 80, 768, 512]          0          Conv2D-31    [[2, 80, 768, 512]]   [2, 16, 768, 512]       11,536      BatchNorm-30   [[2, 16, 768, 512]]   [2, 16, 768, 512]         64           ReLU-30     [[2, 16, 768, 512]]   [2, 16, 768, 512]          0         Upsample-8    [[2, 80, 192, 128]]   [2, 80, 768, 512]          0          Conv2D-32    [[2, 80, 768, 512]]   [2, 16, 768, 512]       11,536      BatchNorm-31   [[2, 16, 768, 512]]   [2, 16, 768, 512]         64           ReLU-31     [[2, 16, 768, 512]]   [2, 16, 768, 512]          0         Upsample-9     [[2, 80, 96, 64]]    [2, 80, 768, 512]          0          Conv2D-33    [[2, 80, 768, 512]]   [2, 16, 768, 512]       11,536      BatchNorm-32   [[2, 16, 768, 512]]   [2, 16, 768, 512]         64           ReLU-32     [[2, 16, 768, 512]]   [2, 16, 768, 512]          0         Upsample-10    [[2, 256, 48, 32]]   [2, 256, 768, 512]         0          Conv2D-34    [[2, 256, 768, 512]]  [2, 16, 768, 512]       36,880      BatchNorm-33   [[2, 16, 768, 512]]   [2, 16, 768, 512]         64           ReLU-33     [[2, 16, 768, 512]]   [2, 16, 768, 512]          0          Conv2D-35    [[2, 80, 768, 512]]   [2, 80, 768, 512]       57,680      BatchNorm-34   [[2, 80, 768, 512]]   [2, 80, 768, 512]         320          ReLU-34     [[2, 80, 768, 512]]   [2, 80, 768, 512]          0          Conv2D-36    [[2, 80, 768, 512]]    [2, 1, 768, 512]         721      ===========================================================================Total params: 1,693,537Trainable params: 1,687,009Non-trainable params: 6,528---------------------------------------------------------------------------Input size (MB): 9.00Forward/backward pass size (MB): 8980.50Params size (MB): 6.46Estimated Total Size (MB): 8995.96---------------------------------------------------------------------------

       

{'total_params': 1693537, 'trainable_params': 1687009}

               

3.5 开始训练

In [14]

model =  UNet_3Plus(in_channels=3, n_classes=1)#SEUNet(3,1)# 开启模型训练模式model.train()# 定义优化算法,使用随机梯度下降SGD,学习率设置为0.01scheduler = paddle.optimizer.lr.StepDecay(learning_rate=0.01, step_size=30, gamma=0.1, verbose=False)optimizer = paddle.optimizer.Adam(learning_rate=scheduler, parameters=model.parameters())EPOCH_NUM = 60  # 设置外层循环次数BATCH_SIZE = 2  # 设置batch大小train_dataset =  MRILocationDataset(mode='train')test_dataset =  MRILocationDataset(mode='test')# 使用paddle.io.DataLoader 定义DataLoader对象用于加载Python生成器产生的数据,data_loader = paddle.io.DataLoader(train_dataset, batch_size=BATCH_SIZE, shuffle=False)test_data_loader = paddle.io.DataLoader(test_dataset, batch_size=BATCH_SIZE, shuffle=False)loss_BCEloss = paddle.nn.BCELoss()# 定义外层循环for epoch_id in range(EPOCH_NUM):    # 定义内层循环    for iter_id, data in enumerate(data_loader()):        x, y = data # x 为数据 ,y 为标签        # 将numpy数据转为飞桨动态图tensor形式        x = paddle.to_tensor(x,dtype='float32')        y = paddle.to_tensor(y,dtype='float32')        # 前向计算        predicts = model(x)        # 计算损失        loss = loss_BCEloss(predicts, y)        # 清除梯度        optimizer.clear_grad()        # 反向传播        loss.backward()        # 最小化loss,更新参数        optimizer.step()    scheduler.step()    print("epoch: {}, iter: {}, loss is: {}".format(epoch_id+1, iter_id+1, loss.numpy()))# 保存模型参数,文件名为Unet_model.pdparamspaddle.save(model.state_dict(), 'work/Unet3p_model.pdparams')print("模型保存成功,模型参数保存在Unet3p_model.pdparams中")

   

3.6 模型验证

In [15]

import paddle# 模型验证Error = []# 清理缓存print("开始测试")# 用于加载之前的训练过的模型参数para_state_dict = paddle.load('work/Unet3p_model.pdparams')model =  UNet_3Plus(in_channels=3, n_classes=1)#SEUNet(3,1)model.set_dict(para_state_dict)for iter_id, data in enumerate(test_data_loader()):    x, y = data    # 将numpy数据转为飞桨动态图tensor形式    x = paddle.to_tensor(x)    y = paddle.to_tensor(y)    predicts = model(x)    for i in range(predicts.shape[0]):        predict = predicts[i,:,:,:].cpu().numpy()        label = y[i,:,:,:].cpu().numpy()        inputs = x[i,1,:,:].cpu().numpy()        predict = np.squeeze(predict)        label = np.squeeze(label)        inputs = np.squeeze(inputs)        #当要保存的图片为灰度图像时,灰度图像的 numpy 尺度是 [1, h, w]。需要将 [1, h, w] 改变为 [h, w]        plt.figure(figsize=(18, 6))        plt.subplot(1,3,1),plt.xticks([]),plt.yticks([]),plt.imshow(predict)        plt.subplot(1,3,2),plt.xticks([]),plt.yticks([]),plt.imshow(label)        plt.subplot(1,3,3),plt.xticks([]),plt.yticks([]),plt.imshow(inputs)        plt.show()                index_predict= np.argmax(np.max(predict,1))+3        index_label = np.argmax(np.max(label,1))        print('真实位置:',index_label,'预测位置:',index_predict)        Error.append(np.abs(index_label-index_predict))    breakprint("模型测试集平均定位误差为:",np.mean(Error))

       

开始测试

       

               

真实位置: 416 预测位置: 420

       

               

真实位置: 484 预测位置: 480模型测试集平均定位误差为: 4.0

       

以上就是『医学影像』基于Unet+++实现脊柱MRI定位(上)的详细内容,更多请关注创想鸟其它相关文章!

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如发现本站有涉嫌抄袭侵权/违法违规的内容, 请发送邮件至 chuangxiangniao@163.com 举报,一经查实,本站将立刻删除。
发布者:程序猿,转转请注明出处:https://www.chuangxiangniao.com/p/316051.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
三星S26系列有望率先支持蓝牙6.1 新机或明年3月发布
上一篇 2025年11月5日 07:46:01
谷歌浏览器为什么提示Adobe Flash Player已过期_谷歌浏览器Flash失效原因与替代方案
下一篇 2025年11月5日 07:46:09

相关推荐

  • composer require-dev和require有什么不同_Composer Require与Require-Dev区别解析

    require用于声明项目运行必需的依赖,如框架、数据库组件和第三方SDK,这些包会随项目部署到生产环境;2. require-dev用于声明仅在开发和测试阶段需要的工具,如PHPUnit、PHPStan、Faker等,不会默认部署到生产环境;3. 安装时composer install根据环境决定…

    2026年5月10日
    1000
  • Matplotlib 地图中多类型图例的创建与优化

    Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化Matplotlib 地图中多类型图例的创建与优化

    本教程旨在解决matplotlib地图可视化中,如何在一个图例中同时展示颜色块(如区域分类)和自定义标记(如特定兴趣点)的问题。文章详细介绍了当传统`patch`对象无法正确显示标记时,如何利用`matplotlib.lines.line2d`创建标记图例句柄,并将其与颜色块图例句柄合并,从而生成一…

    2026年5月10日 用户投稿
    100
  • Golang JSON序列化:控制敏感字段暴露的最佳实践

    本教程探讨golang中如何高效控制结构体字段在json序列化时的可见性。当需要将包含敏感信息的结构体数组转换为json响应时,通过利用`encoding/json`包提供的结构体标签,特别是`json:”-“`,可以轻松实现对特定字段的忽略,从而避免敏感数据泄露,确保api…

    2026年5月10日
    000
  • 利用海象运算符简化条件赋值:Python教程与最佳实践

    本文旨在探讨Python中海象运算符(:=)在条件赋值场景下的应用。通过对比传统if/else语句与海象运算符,以及条件表达式,分析海象运算符在简化代码、提高可读性方面的优势与局限性。并通过具体示例,展示如何在列表推导式等场景下合理使用海象运算符,同时强调其潜在的复杂性及替代方案,帮助开发者更好地掌…

    2026年5月10日
    000
  • Debian syslog性能优化技巧有哪些

    提升Debian系统syslog (通常基于rsyslog)性能,关键在于精简配置和高效处理日志。以下策略能有效优化日志管理,提升系统整体性能: 精简配置,高效加载: 在rsyslog配置文件中,仅加载必要的输入、输出和解析模块。 使用全局指令设置日志级别和格式,避免不必要的处理。 自定义模板: 创…

    2026年5月10日
    000
  • 比特币新手教程 比特币交易平台有哪些

    比特币是一种去中心化的数字货币,基于区块链技术实现点对点交易,具有匿名性、有限发行和不可篡改等特点;新手可通过交易所购买,P2P交易获得比特币,常用平台包括Binance、OKX和Huobi;交易流程包括注册账户、实名认证、绑定支付方式、充值法币并下单购买,可选择市价单或限价单;比特币存储方式有交易…

    2026年5月10日
    000
  • c++中的SFINAE技术是什么_c++模板编程中的SFINAE原理与应用

    SFINAE 是“替换失败不是错误”的原则,指模板实例化时若参数替换导致错误,只要存在其他合法候选,编译器不报错而是继续重载决议。它用于条件启用模板、类型检测等场景,如通过 decltype 或 enable_if 控制函数重载,实现类型特征判断。尽管 C++20 引入 Concepts 简化了部分…

    2026年5月10日
    000
  • Go语言mgo查询构建:深入理解bson.M与日期范围查询的正确实践

    本文旨在解决go语言mgo库中构建复杂查询时,特别是涉及嵌套`bson.m`和日期范围筛选的常见错误。我们将深入剖析`bson.m`的类型特性,解释为何直接索引`interface{}`会导致“invalid operation”错误,并提供一种推荐的、结构清晰的代码重构方案,以确保查询条件能够正确…

    2026年5月10日
    100
  • RichHandler与Rich Progress集成:解决显示冲突的教程

    在使用rich库的`richhandler`进行日志输出并同时使用`progress`组件时,可能会遇到显示错乱或溢出问题。这通常是由于为`richhandler`和`progress`分别创建了独立的`console`实例导致的。解决方案是确保日志处理器和进度条组件共享同一个`console`实例…

    2026年5月10日
    000
  • Golang goroutine与channel调试技巧

    使用go run -race检测数据竞争,结合runtime.NumGoroutine监控协程数量,通过pprof分析阻塞调用栈,利用select超时避免永久阻塞,有效排查goroutine泄漏、死锁和数据竞争问题。 Go语言的goroutine和channel是并发编程的核心,但它们也带来了调试上…

    2026年5月10日
    000
  • 《魔兽世界》将于6月11日开启国服回归技术测试

    《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试《魔兽世界》将于6月11日开启国服回归技术测试

    《%ign%ignore_a_1%re_a_1%》官方宣布,将于6月11日开启国服回归技术测试,时间为7天,并称可以在6月内正式开服,玩家们可以访问官网下载战网客户端并预下载“巫妖王之怒”客户端,技术测试详情见下图。 WordAi WordAI是一个AI驱动的内容重写平台 53 查看详情 以上就是《…

    2026年5月10日 用户投稿
    200
  • 使用 Jupyter Notebook 进行探索性数据分析

    Jupyter Notebook通过单元格实现代码与Markdown结合,支持数据导入(pandas)、清洗(fillna)、探索(matplotlib/seaborn可视化)、统计分析(describe/corr)和特征工程,便于记录与分享分析过程。 Jupyter Notebook 是进行探索性…

    2026年5月10日
    000
  • 如何在HTML中插入表单元素_HTML表单控件与输入类型使用指南

    HTML表单通过标签构建,包含action和method属性定义数据提交目标与方式,常用input类型如text、password、email等适配不同输入需求,配合label、required、placeholder提升可用性,结合textarea、select、button等控件实现完整交互,是…

    2026年5月10日
    000
  • 网站标题关键词更新后,搜索引擎为何仍显示旧标题?

    网站标题更新后,搜索引擎为何显示旧标题? 网站SEO优化中,站长常修改网站标题关键词,期望搜索结果显示自定义标题。然而,即使更新标签、meta keywords、meta description和结构化数据中的name属性后,搜索结果仍显示旧标题,这令人费解。本文将对此进行解释。 问题:站长修改了网…

    2026年5月10日
    100
  • 创建指定大小并填充特定数据的Golang文件教程

    本文将介绍如何使用Golang创建一个指定大小的文件,并用特定数据填充它。我们将使用 `os` 包提供的函数来创建和截断文件,从而实现快速生成大文件的目的。示例代码展示了如何创建一个10MB的文件,并将其填充为全零数据。掌握这些方法,可以方便地在例如日志系统或磁盘队列等场景中,预先创建测试文件或初始…

    2026年5月10日
    000
  • Python命令怎样使用profile分析脚本性能 Python命令性能分析的基础教程

    使用Python的cProfile模块分析脚本性能最直接的方式是通过命令行执行python -m cProfile your_script.py,它会输出每个函数的调用次数、总耗时、累积耗时等关键指标,帮助定位性能瓶颈;为进一步分析,可将结果保存为文件python -m cProfile -o ou…

    2026年5月10日
    000
  • 如何插入查询结果数据_SQL插入Select查询结果方法

    如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法如何插入查询结果数据_SQL插入Select查询结果方法

    使用INSERT INTO…SELECT语句可高效插入数据,通过NOT EXISTS、LEFT JOIN、MERGE语句或唯一约束避免重复;表结构不一致时可通过别名、类型转换、默认值或计算字段处理;结合存储过程可提升可维护性,支持参数化与动态SQL。 将查询结果数据插入到另一个表中,可以…

    2026年5月10日 用户投稿
    000
  • 使用 WebCodecs VideoDecoder 实现精确逐帧回退

    本文档旨在解决在使用 WebCodecs VideoDecoder 进行视频解码时,实现精确逐帧回退的问题。通过比较帧的时间戳与目标帧的时间戳,可以避免渲染中间帧,从而提高用户体验。本文将提供详细的解决方案和示例代码,帮助开发者实现精确的视频帧控制。 在使用 WebCodecs VideoDecod…

    2026年5月10日
    000
  • Discord.py 交互按钮超时与持久化解决方案

    本教程旨在解决Discord.py中交互按钮在一段时间后出现“This Interaction Failed”错误的问题。我们将深入探讨视图(View)的超时机制,并提供通过正确设置timeout参数以及利用bot.add_view()方法实现按钮持久化的具体方案,确保您的机器人交互功能稳定可靠,即…

    2026年5月10日
    000
  • Debian Copilot的社区活跃度如何

    debian copilot是codeberg社区维护的ai助手,旨在为debian用户提供服务。尽管搜索结果中没有直接提供关于debian copilot社区支持活跃度的具体数据,但我们可以通过debian社区的整体活跃度和特点来推断其活跃性。 Debian社区的一般情况: Debian拥有详尽的…

    2026年5月10日
    000

发表回复

登录后才能评论
关注微信