Wider Face+YOLOV7人脸检测

news2024/11/25 13:23:23

1 Wider Face标注格式转成YOLO格式

1.1 Wider Face标注介绍

The format of txt ground truth.
File name
Number of bounding box
x1, y1, w, h, blur, expression, illumination, invalid, occlusion, pose
'''
0--Parade/0_Parade_marchingband_1_849.jpg
1
449 330 122 149 0 0 0 0 0 0 
0--Parade/0_Parade_Parade_0_904.jpg
1
361 98 263 339 0 0 0 0 0 0
'''

1.2 YOLO标注介绍

The format of txt ground truth.
class, cx, cy, w, h(坐标为归一化后的数值)
'''
0 0.04248046875 0.5455729166666666 0.0283203125 0.046875
0 0.2646484375 0.505859375 0.03125 0.05078125
0 0.33642578125 0.50390625 0.0185546875 0.028645833333333332
'''

1.3 数据格式转换

1.3.1 Wider Face格式转成VOC格式

首先创建文件夹1_face2voc,在该文件夹下创建Annotations、JPEGImages、Labels文件夹

格式转换代码需要运行两次(将路径中的train换成val即可),分别生成对应的train和val文件,转换代码如下:

# coding:utf-8
import cv2
from xml.dom.minidom import Document
 
  
def writexml(filename, saveimg, bboxes, xmlpath):
    doc = Document()
 
    annotation = doc.createElement('annotation')
 
    doc.appendChild(annotation)
 
    folder = doc.createElement('folder')
 
    folder_name = doc.createTextNode('widerface')
    folder.appendChild(folder_name)
    annotation.appendChild(folder)
    filenamenode = doc.createElement('filename')
    filename_name = doc.createTextNode(filename)
    filenamenode.appendChild(filename_name)
    annotation.appendChild(filenamenode)
    source = doc.createElement('source')
    annotation.appendChild(source)
    database = doc.createElement('database')
    database.appendChild(doc.createTextNode('wider face Database'))
    source.appendChild(database)
    annotation_s = doc.createElement('annotation')
    annotation_s.appendChild(doc.createTextNode('PASCAL VOC2007'))
    source.appendChild(annotation_s)
    image = doc.createElement('image')
    image.appendChild(doc.createTextNode('flickr'))
    source.appendChild(image)
    flickrid = doc.createElement('flickrid')
    flickrid.appendChild(doc.createTextNode('-1'))
    source.appendChild(flickrid)
    owner = doc.createElement('owner')
    annotation.appendChild(owner)
    flickrid_o = doc.createElement('flickrid')
    flickrid_o.appendChild(doc.createTextNode('muke'))
    owner.appendChild(flickrid_o)
    name_o = doc.createElement('name')
    name_o.appendChild(doc.createTextNode('muke'))
    owner.appendChild(name_o)
 
    size = doc.createElement('size')
    annotation.appendChild(size)
 
    width = doc.createElement('width')
    width.appendChild(doc.createTextNode(str(saveimg.shape[1])))
    height = doc.createElement('height')
    height.appendChild(doc.createTextNode(str(saveimg.shape[0])))
    depth = doc.createElement('depth')
    depth.appendChild(doc.createTextNode(str(saveimg.shape[2])))
 
    size.appendChild(width)
 
    size.appendChild(height)
    size.appendChild(depth)
    segmented = doc.createElement('segmented')
    segmented.appendChild(doc.createTextNode('0'))
    annotation.appendChild(segmented)
    for i in range(len(bboxes)):
        bbox = bboxes[i]
        objects = doc.createElement('object')
        annotation.appendChild(objects)
        object_name = doc.createElement('name')
        object_name.appendChild(doc.createTextNode('face'))
        objects.appendChild(object_name)
        pose = doc.createElement('pose')
        pose.appendChild(doc.createTextNode('Unspecified'))
        objects.appendChild(pose)
        truncated = doc.createElement('truncated')
        truncated.appendChild(doc.createTextNode('0'))
        objects.appendChild(truncated)
        difficult = doc.createElement('difficult')
        difficult.appendChild(doc.createTextNode('0'))
        objects.appendChild(difficult)
        bndbox = doc.createElement('bndbox')
        objects.appendChild(bndbox)
        xmin = doc.createElement('xmin')
        xmin.appendChild(doc.createTextNode(str(bbox[0])))
        bndbox.appendChild(xmin)
        ymin = doc.createElement('ymin')
        ymin.appendChild(doc.createTextNode(str(bbox[1])))
        bndbox.appendChild(ymin)
        xmax = doc.createElement('xmax')
        xmax.appendChild(doc.createTextNode(str(bbox[0] + bbox[2])))
        bndbox.appendChild(xmax)
        ymax = doc.createElement('ymax')
        ymax.appendChild(doc.createTextNode(str(bbox[1] + bbox[3])))
        bndbox.appendChild(ymax)
    f = open(xmlpath, "w")
    f.write(doc.toprettyxml(indent=''))
    f.close()
 
rootdir = "/kaxier01/projects/FAS/yolov7/wider_face/1_face2voc"  # 根目录
gtfile = "/kaxier01/projects/FAS/yolov7/wider_face/wider_face_split/wider_face_val_bbx_gt.txt"  # Wider Face原始标注
im_folder = "/kaxier01/projects/FAS/yolov7/wider_face/WIDER_val/images"
fwrite = open("/kaxier01/projects/FAS/yolov7/wider_face/1_face2voc/Labels/val.txt", "w")

with open(gtfile, "r") as gt:
    while(True):
        gt_con = gt.readline()[:-1]
        if gt_con is None or gt_con == "":
            break
        im_path = im_folder + "/" + gt_con
        print(im_path)
        im_data = cv2.imread(im_path)
        if im_data is None:
            continue
 
        numbox = int(gt.readline())
 
        # 获取每一行人脸数据
        bboxes = []
        if numbox == 0:  # numbox 为0 的情况处理
            gt.readline()
        else:
            for i in range(numbox):
                line = gt.readline()
                infos = line.split(" ")  # 用空格分割
                bbox = (int(infos[0]), int(infos[1]), int(infos[2]), int(infos[3]))
                bboxes.append(bbox)  # 将一张图片的所有人脸数据加入bboxes
            filename = gt_con.replace("/", "_")  # 将存储位置作为图片名称,斜杠转为下划线
            fwrite.write(filename.split(".")[0] + "\n")
            cv2.imwrite("{}/JPEGImages/{}".format(rootdir, filename), im_data)
            xmlpath = "{}/Annotations/{}.xml".format(rootdir, filename.split(".")[0])  # xml文件保存路径
            writexml(filename, im_data, bboxes, xmlpath)
fwrite.close()

1.3.2 VOC格式转成COCO格式

首先创建2_voc2coco文件夹,在该文件夹下创建wider_coco文件夹,在wider_coco文件夹下创建images和xml_annotations文件夹

格式转换:1)利用1.3.1步骤中生成的xml文件(生成的train和val的xml文件合并放在一个文件夹内,路径为'1_face2voc/Annotations')以及'1_face2voc/Labels'下的train.txt和val.txt,将图片和xml文件分为训练集和验证集,

代码如下:

# coding:utf-8
import os
import shutil
from tqdm import tqdm


SPLIT_PATH = "/kaxier01/projects/FAS/yolov7/wider_face/1_face2voc/Labels"
IMGS_PATH = "/kaxier01/projects/FAS/yolov7/wider_face/1_face2voc/JPEGImages"
TXTS_PATH = "/kaxier01/projects/FAS/yolov7/wider_face/1_face2voc/Annotations"

TO_IMGS_PATH = '/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/wider_coco/images'
TO_TXTS_PATH = '/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/wider_coco/xml_annotations'

data_split = ['train.txt', 'val.txt']
to_split = ['train', 'val']

train_file = '/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/images_train.txt'
val_file = '/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/images_val.txt'
train_file_txt = ''
val_file_txt = ''

for index, split in enumerate(data_split):
    split_path = os.path.join(SPLIT_PATH, split)

    to_imgs_path = os.path.join(TO_IMGS_PATH, to_split[index])
    if not os.path.exists(to_imgs_path):
        os.makedirs(to_imgs_path)

    to_txts_path = os.path.join(TO_TXTS_PATH, to_split[index])
    if not os.path.exists(to_txts_path):
        os.makedirs(to_txts_path)

    f = open(split_path, 'r')
    count = 1

    for line in tqdm(f.readlines(), desc="{} is copying".format(to_split[index])):
        # 复制图片
        src_img_path = os.path.join(IMGS_PATH, line.strip() + '.jpg')
        dst_img_path = os.path.join(to_imgs_path, line.strip() + '.jpg')
        if os.path.exists(src_img_path):
            shutil.copyfile(src_img_path, dst_img_path)
        else:
            print("error file: {}".format(src_img_path))
        if to_split[index] == 'train':
            train_file_txt = train_file_txt + dst_img_path + '\n'
        elif to_split[index] == 'val':
            val_file_txt = val_file_txt + dst_img_path + '\n'

        # 复制txt标注文件
        src_txt_path = os.path.join(TXTS_PATH, line.strip() + '.xml')
        dst_txt_path = os.path.join(to_txts_path, line.strip() + '.xml')
        if os.path.exists(src_txt_path):
            shutil.copyfile(src_txt_path, dst_txt_path)
        else:
            print("error file: {}".format(src_txt_path))
    with open(train_file, 'w') as out_train:
        out_train.write(train_file_txt)

    with open(val_file, 'w') as out_val:
        out_val.write(val_file_txt)

2)将VOC标注格式转换成COCO格式,生成的json文件用于评估模型性能,代码如下:

import sys
import os
import json
import xml.etree.ElementTree as ET
import glob

START_BOUNDING_BOX_ID = 1
PRE_DEFINE_CATEGORIES = {"face" : 0}


def get(root, name):
    vars = root.findall(name)
    return vars


def get_and_check(root, name, length):
    
    vars = root.findall(name)
    if len(vars) == 0:
        raise ValueError("Can not find %s in %s." % (name, root.tag))
    if length > 0 and len(vars) != length:
        raise ValueError(
            "The size of %s is supposed to be %d, but is %d."
            % (name, length, len(vars))
        )
    if length == 1:
        vars = vars[0]
    return vars


def get_filename_as_int(filename):
    try:
        # print(filename,filename[6:])
        filename = filename.replace("\\", "/")
        filename = os.path.splitext(os.path.basename(filename))[0]
        if filename[:5] == "India" :  return  int("2"+filename[6:])
        elif filename[:5] == "Japan" :  return  int("3"+filename[6:])
        else : return int("1"+filename[6:])
        
        #return int(filename[6:])
    except:
        raise ValueError("Filename %s is supposed to be an integer." % (filename))


def get_categories(xml_files):
    """Generate category name to id mapping from a list of xml files.
    
    Arguments:
        xml_files {list} -- A list of xml file paths.
    
    Returns:
        dict -- category name to id mapping.
    """
    acceptable_classes = ["car","truck","bus"]
    classes_names = []
    for xml_file in xml_files:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        for member in root.findall("object"):
            classes_names.append(member[0].text)
    classes_names = list(set(classes_names))
    return {name: i for i, name in enumerate(classes_names)}


def convert(xml_files, json_file):
    json_dict = {"images": [], "type": "instances", "annotations": [], "categories": []}
    if PRE_DEFINE_CATEGORIES is not None:
        categories = PRE_DEFINE_CATEGORIES
    else:
        categories = get_categories(xml_files)
    bnd_id = START_BOUNDING_BOX_ID
    for xml_file in xml_files:
        tree = ET.parse(xml_file)
        root = tree.getroot()
        path = get(root, "path")
        if len(path) == 1:
            filename = os.path.basename(path[0].text)
        elif len(path) == 0:
            filename = get_and_check(root, "filename", 1).text
        else:
            raise ValueError("%d paths found in %s" % (len(path), xml_file))
        image_id = filename[:-4]
        size = get_and_check(root, "size", 1)
        width = int(get_and_check(size, "width", 1).text)
        height = int(get_and_check(size, "height", 1).text)
        image = {
            "file_name": filename,
            "height": height,
            "width": width,
            "id": filename[:-4],
        }
        json_dict["images"].append(image)
        
        for obj in get(root, "object"):
            category = get_and_check(obj, "name", 1).text
            if category not in categories:
                continue
            category_id = categories[category]
            bndbox = get_and_check(obj, "bndbox", 1)
            xmin = int(get_and_check(bndbox, "xmin", 1).text) - 1
            ymin = int(get_and_check(bndbox, "ymin", 1).text) - 1
            xmax = int(get_and_check(bndbox, "xmax", 1).text)
            ymax = int(get_and_check(bndbox, "ymax", 1).text)
            assert xmax > xmin
            assert ymax > ymin
            o_width = abs(xmax - xmin)
            o_height = abs(ymax - ymin)
            ann = {
                "area": o_width * o_height,
                "iscrowd": 0,
                "image_id": image_id,
                "bbox": [xmin, ymin, o_width, o_height],
                "category_id": category_id,
                "id": bnd_id,
                "ignore": 0,
                "segmentation": [],
            }
            json_dict["annotations"].append(ann)
            bnd_id = bnd_id + 1

    for cate, cid in categories.items():
        cat = {"supercategory": "none", "id": cid, "name": cate}
        json_dict["categories"].append(cat)

    os.makedirs(os.path.dirname(json_file), exist_ok=True)
    json_fp = open(json_file, "w")
    json_str = json.dumps(json_dict, indent=4)
    json_fp.write(json_str)
    json_fp.close()


if __name__ == "__main__":
    import argparse

    parser = argparse.ArgumentParser(
        description="Convert Pascal VOC annotation to COCO format."
    )

    xml_path = '/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/wider_coco/xml_annotations/val'  # 这是xml文件所在的地址
    json_file = '/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/annotations/val.json'  # 这是你要生成的json文件
    xml_files = glob.glob(os.path.join(xml_path, "*.xml"))

    # If you want to do train/test split, you can pass a subset of xml files to convert function.
    print("Number of xml files: {}".format(len(xml_files)))
    convert(xml_files, json_file)
    print("Success: {}".format(json_file))

1.3.3 VOC格式转成YOLO格式

首先创建3_voc2yolo文件夹,在该文件夹下创建images和labels文件夹

1)提取由1.3.2步骤划分好的xml文件的文件名('2_voc2coco/wider_coco/xml_annotations/val/***.xml')并将文件名保存在'2_voc2coco/name_val.txt or name_train.txt ',代码需要执行两次,代码如下:

import os

file_path = "/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/wider_coco/xml_annotations/val/"
path_list = os.listdir(file_path)  # os.listdir(file)会历遍文件夹内的文件并返回一个列表
path_name = []  # 把文件列表写入save.txt中


def saveList(pathName):
    for file_name in pathName:
        with open("/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/name_val.txt", "a") as f:
            f.write(file_name.split(".")[0] + "\n")


def dirList(path_list):
    for i in range(0, len(path_list)):
        path = os.path.join(file_path, path_list[i])
    if os.path.isdir(path):
        saveList(os.listdir(path))


dirList(path_list)
saveList(path_list)

2)将xml格式转成YOLO格式,代码需要执行两次,代码如下:

import xml.etree.ElementTree as ET
import os


classes = ['face']

train_file = '/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/wider_val.txt'  
train_file_txt = ''

wd = os.getcwd()

def convert(size, box):
    dw = 1. / size[0]
    dh = 1. / size[1]
    box = list(box)
    box[1] = min(box[1], size[0])   # 限制目标的范围在图片尺寸内
    box[3] = min(box[3], size[1])
    x = ((box[0] + box[1]) / 2.0) * dw
    y = ((box[2] + box[3]) / 2.0) * dh
    w = (box[1] - box[0]) * dw
    h = (box[3] - box[2]) * dh
    return (x, y, w, h)   


def convert_annotation(image_id):
    in_file = open('/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/wider_coco/xml_annotations/val/%s.xml' % (image_id))  # 读取xml文件路径

    out_file = open('/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/labels/val/%s.txt' % (image_id), 'w')  # 需要保存的txt格式文件路径
    tree = ET.parse(in_file)
    root = tree.getroot()
    size = root.find('size')
    w = int(size.find('width').text)
    h = int(size.find('height').text)

    for obj in root.iter('object'):
        cls = obj.find('name').text
        if cls not in classes:
            continue
        cls_id = classes.index(cls)
     
        xmlbox = obj.find('bndbox')
        b = (float(xmlbox.find('xmin').text), float(xmlbox.find('xmax').text), float(xmlbox.find('ymin').text),
             float(xmlbox.find('ymax').text))
        bb = convert((w, h), b)
        out_file.write(str(cls_id) + " " + " ".join([str(a) for a in bb]) + '\n')


image_ids_train = open('/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/name_val.txt').read().strip().split()  # 读取xml文件名索引

for image_id in image_ids_train:
    convert_annotation(image_id)

anns = os.listdir('/kaxier01/projects/FAS/yolov7/wider_face/2_voc2coco/wider_coco/xml_annotations/val/')
for ann in anns:
    ans = ''
    outpath = '/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/labels/val/' + ann
    if ann[-3:] != 'xml':
        continue
    train_file_txt = train_file_txt + '/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/images/val/' + ann[:-3] + 'jpg\n'

with open(train_file, 'w') as outfile:
    outfile.write(train_file_txt)

至此,便把Wider Face标注格式转换成了YOLO格式,对应的图片和标签分别保存在'wider_face/3_voc2yolo/images/'和'wider_face/3_voc2yolo/labels/',

转换后的标注如下:

2 使用Wider Face数据集训练YOLOV7

模仿coco.yaml生成wider_face.yaml文件,文件内如如下:

# train and val data as 1) directory: path/images/, 2) file: path/images.txt, or 3) list: [path1/images/, path2/images/]
# 以下两个txt文件由步骤1.3.2生成
train: /kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/wider_train.txt  # 12876 images
val: /kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/wider_val.txt  # 3226 images

# number of classes
nc: 1

# class names
names: [ 'face' ]

配置文件'cfg/training/yolov7.yaml'中的anchors大小不需要修改,如果anchors不合适,算法会重新聚类anchors

修改YOLOV7工程中的test.py脚本中的anno_json(Line 257):

anno_json = '/kaxier01/projects/FAS/yolov7/wider_face/3_voc2yolo/annotations/val.json'  # 该文件由1.3.2中格式转换的步骤2生成

测试结果

目前模型训练到了46epoch(总共300epoch),性能如下:

可视化结果如下:

标注格式转换参考

https://blog.csdn.net/mary_0830/article/details/116589279

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

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

相关文章

Java开发 - Spring Test知多少?

前言 在前文中,我们也使用了测试代码来进行简单的单元测试,但是我们会发现,里面有大量的重复代码,实际给我们的体验并不是太好,所以这篇,我们来学习Spring Test,Spring Test不仅仅限于在Mybati…

AUTOSAR RTE 总结

1. Interface和在Interface下面包含哪些data element在SWC创立之前就定义好了,存储在一个arxml文件里面, 它相当于一个库文件,在新建AUTOSAR project的第一步就应该被导入进来 2. port在SWC创建的阶段被create,因为Interface没有…

某博数据挖掘:使用Scrapy构建自定义数据采集提取洞察信息

想要深入了解某博上最新的动态和信息吗?那么学习如何使用Scrapy构建一个某博数据采集将是不二之选。Scrapy是一个强大的框架,能够快速地爬取网站上的数据。 新版API构建的某博数据采集拥有最丰富的字段信息,能够更好地深入挖掘某博上的数据。提供了多种采集模式,包括用户、…

邮件定时发送java实现

本文总结如何通过java实现邮件接口的定时发送任务。1、邮箱服务器地址和端口以139邮箱为例,获取服务器地址和端口。139邮箱的路径:设置-常见设置-邮箱协议设置2、客户端配置工具:springboot2.4.3使用maven,使用java11pom.xml引入m…

华为机试题:HJ14 字符串排序(python)

文章目录知识点详解1、input():获取控制台(任意形式)的输入。输出均为字符串类型。2、print() :打印输出。3、int() :将一个字符串或数字转换为整型(强转)。4、range() :输出指定范围…

《Linux Shell脚本攻略》学习笔记-第十二章

12.1 简介 我们可以通过关闭无用的服务、调整内核参数或是添加新的硬件来改善系统性能。 12.2 识别服务 Linux系统可以同时运行数百个任务,其中可能也会有那么一两个你不需要的守护进程。 有三种可以用于启动守护进程和服务的工具,Linux发行版支持其中任…

LeetCode题解 贪心(一):455 分发饼干;376 摆动序列;53 最大子序和

随想录 && LeetCode 贪心算法 贪心之于算法,内核是一个最优解是由多个局部最优解组合而成的 比如,如何在一个月之内最有效的减肥,子问题就是每周如何减肥,再拆分就是每一天如何减肥 如果能找到令每一天都有效减肥的策…

vue实现购物车思想

vue实现购物车思想一、问题:二、解决步骤一、问题: 实现购物车功能,具体如下 在该界面显示所有物品的列表,点击开菜显示购物车 在该界面只显示订单的列表 如何实现购物车数据的同步呢? 二、解决步骤 具体思路如…

day21-反射枚举

day21_反射&枚举 课程目标 1. 【理解】类加载器 2. 【理解】什么是反射 3. 【掌握】获取Class对象的三种方式 4. 【掌握】反射获取构造方法并创建对象 5. 【掌握】反射获取成员变量并使用 6. 【掌握】反射获取成员方法并使用 7. 【掌握】反射综合案例 8. 【理解】枚举类加…

在Ubuntu上安装 Hadoop 3详细过程(验证+填坑总结)

在Ubuntu上安装 Hadoop 3 前提条件: Python 推荐3.8JDK 推荐1.8 解压安装 sudo tar -zxvf hadoop-3.3.0.tar.gz -C /usr/local cd /usr/local sudo mv hadoop-3.3.0 hadoop sudo chown -R hadoop ./hadoop 配置环境变量 vim ~/.bashrc # hadoop export…

5、数组的创建和操作

目录 一、创建空数组、行向量、列向量 二、访问数组 三、 子数组的赋值(Assign) 四、其他创建数组的方式 1. 通过冒号创建一维数组 2.通过logspace函数创建一维数组 3.通过linspace函数创建一维数组 在MATLAB中一般使用方括号“[ ]”、逗号“,”、…

Python FastAPI 框架入门(一)【用于后端API快捷开发】

FastAPI 框架,高性能,易于学习,高效编码,生产可用 官方中文文档:FastAPI 框架中文文档 官方介绍: FastAPI 是一个用于构建 API 的现代、快速(高性能)的 web 框架,使用 Py…

【GD32F427开发板试用】-05-GD32F427移植Coremark

本篇文章来自极术社区与兆易创新组织的GD32F427开发板评测活动,更多开发板试用活动请关注极术社区网站。作者:申小林 如何在GD32F427开发板上移植CoreMARK? 1 下载CoreMARK源码 CoreMark开源的代码可以在Gitbub上自己做下载。 下载地址:ht…

CSS设置元素字体、降级使用字体、引入外部字体

设置元素字体 通过font-family属性,可以设置元素里面的字体样式。 font-family 可以把设置多个字体名称。 降级使用字体 几乎所有浏览器都有支持几种通用字体。比如: monospace,serif和sans-serif,当字体不可用,浏览器可以 “…

通过Docker启动Solace,并在Spring Boot通过JMS整合Solace

1 简介 Solace是一个强大的实时性的事件驱动消息队列。本文将介绍如何在Spring中使用,虽然代码使用的是Spring Boot,但并没有使用相关starter,跟Spring的整合一样,可通用。JMS是通过的消息处理框架,可以深入学习一下&…

02.指针的进阶1.练习题

1.辨析 //数组指针是一种指针,指向数组的指针 //数组指针是指向数组地址的指针 //回调函数是调用函数指针指向函数 EG1:杨氏矩阵 有一个数字矩阵,矩阵的每行从左到右是递增的,矩阵从上到下是递增的,请编写程序在这样的矩阵中…

微服务间通讯负载均衡以及日志

2.通信 HTTP ResthttpJSONRPC远程过程调用二进制 1.使用 RestTemplate RestTemplate restTemplate new RestTemplate(); String forObject restTemplate.getForObject("http://localhot:8888/user", String.class);其负载均衡有问题其无法实现健康检查 2.使用Ri…

新年新气象,跨境电商助推出口再创新高

受疫情等多方面影响,2022年纯棉纱进口量及产量均出现一定幅度地下滑。由于库存增加,消费量下降,供需矛盾也不断加剧。 新年新气象,2023年据预计纯棉纱产量将小幅回升,初步预计将达到535万吨,同比增加5.6%。…

Allegro如何快速打开和关闭层面操作指导

Allegro如何快速打开和关闭层面操作指导 在做PCB设计的时候,打开和关闭某个层面是非常频繁的操作,尤其是丝印等等层面。 Allgeo升级到了172版本的时候,可以将常用的层面添加到Visibility菜单里,就不需要频繁打开颜色管理器打卡和关闭层面了,如下图 具体操作如下 打开颜色…

归纳一下软件测试中「安全测试工具」

大家好啊,我是大田。今天归纳一下安全测试工具,分别用这些工具做哪些工作。自动化测试人员、功能测试人员平常可能用的不多,但是面试时也需要准备,需要知道安全测试工具有什么,还要关注现在有哪些漏洞。本篇先归纳整理…