组合实现多类别分割(含实战代码)

news2024/9/20 14:40:26

来源:投稿 作者:AI浩
编辑:学姐

摘要

segmentation_models_pytorch是一款非常优秀的图像分割库,albumentations是一款非常优秀的图像增强库,这篇文章将这两款优秀结合起来实现多类别的图像分割算法。数据集选用CamVid数据集,类别有:sky,building,pole,road,pavement,tree,signsymbol,fence,car,pedestrian,bicyclist,unlabelled等12个类别。数据量不大,下载地址:mirrors/alexgkendall/segnet-tutorial·GitCode。

通过这篇文章,你可以学习到:

1、如何在图像分割使用albumentations增强算法?

2、如何使用dice_losscross_entropy_loss

3、如何segmentation_models_pytorch构架UNET++模型?

4、如何对分割数据做one-hot编码?

项目结构

项目的结构如下:

训练

新建train.py,插入一下代码:

import os
import numpy as np
import cv2
import albumentations as albu
import torch
import segmentation_models_pytorch as smp
from torch.utils.data import DataLoader
from torch.utils.data import Dataset as BaseDataset

导入需要的安装包,接下来编写数据载入部分。

# ---------------------------------------------------------------
### 加载数据
# CamVid数据集中用于图像分割的所有标签类别
CLASSES = ['sky', 'building', 'pole', 'road', 'pavement',
           'tree', 'signsymbol', 'fence', 'car',
           'pedestrian', 'bicyclist', 'unlabelled']
class Dataset(BaseDataset):
    """CamVid数据集。进行图像读取,图像增强增强和图像预处理.

    Args:
        images_dir (str): 图像文件夹所在路径
        masks_dir (str): 图像分割的标签图像所在路径
        class_values (list): 用于图像分割的所有类别数
        augmentation (albumentations.Compose): 数据传输管道
        preprocessing (albumentations.Compose): 数据预处理
    """

    def __init__(
            self,
            images_dir,
            masks_dir,
            augmentation=None,
            preprocessing=None,
    ):
        self.ids = os.listdir(images_dir)
        self.images_fps = [os.path.join(images_dir, image_id) for image_id in self.ids]
        self.masks_fps = [os.path.join(masks_dir, image_id) for image_id in self.ids]
        # convert str names to class values on masks
        self.class_values = list(range(len(CLASSES)))
        self.augmentation = augmentation
        self.preprocessing = preprocessing

    def __getitem__(self, i):

        # read data
        image = cv2.imread(self.images_fps[i])
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mask = cv2.imread(self.masks_fps[i], 0)
        # 从标签中提取特定的类别 (e.g. cars)
        masks = [(mask == v) for v in self.class_values]
        mask = np.stack(masks, axis=-1).astype('float')
        # 图像增强应用
        if self.augmentation:
            sample = self.augmentation(image=image, mask=mask)
            image, mask = sample['image'], sample['mask']
        # 图像预处理应用
        if self.preprocessing:
            sample = self.preprocessing(image=image, mask=mask)
            image, mask = sample['image'], sample['mask']
            print(mask.shape)
        return image, mask

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

定义类别。类别的顺序对应mask的类别。
self.images_fps和self.masks_fps是图片的list和对应的mask图片的list。
self.class_values,类别对应的index,index的值对应mask上的类别值。
self.augmentation数据增强,使用albumentations增强。self.preprocessing数据的预处理,包含归一化和标准化,预处理的方法来自smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)。
接下来,解释__getitem__函数的内容:

读取图片。
将图片转为RGB,cv2读取图片,默认是BGR,所以需要做转化。
接下来两行代码,实现将mask转为one-hot编码。输入的shape是(360,480)输出是(360,480,12)
图像增强。
图像预处理。
然后返回预处理后的图片和mask。

接下来是图片增强的代码:

def get_training_augmentation():
    train_transform = [
        albu.HorizontalFlip(p=0.5),
        albu.ShiftScaleRotate(scale_limit=0.5, rotate_limit=0, shift_limit=0.1, p=1, border_mode=0),
        albu.PadIfNeeded(min_height=384, min_width=480, always_apply=True, border_mode=0),
        albu.IAAAdditiveGaussianNoise(p=0.2),
        albu.IAAPerspective(p=0.5),
        albu.OneOf(
            [
                albu.CLAHE(p=1),
                albu.RandomBrightness(p=1),
                albu.RandomGamma(p=1),
            ],
            p=0.9,
        ),

        albu.OneOf(
            [
                albu.IAASharpen(p=1),
                albu.Blur(blur_limit=3, p=1),
                albu.MotionBlur(blur_limit=3, p=1),
            ],
            p=0.9,
        ),

        albu.OneOf(
            [
                albu.RandomContrast(p=1),
                albu.HueSaturationValue(p=1),
            ],
            p=0.9,
        ),
    ]
    return albu.Compose(train_transform)

def get_validation_augmentation():
    """调整图像使得图片的分辨率长宽能被32整除"""
    test_transform = [
        albu.PadIfNeeded(384, 480)
    ]
    return albu.Compose(test_transform)


def to_tensor(x, **kwargs):
    return x.transpose(2, 0, 1).astype('float32')


def get_preprocessing(preprocessing_fn):
    """进行图像预处理操作

    Args:
        preprocessing_fn (callbale): 数据规范化的函数
            (针对每种预训练的神经网络)
    Return:
        transform: albumentations.Compose
    """

    _transform = [
        albu.Lambda(image=preprocessing_fn),
        albu.Lambda(image=to_tensor, mask=to_tensor),
    ]
    return albu.Compose(_transform)

首先,我们一起查看get_training_augmentation里面的代码。这里比较复杂。这些需要注意的是PadIfNeeded方法。
由于UNet系列的文章经历了5次缩放,所以图片必须被32整除。所以通过填充的方式将图片的尺寸改为(384,480)。
同样,在验证集也要做这样的操作。 to_tensor函数是将图片的值转为tensor,并将维度做交换。由于cv2读取的图片和mask的onehot的维度都是(W,H,C),需要高改为(C,W,H)。
get_preprocessing是对数据做预处理,有归一化和标准化,然后,将图片和mask转为to_tensor。

接下来,将最重要的训练部分:

# $# 创建模型并训练
# ---------------------------------------------------------------
if __name__ == '__main__':
    ENCODER = 'efficientnet-b1'
    ENCODER_WEIGHTS = 'imagenet'
    ACTIVATION = 'softmax'  # could be None for logits or 'softmax2d' for multiclass segmentation
    DEVICE = 'cuda'
    # 使用unet++模型
    model = smp.UnetPlusPlus(
        encoder_name=ENCODER,
        encoder_weights=ENCODER_WEIGHTS,
        classes=len(CLASSES),
        activation=ACTIVATION,
    )
    preprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)

这部分的代码主要是定义模型。

模型选用unet++,解码器是efficientnet-b1,预训练权重为:imagenet。 定义类别。
preprocessing_fn获取 smp.encoders的预处理方法。

    # 数据集所在的目录
    DATA_DIR = './data/CamVid/'
    # 如果目录下不存在CamVid数据集,则克隆下载
    if not os.path.exists(DATA_DIR):
        print('Loading data...')
        os.system('git clone https://github.com/alexgkendall/SegNet-Tutorial ./data')
        print('Done!')
    # 训练集
    x_train_dir = os.path.join(DATA_DIR, 'train')
    y_train_dir = os.path.join(DATA_DIR, 'trainannot')
    # 验证集
    x_valid_dir = os.path.join(DATA_DIR, 'val')
    y_valid_dir = os.path.join(DATA_DIR, 'valannot')
    # 加载训练数据集
    train_dataset = Dataset(
        x_train_dir,
        y_train_dir,
        augmentation=get_training_augmentation(),
        preprocessing=get_preprocessing(preprocessing_fn)
    )
    # 加载验证数据集
    valid_dataset = Dataset(
        x_valid_dir,
        y_valid_dir,
        augmentation=get_validation_augmentation(),
        preprocessing=get_preprocessing(preprocessing_fn)
    )
    # 需根据显卡的性能进行设置,batch_size为每次迭代中一次训练的图片数,num_workers为训练时的工作进程数,如果显卡不太行或者显存空间不够,将batch_size调低并将num_workers调为0
    train_loader = DataLoader(train_dataset, batch_size=2, shuffle=True, num_workers=0)
    valid_loader = DataLoader(valid_dataset, batch_size=1, shuffle=False, num_workers=0)

这部分的代码主要是数据集加载。

定义数据集所在路径。
获取训练集和验证集的路径。
加载训练集和验证集。
将训练集和测试集放入DataLoader中,根据显卡的大小定义batch_size,训练集需要shuffle,验证集不需要。

然后,定义loss

    loss = smp.utils.losses.DiceLoss() + smp.utils.losses.CrossEntropyLoss()
    metrics = [
        smp.utils.metrics.IoU(threshold=0.5),
        smp.utils.metrics.Recall()
    ]

    optimizer = torch.optim.Adam([
        dict(params=model.parameters(), lr=0.0001),
    ])

loss是DiceLoss和CrossEntropyLoss组合。
评分标准为IoU和Recall。
优化器选用Adam。

 # 创建一个简单的循环,用于迭代数据样本
    train_epoch = smp.utils.train.TrainEpoch(
        model,
        loss=loss,
        metrics=metrics,
        optimizer=optimizer,
        device=DEVICE,
        verbose=True,
    )

    valid_epoch = smp.utils.train.ValidEpoch(
        model,
        loss=loss,
        metrics=metrics,
        device=DEVICE,
        verbose=True,
    )

    # 进行40轮次迭代的模型训练
    max_score = 0

    for i in range(0, 40):

        print('\nEpoch: {}'.format(i))
        train_logs = train_epoch.run(train_loader)
        valid_logs = valid_epoch.run(valid_loader)

        # 每次迭代保存下训练最好的模型
        if max_score < valid_logs['iou_score']:
            max_score = valid_logs['iou_score']
            torch.save(model, './best_model.pth')
            print('Model saved!')

        if i == 25:
            optimizer.param_groups[0]['lr'] = 1e-5
            print('Decrease decoder learning rate to 1e-5!')

创建TrainEpoch和ValidEpoch循环用来迭代数据集。
按照迭代次数循环,保存最好的模型。

完成上面的工作后就可以开始训练了。

测试

完成训练后就开始测试部分。

import os

import albumentations as albu
import cv2
import matplotlib.pyplot as plt
import numpy as np
import segmentation_models_pytorch as smp
import torch
from torch.utils.data import Dataset as BaseDataset

os.environ['CUDA_VISIBLE_DEVICES'] = '0'

导入所需要的包

# ---------------------------------------------------------------
### 加载数据
# CamVid数据集中用于图像分割的所有标签类别
CLASSES = ['sky', 'building', 'pole', 'road', 'pavement',
           'tree', 'signsymbol', 'fence', 'car',
           'pedestrian', 'bicyclist', 'unlabelled']
class Dataset(BaseDataset):
    """CamVid数据集。进行图像读取,图像增强增强和图像预处理.

    Args:
        images_dir (str): 图像文件夹所在路径
        masks_dir (str): 图像分割的标签图像所在路径
        class_values (list): 用于图像分割的所有类别数
        augmentation (albumentations.Compose): 数据传输管道
        preprocessing (albumentations.Compose): 数据预处理
    """

    def __init__(
            self,
            images_dir,
            masks_dir,
            augmentation=None,
            preprocessing=None,
    ):
        self.ids = os.listdir(images_dir)
        self.images_fps = [os.path.join(images_dir, image_id) for image_id in self.ids]
        self.masks_fps = [os.path.join(masks_dir, image_id) for image_id in self.ids]

        # convert str names to class values on masks
        self.class_values = list(range(len(CLASSES)))

        self.augmentation = augmentation
        self.preprocessing = preprocessing

    def __getitem__(self, i):

        # read data
        image = cv2.imread(self.images_fps[i])
        image = cv2.cvtColor(image, cv2.COLOR_BGR2RGB)
        mask = cv2.imread(self.masks_fps[i], 0)

        # 从标签中提取特定的类别 (e.g. cars)
        masks = [(mask == v) for v in self.class_values]
        mask = np.stack(masks, axis=-1).astype('float')

        # 图像增强应用
        if self.augmentation:
            sample = self.augmentation(image=image, mask=mask)
            image, mask = sample['image'], sample['mask']

        # 图像预处理应用
        if self.preprocessing:
            sample = self.preprocessing(image=image, mask=mask)
            image, mask = sample['image'], sample['mask']

        return image, mask

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


# ---------------------------------------------------------------
### 图像增强

def get_validation_augmentation():
    """调整图像使得图片的分辨率长宽能被32整除"""
    test_transform = [
        albu.PadIfNeeded(384, 480)
    ]
    return albu.Compose(test_transform)


def to_tensor(x, **kwargs):
    return x.transpose(2, 0, 1).astype('float32')


def get_preprocessing(preprocessing_fn):
    """进行图像预处理操作

    Args:
        preprocessing_fn (callbale): 数据规范化的函数
            (针对每种预训练的神经网络)
    Return:
        transform: albumentations.Compose
    """

    _transform = [
        albu.Lambda(image=preprocessing_fn),
        albu.Lambda(image=to_tensor, mask=to_tensor),
    ]
    return albu.Compose(_transform)

上面的代码是数据加载和数据增强,和训练集的代码一样。

# 图像分割结果的可视化展示
def visualize(**images):
    """PLot images in one row."""
    n = len(images)
    plt.figure(figsize=(16, 5))
    for i, (name, image) in enumerate(images.items()):
        plt.subplot(1, n, i + 1)
        plt.xticks([])
        plt.yticks([])
        plt.title(' '.join(name.split('_')).title())
        plt.imshow(image)
    plt.show()

可视化测试结果,展示原图,真实的mask,预测的mask。

# ---------------------------------------------------------------
if __name__ == '__main__':

    DATA_DIR = './data/CamVid/'

    # 测试集
    x_test_dir = os.path.join(DATA_DIR, 'test')
    y_test_dir = os.path.join(DATA_DIR, 'testannot')

    ENCODER = 'efficientnet-b1'
    ENCODER_WEIGHTS = 'imagenet'

    ACTIVATION = 'softmax'  # could be None for logits or 'softmax2d' for multiclass segmentation
    DEVICE = 'cuda'

    preprocessing_fn = smp.encoders.get_preprocessing_fn(ENCODER, ENCODER_WEIGHTS)
    # ---------------------------------------------------------------
    # $# 测试训练出来的最佳模型

    # 加载最佳模型
    best_model = torch.load('./best_model.pth')

    # 创建测试数据集
    test_dataset = Dataset(
        x_test_dir,
        y_test_dir,
        augmentation=get_validation_augmentation(),
        preprocessing=get_preprocessing(preprocessing_fn),
    )

    # ---------------------------------------------------------------
    # $# 图像分割结果可视化展示
    # 对没有进行图像处理转化的测试集进行图像可视化展示
    test_dataset_vis = Dataset(
        x_test_dir, y_test_dir
    )
    # 从测试集中随机挑选3张图片进行测试
    for i in range(3):
        n = np.random.choice(len(test_dataset))

        image_vis = test_dataset_vis[n][0].astype('uint8')
        image, gt_mask = test_dataset[n]
        gt_mask = (np.argmax(gt_mask, axis=0) * 255 / (gt_mask.shape[0])).astype(np.uint8)

        x_tensor = torch.from_numpy(image).to(DEVICE).unsqueeze(0)
        pr_mask = best_model.predict(x_tensor)

        pr_mask = (pr_mask.squeeze().cpu().numpy())
        pr_mask = (np.argmax(pr_mask, axis=0) * 255 / (pr_mask.shape[0])).astype(np.uint8)
        # 恢复图片原来的分辨率
        gt_mask = cv2.resize(gt_mask, (480, 360))
        pr_mask = cv2.resize(pr_mask, (480, 360))
        visualize(
            image=image_vis,
            ground_truth_mask=gt_mask,
            predicted_mask=pr_mask
        )

获取测试集的路径。
定义ENCODER 为 efficientnet-b1,ENCODER_WEIGHTS为imagenet,ACTIVATION为softmax。
获取预训练参数。
加载模型。
加载数据集。
加载没有做处理的图片。
随机选择3张图片

从test_dataset_vis获取图片。
从test_dataset获取对应的图片和mask。
将mask放大255的范围。
预测图片,生成预测的mask。
将预测的mask也对应的放到255的范围。
然后重新resize到原来的尺寸。
可视化结果。

运行结果:

CVPR图像分割必读论文🚀🚀🚀

关注下方卡片《学姐带你玩AI》

回复“CVPR”免费领取

码字不易,欢迎大家点赞评论收藏!

引用 https://blog.csdn.net/hhhhhhhhhhwwwwwwwwww/article/details/124086830

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

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

相关文章

Java实现JDBC工具类DbUtils的抽取及程序实现数据库的增删改操作

封装DbUtils 工具类 不知道我们发现没有&#xff0c;不管是对数据库进行查询&#xff0c;还是标准的JDBC 步骤&#xff0c;其开端都是先实现JDBC 的加载注册&#xff0c;接着是获取数据库的连接&#xff0c;最后都是实现关闭连接&#xff0c;释放资源的操作。那我们何不直接把…

起薪2万的爬虫工程师,Python需要学到什么程度才可以就业?

爬虫工程师的的薪资为20K起&#xff0c;当然&#xff0c;因为大数据&#xff0c;薪资也将一路上扬。那么&#xff0c;Python需要学到什么程度呢?今天我们来看看3位前辈的回答。 1、前段时间快要毕业&#xff0c;而我又不想找自己的老本行Java开发了&#xff0c;所以面了很多P…

【Python】用sympy判断函数的单调性和极值

文章目录单调性和奇异性连续性、极值、周期、不动点单调性和奇异性 sympy.calculus.singularities提供了4个关于单调性判定的函数&#xff0c;分别是 is_decreasing(expr, interval, symbolNone) is_increasing(expr, interval, symbolNone) is_strictly_decreasing(expr, in…

【期末复习】例题讲解Dijkstra算法

使用场景Dijkstra算法用于解决单源点最短路径问题&#xff0c;即给一个顶点作为源点&#xff0c;依次求它到图中其他n-1个顶点的最短距离。例题讲解Dijkstra算法将图中所有顶点分成两部分&#xff0c;第一部分是已知到源点最短距离的顶点Known(K)&#xff0c;第二部分是不知道到…

教你如何搭建人事OA-考勤管理系统,demo可分享

1、简介1.1、案例简介本文将介绍&#xff0c;如何搭建人事OA-考勤管理。1.2、应用场景可查看员工考勤的数据统计&#xff0c;可进行考勤签到、补签、请假、加班、调休等流程。2、设置方法2.1、表单搭建1&#xff09;新建表单【考勤签到】&#xff0c;字段设置如下&#xff1a;名…

VS Code Spring 全新功能来了!

大家好&#xff0c;欢迎来到我们 2023 年的第一篇博客&#xff01;我们想与您分享几个与 Spring 插件、代码编辑和性能相关的激动人心的更新&#xff0c;让我们开始吧&#xff01; Spring 插件包的新入门演练 演练&#xff08;Walkthrough&#xff09; 是一种多步骤、向导式的体…

跨境进口税费计算

以前搞跨境进口的时候&#xff0c;需要计算商品税费。税费计算比较复杂&#xff0c;把信息整理了一下&#xff0c;分享给大家。 一、基础知识 1.1税费类型 BBC/BC&#xff1a;跨境电商进口综合税 关税*0 (消费税进口环节增值税)*70%CC&#xff1a;行邮税&#xff0c;税额低…

Vue3电商项目实战-商品详情模块1【01-商品详情-基础布局、02-商品详情-渲染面包屑、03-商品详情-图片预览组件、04-商品详情-图片放大镜】

文章目录01-商品详情-基础布局02-商品详情-渲染面包屑03-商品详情-图片预览组件04-商品详情-图片放大镜01-商品详情-基础布局 目的&#xff1a;完成商品详情基础布局&#xff0c;路由配置&#xff0c;搭好页面架子。 大致步骤&#xff1a; 准备组件结构容器提取商品推荐组件且…

十二月券商金工精选

✦研报目录✦ ✦简述✦ 按发布时间排序 华宝证券 主动暴露的得与失—从Barra框架到私募指增因子分析方法 发布日期&#xff1a;2022-12-01 关键词&#xff1a;股票、Barra、风险暴露、指数增强 主要内容&#xff1a;本文针对私募指数增强产品的策略流程&#xff0c;设计…

<JVM上篇:内存与垃圾回收篇>11 - 垃圾回收相关算法

对象存活判断 在堆里存放着几乎所有的 Java 对象实例&#xff0c;在 GC 执行垃圾回收之前&#xff0c;首先需要区分出内存中哪些是存活对象&#xff0c;哪些是已经死亡的对象。只有被标记为己经死亡的对象&#xff0c;GC 才会在执行垃圾回收时&#xff0c;释放掉其所占用的内存…

vue项目开发(vue2与vue3对比)

vue中使用typescrip 初与typescrip的约定情愫&#xff1a;最近项目要用到vue3,所以打算使用typescrip语法&#xff01; 安装&#xff1a; npx tyarn add typescript vue/cli-plugin-typescript -D npx tsc --init修改 tsconfig.json&#xff1a; {"compilerOptions&quo…

LVS中的keepalived高可用

文章目录前言一、Keepalived简介二、keepalived工作原理三、配置文件四、实验1.某台Real Server down2.LVS本身down实验过程&#xff1a;五、代码详细演示整体过程调度器安装软件、设置测试keepalived对后端RS的健康检测backup服务主机设置前言 一、Keepalived简介 Keepalived是…

ubuntu18.4安装Redmine软件

1.github网站下载Redmine4.2.0的安装包&#xff0c;如下图所示&#xff1a; https://github.com/redmine/redmine/releases/tag/4.2.0 图1 github网站下载redmine4.2.0安装包2.拷贝Redmine4.2.0的安装包到ubuntu工作目录&#xff0c;并解压缩&#xff0c;如下所示&#xff1a;…

Springboot前后端分离国际化实现-chatgpt

前言 Springboot国际化可以帮助使用者在不同语言环境中构建应用程序&#xff0c;这样应用程序可以有效地适应不同语言文化背景下的用户需求。 此外&#xff0c;Springboot国际化也可以方便多语言应用程序重用和维护&#xff0c;从而减少了系统部署的时间成本和维护的费用。 要实…

医疗方案 | 星辰天合入选“2022智慧新医信优秀解决方案”

近日&#xff0c;由 HC3i数字医疗网主办的《数字化转型驱动下的医院高质量发展论坛》暨 2022 智慧新医信优秀解决方案发布仪式在线上召开。XSKY星辰天合的“智慧医疗软件定义数据基础设施”解决方案成功入选 2022 智慧新医信优秀解决方案&#xff0c;。此次论坛由 HC3i 数字医疗…

会利用信息差赚钱的人才是聪明人

毕业后找不到工作&#xff0c;穷到只剩下时间&#xff0c;大小做了20多份副业兼职&#xff0c;终于找到了可靠的渠道&#xff0c; 我是专科生&#xff0c;学历不好&#xff0c;专业拉胯。毕业后&#xff0c;我找了两三份工作。要么工资太低&#xff0c;只能交房租&#xff0c;…

javaFx实现鼠标穿透画布,同时操作画布和桌面,背景透明,类似ppt批注

一、功能需要由来和大致效果 今天&#xff0c;我们要用javaFx来实现一个鼠标穿透画布的功能&#xff0c;该需求来自于在我们的javaFx桌面应用中&#xff0c;需要实现一个悬浮的桌面侧边工具栏&#xff0c;在工具栏中有画笔绘制&#xff0c;批注的功能&#xff0c;能够实现在任何…

【NLP】一种联合关系抽取模型——TPLinker

背景 前文介绍了【NLP】一种基于联合方式的三元组抽取模型——CasRel.这个模型虽然实体和关系同时训练&#xff0c;但本质上来说还是分阶段的预测实体和关系&#xff0c;依然存在暴露偏差问题。下面介绍一个解决暴露偏差的模型&#xff1a;TPLinker&#xff0c;论文地址&#…

【OJ】A+B=X

&#x1f4da;Description: 数列S中有n个整数&#xff0c;判断S中是否存在两个数A、B&#xff0c;使之和等于X。 ⏳Input: 第一行为T&#xff0c;输入包括T组测试数据。 每组数据第一行包括两个数字n和X&#xff0c;第二行有n个整数&#xff0c;表示数列S&#xff0c;(1&l…

Linux-0.11 文件系统inode.c详解

Linux-0.11 文件系统inode.c详解 Linux-0.11中使用的文件系统为minix&#xff0c; inode.c中的函数和该文件系统强关联。 inode节点在文件系统中与文件相关联&#xff0c;一个文件的就由一个inode来管理&#xff0c;这个inode节点将记录文件的权限&#xff0c;大小&#xff0…