SAT分离轴定理的c++/python实现

news2024/10/3 19:12:25

分离轴定理的c++/python实现

现在要对BEV模型检查出来的车辆做NMS,把3d框的平面属性获取到后,配合旋转角度投影到地面就是2D图形。

开始碰撞检测,判断是否重叠,保留置信度高的框就行。

原理

分离轴定理(Separating Axis Theorem,简称 SAT)是一种常用的碰撞检测算法,尤其在检测两个凸多边形或凸多面体是否相交时非常有效。SAT 的核心思想是:如果两个多边形在任何一条“轴”上都没有重叠投影,那么它们就不相交。如果在所有潜在的轴上都存在重叠投影,那么它们就是相交的。

SAT 算法的核心思想:

  1. 分离轴的定义:两个凸多边形相交的必要条件是,在某个方向上,两个多边形的投影不重叠。这个方向称为“分离轴”。如果存在这样的轴,称为“分离轴”,那么这两个多边形一定不相交。
  2. 投影:将多边形的所有顶点投影到某个轴上,可以得到一个一维的区间。如果两个多边形在某条轴上的投影不重叠,说明在该方向上它们是分开的,所以不可能相交。
  3. 边法线作为分离轴:对凸多边形的每一条边,计算其垂直方向(即边的法线),并将所有顶点沿着这个法线方向进行投影。如果在任何一条法线方向上,两个多边形的投影不重叠,则这两个多边形不会相交。
  4. 反例与正例:如果在所有法线方向上,两个多边形的投影都重叠,那么它们是相交的;如果找到一个轴使得投影不重叠,那么它们是不相交的。

SAT 算法的步骤:

  1. 获取多边形的所有边:对于两个多边形,分别获取它们所有的边。
  2. 计算边的法线(轴):对每一条边,计算其法线。这些法线是我们用来做投影的方向(轴)。
  3. 将顶点投影到轴上:对每条轴,分别将两个多边形的所有顶点投影到该轴上,得到两个投影区间。
  4. 检查投影区间是否重叠:比较两个多边形在该轴上的投影区间。如果任何一个轴上的投影不重叠,立即返回不相交(即找到了分离轴)。
  5. 判断相交:如果所有轴上的投影都重叠,那么可以判断两个多边形相交。

具体例子:

假设有两个二维矩形 A 和 B,算法流程如下:

  1. 计算 A 的所有边以及 B 的所有边。
  2. 对每条边,计算它的垂直方向作为分离轴。
  3. 将 A 和 B 的顶点投影到每条分离轴上,得到两个投影区间。
  4. 检查每一条轴上 A 和 B 的投影是否有重叠。
    • 如果在某条轴上投影没有重叠,A 和 B 不相交。
    • 如果在所有轴上投影都重叠,A 和 B 相交。

示例图解:

请添加图片描述

  1. 假设两个矩形相对旋转,SAT 会先找出每个矩形的边,接着计算这些边的法线(垂直方向)。
  2. 在这些法线方向上,将矩形的所有顶点投影到这些法线上。
  3. 如果在所有法线上的投影都重叠,就说明矩形相交;如果存在一条轴上的投影不重叠,矩形就不相交。

SAT 算法的优点:

  • 高效:特别适用于凸多边形,时间复杂度相对较低。
  • 通用性强:适用于任何凸多边形,不仅仅是矩形,也包括任意形状的多边形或三维多面体。

SAT 的局限:

  • 仅适用于凸多边形:对凹多边形不适用。
  • 计算较复杂的形状时需要的边数较多:多边形的边数越多,所需的计算量也会随之增加。

应用场景:

  • 游戏开发:在物理引擎中用于碰撞检测,判断玩家、敌人或者其他物体是否相互碰撞。
  • 图形处理:用于检测两个二维或三维形状是否相交,通常用于图像合成、动画、模拟仿真等场景。

总结:

分离轴定理通过投影和比较的方式,能够有效地判断两个凸多边形是否相交。它利用边法线作为可能的分离轴,并通过检测是否存在投影不重叠的轴来快速判断是否有碰撞。

cpp实现

#include <vector>
#include <cmath>
#include <iostream>
#include <algorithm>
#include <cassert>
#include <algorithm>
#include <fstream>
#include <vector>
#include <string>
#include <sstream>
struct Box3D {
    float conf;
    int sub_cls;
    float x;
    float y;
    float z;
    float w;  // 宽度
    float l;  // 长度
    float h;  // 高度
    float yaw; // 旋转角度
    float velocity_x = 0.0f;
    float velocity_y = 0.0f;
};

// 旋转一个点(x, y)围绕中心点(cx, cy)旋转angle_degrees角度
std::pair<float, float> RotatePoint(float x, float y, float cx, float cy, float angleRad) {
    // float angleRad = angleDegrees * M_PI / 180.0f;
    float cosA = std::cos(angleRad);
    float sinA = std::sin(angleRad);
    float rx = cx + (x - cx) * cosA - (y - cy) * sinA;
    float ry = cy + (x - cx) * sinA + (y - cy) * cosA;
    return {rx, ry};
}

// 获取旋转后的边框的顶点
std::vector<std::pair<float, float>> GetRotatedBboxVertices(float cx, float cy, float w, float h, float angle) {
    float half_w = w / 2;
    float half_h = h / 2;

    // 定义未旋转的矩形的四个顶点
    std::vector<std::pair<float, float>> vertices = {
        {cx - half_w, cy - half_h},
        {cx + half_w, cy - half_h},
        {cx + half_w, cy + half_h},
        {cx - half_w, cy + half_h}
    };

    // 旋转每个顶点
    for (auto& vertex : vertices) {
        vertex = RotatePoint(vertex.first, vertex.second, cx, cy, angle);
    }

    return vertices;
}


// 获取边框的边
std::vector<std::pair<std::pair<float, float>, std::pair<float, float>>> GetEdges(const std::vector<std::pair<float, float>>& vertices) {
    std::vector<std::pair<std::pair<float, float>, std::pair<float, float>>> edges;
    for (size_t i = 0; i < vertices.size(); ++i) {
        edges.push_back({vertices[i], vertices[(i + 1) % vertices.size()]});
    }
    return edges;
}

// 获取边缘的轴
std::pair<float, float> GetAxis(const std::pair<std::pair<float, float>, std::pair<float, float>>& edge) {
    float x1 = edge.first.first;
    float y1 = edge.first.second;
    float x2 = edge.second.first;
    float y2 = edge.second.second;
    return {y2 - y1, x1 - x2}; // 垂直于边的向量
}

// 投影顶点到轴上
std::pair<float, float> Project(const std::vector<std::pair<float, float>>& vertices, const std::pair<float, float>& axis) {
    std::vector<float> dots;
    for (const auto& vertex : vertices) {
        dots.push_back(vertex.first * axis.first + vertex.second * axis.second);
    }
    return {*std::min_element(dots.begin(), dots.end()), *std::max_element(dots.begin(), dots.end())};
}

// 判断投影是否重叠
bool Overlap(const std::pair<float, float>& projection1, const std::pair<float, float>& projection2) {
    return std::min(projection1.second, projection2.second) >= std::max(projection1.first, projection2.first);
}

// 使用分离轴定理(SAT)判断两个二维框是否相交
bool SatIntersection(const std::vector<float>& box1, const std::vector<float>& box2) {
    std::vector<std::pair<float, float>> vertices1 = GetRotatedBboxVertices(box1[0], box1[1], box1[2], box1[3], box1[4]);
    std::vector<std::pair<float, float>> vertices2 = GetRotatedBboxVertices(box2[0], box2[1], box2[2], box2[3], box2[4]);

    std::vector<std::pair<std::pair<float, float>, std::pair<float, float>>> edges;
    auto edges1 = GetEdges(vertices1);
    auto edges2 = GetEdges(vertices2);
    edges.insert(edges.end(), edges1.begin(), edges1.end());
    edges.insert(edges.end(), edges2.begin(), edges2.end());

    for (const auto& edge : edges) {
        auto axis = GetAxis(edge);
        auto projection1 = Project(vertices1, axis);
        auto projection2 = Project(vertices2, axis);
        std::cout << projection1.first << " " << projection1.second << " " << projection2.first << " " << projection2.second << std::endl;
        if (!Overlap(projection1, projection2)) {
            return false; // 找到了分离轴,两个框不相交
        }
    }
    return true; // 没有分离轴,两个框相交
}

// 解析 Box3D 对象为二维 box 参数
std::vector<float> ParseBox3D(const Box3D& box) {
    return {box.x, box.y, box.l, box.w, box.yaw};
}

// SAT NMS 实现,返回选择的索引
std::vector<int> SatNms(std::vector<Box3D> obj_list) {
    // 如果输入为空,返回空结果
    if (obj_list.empty()) {
        return {};
    }

    if (obj_list.size() == 1) {
        return {1};  // 只有一个框时,直接返回
    }

    // 保存原始索引
    std::vector<int> original_indices(obj_list.size());
    std::iota(original_indices.begin(), original_indices.end(), 0);  // 生成 0 到 obj_list.size()-1 的索引

    // 按置信度从高到低排序,同时调整原始索引顺序
    std::sort(original_indices.begin(), original_indices.end(), [&](int i, int j) {
        return obj_list[i].conf > obj_list[j].conf;
    });

    std::sort(obj_list.begin(), obj_list.end(), [&](const Box3D& a, const Box3D& b) {
        return a.conf > b.conf;
    });

    std::vector<std::vector<float>> boxes;
    std::vector<int> keep(obj_list.size(), 0);  // 初始化 keep 向量为 0,大小与 obj_list 相同

    // 解析 Box3D 为二维 box 参数并存储
    for (const auto& obj : obj_list) {
        boxes.push_back({obj.x, obj.y, obj.l, obj.w, obj.yaw});
    }

    // 执行 NMS 逻辑
    while (!boxes.empty()) {
        std::vector<float> next_box = boxes.front();  // 获取置信度最高的 box
        boxes.erase(boxes.begin());                   // 移除第一个框

        // 获取对应的原始索引
        int original_index = original_indices.front();
        original_indices.erase(original_indices.begin());  // 移除处理过的索引
        keep[original_index] = 1;  // 标记为保留
        std::cout << "see: " << original_index << std::endl;

        // 同时移除 obj_list 中对应的 Box3D 对象
        obj_list.erase(obj_list.begin());

        std::vector<std::vector<float>> remain_boxes;
        std::vector<Box3D> remaining_obj;
        std::vector<int> remaining_indices;  // 新增:存储不相交框的原始索引

        // 遍历剩下的 boxes,检查与当前 box 的相交情况
        for (size_t i = 0; i < boxes.size(); ++i) {
            std::cout << "compare with: " << original_indices[i] << std::endl;
            if (!SatIntersection(next_box, boxes[i])) {
                remain_boxes.push_back(boxes[i]);  // 保留不相交的框
                remaining_obj.push_back(obj_list[i]);  // 保留不相交的 obj_list 对象
                remaining_indices.push_back(original_indices[i]);  // 保留不相交框的原始索引
            } else {
                std::cout << "remove: " << original_indices[i] << std::endl;
            }
        }

        // 更新剩余的 boxes、obj_list 和 original_indices
        boxes = remain_boxes;
        obj_list = remaining_obj;
        original_indices = remaining_indices;  // 更新原始索引
    }

    return keep;  // 返回保留的框的索引向量
}


std::vector<Box3D> readData(const std::string& filename) {
    std::vector<Box3D> boxes;
    std::ifstream file(filename);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for reading: " << filename << std::endl;
        return boxes;
    }

    std::string line;
    while (std::getline(file, line)) {
        std::istringstream iss(line);
        Box3D box;
        if (!(iss >> box.conf >> box.sub_cls >> box.x >> box.y >> box.z >> box.w >> box.l >> box.h >> box.yaw >> box.velocity_x >> box.velocity_y)) {
            std::cerr << "Error reading line: " << line << std::endl;
            continue;
        }
        boxes.push_back(box);
    }

    file.close();
    return boxes;
}

void dumpData(const std::vector<Box3D>& boxes, const std::string& filename) {
    std::ofstream file(filename);
    if (!file.is_open()) {
        std::cerr << "Failed to open file for writing: " << filename << std::endl;
        return;
    }

    for (const auto& box : boxes) {
        file << box.conf << " " << box.sub_cls << " " << box.x << " " << box.y << " "
             << box.z << " " << box.w << " " << box.l << " " << box.h << " " << box.yaw
             << " " << box.velocity_x << " " << box.velocity_y << "\n";
    }

    file.close();
}

int main() {
    // 创建测试用例
    #define PI 3.1415926f
    Box3D bbox1;
    bbox1.conf = 0.9f;
    bbox1.sub_cls = 0;
    bbox1.x = 100.0f;
    bbox1.y = 150.0f;
    bbox1.z = 0.0f;
    bbox1.w = 40.0f;
    bbox1.l = 80.0f;
    bbox1.h = 0.0f;
    bbox1.yaw = fmod(10.0f*PI/180, PI * 2);

    Box3D bbox2;
    bbox2.conf = 0.85f;
    bbox2.sub_cls = 0;
    bbox2.x = 105.0f;
    bbox2.y = 160.0f;
    bbox2.z = 0.0f;
    bbox2.w = 60.0f;
    bbox2.l = 90.0f;
    bbox2.h = 0.0f;
    bbox2.yaw = fmod(20.0f*PI/180, PI * 2);

    Box3D bbox3;
    bbox3.conf = 0.7f;
    bbox3.sub_cls = 0;
    bbox3.x = 300.0f;
    bbox3.y = 400.0f;
    bbox3.z = 0.0f;
    bbox3.w = 30.0f;
    bbox3.l = 70.0f;
    bbox3.h = 0.0f;
    bbox3.yaw = fmod(45.0f*PI/180, PI * 2);

    Box3D bbox4;
    bbox4.conf = 0.75f;
    bbox4.sub_cls = 0;
    bbox4.x = 110.0f;
    bbox4.y = 160.0f;
    bbox4.z = 0.0f;
    bbox4.w = 40.0f;
    bbox4.l = 85.0f;
    bbox4.h = 0.0f;
    bbox4.yaw = fmod(30.0f*PI/180, PI * 2);

    Box3D bbox5;
    bbox5.conf = 0.6f;
    bbox5.sub_cls = 0;
    bbox5.x = 500.0f;
    bbox5.y = 600.0f;
    bbox5.z = 0.0f;
    bbox5.w = 50.0f;
    bbox5.l = 100.0f;
    bbox5.h = 0.0f;
    bbox5.yaw = fmod(90.0f*PI/180, PI * 2);

    Box3D bbox6;
    bbox6.conf = 0.5f;
    bbox6.sub_cls = 0;
    bbox6.x = 505.0f;
    bbox6.y = 610.0f;
    bbox6.z = 0.0f;
    bbox6.w = 45.0f;
    bbox6.l = 105.0f;
    bbox6.h = 0.0f;
    bbox6.yaw = fmod(75.0f*PI/180, PI * 2);

    Box3D bbox7;
    bbox7.conf = 0.3f;
    bbox7.sub_cls = 0;
    bbox7.x = 700.0f;
    bbox7.y = 800.0f;
    bbox7.z = 0.0f;
    bbox7.w = 30.0f;
    bbox7.l = 60.0f;
    bbox7.h = 0.0f;
    bbox7.yaw = fmod(15.0f*PI/180, PI * 2);

    // 创建框列表
    // std::vector<Box3D> boxes = {bbox1, bbox2, bbox3, bbox4, bbox5, bbox6, bbox7};


    std::vector<Box3D> boxes = readData("data.txt");

    // 执行 SAT NMS
    std::vector<int> keep = SatNms(boxes);

    std::vector<Box3D> filtered_boxes;
    for (size_t i = 0; i < keep.size(); ++i) {
        if (keep[i]) {
            filtered_boxes.push_back(boxes[i]);
        }
    }
    boxes = filtered_boxes;


    dumpData(boxes, "filtered_data.txt");

    return 0;
}

python实现

import math

def sat_nms(obj_list, conf_score_key, parse_obj_func):
    original_indices = list(range(len(obj_list)))
    original_indices = sorted(original_indices, key=lambda i: obj_list[i][conf_score_key], reverse=True)
    obj_list = sorted(obj_list, key=lambda i: i[conf_score_key], reverse=True)
    if len(obj_list) < 2:
        return obj_list
    boxes = []
    for obj in obj_list:
        X, Y, L, W, _, yaw = parse_obj_func(obj)
        box = [X, Y, L, W, yaw]
        boxes.append(box)

    selected_boxes = []
    selected_obj = []
    while boxes:
        next_box = boxes.pop(0)
        original_index = original_indices.pop(0)
        selected_boxes.append(next_box)
        next_obj = obj_list.pop(0)
        selected_obj.append(next_obj)
        print(f"See {original_index} with conf: {next_obj[conf_score_key]}")
        remain_boxes = []
        remaining_obj = []
        remaining_indices = []
        for idx, (rest_box, rest_obj) in enumerate(zip(boxes, obj_list)):
            print(f"compare: {original_indices[idx]} ")
            if not sat_intersection(next_box, rest_box):
                remain_boxes.append(rest_box)
                remaining_obj.append(rest_obj)
                remaining_indices.append(original_indices[idx])
            else:
                print(f"remove {original_indices[idx]} with conf: {rest_obj[conf_score_key]}")
        boxes = remain_boxes
        obj_list = remaining_obj
        original_indices = remaining_indices
    return selected_obj

def rotate_point(x, y, cx, cy, angle_rad):
    cos_a, sin_a = math.cos(angle_rad), math.sin(angle_rad)
    rx = cx + (x - cx) * cos_a - (y - cy) * sin_a
    ry = cy + (x - cx) * sin_a + (y - cy) * cos_a
    return rx, ry


def get_rotated_bbox_vertices(cx, cy, w, h, angle):
    half_w, half_h = w / 2, h / 2
    vertices = [ (cx - half_w, cy - half_h), (cx + half_w, cy - half_h), (cx + half_w, cy + half_h), (cx - half_w, cy + half_h) ]
    return [rotate_point(x, y, cx, cy, angle) for x, y in vertices]


def get_edges(vertices):
    return [(vertices[i], vertices[(i + 1) % len(vertices)]) for i in range(len(vertices))] 


def get_axis(edge):
    x1, y1 = edge[0]
    x2, y2 = edge[1]
    return y2 - y1, x1 - x2


def project(vertices, axis):
    dots = [vertex[0] * axis[0] + vertex[1] * axis[1] for vertex in vertices]
    return min(dots), max(dots)


def overlap(projection1, projection2):
    return min(projection1[1], projection2[1]) >= max(projection1[0], projection2[0])


def sat_intersection(bbox1, bbox2):
    vertices1 = get_rotated_bbox_vertices(*bbox1)
    vertices2 = get_rotated_bbox_vertices(*bbox2)
    edges = get_edges(vertices1) + get_edges(vertices2)
    for edge in edges: 
        axis = get_axis(edge)
        projection1 = project(vertices1, axis)
        projection2 = project(vertices2, axis)
        print(f"Projection1: {projection1[0]} {projection1[1]}, Projection2: {projection2[0]} {projection2[1]}")
        if not overlap(projection1, projection2): 
            return False # Separating axis found, no intersection 
    return True # No separating axis found, boxes intersect

def parse_box3d(obj):
    return obj['x'], obj['y'], obj['l'], obj['w'], obj['z'], obj['yaw']

# 创建测试用例
PI = 3.1415926

boxes = [
    {'conf': 0.9, 'sub_cls': 0, 'x': 100.0, 'y': 150.0, 'z': 0.0, 'w': 40.0, 'l': 80.0, 'h': 0.0, 'yaw': math.fmod(10.0 * PI / 180, PI * 2)},
    {'conf': 0.85, 'sub_cls': 0, 'x': 105.0, 'y': 160.0, 'z': 0.0, 'w': 60.0, 'l': 90.0, 'h': 0.0, 'yaw': math.fmod(20.0 * PI / 180, PI * 2)},
    {'conf': 0.7, 'sub_cls': 0, 'x': 300.0, 'y': 400.0, 'z': 0.0, 'w': 30.0, 'l': 70.0, 'h': 0.0, 'yaw': math.fmod(45.0 * PI / 180, PI * 2)},
    {'conf': 0.75, 'sub_cls': 0, 'x': 110.0, 'y': 160.0, 'z': 0.0, 'w': 40.0, 'l': 85.0, 'h': 0.0, 'yaw': math.fmod(30.0 * PI / 180, PI * 2)},
    {'conf': 0.6, 'sub_cls': 0, 'x': 500.0, 'y': 600.0, 'z': 0.0, 'w': 50.0, 'l': 100.0, 'h': 0.0, 'yaw': math.fmod(90.0 * PI / 180, PI * 2)},
    {'conf': 0.5, 'sub_cls': 0, 'x': 505.0, 'y': 610.0, 'z': 0.0, 'w': 45.0, 'l': 105.0, 'h': 0.0, 'yaw': math.fmod(75.0 * PI / 180, PI * 2)},
    {'conf': 0.3, 'sub_cls': 0, 'x': 700.0, 'y': 800.0, 'z': 0.0, 'w': 30.0, 'l': 60.0, 'h': 0.0, 'yaw': math.fmod(15.0 * PI / 180, PI * 2)},
]

# 执行 SAT NMS
selected_boxes = sat_nms(boxes, 'conf', parse_box3d)

# 输出结果
print("Kept boxes:")
for box in selected_boxes:
    print(box)

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

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

相关文章

(C语言贪吃蛇)11.贪吃蛇方向移动和刷新界面一起实现面临的问题

目录 前言 实现效果 支持方向变换 修改默认效果 如何修改 总结 前言 我们上节实现了不需要按下右键就可以是贪吃蛇自发的向右移动&#xff0c;本节我们主要来解决贪吃蛇方向移动和刷新界面所遇到的问题。 实现效果 上图是我们希望实现的效果&#xff0c;我们可以自发地控…

Go 进阶:Go + gin 极速搭建 EcommerceSys 电商系统

Go 进阶:Go + gin 极速搭建 EcommerceSys 电商系统 前言 本章节适合有一定基础的 Golang 初学者,通过简单的项目实践来加深对 Golang 的基本语法和 Web 开发的理解。 具体请联系作者 项目结构 项目流程图 技术栈 项目结构 项目路由 4. 项目模型 项目初始化 初始化项目文…

归并排序【C语言版-笔记】

目录 一、概念二、排序流程理解三、代码实现3.1主调函数3.2 merge函数 四、性能分析 一、概念 归并是一种算法思想&#xff0c;是将两个或两个一上的有序表合并成一个长度较大的有序表。若一开始无序表中有n个元素&#xff0c;可以把n个元素看作n个有序表&#xff0c;把它们两…

Java中数据转换以及字符串的“+”操作

隐式转换&#xff08;自动类型转换&#xff09; 较小范围的数据类型转成较大范围的数据类型 强制转换&#xff08;显式转换&#xff09; 将数据范围大的数据类型转换为数据范围小的数据类型 基本数据类型之间的转换 当需要将一个较大的数据类型&#xff08;如float或double…

Linux:进程控制(一)

目录 一、写时拷贝 1.创建子进程 2.写时拷贝 二、进程终止 1.函数返回值 2.错误码 3.异常退出 4.exit 5._exit 一、写时拷贝 父子进程&#xff0c;代码共享&#xff0c;不作写入操作时&#xff0c;数据也是共享的&#xff0c;当任意一方试图写入&#xff0c;便通过写时拷…

影刀RPA实战:excel相关图片操作指令解

1.实战目标 excel是工作中必不缺少的工具&#xff0c;今天我们继续使用影刀RPA来实现excel操作的便利性&#xff0c;让影刀自动化来帮我们完成工作。 2.单元格填充图片 2.1 指令说明 功能&#xff1a;向 Excel 单元格插入本地图片或网络图片&#xff0c;支持Office和WPS&…

波阻抗,是电场矢量的模值/磁场矢量的模值

波阻抗是电场复振幅除以磁场复振幅&#xff0c;最后只与介质με有关 所以磁场可用电场强度表示&#xff08;利用波阻抗&#xff09; 问题&#xff0c;复振幅是矢量&#xff0c;波阻抗的定义是复振幅的比值&#xff1f;答案&#xff1a;不是&#xff0c;很明显&#xff0c;波阻…

Web3 游戏周报(9.22 - 9.28)

回顾上周的区块链游戏概况&#xff0c;查看 Footprint Analytics 与 ABGA 最新发布的数据报告。 【9.22-9.28】Web3 游戏行业动态&#xff1a; Axie Infinity 将 Fortune Slips 的冷却时间缩短至 24 小时&#xff0c;从而提高玩家的收入。 Web3 游戏开发商 Darkbright Studios…

Pikachu-Sql Inject-搜索型注入

MySQL的搜索语句&#xff1a; select * from table where column like %text%&#xff1b; 如&#xff1a;使用引号闭合左边的引号&#xff0c; or 11 把所有数据查询出来&#xff1b; # 注释掉后面的 引号等&#xff1b; test or 11# 查询出结果&#xff1a; 注入的核心点…

Cloneable接口(浅拷贝和深拷贝的区别)

前言 Object类中存在这一个clone方法&#xff0c;调用这个方法可以创建一个对象的“拷贝”。但是想要合法调用clone方法&#xff0c;必须要先实现Clonable接口&#xff0c;否则就会抛出CloneNotSupportedException异常。 1 Cloneable接口 //Cloneable接口声明 public interf…

CentOS 7文件系统

从centos7开始&#xff0c;默认的文件系统从ext4变成了XFS。随着虚拟化的应用越来越广泛&#xff0c;作为虚拟化磁盘来源的大文件&#xff08;单个文件几GB级别&#xff09;越来越常见。 1.XFS组成部分&#xff1a; XFS文件系统在数据的分布上主要划分为三部分&#xff1a;数据…

QT篇:QT介绍

一.QT概述 Qt 是一个跨平台的应用程序和用户界面框架&#xff0c;用于开发图形用户界面&#xff08;GUI&#xff09;应用程序以及命令行工 具。它最初由挪威的 Trolltech &#xff08;奇趣科技&#xff09;公司开发&#xff0c;现在由 Qt Company 维护&#xff0c;2020年12月8…

如何在网格中模拟腐烂扩散:如何使用广度优先搜索(BFS)解题

问题描述 你需要在一个二维的网格中处理橘子的腐烂扩散过程&#xff0c;网格中的每个单元格可以有三种状态&#xff1a; 0&#xff1a;表示空格&#xff0c;没有橘子。1&#xff1a;表示一个新鲜的橘子。2&#xff1a;表示一个腐烂的橘子&#xff0c;它可以在 1 分钟内让上下…

模拟算法(1)_替换所有的问号

个人主页&#xff1a;C忠实粉丝 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 C忠实粉丝 原创 模拟算法(1)_替换所有的问号 收录于专栏【经典算法练习】 本专栏旨在分享学习算法的一点学习笔记&#xff0c;欢迎大家在评论区交流讨论&#x1f48c; 目录 1. …

MHA携手Atlas:打造高效读写分离解决方案,引领数据库性能飞跃

作者简介&#xff1a;我是团团儿&#xff0c;是一名专注于云计算领域的专业创作者&#xff0c;感谢大家的关注 座右铭&#xff1a; 云端筑梦&#xff0c;数据为翼&#xff0c;探索无限可能&#xff0c;引领云计算新纪元 个人主页&#xff1a;团儿.-CSDN博客 目录 前言&#…

npm切换到淘宝镜像

1、输入以下命令后回车&#xff0c;npm切换至淘宝镜像 npm config set registry https://registry.npmmirror.com 2、输入以下命令后回车&#xff0c;检查是否切换成功 npm config get registry 若返回此信息&#xff0c;表示切换成功 3、切换后就可使用淘宝镜像加快npm包的…

C语言 | Leetcode C语言题解之第447题回旋镖的数量

题目&#xff1a; 题解&#xff1a; int cmpfunc(const void *a, const void *b) {return (*(int *)a - *(int *)b); } //计算组合数*2 int every(int count) {if (count 1) {return 0;} else {return count * (count - 1);} } //计算每个锚点能产生的回旋镖总数 int part(in…

【嵌入式系统】第18章 脉宽调试器(PWM)

目录 18.1 结构框图 18.3 功能说明 18.3.4 PWM 信号发生器 18.3.5 死区发生器 18.3.6 中断/ADC 触发选择器 18.3.7 同步方法 18.3.8 故障条件 18.3.9 输出控制块 LES 硬件介绍&#xff08;12&#xff09;正交编码接口QEI 19.1 结构框图 19.2 信号描述 19.3 功能说明…

4M-21: An Any-to-Any Vision Model for Tens of Tasks and Modalities论文精度

贡献&#xff1a;在21种高度不同的模态中训练一个统一的模型&#xff0c;并且对比专有模型不会有性能损失做法&#xff1a;将不同模态映射到不同的token空间&#xff0c;并且可以生成不同的模态token【Any-to-any】关键点&#xff1a;如何在不同的模态中应用tokenization进行映…

【MySQL 07】内置函数

目录 1.日期函数 日期函数使用场景&#xff1a; 2.字符串函数 字符串函数使用场景&#xff1a; 3.数学函数 4.控制流函数 1.日期函数 函数示例&#xff1a; 1.在日期的基础上加日期 在该日期下&#xff0c;加上10天。 2.在日期的基础上减去时间 在该日期下减去2天 3.计算两…