第91步 深度学习图像分割:FCN建模

news2024/11/26 8:20:48

基于WIN10的64位系统演示

一、写在前面

本期,我们继续学习深度学习图像分割系列的另一个模型,FCN (Fully Convolutional Network)

二、FCN

FCN是一种用于图像语义分割的神经网络。与传统的分类网络(如VGG、AlexNet)不同,FCN可以为输入图像中的每个像素生成一个分类标签。

(1)核心特点与组成部分

全卷积化:FCN的名称来源于其结构,它不包含任何全连接层。传统的全连接层被转化为卷积层,使得网络可以处理任意大小的输入图像。

上采样与反卷积:由于连续的卷积和池化操作,特征图的分辨率降低。为了恢复到原始大小,FCN使用反卷积层进行上采样。

跳跃结构:FCN引入了从中间卷积层的跳跃连接,这些连接将不同分辨率的特征图融合,使得模型能够结合深层语义信息和浅层细节信息。

(2)优势

灵活性:由于其全卷积的结构,FCN可以处理任意大小的输入,输出与输入具有相同的空间尺寸的分割图。

端到端训练:FCN可以直接从像素到像素地进行训练,不需要任何中间步骤或后处理。

效率:在推理时,FCN可以非常快速地处理图像,特别是当与现代硬件结合使用时。

简而言之,FCN是语义分割任务的一种开创性模型,它利用全卷积结构和特殊的上采样机制,成功地为每个像素提供了分类标签。

(2)数据源:

来源于公共数据,主要目的是使用FCN分割出电子显微镜下的细胞边缘:

数据分为训练集(train)、训练集的细胞边缘数据(label)以及验证集(test)注意哈,没有提供验证集的细胞边缘数据。因此,后面是算不出验证集的性能参数的。

(2)FCN实战:

这里,我们使用的是Pytorch的FCN预训练模型。

上代码:

(a)数据读取和数据增强

import os
import numpy as np
from skimage.io import imread
from torchvision import transforms
from torch.utils.data import DataLoader, Dataset
from torchvision.models.segmentation import fcn_resnet50
import torch
import torch.nn.functional as F
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc, accuracy_score, recall_score, precision_score, f1_score

# 设置文件路径
data_folder = 'U-net-master\data_set'
train_images_folder = os.path.join(data_folder, 'train')
label_images_folder = os.path.join(data_folder, 'label')
test_images_folder = os.path.join(data_folder, 'test')

train_images = sorted(os.listdir(train_images_folder))
label_images = sorted(os.listdir(label_images_folder))
test_images = sorted(os.listdir(test_images_folder))

# 定义数据集类
class CustomDataset(Dataset):
    def __init__(self, image_paths, mask_paths, transform=None):
        self.image_paths = image_paths
        self.mask_paths = mask_paths
        self.transform = transform

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

    def __getitem__(self, idx):
        image = imread(self.image_paths[idx])
        mask = imread(self.mask_paths[idx])
        mask[mask == 255] = 1
        # Ensure the image has 3 channels
        if len(image.shape) == 2:
            image = np.stack((image,) * 3, axis=-1)        
        sample = {'image': image, 'mask': mask}
        if self.transform:
            sample = self.transform(sample)
        return sample

class ToTensorAndNormalize(object):
    def __init__(self, mean, std):
        self.mean = mean
        self.std = std
        
    def __call__(self, sample):
        image, mask = sample['image'], sample['mask']
        # Swap color axis
        # numpy image: H x W x C
        # torch image: C x H x W
        image = image.transpose((2, 0, 1))
        image = torch.from_numpy(image).float()
        mask = torch.from_numpy(mask).float()

        # Normalize the image
        for t, m, s in zip(image, self.mean, self.std):
            t.sub_(m).div_(s)

        return {'image': image, 'mask': mask}

# Use the new transform for normalization
transform = ToTensorAndNormalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])

# 获取数据集
train_dataset = CustomDataset(
    image_paths=[os.path.join(train_images_folder, img) for img in train_images],
    mask_paths=[os.path.join(label_images_folder, img) for img in label_images],
    transform=transform
)
train_loader = DataLoader(train_dataset, batch_size=8, shuffle=True)

解读:

其他没什么好说的,就是要注意:上述代码的数据需要人工的安排训练集和测试集。严格按照下面格式放置好各个文件,包括文件夹的命名也不要变动:

(b)FCN建模

# 获取FCN模型
device = torch.device('cuda' if torch.cuda.is_available() else 'cpu')
model = fcn_resnet50(pretrained=True).to(device)
# 更改FCN的分类数
model.classifier[4] = torch.nn.Conv2d(512, 2, kernel_size=(1, 1), stride=(1, 1))
model.classifier[4] = model.classifier[4].to(device)

optimizer = torch.optim.Adam(model.parameters(), lr=1e-4)
criterion = torch.nn.CrossEntropyLoss()

def calc_iou(pred, target):
    # Convert prediction to boolean values and flatten
    pred = (pred > 0.5).view(-1)
    target = target.view(-1)
    
    # Calculate intersection and union
    intersection = torch.sum(pred & target)
    union = torch.sum(pred | target)
    
    # Avoid division by zero
    iou = (intersection + 1e-8) / (union + 1e-8)
    
    return iou.item()

# 初始化损失和IoU的历史记录列表
losses_history = []
ious_history = []

# 训练模型
epochs = 100
for epoch in range(epochs):
    model.train()
    running_loss = 0.0
    total_iou = 0.0
    
    for samples in train_loader:
        images = samples['image'].to(device)
        masks = samples['mask'].long().to(device)

        optimizer.zero_grad()
        outputs = model(images)['out']
        loss = criterion(outputs, masks)
        loss.backward()
        optimizer.step()
        running_loss += loss.item()
        
        # Calculate IOU
        pred_masks = F.softmax(outputs, dim=1)[:, 1]
        total_iou += calc_iou(pred_masks, masks)

    avg_loss = running_loss / len(train_loader)
    avg_iou = total_iou / len(train_loader)
    print(f"Epoch {epoch+1}/{epochs}, Loss: {avg_loss}, IOU: {avg_iou}")
    
    # Append to history
    losses_history.append(avg_loss)
ious_history.append(avg_iou)

2分钟不到:

解读:

在PyTorch的torchvision库中,为FCN提供了以下预训练模型:

(a)FCN ResNet50: 使用ResNet50作为骨干网络的FCN模型。

(b)FCN ResNet101: 使用ResNet101作为骨干网络的FCN模型。

这些模型已经在COCO数据集上进行了预训练,并可以用于进行迁移学习或直接用于语义分割任务。可以通过以下方式导入:

import torchvision.models.segmentation as segmentation
model = segmentation.fcn_resnet50(pretrained=True)
# 或者
# model = segmentation.fcn_resnet101(pretrained=True)

这将加载预训练的模型。如果你想进行微调或用于其他分割任务,可以将pretrained参数设置为False。

(c)各种性能指标打印和可视化

###################################误差曲线#######################################

import matplotlib.pyplot as plt

# 设置matplotlib支持中文显示
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 绘制训练损失和IoU
plt.figure(figsize=(12, 5))

# 绘制损失
plt.subplot(1, 2, 1)
plt.plot(losses_history, label='训练损失')
plt.title('损失随迭代次数的变化')
plt.xlabel('迭代次数')
plt.ylabel('损失')
plt.legend()

# 绘制IoU
plt.subplot(1, 2, 2)
plt.plot(ious_history, label='训练IoU')
plt.title('IoU随迭代次数的变化')
plt.xlabel('迭代次数')
plt.ylabel('IoU')
plt.legend()

plt.tight_layout()
plt.show()

直接看结果:

误差和IOU曲线,看起来模型收敛的不错。

##############################评价指标,对于某一个样本#######################################
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc, accuracy_score, recall_score, precision_score, f1_score

def calc_iou(y_true, y_pred):
    intersection = np.logical_and(y_true, y_pred)
    union = np.logical_or(y_true, y_pred)
    return np.sum(intersection) / np.sum(union)

# 从数据集中获取一个样本
sample = train_dataset[0]
sample_img = sample['image'].unsqueeze(0).to(device)
sample_mask = sample['mask'].cpu().numpy()

# 使用模型进行预测
with torch.no_grad():
    model.eval()
    prediction = model(sample_img)['out']

# 取前景类并转为CPU
predicted_mask = prediction[0, 1].cpu().numpy()
predicted_mask = (predicted_mask > 0.5).astype(np.uint8)

# 计算ROC曲线
fpr_train, tpr_train, _ = roc_curve(sample_mask.ravel(), predicted_mask.ravel())

# 计算AUC
auc_train = auc(fpr_train, tpr_train)

# 计算其他评估指标
pixel_accuracy_train = accuracy_score(sample_mask.ravel(), predicted_mask.ravel())
iou_train = calc_iou(sample_mask, predicted_mask)
accuracy_train = accuracy_score(sample_mask.ravel(), predicted_mask.ravel())
recall_train = recall_score(sample_mask.ravel(), predicted_mask.ravel())
precision_train = precision_score(sample_mask.ravel(), predicted_mask.ravel())
f1_train = f1_score(sample_mask.ravel(), predicted_mask.ravel())

# 绘制ROC曲线
plt.figure()
plt.plot(fpr_train, tpr_train, color='blue', lw=2, label='Train ROC curve (area = %0.2f)' % auc_train)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend(loc='lower right')
plt.show()

# 定义指标列表
metrics = [
    ("Pixel Accuracy", pixel_accuracy_train),
    ("IoU", iou_train),
    ("Accuracy", accuracy_train),
    ("Recall", recall_train),
    ("Precision", precision_train),
    ("F1 Score", f1_train)
]

# 打印表格的头部
print("+-----------------+------------+")
print("| Metric          | Value      |")
print("+-----------------+------------+")

# 打印每个指标的值
for metric_name, metric_value in metrics:
    print(f"| {metric_name:15} | {metric_value:.6f} |")
print("+-----------------+------------+")

注意哈,这个代码只是针对某一个样本的结果:

ROC曲线:这里存疑,感觉没啥意义,而且这个曲线看起来有问题,是一个三点折线。

一些性能指标,稍微解释,主要是前两个:

A)Pixel Accuracy:

定义:它是所有正确分类的像素总数与图像中所有像素的总数的比率。

计算:(正确预测的像素数量) / (所有像素数量)。

说明:这个指标评估了模型在每个像素级别上的准确性。但在某些场景中(尤其是当类别非常不平衡时),这个指标可能并不完全反映模型的表现。

B)IoU (Intersection over Union):

定义:对于每个类别,IoU 是该类别的预测结果(预测为该类别的像素)与真实标签之间的交集与并集的比率。

计算:(预测与真实标签的交集) / (预测与真实标签的并集)。

说明:它是一个很好的指标,因为它同时考虑了假阳性和假阴性,尤其在类别不平衡的情况下。

C)Accuracy:

定义:是所有正确分类的像素与所有像素的比例,通常与 Pixel Accuracy 相似。

计算:(正确预测的像素数量) / (所有像素数量)。

D)Recall (or Sensitivity or True Positive Rate):

定义:是真实正样本被正确预测的比例。

计算:(真阳性) / (真阳性 + 假阴性)。

说明:高召回率表示少数阳性样本不容易被漏掉。

E)Precision:

定义:是被预测为正的样本中实际为正的比例。

计算:(真阳性) / (真阳性 + 假阳性)。

说明:高精度表示假阳性的数量很少。

F)F1 Score:

定义:是精度和召回率的调和平均值。它考虑了假阳性和假阴性,并试图找到两者之间的平衡。

计算:2 × (精度 × 召回率) / (精度 + 召回率)。

说明:在不平衡类别的场景中,F1 Score 通常比单一的精度或召回率更有用。

##############################评价指标,对于全部训练集的样本#######################################
import numpy as np
import matplotlib.pyplot as plt
from sklearn.metrics import roc_curve, auc, accuracy_score, recall_score, precision_score, f1_score

# 初始化变量来存储评估指标的累积值和真实标签与预测值
total_pixel_accuracy = 0
total_iou = 0
total_accuracy = 0
total_recall = 0
total_precision = 0
total_f1 = 0
total_auc = 0
all_true_masks = []
all_predicted_masks = []

# 遍历整个训练集
for sample in train_dataset:
    sample_img = sample['image'].unsqueeze(0).to(device)
    sample_mask = sample['mask'].cpu().numpy()

    # 使用模型进行预测
    with torch.no_grad():
        model.eval()
        prediction = model(sample_img)['out']

    # 取前景类并转为CPU
    predicted_mask = prediction[0, 1].cpu().numpy()
    predicted_mask = (predicted_mask > 0.5).astype(np.uint8)

    # 收集真实标签和预测值
    all_true_masks.extend(sample_mask.ravel())
    all_predicted_masks.extend(predicted_mask.ravel())

# 计算ROC曲线和AUC
fpr_train, tpr_train, _ = roc_curve(all_true_masks, all_predicted_masks)
avg_auc = auc(fpr_train, tpr_train)

# 计算其他评估指标
avg_pixel_accuracy = accuracy_score(all_true_masks, all_predicted_masks)
avg_iou = calc_iou(np.array(all_true_masks), np.array(all_predicted_masks))
avg_accuracy = accuracy_score(all_true_masks, all_predicted_masks)
avg_recall = recall_score(all_true_masks, all_predicted_masks)
avg_precision = precision_score(all_true_masks, all_predicted_masks)
avg_f1 = f1_score(all_true_masks, all_predicted_masks)

# 绘制ROC曲线
plt.figure()
plt.plot(fpr_train, tpr_train, color='blue', lw=2, label='Train ROC curve (area = %0.2f)' % avg_auc)
plt.xlabel('False Positive Rate')
plt.ylabel('True Positive Rate')
plt.title('ROC Curve')
plt.legend(loc='lower right')
plt.show()

# 打印评估指标的平均值
metrics = [
    ("Pixel Accuracy", avg_pixel_accuracy),
    ("IoU", avg_iou),
    ("Accuracy", avg_accuracy),
    ("Recall", avg_recall),
    ("Precision", avg_precision),
    ("F1 Score", avg_f1),
    ("AUC", avg_auc)
]

print("+-----------------+------------+")
print("| Metric          | Value      |")
print("+-----------------+------------+")
for metric_name, metric_value in metrics:
    print(f"| {metric_name:15} | {metric_value:.6f} |")
    print("+-----------------+------------+")

这个结果是针对有所训练集的:

(d)查看验证集和验证集的具体分割情况

#######################看训练集图片的具体分割效果###########################################
import matplotlib.pyplot as plt
import numpy as np

# 选择一张训练集图片
img_index = 20
sample = train_dataset[img_index]
train_img = sample['image'].unsqueeze(0).to(device)  # 为batch_size添加一个维度

with torch.no_grad():
    model.eval()
    prediction = model(train_img)['out']

# 使用阈值处理预测掩码
mask_threshold = 0.5
pred_mask = prediction[0, 1].cpu().numpy()  # 选择前景类
pred_mask = (pred_mask > mask_threshold).astype(np.uint8)

# 使用matplotlib来展示原始图像、真实掩码和预测的分割图像
plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
plt.title("Original Image")
# Normalize the image to [0,1] range
denorm_img = train_img[0].permute(1, 2, 0).cpu().numpy()
denorm_img = denorm_img - denorm_img.min()
denorm_img = denorm_img / denorm_img.max()
plt.imshow(denorm_img.clip(0, 1))

plt.subplot(1, 3, 2)
plt.title("True Segmentation")
true_mask = sample['mask'].cpu().numpy()
if len(true_mask.shape) == 1:  # Ensure mask is 2D
    true_mask = true_mask.reshape(int(np.sqrt(true_mask.shape[0])), -1)
plt.imshow(true_mask, cmap='gray')

plt.subplot(1, 3, 3)
plt.title("Predicted Segmentation")
plt.imshow(pred_mask, cmap='gray')

plt.show()

#######################看测试集图片的具体分割效果###########################################
#看具体分割的效果
import matplotlib.pyplot as plt

test_dataset = CustomDataset(
    image_paths=[os.path.join(test_images_folder, img) for img in test_images],
    mask_paths=[os.path.join(label_images_folder, img) for img in label_images],  # 这里我假设您的测试集的标签也在label_images_folder中
    transform=transform
)

# 选择一张测试图片
img_index = 20
sample = test_dataset[img_index]
test_img = sample['image'].unsqueeze(0).to(device)

with torch.no_grad():
    model.eval()
    prediction = model(test_img)['out']

# 使用阈值处理预测掩码
mask_threshold = 0.5
pred_mask = prediction[0, 1].cpu().numpy()
pred_mask = (pred_mask > mask_threshold).astype(np.uint8)

# 使用matplotlib来展示原始图像、真实掩码和预测的分割图像
plt.figure(figsize=(15, 5))

plt.subplot(1, 3, 1)
plt.title("Original Image")
# Normalize the image to [0,1] range
denorm_img = test_img[0].permute(1, 2, 0).cpu().numpy()
denorm_img = denorm_img - denorm_img.min()
denorm_img = denorm_img / denorm_img.max()
plt.imshow(denorm_img.clip(0, 1))

plt.subplot(1, 3, 3)
plt.title("Predicted Segmentation")
plt.imshow(pred_mask, cmap='gray')

plt.show()

查看训练集分割效果:

查看验证集分割效果(验证集没有label,所以中间是空的):

总体来看,勉强过关,收工!

四、写在后面

略~

五、数据

链接:https://pan.baidu.com/s/1Cb78MwfSBfLwlpIT0X3q9Q?pwd=u1q1

提取码:u1q1

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

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

相关文章

ARPG----C++学习记录04 Section8 角色类,移动

角色类输入 新建一个角色C,继承建立蓝图,和Pawn一样,绑定输入移动和相机. 在构造函数中添加这段代码也能实现。打开UsePawnControlRotation就可以让人物不跟随鼠标旋转 得到旋转后的向前向量 使用旋转矩阵 想要前进方向和旋转的方向对应。获取当前控制…

如何通过把setTimeout异步转为同步

一.封装定时器函数 function delayed(time){return new Promise((resolve,reject)>{setTimeout( () > {resolve(time)}, time);}) }二调用的时候通过async await 修饰 async function demo() {console.log(new Date().getMinutes(): new Date().getSeconds())await del…

你知道王者荣耀是怎么实现技能范围指示器的吗?

引言 一文教会你实现类似王者荣耀的技能范围指示器。 技能范围指示器是许多游戏中常见的一个元素,特别是在MOBA(多人在线战斗竞技场)游戏中,如《王者荣耀》、《英雄联盟》等。 本文将介绍如何在Cocos Creator中实现一个技能范围…

6.jvm中对象创建流程与内存分配

目录 概述对象的创建流程对象的内存分配方式对象怎样才会进入老年代大对象直接进入老年代内存担保 jvc 相关指令查看jdk默认使用的gc查看当前jdk支持的有哪些gc查看指定进程当前正在使用的gc 结束 概述 相关文章在此总结如下: 文章地址jvm基本知识地址jvm类加载系…

Linux常用命令——bzip2命令

在线Linux命令查询工具 bzip2 将文件压缩成bz2格式 补充说明 bzip2命令用于创建和管理(包括解压缩)“.bz2”格式的压缩包。我们遇见Linux压缩打包方法有很多种,以下讲解了Linux压缩打包方法中的Linux bzip2命令的多种范例供大家查看&…

Python | 机器学习之数据清洗

​ 🌈个人主页:Sarapines Programmer🔥 系列专栏:《人工智能奇遇记》🔖少年有梦不应止于心动,更要付诸行动。 目录结构 1. 机器学习之数据清洗概念 1.1 机器学习 1.2 数据清洗 2. 数据清洗 2.1 实验目的…

Changes to Captions: An Attentive Network forRemote Sensing Change Captioning

字幕的变化:一个用于遥感变化字幕的关注网络 IEEE Transactions on Image Processing Shizhen Chang, Pedram Ghamisi 2023 摘要:近年来,高级研究集中在使用自然语言处理(NLP)技术对遥感图像进行直接学习和分析。准…

Origin:科研绘图与学术图表绘制从入门到精通

文章目录 一、引言二、安装和启动Origin三、创建和保存图表四、深入学习Origin绘图功能五、应用Origin进行科研绘图和学术图表绘制六、总结与建议《Origin科研绘图与学术图表绘制从入门到精通》亮点内容简介作者简介目录获取方式 一、引言 Origin是一款功能强大的数据分析和科…

家政服务小程序源码系统+上门预约服务 源码完全开源可二次开发 带完整的搭建教程

在互联网的快速发展下,传统的家政服务行业也在逐步向数字化、智能化方向转型。为了满足消费者对于家政服务的高品质需求,罗峰给大家分享一款基于微信小程序的上门预约家政服务系统。该系统采用完全开源的源码系统,可进行二次开发,…

CAD Exchanger SDK 须知的开发配置--Crack

支持的配置 目录 支持的编程语言 C 支持C# 支持Java支持Python支持JavaScript 支持 CAD Exchanger SDK 是一组跨平台库,目前支持下列配置。随着时间的推移,旧版本的编译器、体系结构或依赖的第三方库从主要支持级别变为次要支持级别,然后被弃…

ubuntu利用crontab反弹shell

事情源于自,我利用redis未授权访问漏洞在向ubuntu的/varspool/cron/crontabs目录下创建的任务计划文件去反弹shell时,发现shell并不能反弹到自己的centos2上 (1)在ubuntu中进入/var/spool/cron/crontabs/目录 cd /var/spool/cro…

不应该被忽视的10个好用的PDF文档修改器

您在寻找最好的免费开源 PDF 编辑器吗?您是否正在寻找免费编辑 PDF 文档的解决方案?如果您正在寻找此类问题的答案。那么,亲爱的朋友,您来对地方了,因为今天,在本文中,我将讨论一些适用于 Windo…

2023-2024 年适用于 Windows 电脑的顶级视频录制软件

想捕捉您正在在线观看的视频吗?使用网络摄像头录制视频会议以供日后参考。正在寻找可以完成这些任务的视频捕捉软件?这篇文章说明了一切。以下是一些适用于 Windows PC 的最佳视频录制工具。 什么是视频录制软件? 顾名思义,视频捕…

基于SSM的“镜头人生”约拍网站设计与实现

末尾获取源码 开发语言:Java Java开发工具:JDK1.8 后端框架:SSM 前端:JSP 数据库:MySQL5.7和Navicat管理工具结合 服务器:Tomcat8.5 开发软件:IDEA / Eclipse 是否Maven项目:是 目录…

Python机器学习算法入门教程(第五部分)

接着Python机器学习算法入门教程(第四部分),继续展开描述。 二十五、Python Sklearn库SVM算法应用 SVM 是一种有监督学习分类算法,输入值为样本特征值向量和其对应的类别标签,输出具有预测分类功能的模型&#xff0c…

电脑技巧:推荐基于浏览器的远程桌面访问控制工具

一、软件简介 Getscreen.me是一个基于浏览器的远程桌面访问控制工具,可以轻松地远程访问控制特定设备。并且注册登录账户实现允许设置具有永久访问权限的设备,可以通过一键进行快速连接访问,无需共享 ID、密码或任何内容。 Getscreen.me采用…

C++标准模板(STL)- 类型支持 (受支持操作,检查类型是否拥有未被弃置的析构函数)

类型特性 类型特性定义一个编译时基于模板的结构&#xff0c;以查询或修改类型的属性。 试图特化定义于 <type_traits> 头文件的模板导致未定义行为&#xff0c;除了 std::common_type 可依照其所描述特化。 定义于<type_traits>头文件的模板可以用不完整类型实例…

记录pytorch实现自定义算子并转onnx文件输出

概览&#xff1a;记录了如何自定义一个算子&#xff0c;实现pytorch注册&#xff0c;通过C编译为库文件供python端调用&#xff0c;并转为onnx文件输出 整体大概流程&#xff1a; 定义算子实现为torch的C版本文件注册算子编译算子生成库文件调用自定义算子 一、编译环境准备…

Linux可以投屏到电视吗?用网页浏览器就能投屏到电视!

Linux系统的电脑如果要投屏到安卓电视屏幕上&#xff0c;可以使用投屏工具AirDroid Cast的网页版和TV版一起实现。 首先&#xff0c;在Linux系统的电脑里用chrome浏览器或edge浏览器打开webcast.airdroid.com。这就是AirDroid Cast的网页版。你可以看到中间白色框框的右上角有个…

12358748257

问题一&#xff1a;.浮点数打印问题 float red_increment (target_red_value - initial_red_value) / STEPS; u8 STEPS 100; printf("绿色值每一次增量------%f\n", red_increment); 后面三个参数均为u8类型 希望采用 %f打印出每次的步进值。但是结果为空白 希…