基于全卷积网络的彩色显微图像光照不均匀校正

news2024/9/20 18:37:18

参考:论文英文题目:Correction of uneven illumination in color microscopic image based on fully convolutional network

参考论文链接:https://opg.optica.org/oe/fulltext.cfm?uri=oe-29-18-28503&id=457387

 在做显微图像相关任务是,遇到由于显微光源或者光学系统孔阑造成的图像局部阴影问题,也可称作图像渐晕。为了校正这类图像问题,看了一些相关的论文。传统方法的校正总是不太理想颜色偏移啊之类的问题,某类算法可能就针对一定领域,像暗场的光照校正与明场的光照校正方法和原理就完全不同,一般就是估计光照背景,暗场是减去光照背景,明场就是除光照背景。所以就想试试深度学习的方法,有些模型不是端到端的,处理起来还是比较麻烦,接下来看看这个端到端的模型。

图像校正在深度学习领域本质上还是类似于风格迁移。在训练时将调整好的图像作为参考,给模型输入原图让模型去学习这类校正过程。

论文模型

论文模型总体上来说很简单,论文中说是3部。特征编码,特征解码,细节补充。本质就是unet+带残差结构的FCN。

但论文这里对UNet的具体结构进行了改进:

1.图像在输出后模型后采用双线性插值将原始图像缩放到特定大小

2.下采样没有用池化层,而是选择用步长为2的卷积直接将特征图尺寸减半。标准Unet是卷积,relu,在池化下采样,这种结构对于不均匀光照成像有一些缺陷。例如,它使 标准U-Net缺乏全局颜色信息,这导致生成的图像颜色不一致。减少了网络层数并提高了网 络效率。

3.编码也就是下采样阶段,卷积后得到的feturemap通道数没有增加,标准Unet会增加。

4.解码也就是上采样阶段,没有用标准Unet的反卷积上采样,因为通过反卷积重建的高分辨率特征通常 会有“不均匀重叠”,导致在后来重构的图像中出现高频棋盘格伪影或低频伪影。所以这里采用最近邻插值的方法进行上采样,后卷积,避免图像边界的伪影。NN调整大小卷积(最近邻插值与相同卷积)在防止图像伪影方面取得了最佳效果。最重要的是,NN调整大小卷积在保持瓶颈层传递的照明信息方面表现更佳,这被认为有助于预测图像照明分布。

5.在上采样到与输入图像大小一致的featuremap后,来了与原图来了一个跳过卷积。不同于U-Net,我们提出方法中的跳过连接使得特征解码器 模块中的特征图和特征编码器模块中相应位置的特征图(通过ReLU激活)直接相加。通过添加编码 器网络的局部特征,强迫解码器网络预测更多的特征信息,而不是预测特定的语义像素值。这有利于对输入 图像的光分布的表征。

标准U-Net通过使用复制和裁剪操作,在编码器-解码器模块中完成相应通道特征信息的融合。裁剪操作使得编码器-解码器模块中相应的特征图的大小一致。复制操作是相应特征的连接。

6.细节补充模块,5个卷积层重构最终输出图像,中间加了两个skip connection。

卷积核大小一般为3*3大小,并由Relu函数激活。

在最后输出时,我试了以下,relu可加可不加。

7.损失函数使用的时SSIM结构相似性,还有L1损失函数,结构相似性(SSIM)指数是一 种基于感知的度量[56]。它定义了结构信息(即,在空间上彼此接近的像素高度相关)作为独立于照明和对 比度信息的属性,以反映场景中物体的结构。然后,将图像失真建模为照明、对比度和结构的组合,并采用L1来进一步优化重构图像的亮度和颜色。

所以一个深度学习任务,除了模型的输入输出,还需要好的损失函数,光照不均的重建与图像的内容结构,以及亮度有关,所以采用这两个作为损失函数是没啥问题。

数据集

论文中的数据集是自己的,没有公开,通过作者联系邮箱要也没给,那就只能用github上公开的数据集了。

数据集链接:GitHub - pair-kopti/Shading-correction

内容:分为correct(已校正),original(未校正)

这个数据集据说是通过专业人士调整的,质量确实挺高的。Sensor 2020与Slide images下一个就行,建议下第二个,还有下载时最好用git工具下,直接下太大了,下的不完整。

sensor 2020里大小为5.63GB,分类两个大文件夹correct与original,里面包含40个小文件。

每个小文件中大约为100张彩色图片,图片大小为2304*1719,还是蛮大的。

Slide image 中就小一些,1.24GB,也有小文件,大概10个,每个小文件也是包含大约100张图,图片大小同样为2304*1719。

在数据集文件注释中说Slide image是用于深度学习训练的,看自己个人吧,我感觉都能用。

不过这个数据集还是比较单一的,我也试了一下自己拍的图像,还是校正后差的多,还是得需要自己的数据集进行训练,才能用。

论文代码复现

好了,不多说了,下面直接上代码。

首先呢,模型架构

import torch
import torch.nn as nn
import torch.nn.functional as F


class Encoder(nn.Module):
    def __init__(self, in_channel, out_channel):
        super(Encoder, self).__init__()
        # 一层卷积 一层relu 完成下采样
        self.conv_relu = nn.Sequential(
            # 先卷两次
            nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=2, padding=1),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        x = self.conv_relu(x)
        return x


class Decoder(nn.Module):
    def __init__(self, in_channel, out_channel):
        super(Decoder, self).__init__()
        # 相同卷积
        self.conv_relu = nn.Sequential(
            nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True),
            nn.Conv2d(out_channel, out_channel, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        x = self.conv_relu(x)
        x = F.interpolate(x, scale_factor=2, mode='nearest')
        return x


class DS(nn.Module):
    def __init__(self, in_channel, out_channel):
        super(DS, self).__init__()
        self.conv_relu1 = nn.Sequential(
            nn.Conv2d(in_channel, out_channel, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True)
        )
        self.conv_relu2 = nn.Sequential(
            nn.Conv2d(out_channel, out_channel, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True)
        )
        self.conv_relu3 = nn.Sequential(
            nn.Conv2d(out_channel, out_channel, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True)
        )

    def forward(self, x):
        x1 = self.conv_relu1(x)
        x2 = x + self.conv_relu2(x1)
        x3 = x1 + self.conv_relu3(x2)
        return x3


class MICnet(nn.Module):
    def __init__(self):
        super(MICnet, self).__init__()
        self.encode1 = Encoder(3, 64)
        self.encode2 = Encoder(64, 64)
        self.encode3 = Encoder(64, 64)
        self.encode4 = Encoder(64, 64)
        self.encode5 = Encoder(64, 64)

        self.decode1 = Decoder(64, 64)
        self.decode2 = Decoder(64, 64)
        self.decode3 = Decoder(64, 64)
        self.decode4 = Decoder(64, 64)

        self.conv_relu1 = nn.Sequential(
            nn.Conv2d(67, 128, kernel_size=3, stride=1, padding=1),
            nn.ReLU(inplace=True)
        )
        self.conv_relu2 = nn.Sequential(
            nn.Conv2d(128, 3, kernel_size=3, stride=1, padding=1),
        )
        self.DS = DS(128,128)

    def forward(self, x):
        resize = F.interpolate(x, size=(96, 96), align_corners=False, mode='bilinear')
        # 下采样编码
        encode1 = self.encode1(resize)
        encode2 = self.encode2(encode1)
        encode3 = self.encode2(encode2)
        encode4 = self.encode2(encode3)
        encode5 = self.encode2(encode4)
        # 谷底上采样次
        x_mid = F.interpolate(encode5, scale_factor=2, mode='nearest')
        add1 = x_mid + encode4
        decode1 = self.decode1(add1)
        add2 = decode1 + encode3
        decode2 = self.decode2(add2)
        add3 = decode2 + encode2
        decode3 = self.decode3(add3)
        add4 = decode3 + encode1
        decode4 = self.decode4(add4)
        # 双线性插值和输入尺寸一致  编码,解码结束
        midblock1 = F.interpolate(decode4, size=(x.shape[2], x.shape[3]), mode='bilinear', align_corners=False)
        midblock = torch.cat((x, midblock1), dim=1)

        # 做一次conv进入细节补充模块
        conv1 = self.conv_relu1(midblock)
        ds = self.DS(conv1)
        result = self.conv_relu2(ds)
        return result

模型大概框架就是这样,DS就是残差那一部分,只不过分开写了,encoder与decoder是Unet的一下层。

2.数据集制作

import os
import glob
import cv2
import numpy as np
import torch
import torch.nn.functional as F
from PIL import Image
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
import random
import matplotlib.pyplot as plt

# 数据目录
ci_set = "./Shading-correction/Slide images/Corrected (EMPTY-ZERO method)/"
or_set = "./Shading-correction/Slide images/Original/"

ci_dirs = glob.glob(ci_set + '*')
or_dirs = glob.glob(or_set + '*')

or_img = []
ci_img = []

for subdir in or_dirs:
    for filename in os.listdir(subdir):
        img_path = subdir + "/" + filename
        # print(img_path)
        if '.jpg' in img_path:
            or_img.append(img_path)

for subdir in ci_dirs:
    for filename in os.listdir(subdir):
        img_path = subdir + "/" + filename
        if '.jpg' in img_path:
            # print(img_path)
            ci_img.append(img_path)

# 检查长度
# print(len(or_img))  # 994
# print(len(ci_img))  # 1000

ci_img_new = []
for i in range(len(or_img)):
    or_img_name = or_img[i]
    or_img_name = or_img_name[43:]
    ci_img_name = ci_set + or_img_name
    # print(ci_img_name)
    ci_img_new.append(ci_img_name)
# print(len(or_img))  # 994
# print(len(ci_img_new))  # 1000


new_width = 1600
new_height = 1200
for i in range(len(or_img)):
    or_img_ad = or_img[i]
    ci_img_ad = ci_img_new[i]

    or_img_1 = cv2.imread(or_img_ad)
    or_img_resize = cv2.resize(or_img_1,(new_width,new_height),interpolation=cv2.INTER_LINEAR)

    ci_img_1 = cv2.imread(ci_img_ad)
    ci_img_resize = cv2.resize(ci_img_1, (new_width, new_height), interpolation=cv2.INTER_LINEAR)

    or_img_filename = os.path.join('./data/or_data',f'{i+1}.jpg')
    ci_img_filename = os.path.join('./data/ci_data', f'{i + 1}.jpg')
    cv2.imwrite(or_img_filename, or_img_resize)
    cv2.imwrite(ci_img_filename, ci_img_resize)

这里因为之前的数据分散在各个文件夹里,我觉得太麻烦就全拿出来,放在一个文件夹了,or是原图,ci是校正后的图,并且保存在了data文件下。

import os
import glob
from PIL import Image
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
import random
import matplotlib.pyplot as plt

or_data = "./data/or_data/"
ci_data = "./data/ci_data/"

ci_img_dirs = glob.glob(ci_data + '*')
or_img_dirs = glob.glob(or_data + '*')

# print(len(ci_img_dirs))
# print(len(or_img_dirs))

# 打乱
or_img_new = []
ci_img_new = []

index = list(range(len(or_img_dirs)))
random.shuffle(index)

for i in range(len(or_img_dirs)):
    or_img_new.append(or_img_dirs[index[i]])
    ci_img_new.append(ci_img_dirs[index[i]])

train_transformer = transforms.Compose([
    transforms.ToTensor()
])
test_transformer = transforms.Compose([
    transforms.ToTensor()
])


class ICdataSet(Dataset):
    def __init__(self, or_img1, ic_img1, transformer):
        self.or_img = or_img1
        self.ic_img = ic_img1
        self.transformer = transformer

    def __getitem__(self, index):
        or_img1 = self.or_img[index]
        ic_img1 = self.ic_img[index]

        or_img1_open = Image.open(or_img1)
        or_img_tensor = self.transformer(or_img1_open)

        ic_img1_open = Image.open(ic_img1)
        ic_img_tensor = self.transformer(ic_img1_open)
        return or_img_tensor, ic_img_tensor

    def __len__(self):
        return len(self.or_img)


# 划分数据集
# 按照train 8:test 1:valid 1  共994  所以 800,100,94
end1 = 800
end2 = -94

train_or = or_img_new[:end1]
train_ci = ci_img_new[:end1]
# print(len(train_ci))
test_or = or_img_new[end1 + 1:end1 + 1 + 100]
test_ci = ci_img_new[end1 + 1:end1 + 1 + 100]
# print(len(test_ci))
valid_or = or_img_new[-94:]
valid_ci = ci_img_new[-94:]
# print(len(valid_ci))

train_data = ICdataSet(train_or, train_ci, train_transformer)
test_data = ICdataSet(test_or, test_ci, test_transformer)

dl_train = DataLoader(train_data, batch_size=4, shuffle=True)
dl_test = DataLoader(test_data, batch_size=4, shuffle=True)

or1,ci1 = next(iter(dl_train))
plt.figure(figsize=(12,8))
for i,(img1,img2) in enumerate(zip(or1[:4],ci1[:4])):
    #zip 打包为元组
    img1 =img1.permute(1,2,0).numpy()
    img2 = img2.permute(1, 2, 0).numpy()
    plt.subplot(2,4,i+1)
    plt.imshow(img1)
    plt.subplot(2,4,i+5)
    plt.imshow(img2)
plt.show()

接下来就是将分好的数据制作成数据集,并使用dataloader加载。最后检查了一下可以不写。

可以看到数据是对应的,上面是原图,下面是校正后的图。这样就可以通过dataloder将图像喂给模型。

4.训练函数

import torch
import torch.nn as nn
import torch.nn.functional as F
import os
import glob
from PIL import Image
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
import random
from pytorch_ssim import ssim
from tqdm import tqdm
import torch.optim.lr_scheduler as lrs



# 损失函数计算
def com_loss_fn(y_pred, y, sigma):
    ssim_loss = 1 - ssim(y_pred,y)
    l1_loss = F.l1_loss(y_pred,y)
    batch_loss = sigma * ssim_loss + (1 - sigma) * l1_loss
    return ssim_loss, l1_loss, batch_loss


# 训练函数
def train_epoch(model, trainloader, testloader, device, optimizer, epoch, sigma, scheduler):
    SSIM_loss = 0
    L1_loss = 0
    Loss = 0
    model.train()
    for x, y in tqdm(trainloader):
        x, y = x.to(device), y.to(device)
        y_pred = model(x)
        # 损失函数
        batch_N = y.size(0)
        ssim_loss,l1_loss,loss = com_loss_fn(y_pred, y, sigma)
        optimizer.zero_grad()
        loss.backward()
        optimizer.step()

        with torch.no_grad():
            SSIM_loss += ssim_loss.item()
            L1_loss += l1_loss.item()
            Loss += loss.item

    mean_SSIM_loss = SSIM_loss / len(trainloader.dataset)
    mean_L1_loss = L1_loss / len(trainloader.dataset)
    mean_Loss = Loss / len(trainloader.dataset)
    scheduler.step()

    test_SSIM_loss = 0
    test_L1_loss = 0
    test_Loss = 0

    model.eval()
    with torch.no_grad():
        for x, y in tqdm(testloader):
            x, y = x.to(device), y.to(device)
            y_pred = model(x)
            #损失计算
            batch_N = y.size(0)
            test_ssim_loss, test_l1_loss, test_loss = com_loss_fn(y_pred, y, sigma)

            test_SSIM_loss += test_ssim_loss.item()
            test_L1_loss += test_l1_loss.item()
            test_Loss += test_loss.item()

    test_mean_SSIM_loss = test_SSIM_loss / len(testloader.dataset)
    test_mean_L1_loss = test_L1_loss / len(testloader.dataset)
    test_mean_Loss = test_Loss / len(testloader.dataset)

    staic_dict = model.state_dict()
    torch.save(staic_dict, './checkpoint/{}_train_loss_{}_test_loss_{}.pth'.format(epoch, mean_Loss, test_mean_Loss))
    torch.save(optimizer.state_dict(), './checkpoint/{}the epoch optimizer.pth'.format(epoch))
    print('Learning rate:', scheduler.get_last_lr()[0])
    print(
        'epoch', epoch,
        'train_ssim_loss', mean_SSIM_loss,
        'train_l1_loss', mean_L1_loss,
        'train_loss', mean_Loss,
        'test_ssim_loss', test_mean_SSIM_loss,
        'test_l1_loss', test_mean_L1_loss,
        'test_loss', test_mean_Loss,
    )
    return mean_SSIM_loss, mean_L1_loss, mean_Loss, test_mean_SSIM_loss, test_mean_L1_loss, test_mean_Loss


# 载入数据制作数据集
or_data = "./data/or_data/"
ci_data = "./data/ci_data/"

ci_img_dirs = glob.glob(ci_data + '*')
or_img_dirs = glob.glob(or_data + '*')

# print(len(ci_img_dirs))
# print(len(or_img_dirs))

# 打乱
or_img_new = []
ci_img_new = []

index = list(range(len(or_img_dirs)))
random.shuffle(index)

for i in range(len(or_img_dirs)):
    or_img_new.append(or_img_dirs[index[i]])
    ci_img_new.append(ci_img_dirs[index[i]])


end1 = 800
end2 = -94

train_or = or_img_new[:end1]
train_ci = ci_img_new[:end1]
# print(len(train_ci))
test_or = or_img_new[end1 + 1:end1 + 1 + 100]
test_ci = ci_img_new[end1 + 1:end1 + 1 + 100]
# print(len(test_ci))
valid_or = or_img_new[-94:]
valid_ci = ci_img_new[-94:]
# 将valid信息写进txt文件备用:
valid_or_file = open('valid_or.txt', 'w')
for item in valid_or:
    valid_or_file.write(str(item) + '\n')
valid_or_file.close()

valid_ci_file = open('valid_ci.txt', 'w')
for item in valid_ci:
    valid_ci_file.write(str(item) + '\n')
valid_ci_file.close()

# print(len(valid_ci))

train_transformer = transforms.Compose([
    transforms.ToTensor()
])
test_transformer = transforms.Compose([
    transforms.ToTensor()
])

train_data = ICdataSet(train_or, train_ci, train_transformer)
test_data = ICdataSet(test_or, test_ci, test_transformer)

dl_train = DataLoader(train_data, batch_size=4, shuffle=True)
dl_test = DataLoader(test_data, batch_size=4, shuffle=True)

# 主要参数
model = MICnet()
device = 'cpu'
model.to(device)
optimizer = torch.optim.Adam(model.parameters(), lr=0.0001)
sigma = 0.4
scheduler = lrs.StepLR(optimizer, step_size=100, gamma=0.9)

epochs = 120
for epoch in range(epochs):
    train_epoch(model, dl_train, dl_test, device, optimizer, epoch, sigma, scheduler)

这里损失函数ssim这里调了一个包pytorch_ssim。

需要的在这里取链接:https://pan.baidu.com/s/1kirMcwq_zsBjH7acaaXHAw?pwd=gcxy 
提取码:gcxy

还有就是制作数据集时是打乱再分成train,test,vaild,为了防止训练后不知道那个是valid,挣了两个txt,一个存放valid_ci,校正后的图像名地址,还有valid_or原图名和地址,在后面模型验证时调用。

最后模型验证:

import torch
import torch.nn as nn
import torch.nn.functional as F
import os
import glob
from PIL import Image
from torch.utils.data import Dataset, DataLoader
from torchvision import transforms
import random
from pytorch_ssim import ssim
from tqdm import tqdm
import torch.optim.lr_scheduler as lrs
from model_train import MICnet,ICdataSet
import matplotlib.pyplot as plt


#从valid_ci与valid_or,读取验证集文件地址
with open('valid_or.txt','r') as valid_or_dir:
    valid_or_dirs = valid_or_dir.readlines()
#去除换行符
valid_or_dirs = [valid_or_dir.strip() for valid_or_dir in valid_or_dirs]
print(valid_or_dirs)

with open('valid_ci.txt','r') as valid_ci_dir:
    valid_ci_dirs = valid_ci_dir.readlines()
#去除换行符
valid_ci_dirs = [valid_ci_dir.strip() for valid_ci_dir in valid_ci_dirs]
print(valid_ci_dirs)

valid_transformer = transforms.Compose([
    transforms.ToTensor()
])

valid_data = ICdataSet(valid_or_dirs,valid_ci_dirs,valid_transformer)

dl_valid = DataLoader(valid_data,batch_size=1,shuffle=False)

or_img,ci_img = next(iter(dl_valid))
or_img = or_img.to('cuda')

model = MICnet()
start_dict = torch.load('')
model = model.to('cuda')
model.eval()

pred_ci_img = model(or_img)
print(pred_ci_img.shape)
pred_ci_img = pred_ci_img.squeeze(0)


plt.figure(figsize=(20, 20))
plt.subplot(1,2,1)
plt.imshow(or_img.permute(1, 2, 0).cpu().numpy())
plt.subplot(1,2,2)
plt.imshow(pred_ci_img.permute(1, 2, 0).cpu().numpy())


我的配置是3090,24G,输入的图像得resize成1000左右大小才能跑,大图跑起来非常慢,还容易爆显卡。所以在训练时resize的图像小一点好跑。

这篇论文最大作用就是应用于图像拼接场景。但是模型泛化能力不够,如果要用还是需要自己做数据集,但图像渐晕校正这一块,就是因为没有好的校正算法才想求助于深度学习的。所以还是得靠发展。

目前有几个好的思路:

将后面的FCN全卷积换成深度可分离卷积,减少参数量,因为这个模型参数量太大了,大图像进模型就跑不了,尤其是显微图像,一般都2000左右了。

试试vit transformer,就是一种编码解码结构。挺感叹的,深度学习发展至今起始很短,尤其是15年后开始蓬勃发展,一年一个大成果,各种模型网络井喷式出现,需要学习的也越来越多,目前检测,分割都已经到头了,大模型一出来解决了这种小模型泛化能力不强的问题,最后归根到底还是堆参数,模型才能进化,具有涌现的功能。

虽然但是,想要入门的同学还是可以大概看看学学,然后找一个好方向是最好的。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

离散化学习笔记(超详细)

离散化学习笔记 什么是离散化 对于“什么是离散化”,搜索帖子你会发现有各种说法,比如“排序后处理”、“对坐标的近似处理”等等。哪个是对的呢?哪个都对。关键在于,这需要一些例子和不少的讲解才能完全解释清楚。 离散化是程序…

微服务篇之分布式系统理论

一、CAP定理 1.什么是CAP 1998年,加州大学的计算机科学家 Eric Brewer 提出,分布式系统有三个指标: 1. Consistency(一致性)。 2. Availability(可用性)。 3. Partition tolerance &#xff0…

APEX开发过程的一个细节

开发过程中发现有一些特殊代码命名有要求 比如 代码: select "project_id",null LINK_CLASS,apex_page.get_url(p_items > P201_PROJECT_ID, p_values > "project_id") LINK,null ICON_CLASS,null LINK_ATTR,null ICON_COLOR_CLASS,cas…

Sora----打破虚实之间的最后一根枷锁----这扇门的背后是人类文明的晟阳还是最后的余晖

目录 一.Sora出道即巅峰 二.为何说Sora是该领域的巨头 三.Sora无敌的背后究竟有怎样先进的处理技术 1.Spacetime Latent Patches 潜变量时空碎片,建构视觉语言系统 2.扩散模型与Diffusion Transformer,组合成强大的信息提取器 3.DiT应用于潜变量时…

【python 的各种模块】(10) 在python3使用turtle 模块画图

目录 1 在anaconda里用python3安装turtle 1.1 因为turtle 本来是适应python2的,所以直接安装报错 1.2 准备好手动下载,官网下载安装包 1.2.1 去官方手册看了下,其实是支持python3的 1.2.2 官网下载,手动安装 1.3 解决办法&…

【Spring】IoC容器 控制反转 与 DI依赖注入 配置类实现版本 第四期

文章目录 基于 配置类 方式管理 Bean一、 配置类和扫描注解二、Bean定义组件三、高级特性:Bean注解细节四、高级特性:Import扩展五、基于注解配置类方式整合三层架构组件总结 基于 配置类 方式管理 Bean Spring 完全注解配置(Fully Annotatio…

MATLAB环境下基于短时傅里叶变换和Rényi熵的脑电信号和语音信号分析

傅里叶变换是不能很好的反映信号在时域的某一个局部范围的频谱特点的,这一点很可惜。因为在许多实际工程中,人们对信号在局部区域的特征是比较关心的,这些特征包含着十分有用的信息。这类信号因为在时域(或者是空间域)上具有突变的非稳定性和…

切比雪夫(最小区域法)圆拟合算法

欢迎关注更多精彩 关注我,学习常用算法与数据结构,一题多解,降维打击。 本期话题:切比雪夫(最小区域法)直线拟合算法 相关背景和理论 点击前往 主要介绍了应用背景和如何转化成线性规划问题 圆拟合输入和…

WordPress使用

WordPress功能菜单 仪表盘 可以查看网站基本信息和内容。 文章 用来管理文章内容,分类以及标签。编辑文章以及设置分类标签,分类和标签可以被添加到 外观-菜单 中。 分类名称自定义;别名为网页url链接中的一部分,最好别设置为中文…

Uniapp + VUE3.0 实现双向滑块视频裁剪效果

效果图 <template><view v-if"info" class"all"><video:src"info.videoUrl"class"video" id"video" :controls"true" object-fit"fill" :show-fullscreen-btn"false"play-btn…

极电电子WMS项目顺利验收,盘古信息助推新能源车企数字化转型

近年来&#xff0c;中国新能源汽车产销持续保持着较高增速&#xff0c;产销总量连续9年位居全球第一。 在产销高涨的背后&#xff0c;新能源汽车行业“内卷”现象也日益加剧&#xff0c;“配置战”、“价格战”等愈发激烈&#xff0c;驱动车企提高自身竞争力&#xff0c;以抢占…

基于AdaBoost算法的情感分析研究-微博情感分析-文本分类

基于AdaBoost算法的情感分析研究 摘 要 随着互联网的快速发展&#xff0c;各类社交媒体平台如微信、QQ等也与日俱增&#xff0c;而微博更是集成了传统网站、论坛、博客等的优点&#xff0c;并加上了人与人之间的互动性、关系亲密程度等多种智能算法&#xff0c;并以简练的形式…

华清远见嵌入式学习——驱动开发——day9

目录 作业要求&#xff1a; 作业答案&#xff1a; 代码效果&#xff1a; ​编辑 Platform总线驱动代码&#xff1a; 应用程序代码&#xff1a; 设备树配置&#xff1a; 作业要求&#xff1a; 通过platform总线驱动框架编写LED灯的驱动&#xff0c;编写应用程序测试&…

Docker容器故障排查与解决方案

Docker是一种相对使用较简单的容器&#xff0c;我们可以通过以下几种方式获取信息&#xff1a; 1、通过docker run执行命令&#xff0c;或许返回信息 2、通过docker logs 去获取日志&#xff0c;做有针对性的筛选 3、通过systemctl status docker查看docker服务状态 4、通过…

React学习——快速上手

文章目录 初步模块思维 初步 https://php.cn/faq/400956.html 1、可以手动使用npm来安装各种插件&#xff0c;来从头到尾自己搭建环境。 如&#xff1a; npm install react react-dom --save npm install babel babel-loader babel-core babel-preset-es2015 babel-preset-rea…

一休哥助手网页版如何使用

一休哥助手网页版可以使用GPT4提问了&#xff0c;具体操作流程如下&#xff1a; 1.登录网页版一休哥助手&#xff08;首次打开页面时&#xff0c;初始化久一点&#xff0c;请耐心等一下&#xff09; https://www.fudai.fun 2.登录后就可以使用GPT4了 3.你还可以自定义系统角色…

备战蓝桥杯---基础算法刷题1

最近在忙学校官网上的题&#xff0c;就借此记录分享一下有价值的题&#xff1a; 1.注意枚举角度 如果我们就对于不同的k常规的枚举&#xff0c;复杂度直接炸了。 于是我们考虑换一个角度&#xff0c;我们不妨从1开始枚举因子&#xff0c;我们记录下他的倍数的个数sum个&#…

c++笔记理解

1.封装 &#xff08;1&#xff09;构造函数不是必须在的 可以通过行为修改属性 &#xff08;2&#xff09;private和protected区别在于继承那里要学 &#xff08;3&#xff09;类默认是私有&#xff0c;struct是共有 私有的好处&#xff1a;控制数据的有效性&#xff0c;意…

如何快速提升Lazada和Shopee店铺订单量:自养号测评补单策略详解

Lazada和Shopee&#xff0c;作为东南亚地区领先的电商平台&#xff0c;汇聚了无数卖家和消费者。然而&#xff0c;随着市场竞争的日益激烈&#xff0c;如何有效地推广自己的店铺&#xff0c;成为卖家们亟待解决的问题。本文将深入探讨店铺推广的策略&#xff0c;并分享如何迅速…

百度百科词条在网络推广中的六大作用

也许很多网友都发现了&#xff0c;在网上查资料&#xff0c;百科词条往往是优先展示的。一方面因为百科是搜索引擎自身的平台&#xff0c;另一方面就是因为百科信息权威&#xff0c;网友认可度高。所以企业开展网络营销&#xff0c;百科营销是一块重要阵地。 也有的企业认为百科…