Bubbliiiing 的 Retinaface rknn python推理分析

news2024/9/21 16:29:04

Bubbliiiing 的 Retinaface rknn python推理分析

项目说明

使用的是Bubbliiiing的深度学习教程-Pytorch 搭建自己的Retinaface人脸检测平台的模型,下面是项目的Bubbliiiing视频讲解地址以及源码地址和博客地址;

作者的项目讲解视频:https://www.bilibili.com/video/BV1yK411K79y/?p=1&vd_source=7fc00062d9cd78f73503c26f05fad664

项目源码地址:https://github.com/bubbliiiing/retinaface-pytorch

作者博客地址:https://blog.csdn.net/weixin_44791964/article/details/106872072

本文的内容相当于是对Bubbliiiing大佬的教程做一个简易的总结!!!

RKNN模型输出

使用Netron观察此网络的输入和输出,如下所示:

在这里插入图片描述

这里模型的输入为:1 x 3 x 640 x 640 (NCHW)

输出结果分为三个,分别是框的回归预测结果(output),分类预测结果(output1)和人脸关键点的回归预测结果(output2),共计输出16800个先验框的三个相关信息;

16800是什么?

首先RetinaFace在特征金字塔上有3个检测分支,分别对应3个stride: 32, 16和8。

  • 在stride32上一个feature map对应的原图的32 X 32的感受野即 stride32 对应的feature map的一个格子可以看到原图32 x 32的区域,可以用来检测较大的区域人脸,stride32 对应的feature map的大小为20 × 20,这是因为640 / 32 = 20 ;stride32是最深的有效特征层,其经过不断的卷积后小物体的特征便会消失,从这一方面来看也是它更适合取检测大物体的原因;
  • 同理stride16可用于中等人脸区域的检测stride16对应的feature map大小为40 X 40;
  • stride8用于较小人脸区域的检测stride8对应的feature map大小为80 X 80

其次需要明确的是在retinafce模型上的每个像素点对应的原图位置上生成的anchor个数是两个;

  • stride32对应的feature map的每个位置会在原图上生成两个anchor box,即输入大小640 × 640尺寸的图像, stride32 对应的feature map大小为20 × 20 (640 / 32),那么在stride32对应的feature map上一共可以得到 20 × 20 × 2 = 800个anchor
  • stride16对应的feature map大小为40 × 40(640 / 16),共生成40 × 40 × 2 = 3200个anchor
  • stride8对应的feature map大小为80 x 80(640 / 8),共生成80 × 80 × 2 = 12800个anchor,

因此3个尺寸总共可以生成800 + 3200 + 12800 = 16800个anchor

anchor的三个相关信息的解释:

  • 框的回归预测结果output用于对先验框进行调整获得预测框,输出为1 x 16800 x 4 x 1,我们需要使用输出的四个参数对先验框进行调整来获得真实的人脸预测框。输出为num_anchors x 4

  • 分类预测结果output1用于判断先验框内部是否包含物体,用于代表每个先验框内部包含人脸的概率,其有两个输出,第一个输出为先验框内部为背景的概率,第二个输出为先验框内部为人脸的概率;输出为num_anchors x 2

  • 人脸关键点的回归预测结果output2用于对先验框进行调整获得人脸关键点,每一个人脸关键点需要两个调整参数,一共有五个人脸关键点,故需要10个参数去调整。输出为num_anchors x 10(num_anchors x 5 x 2),用于代表每个先验框的每个人脸关键点的调整。

模型推理前处理

模型前处理与rockchip的yolov5 rknn python推理分析前处理相同,可以参考其讲解

参考作者源码的额外处理

在作者的源码中进行推理测试的时候出现了如下操作:

image = torch.from_numpy(preprocess_input(image).transpose(2, 0, 1)).unsqueeze(0).type(torch.FloatTensor)
 
def preprocess_input(image):
	image -= np.array((104, 117, 123),np.float32)
    return image

我们将对img经过letterbox和色彩空间转换后也同样进行此操作

img = img.astype(dtype = np.float32)
img -= np.array((104,117,123), np.float32)

上面的操作会使得图像数据的分布会变得更加标准化

两个图像

在代码中出现了两个图像分别为img和or_img,img经过一系列处理后最终用于模型推理而or_img用于画人脸框和人脸关键点信息

# img为模型输入
img = cv2.imread(img_path)
img = letterbox(img, (IMG_SIZE,IMG_SIZE))
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)

# or_img用于画人脸框
or_img = np.array(img, np.uint8)
or_img = cv2.cvtColor(or_img, cv2.COLOR_RGB2BGR)

img = img.astype(dtype = np.float32)
img -= np.array((104,117,123), np.float32)

模型推理

执行rknn模型推理,inference的时间目前处于:0.04S-0.05S之间

outputs = rknn.inference(inputs=[img])

模型输出的outputs为一个列表,这个列别里面分别装了三个数组,三个数组的维度为:(1, 16800, 4, 1)、(1, 16800, 2, 1)、(1, 16800, 10,1)

使用numpy的.squeeze()方法去除数组中所有长度为1的维度,结果维度如下所示:

output_1 = outputs[0].squeeze() # (16800, 4)
output_2 = outputs[1].squeeze() # (16800, 2)
output_3 = outputs[2].squeeze() # (16800, 10)

模型后处理

Anchor先验框详解

作者关于Anchor的讲解视频:https://www.bilibili.com/video/BV1yK411K79y?p=8&vd_source=7fc00062d9cd78f73503c26f05fad664

先验框:就是网络预先设定好的,在图像上的框,网络的预测结果只是对这些先验框进行判断并调整

前面我们讲过3个检测分支的对应的feature map的每个像素点对应的原图位置上生成两个anchor,同时每个检测分支的生成的anchor尺寸是不同的;对于比较深的特征层stride32对应anchor的尺寸大,因为经过不断的卷积,小物体的特征会消失,它更适合取检测大物体,它对应的两个anchor尺寸分别为512 × 512和256 × 256;stride16对应的feature map生成的anchor尺寸大小分别为128 × 128和64 × 64;stride8对应的feature map可以生成的anchor大小为32 × 32 和 16 × 16;

在代码中的体现如下所示:

# 计算生成先验框anchor
anchors = Anchors(cfg_mnet, image_size=(640, 640)).get_anchors()
cfg_mnet={
    'min_sizes': [[16, 32], [64, 128], [256, 512]], 
    'steps': [8, 16, 32],
    'variance': [0.1, 0.2],
}
# 得到anchor
class Anchors(object):
    def __init__(self, cfg, image_size=None):
        super(Anchors, self).__init__()
        # Anchors先验框基础的边长 
        self.min_sizes  = cfg['min_sizes']
        # 指向了三个有效特征层对输入进来的图片 长和宽压缩的倍数 对于比较浅的输入特征层 长和宽压缩了三次8=2^3 ,即长和宽变为了原来的1/8  对于最深的有效特征层 会对输入进去的图片进行5次长和宽的压缩 
        self.steps      = cfg['steps']
        # 输入进来的图片的尺寸 根据图片的大小生成先验框
        self.image_size = image_size
        # 三个有效特征层高和宽
        self.feature_maps = [[ceil(self.image_size[0]/step), ceil(self.image_size[1]/step)] for step in self.steps]

    def get_anchors(self): # 获得先验框
        anchors = []
        for k, f in enumerate(self.feature_maps): # 首先对所有的特征层进行循环
            min_sizes = self.min_sizes[k] # 取出每一个特征层对应的先验框
            #   对特征层的高和宽网格进行循环迭代
            for i, j in product(range(f[0]), range(f[1])):
                for min_size in min_sizes:
                    # 将先验框映射到网格点上
                    s_kx = min_size / self.image_size[1]
                    s_ky = min_size / self.image_size[0]
                    dense_cx = [x * self.steps[k] / self.image_size[1] for x in [j + 0.5]]
                    dense_cy = [y * self.steps[k] / self.image_size[0] for y in [i + 0.5]]
                    for cy, cx in product(dense_cy, dense_cx):
                        # 把获得的先验框添加到anchors列表中
                        anchors += [cx, cy, s_kx, s_ky] # 先验框的形式是中心宽高的形式
        output_np=np.array(anchors).reshape(-1,4)
        return output_np

在作者关于Anchor的讲解视频中,作者在最深的有效特征层20 x 20的特征图上绘制了先验框(其对应的先验框的尺寸为:[256, 512]),并以20 x 20特征图的左上角点为例,其先验框如下所示:

在这里插入图片描述

在获取先验框后,retinaface的网络预测结果会判断先验框内部是否包含人脸,还会对先验框进行调整获得最终的预测框,还会对中心进行调整获得五个先验点

解码-先验框的调整

作者解码的讲解视频:https://www.bilibili.com/video/BV1yK411K79y?p=9&vd_source=7fc00062d9cd78f73503c26f05fad664

先验框的解码过程就是对先验框的中心和宽高进行调整,获得调整后的先验框

# 人脸框解码
boxes = decode(output_1, anchors, cfg_mnet['variance']) 
# 五个人脸关键点解码
landms = decode_landm(output_3, anchors, cfg_mnet['variance'])
# 人脸框坐标解码
def decode(loc, priors, variances):
    boxes = np.concatenate((priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],
                    priors[:, 2:] * np.exp(loc[:, 2:] * variances[1])), 1)
    boxes[:, :2] -= boxes[:, 2:] / 2
    boxes[:, 2:] += boxes[:, :2]
    return boxes

# 人脸关键点解码
def decode_landm(pre, priors, variances):
    landms = np.concatenate((priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:],
                        priors[:, :2] + pre[:, 2:4] * variances[0] * priors[:, 2:],
                        priors[:, :2] + pre[:, 4:6] * variances[0] * priors[:, 2:],
                        priors[:, :2] + pre[:, 6:8] * variances[0] * priors[:, 2:],
                        priors[:, :2] + pre[:, 8:10] * variances[0] * priors[:, 2:],
                        ), 1)
    return landms
  • decode函数会对先验框进行调整,获得最终的预测框

中心调整

priors[:, :2] + loc[:, :2] * variances[0] * priors[:, 2:],

取出网络回归结果loc中的前两个值乘上一个常数variances[0] (值)进行标准化,然后将结果再乘上先验框的宽和高priors[:, 2:],之后再加上先验框的中心priors[:, :2],便获得了调整后的先验框中心即为预测框的中心点;loc[:, :2] * variances[0] * priors[:, 2:]相当于先验框中心偏移的部分

宽高调整

priors[:, 2:] * np.exp(loc[:, 2:] * variances[1])

取出网络回归结果loc中的后两个值乘上一个常数variances[1] (0.2)进行标准化,然后将结果取一个指数,再乘上先验框的宽和高priors[:, 2:],便获地了调整后的先验框的宽高

boxes[:, :2] -= boxes[:, 2:] / 2
boxes[:, 2:] += boxes[:, :2]

最后将调整后的先验框形式,转化为左上角坐标点和右下角坐标点的形式,并返回;

下面为作者解码的讲解视频中对先验框调整后的结果进行的演示:

在这里插入图片描述

对比两边发现右边的图中的蓝色点即为两个先验框调整时,两个anchor的中心点的调整情况,同时发现右边的先验框的宽和高也发生了变化;

  • decode_landm函数会对先验框的中心进行调整,获得五个人脸关键点
priors[:, :2] + pre[:, :2] * variances[0] * priors[:, 2:]

人脸关键点的解码过程与先验框中心点调整的过程一样

取出相应序号的人脸关键点结果pre[:, :2]为人脸关键点中心点的预测结果

取出关键点的结果*variances[0] (归一化),再乘上先验框的宽和高priors[:, 2:],最后再加上先验框的中心priors[:, :2]即可;

先验框调整后,再进行得分的筛选和非极大值抑制便得到了最终的结果

过滤无用的框

在进行过滤无用框的操作前需要对前面先验框的解码和人脸关键点的解码和人脸的概率合并到一起

conf = output_2[:, 1:2] # 置信度序号为0的内容为先验框为背景的概率 序号为1的内容为先验框为人脸的概率
    
#非极大抑制,得到最终输出
boxs_conf = np.concatenate((boxes, conf, landms), -1)

合并后boxs_conf的维度为:(16800, 15)

15的组成为:

  • 0-3:预测框位置信息,左上角坐标点和右下角坐标点
  • 4:预测框包含人脸的概率
  • 5-14:人脸的十个关键点坐标

过滤掉无用的框

boxs_conf = filter_box(boxs_conf, 0.5, 0.45) # 0.5为置信度阈值conf_thres  0.45为非极大值抑制的iou阈值

filter_box代码实现如下所示:

def filter_box(org_box, conf_thres, iou_thres): #过滤掉无用的框
    conf = org_box[..., 4] > conf_thres #删除置信度小于conf_thres的BOX
    box = org_box[conf == True] 
    
    output = []
    curr_cls_box = np.array(box)
    curr_cls_box[:,:4]=curr_cls_box[:,:4]*640
    curr_cls_box[:,5:]=curr_cls_box[:,5:]*640
    curr_out_box = pynms(curr_cls_box, iou_thres) #经过非极大抑制后输出的BOX下标
    for k in curr_out_box:
        output.append(curr_cls_box[k])  #利用下标取出非极大抑制后的BOX
    output = np.array(output)
    return output

首先根据包含人脸的概率进行筛选,保留概率大于conf_thres的人脸框

conf = org_box[..., 4] > conf_thres
box = org_box[conf == True] 

它返回16800个预测框的是否大于conf_thres的布尔值,根据布尔值保留满足要求的预测框

将预测框的位置信息和关键点信息,共计7个点,尺寸上乘上640

curr_cls_box[:,:4]=curr_cls_box[:,:4]*640
curr_cls_box[:,5:]=curr_cls_box[:,5:]*640

进行非极大值抑制,返回经过非极大抑制后输出的剩余满足要求的预测框的下标,将其保存到output中

curr_out_box = pynms(curr_cls_box, iou_thres)
for k in curr_out_box:
    output.append(curr_cls_box[k])  #利用下标取出非极大抑制后的BOX

最后返回剩余的预测框;

非极大值抑制

非极大值抑制的代码与rockchip的yolov5 rknn python推理分析所讲述的非极大值抑制代码相同,可以去参考,这里不做重复讲述

def pynms(dets, thresh): 
    '''
    	非极大抑制
    '''
    x1 = dets[:, 0]
    y1 = dets[:, 1]
    x2 = dets[:, 2]
    y2 = dets[:, 3]

    areas = (y2 - y1) * (x2 - x1)
    
    scores = dets[:, 4]
    keep = []
    index = scores.argsort()[::-1] #置信度从大到小排序的索引

    while index.size > 0:
        i = index[0]
        keep.append(i)
		
        # 计算相交面积
        # 求相交区域的左上角坐标
        x11 = np.maximum(x1[i], x1[index[1:]]) 
        y11 = np.maximum(y1[i], y1[index[1:]])# 求相交区域的右下角坐标
        x22 = np.minimum(x2[i], x2[index[1:]])
        y22 = np.minimum(y2[i], y2[index[1:]])
		
        # 当两个框不想交时x22 - x11或y22 - y11 为负数,则将两框不相交时把相交面积置0
        w = np.maximum(0, x22 - x11 )  
        h = np.maximum(0, y22 - y11 ) 
		
        # 计算相交面积
        overlaps = w * h
        # 计算IOU
        ious = overlaps / (areas[i] + areas[index[1:]] - overlaps)
		
        # IOU小于thresh的框保留下来
        idx = np.where(ious <= thresh)[0]  
        index = index[idx + 1]

    return keep

结果绘制

经过置信度过滤和非极大值抑制之后得到预测框的信息为boxs_conf

boxs_conf = filter_box(boxs_conf, 0.5, 0.45)

在boxs_conf中前四个参数为人脸预测框框的位置信息,左上角和右下角的坐标;第五个参数为人脸的概率;剩下的参数为人脸关键点的位置信息:按顺序分别为

左眼、右眼、鼻子、左脸、右脸,将boxs_conf传递给draw_img绘制结果

#画出人类框和5个人脸关键并保存图片
if boxs_conf is not None:
	draw_img(boxs_conf, or_img)

draw_img函数的代码如下所示:

# 画人脸框和5个关键点
def draw_img(boxes_conf_landms,old_image):
    for b in boxes_conf_landms:
        text = "{:.4f}".format(b[4])
        b = list(map(int, b))
        #   b[0]-b[3]为人脸框的坐标,b[4]为得分
        cv2.rectangle(old_image, (b[0], b[1]), (b[2], b[3]), (0, 0, 255), 2) 
        cx = b[0]
        cy = b[1] + 12
        cv2.putText(old_image, text, (cx, cy),cv2.FONT_HERSHEY_DUPLEX, 0.5, (255, 255, 255))
        #   b[5]-b[14]为人脸关键点的坐标
        cv2.circle(old_image, (b[5], b[6]), 1, (0, 0, 255), 4)
        cv2.circle(old_image, (b[7], b[8]), 1, (0, 255, 255), 4)
        cv2.circle(old_image, (b[9], b[10]), 1, (255, 0, 255), 4)
        cv2.circle(old_image, (b[11], b[12]), 1, (0, 255, 0), 4)
        cv2.circle(old_image, (b[13], b[14]), 1, (255, 0, 0), 4)
    return old_image

结果展示如下所示:

在这里插入图片描述

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

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

相关文章

数据结构排序合集(笔记)

目录 一.插入排序与希尔排序 二.选择排序与堆排序 三.冒泡排序和快速排序 四.归并排序 五.计数排序 一.插入排序与希尔排序 时间复杂度空间复杂度稳定性插入排序O&#xff08;N^2&#xff09;O(1)稳定希尔排序O(N^1.3)O(1)不稳定 插入排序&#xff1a; 希尔排序&#xff…

css-01-如何实现“文本过长(文本在一行排),超出部分被省略号代替”

目录 需求代码代码解释 需求 最近写一个项目&#xff0c;遇到了一个问题&#xff0c;就是希望实现下面这种文字效果&#xff1a;文字在一行排&#xff0c;超出的部分用省略号代替 代码 <!DOCTYPE html> <html lang"en"><head><meta charset…

OCC 创建点线面体

目录 一、利用封装已有算法实现 1、盒子建模算法封装 2、可视化 二、利用OCC 点线面实现 1、实现过程 2、实现一个面 3、拉伸面生成体 4、旋转面生成体 三、总结 一、利用封装已有算法实现 1、盒子建模算法封装 BRepPrimAPI_MakeBox box(2, 2, 2); 2、可视化 void VTK…

Gateway源码分析:路由Route、断言Predicate、Filter

文章目录 源码总流程图说明GateWayAutoConfigurationDispatcherHandlergetHandler()handleRequestWith()RouteToRequestUrlFilterReactiveLoadBalancerClientFilterNettyRoutingFilter 补充知识适配器模式 详细流程图 源码总流程图 在线总流程图 说明 Gateway的版本使用的是…

在线 PDF 制作者泄露用户上传的文档

两家在线 PDF 制作者泄露了数万份用户文档&#xff0c;包括护照、驾驶执照、证书以及用户上传的其他个人信息。 我们都经历过这样的情况&#xff1a;非常匆忙&#xff0c;努力快速制作 PDF 并提交表单。许多人向在线 PDF 制作者寻求帮助&#xff0c;许多人的祈祷得到了回应。 …

Python学习笔记—100页Opencv详细讲解教程

目录 1 创建和显示窗口... - 4 - 2 加载显示图片... - 6 - 3 保存图片... - 7 - 4 视频采集... - 8 - 5视频录制... - 11 - 6 控制鼠标... - 12 - 7 TrackBar 控件... - 14 - 8.RGB和BGR颜色空间... - 16 - 9.HSV和HSL和YUV.. - 17 - 10 颜色空间的转化... - 18 - …

分页查询与分页条件查询

--------------- 无PageHelper插件分页查询 1.创建PageBean实体类 Data NoArgsConstructor AllArgsConstructor public class PageBean<T> {private Long total;//总条数private List<T> items;//当前页数据集合 }类型安全性 泛型&#xff1a;提供了编译时的类型…

【学长工具库】1.如何快速部署开源框架 | 若依框架保姆级搭建教程

今天学长带来了一款十分适合自学的开源框架-若依框架&#xff0c; 本文会详细的教大家怎么部署这个系统。 文末有所有资料获取方式~ 框架技术栈 前端采用 Vue、Element UI。后端采用 Spring Boot、Spring Security、Redis & Jwt。权限认证使用 Jwt&#xff0c;支持多终端…

【IEEE出版,会议历史良好、论文录用检索快】第四届计算机科学与区块链国际学术会议 (CCSB 2024,9月6-8)

CCSB 2024会议由深圳大学主办&#xff0c;旨在探讨计算机科学的最新发展如何与区块链技术相结合&#xff0c;以及这一结合如何推动金融、供应链管理、数据安全和其他多个行业的革新&#xff0c; 本次会议将提供一个多学科交流的平台&#xff0c;汇集来自相关领域学者的研究和思…

vxe-弹窗初始化激活选中Vxe-Table表格中第一行input输入框

1.实现效果 2.Modal弹窗的渲染过程 一、Vue组件的生命周期 Vue组件从创建到销毁会经历一系列的生命周期钩子&#xff0c;这些钩子为开发者提供了在不同阶段插入自定义逻辑的机会。在Modal弹窗的上下文中&#xff0c;这些生命周期钩子同样适用。 beforeCreate&#xff1a;组件…

解决 Ubuntu 用户登录后的 shell 和功能问题

在使用 Ubuntu 系统管理用户时&#xff0c;可能会遇到一些常见的问题&#xff0c;比如新创建的用户无法使用常见命令&#xff08;如 ll&#xff09;以及输出信息没有颜色。这些问题通常与用户的默认 shell 有关。本文将总结如何解决这些问题&#xff0c;并确保新用户能够正常使…

【linux深入剖析】命名管道 | 匿名管道与命名管道的区别 | system V共享内存

&#x1f341;你好&#xff0c;我是 RO-BERRY &#x1f4d7; 致力于C、C、数据结构、TCP/IP、数据库等等一系列知识 &#x1f384;感谢你的陪伴与支持 &#xff0c;故事既有了开头&#xff0c;就要画上一个完美的句号&#xff0c;让我们一起加油 目录 1. 命名管道2. 创建命名管…

STM32之九:ADC模数转换器

目录 1. 简介 2. ADC 2.1 逐次逼近型寄存器SAR 2.2 ADC转换时间 3 ADC框图 3.1 8 bit ADC0809芯片内部框图 3.2 ADC框图 3.2.1 注入通道和规则通道 3.2.2 单次/连续转换模式 3.2.3 扫描模式 3.2.4 外部触发转换 3.2.5 数据对齐 3.2.6 模拟看门狗 4. 总结和ADC驱…

mac无法清空废纸篓怎么办 mac废纸篓清空了如何找回 cleanmymac误删文件怎么恢复

废纸篓相当于“一颗后悔药”&#xff0c;用于临时存储用户删除的文件。我们从从Mac上删除的文件&#xff0c;一般会进入废纸篓中。如果我们后悔了&#xff0c;可以从废纸篓中找回来。然而&#xff0c;有时我们会发现mac无法清空废纸篓&#xff0c;这是怎么回事?本文将探讨一些…

Unity-URP-SSAO记录

勾选After Opacity Unity-URP管线&#xff0c;本来又一个“bug”, 网上查不到很多关于ssao的资料 以为会不会又是一个极度少人用的东西 而且几乎都是要第三方替代 也完全没有SSAO大概的消耗是多少&#xff0c;完全是黑盒(因为用的人少&#xff0c;研究的人少&#xff0c;优…

JMeter学习笔记:线程组

继续&#xff1a;请求&#xff08;Sampler元件模拟的用户请求&#xff09;出错后继续运行&#xff1b; 启动下一进程&#xff1a;如果出错&#xff0c;则同一脚本中的余下请求将不再执行&#xff0c;直接重新开始执行&#xff1b; 停止线程&#xff1a;如果遇到请求&#xff…

51单片机嵌入式开发:14、STC89C52RC 之HX1838红外解码NEC+数码管+串口打印+LED显示

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 STC89C52RC 之HX1838红外解码NEC数码管串口打印LED显示 STC89C52RC 之HX1838红外解码NEC数码管串口打印LED显示1 概述2 硬件电路2.1 遥控器2.2 红外接收器电路2.3 STC89C52单…

深入理解Linux网络(三):TCP对象创建

深入理解Linux网络&#xff08;三&#xff09;&#xff1a;TCP对象创建 TCP对象创建inet_createsock_init_data TCP对象创建 常见的三句TCP编程&#xff1a; int main() {int sk socket(AF_INET, SOCK_STREAM, 0);connect(sk, ...)recv(sk, ...) }简单的两三⾏代码&#xff…

逆向案例二十八——某高考志愿网异步请求头参数加密,以及webpack

网址&#xff1a;aHR0cDovL3d3dy54aW5nYW9rYW90Yi5jb20vY29sbGVnZXMvc2VhcmNo 抓包分析&#xff0c;发现请求头有参数u-sign是加密的&#xff0c;载荷没有进行加密&#xff0c;直接跟栈分析。 进入第二个栈&#xff0c;打上断点&#xff0c;分析有没有加密位置。 可以看到参数…

【PyTorch】图像二分类项目-部署

【PyTorch】图像二分类项目 【PyTorch】图像二分类项目-部署 在独立于训练脚本的新脚本中部署用于推理的模型&#xff0c;需要构造一个模型类的对象&#xff0c;并将权重加载到模型中。操作流程为&#xff1a;定义模型--加载权重--在验证和测试数据集上部署模型。 import torch…