人工智能|深度学习——基于Xception实现戴口罩人脸表情识别

news2024/10/12 12:33:06

一、项目背景

近年来,随着人工智能技术的不断发展,人脸表情识别已经成为了计算机视觉领域中的重要研究方向之一。然而,在当前的疫情形势下,佩戴口罩已经成为了一项必要的防疫措施,但是佩戴口罩会遮挡住人脸的部分区域,给表情识别带来了一定的挑战。

目前,已经有很多关于没有遮挡人脸的表情识别的技术研究,例如基于深度学习的卷积神经网络(CNN)和循环神经网络(RNN)等。这些技术已经取得了不错的成果,并且已经应用于实际的场景中,例如情感识别、人机交互等。
然而,在佩戴口罩的情况下,传统的表情识别技术无法识别出人脸的表情信息,因为口罩遮挡了人脸的大部分区域,导致识别的准确率降低。因此,一些研究人员开始关注口罩人脸识别技术,并提出了一些新的方法来解决这个问题。例如,他们使用图像处理技术来减少口罩的遮挡效果,或者使用额外的传感器来收集面部表情的其他数据。
然而,戴口罩人脸表情识别存在一些技术难点。首先,佩戴口罩会导致人脸的特征信息被遮挡,难以准确识别。其次,戴口罩人脸的表情信息可能会因为口罩遮挡而变得不够丰富,这也给表情识别带来了一定的挑战。最后,因为戴口罩人脸表情识别的研究资料较少,数据集需要个人收集标注。因此,如何在遮挡的情况下准确地识别出人脸表情信息是戴口罩人脸表情识别技术研究的重要问题之一

二、数据集来源与详情

2.1 来源

本项目基于fer2013人脸表情数据集,它于2013年国际机器学习会议(ICML)上推出,并成为比较表情识别模型性能的基准之一,同时也作为了2013年Kaggle人脸识别比赛的数据。Fer2013包含28709张训练集图像、3589张公开测试集图像和3589张私有测试集图像,每张图像为4848大小的灰度图片,如下图所示。Fer2013数据集中由有生气(angry)、厌恶(disgust)、恐惧(fear)、开心(happy)、难过(sad)、惊讶(surprise)和中性(neutral)七个类别组成。由于这个数据集大多是通过爬虫在互联网上进行爬取所得,因此存在一定的误差性。

本项目使用face-mask对人脸添加口罩,简单介绍一下face-mask是基于dlib和face_recognition两大人脸检测的库实现的人脸关键点检测的方法。处理完成后共得到11870张训练集,3016张测试集(原数据集存在一些不能被face-mask所识别到的人脸)。经过实验发现sad、disgust、anger眼部特征十分相识,因此本试验只进行对anger、fear、happy、surprise的分类。

2.2 详细处理

  • 安装face-mask 在安装之前要先配置dlib,具体过程请大家自行探索。
pip install face-mask
  • 关键代码
# -*- coding: utf-8 -*-
import os
import numpy as np
from PIL import Image, ImageFile
 
__version__ = '0.3.0'
 
 
IMAGE_DIR = os.path.join(os.path.dirname(os.path.abspath(__file__)), 'images')
DEFAULT_IMAGE_PATH = os.path.join(IMAGE_DIR, 'default-mask.png')
BLACK_IMAGE_PATH = os.path.join(IMAGE_DIR, 'black-mask.png')
BLUE_IMAGE_PATH = os.path.join(IMAGE_DIR, 'blue-mask.png')
RED_IMAGE_PATH = os.path.join(IMAGE_DIR, 'red-mask.png')
 
 
class FaceMasker:
    KEY_FACIAL_FEATURES = ('nose_bridge', 'chin')
 
    def __init__(self, face_path, mask_path, show=False, model='hog'):
        self.face_path = face_path
        self.mask_path = mask_path
        self.show = show
        self.model = model
        self._face_img: ImageFile = None
        self._mask_img: ImageFile = None
 
    def mask(self):
        import face_recognition
 
        face_image_np = face_recognition.load_image_file(self.face_path)
        face_locations = face_recognition.face_locations(face_image_np, model=self.model)
        face_landmarks = face_recognition.face_landmarks(face_image_np, face_locations)
        self._face_img = Image.fromarray(face_image_np)
        self._mask_img = Image.open(self.mask_path)
 
        found_face = False
        for face_landmark in face_landmarks:
            # check whether facial features meet requirement
            skip = False
            for facial_feature in self.KEY_FACIAL_FEATURES:
                if facial_feature not in face_landmark:
                    skip = True
                    break
            if skip:
                continue
 
            # mask face
            found_face = True
            self._mask_face(face_landmark)
 
        if found_face:
            if self.show:
                self._face_img.show()
 
            # save
            self._save()
        else:
            print('Found no face.')
 
    def _mask_face(self, face_landmark: dict):
        nose_bridge = face_landmark['nose_bridge']
        nose_point = nose_bridge[len(nose_bridge) * 1 // 4]
        nose_v = np.array(nose_point)
 
        chin = face_landmark['chin']
        chin_len = len(chin)
        chin_bottom_point = chin[chin_len // 2]
        chin_bottom_v = np.array(chin_bottom_point)
        chin_left_point = chin[chin_len // 8]
        chin_right_point = chin[chin_len * 7 // 8]
 
        # split mask and resize
        width = self._mask_img.width
        height = self._mask_img.height
        width_ratio = 1.2
        new_height = int(np.linalg.norm(nose_v - chin_bottom_v))
 
        # left
        mask_left_img = self._mask_img.crop((0, 0, width // 2, height))
        mask_left_width = self.get_distance_from_point_to_line(chin_left_point, nose_point, chin_bottom_point)
        mask_left_width = int(mask_left_width * width_ratio)
        mask_left_img = mask_left_img.resize((mask_left_width, new_height))
 
        # right
        mask_right_img = self._mask_img.crop((width // 2, 0, width, height))
        mask_right_width = self.get_distance_from_point_to_line(chin_right_point, nose_point, chin_bottom_point)
        mask_right_width = int(mask_right_width * width_ratio)
        mask_right_img = mask_right_img.resize((mask_right_width, new_height))
 
        # merge mask
        size = (mask_left_img.width + mask_right_img.width, new_height)
        mask_img = Image.new('RGBA', size)
        mask_img.paste(mask_left_img, (0, 0), mask_left_img)
        mask_img.paste(mask_right_img, (mask_left_img.width, 0), mask_right_img)
 
        # rotate mask
        angle = np.arctan2(chin_bottom_point[1] - nose_point[1], chin_bottom_point[0] - nose_point[0])
        rotated_mask_img = mask_img.rotate(angle, expand=True)
 
        # calculate mask location
        center_x = (nose_point[0] + chin_bottom_point[0]) // 2
        center_y = (nose_point[1] + chin_bottom_point[1]) // 2
 
        offset = mask_img.width // 2 - mask_left_img.width
        radian = angle * np.pi / 180
        box_x = center_x + int(offset * np.cos(radian)) - rotated_mask_img.width // 2
        box_y = center_y + int(offset * np.sin(radian)) - rotated_mask_img.height // 2
 
        # add mask
        self._face_img.paste(mask_img, (box_x, box_y), mask_img)
 
    def _save(self):
        path_splits = os.path.splitext(self.face_path)
        new_face_path = path_splits[0] + '-with-mask' + path_splits[1]
        self._face_img.save(new_face_path)
        print(f'Save to {new_face_path}')
 
    @staticmethod
    def get_distance_from_point_to_line(point, line_point1, line_point2):
        distance = np.abs((line_point2[1] - line_point1[1]) * point[0] +
                          (line_point1[0] - line_point2[0]) * point[1] +
                          (line_point2[0] - line_point1[0]) * line_point1[1] +
                          (line_point1[1] - line_point2[1]) * line_point1[0]) / \
                   np.sqrt((line_point2[1] - line_point1[1]) * (line_point2[1] - line_point1[1]) +
                           (line_point1[0] - line_point2[0]) * (line_point1[0] - line_point2[0]))
        return int(distance)
 
 
if __name__ == '__main__':
    FaceMasker("./face/1.jpg", DEFAULT_IMAGE_PATH, True, 'hog').mask()
  • 本项目已为大家提供处理好的数据集地址 ,后边直接使用。如果想了解更多处理细节请关注我的主页

三、模型介绍

3.1 Xception简介

Xception是Google公司继Inception后提出的对 Inception-v3 的另一种改进。作者认为,通道之间的相关性与空间相关性最好要分开处理。于是采用 Separable Convolution来替换原来 Inception-v3中的卷积操作。
传统卷积的实现过程:

Depthwise Separable Convolution 的实现过程:

深度可分离卷积 Depthwise Separable Convolution
Depthwise Separable Convolution 与 极致的 Inception 区别:
极致的 Inception:

  • 第一步:普通 1×1 卷积。
  • 第二步:对 1×1 卷积结果的每个 channel,分别进行 3×3卷积操作,并将结果 concat。

Depthwise Separable Convolution:

  • 第一步:Depthwise 卷积,对输入的每个channel,分别进行 3×3卷积操作,并将结果 concat。
  • 第二步:Pointwise 卷积,对 Depthwise 卷积中的 concat 结果,进行1×1卷积操作。

两种操作的顺序不一致:Inception 先进行1×1卷积,再进行3×3 卷积;Depthwise Separable Convolution 先进行 3×3卷积,再进行 1×1卷积。

3.2 Xception网络框架

先进行普通卷积操作,再对 1×1 卷积后的每个channel分别进行 3×3 卷积操作,最后将结果 concat。

注:Xception包含三个部分:输入部分,中间部分和结尾部分;其中所有卷积层和可分离卷积层后面都使用Batch Normalization处理,所有的可分离卷积层使用一个深度乘数1(深度方向并不进行扩充)。

三、技术路线

3.1 PaddleClas简介

飞桨图像分类套件PaddleClas是飞桨为工业界和学术界所准备的一个图像分类任务的工具集,助力使用者训练出更好的视觉模型和应用落地。

PaddleClas特性:

  1. 丰富的模型库:基于ImageNet1k分类数据集,PaddleClas提供了29个系列的分类网络结构和训练配置,133个预训练模型和性能评估。
  2. SSLD知识蒸馏:基于该方案蒸馏模型的识别准确率普遍提升3%以上。
  3. 数据增广:支持AutoAugment、Cutout、Cutmix等8种数据增广算法详细介绍、代码复现和在统一实验环境下的效果评估。
  4. 10万类图像分类预训练模型:百度自研并开源了基于10万类数据集训练的 ResNet50_vd 模型,在一些实际场景中,使用该预训练模型的识别准确率最多可以提升30%。
  5. 多种训练方案,包括多机训练、混合精度训练等。
  6. 多种预测推理、部署方案,包括TensorRT预测、Paddle-Lite预测、模型服务化部署、模型量化、Paddle Hub等。可运行于Linux、Windows、MacOS等多种系统。

3.2 模型库链接

Paddleclas代码GitHub链接: https://github.com/PaddlePaddle/PaddleClas

Paddleclas代码Gitee链接: https://gitee.com/PaddlePaddle/PaddleClas

Paddleclas文档链接: https://paddleclas.readthedocs.io

四、环境配置

  • 更新pip(很重要,要不然很多包装不上)
pip install --upgrade pip
  • 克隆Paddleclas仓库
%cd /home/aistudio/work/
# 克隆Paddleclas仓库
# gitee 国内下载比较快
!git clone https://gitee.com/PaddlePaddle/PaddleClas.git
# github 
# !git clone https://github.com/PaddlePaddle/PaddleClas.git
  • 导入package
%cd /home/aistudio/work/PaddleClas
# 导入package
!pip install -r requirements.txt
  • 解压数据集
# 解压数据集
# 注意数据集压缩包的名字和位置
!echo "解压数据集"
!unzip -oq /home/aistudio/data/data201630/fer2013(添加口罩_四分类) _.zip -d /home/aistudio/work/PaddleClas/dataset
  • 数据集结构
dataset:
    train:
        imags            # 训练集图片
        train_list.txt   # 训练集标签
    eval:
        imags            # 评估图片
        eval_list.txt    # 评估标签
    test:
        imags            #测试图片
        test_list.txt    #测试标签
        list.txt           # 标签对应含义

五、模型训练

5.1 配置训练参数文件(已为大家提供参考参数文件)

  • 本项目使用的配置文件是
/home/aistudio/work/PaddleClas/ppcls/configs/ImageNet/Xception/Xception65.yaml
# global configs
Global:
  ...
  output_dir: ./output/ # 模型保存路径
  epochs: 200       # 训练步数
  ...
  use_visualdl: Ture  # 训练可视化,在训练时可以绘制出可视化图像
  ...
  save_inference_dir: ./inference #导出模型保存路径
  
  ....


Optimizer:
  ....
  lr:
    name: Cosine
    learning_rate: 0.045 # 学习率,根据训练时的可视化图像进行调整
  regularizer:
    name: 'L2'
    coeff: 0.0001
.....
# data loader for train and eval
DataLoader:
  Train:
    dataset:
      name: ImageNetDataset
      image_root: ./dataset/ # 图片路径
      cls_label_path: ./dataset/train/train_list.txt # 标签路径
      ......
    sampler:
      name: DistributedBatchSampler
      batch_size: 64 # 根据配置进行调整,在环境最左边工具栏的监控中根据显存和cup占用率进行调整
      ....

  Eval:
    dataset: 
      name: ImageNetDataset
      image_root: ./dataset/
      cls_label_path: ./dataset/eval/eval_list.txt # 同上
      .....

Infer:
  infer_imgs: ./dataset/test/imags/10276.jpg  # 预测的图片
  batch_size: 10 # 根据实际调整
  ....
    class_id_map_file: ./dataset/test/list.txt# 此文件是标签对应的含义,根据开始数据集说明时的图片可知:0对应生气 1害怕.....

5.2 训练

%cd /home/aistudio/work/PaddleClas
!echo "开始炼丹"
!python3 -m paddle.distributed.launch tools/train.py \
-c /home/aistudio/work/PaddleClas/ppcls/configs/ImageNet/Xception/Xception65.yaml
  • 精确度

5.3 加载训练可视化

  • 设置VisualDL服务

  • 加载logdir

  • 启动打开

5.4 效果

六、模型预测

备注:

  • 这里-o Global.pretrained_model="/home/aistudio/work/PaddleClas/output/Xception65/best_model" 指定了当前最佳权重所在的路径,如果指定其他权重,只需替换对应的路径即可。
  • 默认是对 /home/aistudio/work/data/test 进行预测,此处也可以通过增加字段 -o Infer.infer_imgs=xxx 对其他图片预测。
  • 默认输出的是 Top-5 的值,如果希望输出 Top-k 的值,可以指定-o Infer.PostProcess.topk=k,其中,k 为您指定的值。

修改 /home/aistudio/work/PaddleClas-2.4.0/ppcls/engine/engine.py 中的推理代码 infer(self),将推理结果存入csv文件。

@paddle.no_grad()
    def infer(self):
        assert self.mode == "infer" and self.eval_mode == "classification"
        total_trainer = dist.get_world_size()
        local_rank = dist.get_rank()
        image_list = get_image_list(self.config["Infer"]["infer_imgs"])
        # data split
        image_list = image_list[local_rank::total_trainer]

        with open("/home/aistudio/work/PaddleClas/dataset/eval/eval_list.txt", "r") as f:
            dat = f.readlines()
        Dta= {}
        for delr in dat:
            wed=delr.replace('./eval/imags/','').replace('\n','').split(' ')
            Dta[wed[0]] =wed[1]
        print(Dta)

        csvfile = open("/home/aistudio/work/PaddleClas/output/result.csv", "w", newline="")
        writer = csv.writer(csvfile)

        batch_size = self.config["Infer"]["batch_size"]
        self.model.eval()
        batch_data = []
        image_file_list = []
        for idx, image_file in enumerate(image_list):
            with open(image_file, 'rb') as f:
                x = f.read()
            for process in self.preprocess_func:
                x = process(x)
            batch_data.append(x)
            image_file_list.append(image_file)
            if len(batch_data) >= batch_size or idx == len(image_list) - 1:
                batch_tensor = paddle.to_tensor(batch_data)

                if self.amp and self.amp_eval:
                    with paddle.amp.auto_cast(
                            custom_black_list={
                                "flatten_contiguous_range", "greater_than"
                            },
                            level=self.amp_level):
                        out = self.model(batch_tensor)
                else:
                    out = self.model(batch_tensor)

                if isinstance(out, list):
                    out = out[0]
                if isinstance(out, dict) and "Student" in out:
                    out = out["Student"]
                if isinstance(out, dict) and "logits" in out:
                    out = out["logits"]
                if isinstance(out, dict) and "output" in out:
                    out = out["output"]
                result = self.postprocess_func(out, image_file_list)
                print(result)
                

                for res in result:
                    file_name = res['file_name'].split('/')[-1]
                    class_id  = res['class_ids'][0]
                    try:
                        ture_id = Dta[file_name]
                        writer.writerow([file_name, class_id,ture_id])
                    except:
                        pass
                batch_data.clear()
                image_file_list.clear()
%cd /home/aistudio/work/PaddleClas/
!echo "模型预测"
!python tools/infer.py \
    -c /home/aistudio/work/PaddleClas/ppcls/configs/ImageNet/Xception/Xception65.yaml\
    -o Global.pretrained_model=/home/aistudio/work/PaddleClas/output/Xception65/best_model
  • 提取预测结果
import glob
import matplotlib.pyplot as plt
import pandas as pd
import cv2

data_1=pd.read_csv("/home/aistudio/work/PaddleClas/output/result.csv",",",encoding="utf-8",header=None)
# 随机抽取数据
data_2=data_1.sample(10)
print(data_2)

ji = 1
for index, row in data_2.iterrows():
    i=row.tolist()
    img = cv2.imread("/home/aistudio/work/PaddleClas/dataset/eval/imags/"+str(i[0]))
    title='pri:'+str(i[1])+'    true:'+str(i[2])
    plt.subplot(3,5,ji)
    plt.imshow(cv2.cvtColor(img, cv2.COLOR_BGR2RGB))
    plt.title(title,fontsize=10)
    ji=ji+1

plt.tight_layout()
plt.show()
sum_num = 0
cr_num = 0
for index, row in data_1.iterrows():
    i = row.tolist()
    if int(i[1])==int(i[2]):
        cr_num = cr_num + 1
    sum_num = sum_num + 1

print("正确:",cr_num,"\n总数:",sum_num)
print('ACC:',cr_num*100/sum_num,"%")

七、模型导出

# 导出模型
!echo "模型导出"
!python tools/export_model.py -c /home/aistudio/work/PaddleClas/ppcls/configs/ImageNet/Xception/Xception65.yaml\
         -o Global.pretrained_model=/home/aistudio/work/PaddleClas/output/Xception65/best_model\
        -o Global.save_inference_dir=./inference
  • 模型结构

八、模型部署

参考
  • EfficientNet实现农业病害识别(FastDeploy部署和安卓端部署)
  • 基于PP-PicoDet行车检测(完成安卓端部署))

九、项目总结

本次项目的完成可谓是经历重重困难,在看到题目时是懵懵的。在我以往学习的知识里,单纯人脸表情识别并没有太大的难度,但是要求对遮挡人脸的表情进行识别(口罩遮挡),查阅资料发现连数据集都没有找到,后来只能自己构建数据集,就利用以往做人脸表情识别的经验,使用未遮挡人脸通过检测人脸面部特征,定位眼、口、鼻、嘴给面部添加口罩。又学习了face-mask的一些操作,算是把数据集构建完成。当选取神经网络时问题有来了,该选取什么网络才好呢?从主流的ResNet、SwinTransformer、VGG效果都不是很理想。仔细分析问题,发现数据集中sad、disgust、anger眼部特征十分相识,所以就减少分类,重新处理数据集。在探索过程中隐约发现Xception网络特性(上文已介绍)可能对此数据集能产生较好的效果,果断进行测试,分别使用Xception41_deeplab和Xception65进行对比,发现ception65精度上升较快(这里可能不严谨,lr等当时并未考虑太多),紧接着使用可视化训练,观察lr,loss,acc曲线变化关系,调整参数,最终只实现了0.72左右精度。因为时间的原因,我没有办法继续优化项目,再以后的时间里我会不断的学习相关知识和优化项目,如果大家有好的想法和观点,欢迎给我留言!!!

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

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

相关文章

政安晨:【Keras机器学习实践要点】(十五)—— KerasTuner 简述

目录 导言 调整模型结构 定义搜索空间 开始搜索 查询结果 重新训练模型 调整模型训练 调整数据预处理 重新训练模型 指定调整目标 以内置指标为目标 以自定义指标为目标 调整端到端工作流程 将 Keras 代码分开 政安晨的个人主页:政安晨 欢迎 &#x1…

CVE-2021-30517:Type confusion bug in LoadSuperIC

前言 这个漏洞是一个比较老的洞,之所以分析这个漏洞,只要是想再学习一下 ICs 相关的知识。并该漏洞的利用是利用与 String/Function 之间的混淆,比较有意思。 环境搭建 sudo apt install python git checkout 7d5e5f6c62c3f38acee12dc4114…

端口映射如何测试?

端口映射是一项网络技术,用于将外部网络中的数据流量映射到内部网络中的特定端口或设备上。通过端口映射,可以实现远程访问内部网络中的设备或应用程序,使其能够在外部网络中得到访问。本文将介绍端口映射测试及其应用场景。 2. 【天联】组网…

【大数据存储】实验4 NoSQL数据库

实验4 NoSQL数据库 NoSQL数据库的安装和使用实验环境: Ubuntu 22.04.3 Jdk 1.8.0_341 Hadoop 3.2.3 Hbase 2.4.17 Redis 6.0.6 mongdb 6.0.12 mogosh 2.1.0 Redis 安装redis完成 新建终端启动redisredis-server新建一个终端redis-cli 建表操作 尝…

详细分析Vuex中的mapGetters

目录 1. 基本知识2. Demo13. Demo2 1. 基本知识 优势和用途 简化代码:用 mapGetters 和 mapState,可以简化组件中对于 Vuex 中状态和 getter 的映射工作,减少了重复的代码书写更易读:组件中直接使用映射的计算属性,使…

项目5-验证码案例

选择使用Google的开源项目Kaptcha来实现. 1.Kaptcha 插件介绍 Kaptcha 是Google的⼀个高度可配置的实⽤验证码⽣成⼯具. 代码: http://code.google.com/p/kaptcha/ ⽹上有很多⼈甚⾄公司基于Google的kaptcha进⾏了⼆次开发. 我们选择⼀个直接适配SpringBoot的 开源项目 htt…

吴恩达:AI 智能体的四种模式

一、背景 吴恩达在《What’s next for AI agentic workflows ft》分享中提出 AI 智能体的四种模式。 反思(Reflection): LLM 检查自己的工作,以提出改进方法。 使用工具(Tool use):LLM 拥有…

MySQL数据库 数据库基本操作(二):表的增删查改(上)

1. CRUD CRUD 即增加(Create)、查询(Retrieve)、更新(Update)、删除(Delete)四个单词的首字母缩写,就是数据库基本操作中针对表的一系列操作. 2. 新增(create) -->insert 语法: insert into 表名 [列名1,列名2…] values (val1,val2…) [注意] 列名可以没有,如果没有列名…

【深度学习|Pytorch】torchvision.datasets.ImageFolder详解

ImageFolder详解 1、数据准备2、ImageFolder类的定义transforms.ToTensor()解析 3、ImageFolder返回对象 1、数据准备 创建一个文件夹,比如叫dataset,将cat和dog文件夹都放在dataset文件夹路径下: 2、ImageFolder类的定义 class ImageFol…

大日志精选案例四:某省级大数据集团日志审计优化实战解析

“在集团日常运营中,数据安全始终是我们关注的重点。过去,数据量大、处理速度慢,导致日志数据难以迅速获取和分析,影响业务决策。但自从引入聚铭大日志解决方案后,系统日志和用户行为数据都得到了高效处理与存储。该方…

SpringCloud Hystrix 服务熔断、服务降级防止服务雪崩

文章目录 SpringCloud Hystrix 熔断器、服务降级防止服务雪崩需求背景引入依赖启动类加Hystrix注解接口配置熔断常规配置超时断开错误率熔断请求数熔断限流 可配置项HystrixCommand.Setter参数Command Properties 服务降级 SpringCloud Hystrix 熔断器、服务降级防止服务雪崩 H…

网络安全基础之网络协议与安全威胁

OSI(OpenSystem Interconnect),即开放式系统互联。 一般都叫OSI参考模型,是ISO(国际标准化组织)组织在1985年研究的网络互联模型。 网络协议的简介: 定义:协议是网络中计算机或设备之间进行通信的一系列规则集合。 什么是规则?…

迅饶科技 X2Modbus 网关 GetUser 信息泄露漏洞复现

0x01 产品简介 X2Modbus是上海迅饶自动化科技有限公司Q开发的一款功能很强大的协议转换网关, 这里的X代表各家不同的通信协议, 2是T0的谐音表示转换, Modbus就是最终支持的标准协议是Modbus协议。用户可以根据现场设备的通信协议进行配置,转成标准的Modbus协议。在PC端仿真…

【考研数学】1800基础做完了,如何无缝衔接660和880❓

基础题做完,不要急着强化 首先做一个复盘,1800基础的正确率如何,如果70%以下的话,从错题入手,把掌握不扎实的地方再进行巩固,否则接下来做题的话效率会很低。 接下来考虑习题衔接的问题。 关于线代复习的…

(免费分享)基于微信小程序自助停取车收费系统

本项目的开发和制作主要采用Java语言编写,SpringBoot作为项目的后端开发框架,vue作为前端的快速开发框架,主要基于ES5的语法,客户端采用微信小程序作为开发。Mysql8.0作为数据库的持久化存储。 获取完整源码: 大家点赞…

mac | Windows 本地部署 Seata2.0.0,Nacos 作为配置中心、注册中心,MySQL 存储信息

1、本人环境介绍 系统 macOS sonama 14.1.1 MySQL 8.2.0 (官方默认是5.7版本) Seata 2.0.0 Nacos 2.2.3 2、下载&数据库初始化 默认你已经有 Nacos、MySQL,如果没有 Nacos 请参考我的文章 : Docker 部署 Nacos(单机…

基于51单片机和MAX1898的智能手机充电器设计

**单片机设计介绍,基于51单片机和MAX1898的智能手机充电器设计 文章目录 一 概要二、功能设计设计思路 三、 软件设计原理图 五、 程序六、 文章目录 一 概要 基于51单片机和MAX1898的智能手机充电器设计概要 一、引言 随着智能手机的普及,其电池续航…

人工智能会拥有反思能力吗?

一、背景 人工智能是否能拥有真正的反思能力,目前仍在探索和发展之中。虽然现有的AI系统可以在一定程度上进行自我学习、自我调整和优化,但是它们的“反思”还远未达到人类意义上的深度和全面性。 传统的人工智能系统依赖于预设的算法和模型&#xff0c…

如何把png格式的图片转换成ico格式的图标

前言 前段时间有朋友说想要把某个图标做成图标,用来更改某个程序或者软件的图标,以求个性化! 但是下载下来的.png图片直接更改文件扩展名为.ico之后,并不能正常使用。这时候就需要有工具把.png格式转换为.ico格式。 文件更换格式…

Python Django全文搜索库之django-haystack使用详解

概要 Django Haystack库是一个用于在Django项目中实现全文搜索功能的强大工具。它集成了各种搜索引擎,如Elasticsearch、Whoosh等,为开发者提供了灵活且高效的搜索解决方案。在本文中,将深入探讨Django Haystack库的安装、配置和应用,以及如何利用其丰富的功能来实现高级全…