opencv dnn模块 示例(24) 目标检测 object_detection 之 yolov8-pose 和 yolov8-obb

前面博文【opencv dnn模块 示例(23) 目标检测 object_detection 之 yolov8】 已经已经详细介绍了yolov8网络和测试。本文继续说明使用yolov8 进行 人体姿态估计 pose旋转目标检测 OBB

文章目录

  • 1、Yolov8-pose 简单使用
  • 2、Yolov8-OBB
    • 2.1、python 命令行测试
    • 2.2、opencv dnn测试
      • 2.2.1、onnx导出
      • 2.2.2、opencv dnn 中的预处理
      • 2.2.3、opencv dnn 中的后处理
      • 2.2.4、完整代码

1、Yolov8-pose 简单使用

人体姿态估计,使用coco数据集标注格式,17个关键点。

yolov8m-pose.pt 转换得到onnx如下,

(yolo_pytorch) E:\DeepLearning\yolov8-ultralytics>yolo pose export model=yolov8m-pose.pt format=onnx  batch=1 imgsz=640
Ultralytics YOLOv8.0.154  Python-3.9.16 torch-1.13.1+cu117 CPU (Intel Core(TM) i7-7700K 4.20GHz)
YOLOv8m-pose summary (fused): 237 layers, 26447596 parameters, 0 gradients, 81.0 GFLOPs

PyTorch: starting from 'yolov8m-pose.pt' with input shape (1, 3, 640, 640) BCHW and output shape(s) (1, 56, 8400) (50.8 MB)

ONNX: starting export with onnx 1.14.0 opset 16...
ONNX: export success  3.3s, saved as 'yolov8m-pose.onnx' (101.2 MB)

Export complete (7.1s)
Results saved to E:\DeepLearning\yolov8-ultralytics
Predict:         yolo predict task=pose model=yolov8m-pose.onnx imgsz=640
Validate:        yolo val task=pose model=yolov8m-pose.onnx imgsz=640 data=/usr/src/app/ultralytics/datasets/coco-pose.yaml
Visualize:       https://netron.app

输入为640时输出纬度为 (56,8400),56维数据格式定义为 4 + 1 + 17*3:
矩形框box[x,y,w,h],目标置信度conf, 17组关键点 (x, y, conf)。

在后处理中,添加一个保存关键点的数据,一个显示关键点的函数

void postprocess(Mat& frame, cv::Size inputSz, const std::vector<Mat>& outs, Net& net)
{
    // yolov8-pose has an output of shape (batchSize, 56, 8400) (  box[x,y,w,h] + conf + 17*(x,y,conf) )
    ...
    std::vector<cv::Mat> keypoints;

    for(int i = 0; i < rows; ++i) {
        float confidence = data[4];
        if(confidence >= confThreshold) {
				...
				
                boxes.push_back(cv::Rect(left, top, width, height));

                cv::Mat keypoint(1, dimensions - 5, CV_32F, tmp.ptr<float>(i, 5));
                for(int i = 0; i < 17; i++) {
                    keypoint.at<float>(i * 3 + 0) *= x_factor;
                    keypoint.at<float>(i * 3 + 1) *= y_factor;
                }
                keypoints.push_back(keypoint);
            }
        data += dimensions;
    }

    std::vector<int> indices;
    NMSBoxes(boxes, confidences, scoreThreshold, nmsThreshold, indices);

    for(size_t i = 0; i < indices.size(); ++i) {
		...
        drawSkelton(keypoints[idx], frame);
    }
}

std::vector<cv::Scalar> kptcolors = {
     {255, 0, 0}, {255, 85, 0}, {255, 170, 0}, {255, 255, 0}, {170, 255, 0}, {85, 255, 0},
     {0, 255, 0}, {0, 255, 85}, {0, 255, 170}, {0, 255, 255}, {0, 170, 255}, {0, 85, 255},
     {0, 0, 255}, {255, 0, 170}, {170, 0, 255}, {255, 0, 255}, {85, 0, 255},
};

std::vector<std::vector<int>> keypairs = {
    {15, 13},{13, 11},{16, 14},{14, 12},{11, 12},{5, 11},
    {6, 12},{5, 6},{5, 7},{6, 8},{7, 9},{8, 10},{1, 2},
    {0, 1},{0, 2},{1, 3},{2, 4},{3, 5},{4, 6}
};

std::vector<std::vector<int>> keypairs = {
    {15, 13},{13, 11},{16, 14},{14, 12},{11, 12},{5, 11},
    {6, 12},{5, 6},{5, 7},{6, 8},{7, 9},{8, 10},{1, 2},
    {0, 1},{0, 2},{1, 3},{2, 4},{3, 5},{4, 6}
};

void drawSkelton(const Mat& keypoints , Mat& frame)
{
    for(auto& pair : keypairs) {
        auto& pt1 = keypoints.at<cv::Point3f>(pair[0]);
        auto& pt2 = keypoints.at<cv::Point3f>(pair[1]);
        if(pt1.z > 0.5 && pt2.z > 0.5) {
            cv::line(frame, cv::Point(pt1.x, pt1.y), cv::Point(pt2.x, pt2.y), {255,255,0}, 2);
        }
    }
    
    for(int i = 0; i < 17; i++) {
        Point3f pt = keypoints.at<cv::Point3f>(i);
        if(pt.z < 0.5) 
        	continue;   	
        cv::circle(frame, cv::Point(pt.x, pt.y), 3, kptcolors[i], -1);
        cv::putText(frame, cv::format("%d", i), cv::Point(pt.x, pt.y), 1, 1, {255,0,0});
    }
}

结果如下:
在这里插入图片描述

2、Yolov8-OBB

2024年1月10号ultralytics发布了 v8.1.0 - YOLOv8 Oriented Bounding Boxes (OBB)。

YOLOv8框架在在支持分类、对象检测、实例分割、姿态评估的基础上更近一步,现支持旋转对象检测(OBB),基于DOTA数据集,支持航拍图像的15个类别对象检测,包括车辆、船只、典型各种场地等。包含2800多张图像、18W个实例对象。

Obb模型在含有15个类别的 DOTAv1 上训练,不同尺度的YOLOv8 OBB模型的精度与输入格式列表如下:

Modelsize
(pixels)
mAPtest
50
Speed
CPU ONNX
(ms)
Speed
A100 TensorRT
(ms)
params
(M)
FLOPs
(B)
YOLOv8n-obb102478.0204.773.573.123.3
YOLOv8s-obb102479.5424.884.0711.476.3
YOLOv8m-obb102480.5763.487.6126.4208.6
YOLOv8l-obb102480.71278.4211.8344.5433.8
YOLOv8x-obb102481.361759.1013.2369.5676.7

官方的船体、车辆检测示例图如下
在这里插入图片描述

2.1、python 命令行测试

例如,使用yolov8m-obb模型进行测试

yolo obb predict model=yolov8m-obb.pt source=t.jpg

Ultralytics YOLOv8.1.19 🚀 Python-3.9.16 torch-1.13.1+cu117 CUDA:0 (NVIDIA GeForce GTX 1080 Ti, 11264MiB)
YOLOv8m-obb summary (fused): 237 layers, 26408752 parameters, 0 gradients, 80.9 GFLOPs

image 1/1 E:\DeepLearning\yolov8-ultralytics\DJI_0390.JPG: 768x1024 36.0ms
Speed: 6.0ms preprocess, 36.0ms inference, 130.5ms postprocess per image at shape (1, 3, 768, 1024)
Results saved to runs\obb\predict2
💡 Learn more at https://docs.ultralytics.com/modes/predict

网络图片 测试如下
在这里插入图片描述

2.2、opencv dnn测试

2.2.1、onnx导出

yolo export model=yolov8s-obb.pt format=onnx

使用netron查看输入输出
在这里插入图片描述

2.2.2、opencv dnn 中的预处理

预处理和yolov5之后基本一致,letterbox处理,

cv::Mat formatToSquare(const cv::Mat &source)
{
    int col = source.cols;
    int row = source.rows;
    int _max = MAX(col, row);
    cv::Mat result = cv::Mat(_max, _max, CV_8UC3, {114,114,114});
    source.copyTo(result(cv::Rect(0, 0, col, row)));
    return result;
}

之后就是将WHC的图片frame转换为NCWH的blob数据,使用函数 dnn::blobFromImages,完整如下

float scale = 1 / 255.0;  //0.00392
Scalar mean = {0,0,0};
bool swapRB = true;
inpWidth = 1024;
inpHeight = 1024;
Mat blob;

// Create a 4D blob from a frame.
cv::Mat modelInput = frame;
if(letterBoxForSquare && inpWidth == inpHeight)
     modelInput = formatToSquare(modelInput);

blobFromImages(std::vector<cv::Mat>{modelInput}, blob, scale, cv::Size2f(inpWidth, inpHeight), mean, swapRB, false);   

2.2.3、opencv dnn 中的后处理

前面通过可视化看到YOLOv8-OBB 网络输入为 1024x1024, 输出为 1x20x21504,也就是预测框为21504个(三个尺度128x128、64x64、32x32),每个预测框的纬度是 20(针对DOTAv1的数据集15个类别)。详细可以表示为如下公式
21504 × 20 = 128 × 128 × 20 + 64 × 64 × 20 + 32 × 32 × 20 = 128 × 128 × ( 1 + 15 + 4 ) + 128 × 64 × ( 1 + 15 + 4 ) + 32 × 32 × ( 1 + 15 + 4 ) \begin{aligned} 21504\times 20 &= 128\times 128\times 20+64\times 64\times 20+32\times 32\times 20 \\ &= 128\times 128\times (1+15+4) + 128\times 64\times (1+15+4) + 32\times 32\times (1+15+4) \end{aligned} 21504×20=128×128×20+64×64×20+32×32×20=128×128×(1+15+4)+128×64×(1+15+4)+32×32×(1+15+4)

其中的 4 对应的是 cx, cy, w, h,分别代表的含义是边界框中心点坐标、宽高;15 对应的是 DOTAv1 数据集中的 15 个类别置信度;1 对应的是旋转框的旋转角度 angle,其取值范围是在 [-pi/4, 3pi/4] 之间。

在yolov8解码基础上修改,后处理主要改变2个地方,目标框从Rect 改变为 RotatedRect,nms的的对象也相应调整。

(1)RotatedRect 的解码
已知矩形框 cx, cy, w, h 和 角度 angle,首先需要计算旋转之后旋转矩形框的新的四个顶点坐标
在这里插入图片描述
这里数学推导坐标系y轴线上,逆时针旋转。对比图像坐标系y轴线下,yolov8-OBB 角度为顺时针,两者其实是统一的。

参考上面原理,得到旋转目标框的4个顶点在原图上的坐标点计算如下

const float cos_value = cos(angle);
const float sin_value = sin(angle);

std::vector<Point2f> pts = {  // 未旋转前顺时针四个点 左上、右上、右下、左下
    Point2f(cx - w / 2,cy - h / 2),  
    Point2f(cx + w / 2,cy - h / 2), 
    Point2f(cx + w / 2,cy + h / 2), 
    Point2f(cx - w / 2,cy + h / 2), 
};

for(auto& pt : pts) {
    auto x = pt.x;
    auto y = pt.y;
    pt.x = cx + (x - cx) * cos_value - (y - cy) * sin_value;
    pt.y = cy + (x - cx) * sin_value + (y - cy) * cos_value;
}

4个顶点的构造和最终变换结果可以简化为:

const cv::Point2f vec1 = { w / 2 * cos_value,w / 2 * sin_value};
const cv::Point2f vec2 = {-h / 2 * sin_value,h / 2 * cos_value};
std::vector<Point2f> pts{  // 按顺序即可
    Point2f(cx,cy) + vec1 + vec2,
    Point2f(cx,cy) + vec1 - vec2,
    Point2f(cx,cy) - vec1 - vec2,
    Point2f(cx,cy) - vec1 + vec2,
};

(2)RotatedRect 的nms

在前面解码基础上,使用参数为 cv::RotatedRect 的 NMSBoxes 重载版本

std::vector<int> class_ids;
std::vector<float> confidences;
//std::vector<cv::Rect> boxes
std::vector<cv::RotatedRect> boxes;
    
for(....) {
	... 获取当前目标框数据
	
	const cv::Point2f vec1 = { w / 2 * cos_value,w / 2 * sin_value};
    const cv::Point2f vec2 = {-h / 2 * sin_value,h / 2 * cos_value};
    std::vector<Point2f> pts{
    	Point2f(cx,cy) + vec1 + vec2,
    	Point2f(cx,cy) + vec1 - vec2,
    	Point2f(cx,cy) - vec1 - vec2,
    	Point2f(cx,cy) - vec1 + vec2,
	};

   boxes.emplace_back(pts[0], pts[1], pts[2]);
}

std::vector<int> indices;
NMSBoxes(boxes, confidences, scoreThreshold, nmsThreshold, indices);

注意:
这里cv::RotatedRect的构造使用了按顺序排列的3个顶点,实际内存保存的是 rect的中线、宽高和旋转角度。 从cv::RotatedRect对象中提取4个顶点需要重新计算。

(3)绘制代码
在前面绘制Rect可以直接使用 cv::retangle函数, 但是 RotatedRect 只能通过四个顶点进行连线绘制

cv::RotatedRect rrect = ...;

cv::Point2f pts[4];
rrect.points(&pts[0]);

for(int i = 0; i < 4; i++) {
   cv::line(frame, pts[i] ,pts[(i+1)%4], color, 2);
}
//cv::circle(frame, pts[0], 3, {0,0,255}, -1);  // 期望绘制解码后的第一个点顶

2.2.4、完整代码

#pragma once

#include "opencv2/opencv.hpp"

#include <fstream>
#include <sstream>

#include <random>
#include <numeric>

namespace YOLOv8_OBB {

using namespace cv;
using namespace dnn;

float inpWidth;
float inpHeight;
float confThreshold, scoreThreshold, nmsThreshold;
std::vector<std::string> classes;
std::vector<cv::Scalar> colors;

bool letterBoxForSquare = true;

cv::Mat formatToSquare(const cv::Mat &source);

void postprocess(Mat& frame, cv::Size inputSz, const std::vector<Mat>& out, Net& net);

// void drawPred(int classId, float conf, const cv::Rect& rect, Mat& frame);
void drawPred(int classId, float conf, const std::vector<cv::Point2f>& pts, Mat& frame);

std::random_device rd;
std::mt19937 gen(rd());
std::uniform_int_distribution<int> dis(100, 255);


int test()
{
    // 根据选择的检测模型文件进行配置 
    confThreshold = 0.25;
    scoreThreshold = 0.45;
    nmsThreshold = 0.5;
    float scale = 1 / 255.0;  //0.00392
    Scalar mean = {0,0,0};
    bool swapRB = true;
    inpWidth = 1024;
    inpHeight = 1024;

    String modelPath = R"(E:\DeepLearning\yolov8-ultralytics\yolov8m-obb.onnx)";
    String configPath;

    String framework = "";

    //int backendId = cv::dnn::DNN_BACKEND_OPENCV;
    //int targetId = cv::dnn::DNN_TARGET_CPU;

    //int backendId = cv::dnn::DNN_BACKEND_OPENCV;
    //int targetId = cv::dnn::DNN_TARGET_OPENCL;

    int backendId = cv::dnn::DNN_BACKEND_CUDA;
    int targetId = cv::dnn::DNN_TARGET_CUDA;

    // Open file with classes names.
    //if(!classesFile.empty()) {
    //    const std::string& file = classesFile;
    //    std::ifstream ifs(file.c_str());
    //    if(!ifs.is_open())
    //        CV_Error(Error::StsError, "File " + file + " not found");
    //    std::string line;
    //    while(std::getline(ifs, line)) {
    //        classes.push_back(line);
    //        colors.push_back(cv::Scalar(dis(gen), dis(gen), dis(gen)));
    //    }
    //}
    for(int i = 0; i< 15; i++){
        classes.push_back(std::to_string(i));
        colors.push_back(cv::Scalar(dis(gen), dis(gen), dis(gen)));
    }

    // Load a model.
    Net net = readNet(modelPath, configPath, framework);
    net.setPreferableBackend(backendId);
    net.setPreferableTarget(targetId);

    std::vector<String> outNames = net.getUnconnectedOutLayersNames();
    //std::vector<String> outNames{"output"};
    if(backendId == cv::dnn::DNN_BACKEND_CUDA) {
        int dims[] = {1,3,inpHeight,inpWidth};
        cv::Mat tmp = cv::Mat::zeros(4, dims, CV_32F);
        std::vector<cv::Mat> outs;

        net.setInput(tmp);
        for(int i = 0; i < 10; i++)
            net.forward(outs, outNames); // warmup
    }

    // Create a window
    static const std::string kWinName = "Deep learning object detection in OpenCV";
    //cv::namedWindow(kWinName, 0);

    // Open a video file or an image file or a camera stream.
    VideoCapture cap;
    //cap.open(0);
    //cap.open(R"(E:\DeepLearning\darknet-yolo3-master\build\darknet\x64\dog.jpg)");
    //cap.open("http://live.cooltv.top/tv/aishang.php?id=cctv1hd");
    //cap.open(R"(F:\测试视频\路口俯拍\snap1.mkv)");
    //cap.open(R"(E:\DeepLearning\yolov5\data\images\bus.jpg)");
    //cap.open(R"(F:\1、交通事故素材\筛选后素材1\DJI_0014.JPG)");
    cap.open(R"(C:\Users\wanggao\Desktop\aa.jpg)"); // t.jpeg   aaa.jpeg

    cv::TickMeter tk;
    // Process frames.
    Mat frame, blob;

    while(waitKey(1) < 0) {

        cap >> frame;
        if(frame.empty()) {
            waitKey();
            break;
        }

        // Create a 4D blob from a frame.
        cv::Mat modelInput = frame;
        if(letterBoxForSquare && inpWidth == inpHeight)
            modelInput = formatToSquare(modelInput);

        blobFromImages(std::vector<cv::Mat>{modelInput}, blob, scale, cv::Size2f(inpWidth, inpHeight), mean, swapRB, false);
        

        // Run a model.
        net.setInput(blob);
        
        std::vector<Mat> outs;

        auto tt1 = cv::getTickCount();
        net.forward(outs, outNames);
        auto tt2 = cv::getTickCount();

        postprocess(frame, modelInput.size(), outs, net);

        std::string label = format("Inference time: %.2f ms", (tt2 - tt1) / cv::getTickFrequency() * 1000);
        cv::putText(frame, label, Point(0, 15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));
        cv::imshow(kWinName, frame);
    }
    return 0;
}


cv::Mat formatToSquare(const cv::Mat &source)
{
    int col = source.cols;
    int row = source.rows;
    int _max = MAX(col, row);
    cv::Mat result = cv::Mat(_max, _max, CV_8UC3, {114,114,114});
    source.copyTo(result(cv::Rect(0, 0, col, row)));
    return result;
}



void postprocess(Mat& frame, cv::Size inputSz, const std::vector<Mat>& outs, Net& net)
{
    // yolov8 has an output of shape (batchSize, 84, 8400) (Num classes + box[x,y,w,h] + confidence[c])
    // yolov8-obb has an output of shape (batchSize, 20, 2150) (box[x,y,w,h] + confidence[c] + angle)
    auto tt1 = cv::getTickCount();

    float x_factor = inputSz.width / inpWidth;
    float y_factor = inputSz.height / inpHeight;

    std::vector<int> class_ids;
    std::vector<float> confidences;

    //std::vector<cv::Rect> boxes;  // 2150
    std::vector<cv::RotatedRect> boxes;
    std::vector<std::vector<Point2f>> boxesPoints; // 减少计算量

    // [1, 84, 8400] -> [8400,84]
    int rows = outs[0].size[2];
    int dimensions = outs[0].size[1];

    auto tmp = outs[0].reshape(1, dimensions);
    cv::transpose(tmp, tmp);

    float *data = (float *)tmp.data;

    for(int i = 0; i < rows; ++i) {
        float *classes_scores = data + 4;

        cv::Mat scores(1, classes.size(), CV_32FC1, classes_scores);
        cv::Point class_id;
        double max_class_score;

        minMaxLoc(scores, 0, &max_class_score, 0, &class_id);

        if(max_class_score > scoreThreshold) {
            confidences.push_back(max_class_score);
            class_ids.push_back(class_id.x);

            //float x = data[0];
            //float y = data[1];
            //float w = data[2];
            //float h = data[3];
            //int left = int((x - 0.5 * w) * x_factor);
            //int top = int((y - 0.5 * h) * y_factor);
            //int width = int(w * x_factor);
            //int height = int(h * y_factor);
            //boxes.push_back(cv::Rect(left, top, width, height));       

            const float cx = data[0] * x_factor;
            const float cy = data[1] * y_factor;
            const float w = data[2] * x_factor;
            const float h = data[3] * y_factor;
            const float angle = data[19];    
             
            const float cos_value = cos(angle);
            const float sin_value = sin(angle);
             
            const cv::Point2f vec1 = { w / 2 * cos_value,w / 2 * sin_value};
            const cv::Point2f vec2 = {-h / 2 * sin_value,h / 2 * cos_value};
            std::vector<Point2f> pts{
                Point2f(cx,cy) + vec1 + vec2,
                Point2f(cx,cy) + vec1 - vec2,
                Point2f(cx,cy) - vec1 - vec2,
                Point2f(cx,cy) - vec1 + vec2,
            };

            boxes.emplace_back(pts[0], pts[1], pts[2]);
            boxesPoints.emplace_back(pts);
        }

        data += dimensions;
    }

    std::vector<int> indices;
    NMSBoxes(boxes, confidences, scoreThreshold, nmsThreshold, indices);

    auto tt2 = cv::getTickCount();
    std::string label = format("NMS time: %.2f ms", (tt2 - tt1) / cv::getTickFrequency() * 1000);
    cv::putText(frame, label, Point(0, 30), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(0, 255, 0));

    for(size_t i = 0; i < indices.size(); ++i) {
        int idx = indices[i];
        //drawPred(class_ids[idx], confidences[idx], boxes[idx], frame);
        drawPred(class_ids[idx], confidences[idx], boxesPoints[idx], frame);
    }
}

void drawPred(int classId, float conf, const std::vector<cv::Point2f>& pts, Mat& frame)
{
    std::string label = format("%.2f", conf);
    Scalar color = Scalar::all(255);
    if(!classes.empty()) {
        CV_Assert(classId < (int)classes.size());
        label = classes[classId] + ": " + label;
        color = colors[classId];
    }

    /*rectangle(frame, Point(left, top), Point(right, bottom), Scalar(0, 255, 0));*/
    for(int i = 0; i < 4; i++) {
        cv::line(frame, pts[i], pts[(i + 1) % 4], color, 2);
    }
    cv::circle(frame, pts[0], 3, {0,0,255}, -1);

    int baseLine;
    Size labelSize = getTextSize(label, FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);

    int left = pts[0].x;
    int top = std::max((int)pts[0].y, labelSize.height);
    rectangle(frame, Point(left, top - labelSize.height),
              Point(left + labelSize.width, top + baseLine), color, FILLED);
    cv::putText(frame, label, Point(left, top), FONT_HERSHEY_SIMPLEX, 0.5, Scalar());
}

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

java算法第十天 | ● 栈和队列理论基础 ● 232.用栈实现队列 ● 225. 用队列实现栈

栈和队列理论基础 栈 &#xff1a;先进后出队列 &#xff1a;先进先出 Java中队列Queue的操作 队列 使用Queue接口创建队列 &#xff0c;Queue的实现类有LinkedList和ArrayDeque。最常用的实现类是LinkedList。 Queue的六种方法&#xff1a; add&#xff08;&#xff09;和…

基于SpringBoot+MYSQL的网页时装购物系统

目录 1、 前言介绍 2、主要技术 3、系统流程分析 3.1、系统登录流程图 3.2、添加信息流程图 3.3、删除信息流程图 4、系统体系结构 4.1、时装购物系统的结构图 4.2、登录系统结构图 4.3、时装购物系统结构图 5、数据库设计原则 5.1、管理员信息属性图 5.2、用户管…

CSS文本样式值,web前端开发资料

正文 什么是行内元素&#xff1f; display属性为inline的元素为行内元素&#xff0c;英文&#xff1a;inline element&#xff0c;其中文叫法有多种&#xff0c;如&#xff1a;内联元素、内嵌元素、行内元素、直进式元素等。 特点&#xff1a; 和其他元素都在一行上&#x…

汉字中文验证码点选识别匹配,达到商用级别

汉字中文验证码点选识别匹配深度学习方法实现&#xff0c;达到商用级别 一、简介1.1 需求分析1.2 运行效果1.3 性能指标 二、使用方法2.1 源码运行2.2 打包后运行2.3 测试效果 三、项目下载 一、简介 1.1 需求分析 针对中文验证码进行识别&#xff0c;当出现以下验证码时&…

MySQL性能优化-Mysql索引篇(4)

概览 承接上文&#xff0c;我们说过数据库读取磁盘的最小单位是页不是行&#xff0c;那么对于数据库来说&#xff0c;如果我们想要查找多行记录&#xff0c;查询时间是否会成倍地提升呢&#xff1f;其实数据库会采用缓冲池的方式提升页的查找效率。 为了更好地理解SQL查询效率…

计算机设计大赛 深度学习的动物识别

文章目录 0 前言1 背景2 算法原理2.1 动物识别方法概况2.2 常用的网络模型2.2.1 B-CNN2.2.2 SSD 3 SSD动物目标检测流程4 实现效果5 部分相关代码5.1 数据预处理5.2 构建卷积神经网络5.3 tensorflow计算图可视化5.4 网络模型训练5.5 对猫狗图像进行2分类 6 最后 0 前言 &#…

【探索AI】程序员如何选择职业赛道?

程序员如何选择职业赛道&#xff1f; 程序员的职业赛道就像是一座迷宫&#xff0c;有前端的美丽花园&#xff0c;后端的黑暗洞穴&#xff0c;还有数据科学的神秘密室。你准备好探索这个充满挑战和机遇的迷宫了吗&#xff1f;快来了解如何选择职业赛道吧&#xff01; 自我评估…

Git分布式管理-头歌实验日志和版本回退

在Git使用过程中&#xff0c;一种很常见的情况是&#xff1a;发现某个已经提交到仓库里的代码文件有致命的bug&#xff0c;必须将代码回滚到上一个版本&#xff0c;在这种情况下就显示出了Git的强大。Git为每次提交&#xff0c;都保留了日志&#xff0c;根据提交日志&#xff0…

人工智能-飞桨

文章目录 概要安装零基础教程基础知识小结 概要 集核心框架、基础模型库、端到端开发套件、丰富的工具组件于一体的深度学习平台 官方入口 安装 python安装 python官方下载 PaddlePaddle安装 python -m pip install paddlepaddle2.6.0 -i https://mirror.baidu.com/pypi/s…

电脑电源电压不足会出现什么问题?怎么办?

电脑电源的电压是多少&#xff1f; 电脑电源输出一般有12V、5V、3.3V三种不同的电压。 电脑电源负责给电脑配件供电&#xff0c;如CPU、主板、内存条、硬盘、显卡等&#xff0c;是电脑的重要组成部分。 新规格的12V、5V、3.3V等输出的电源分配一般更适合当前电脑配件的电源需求…

【中国电信】光猫 PT632 使用超管权限修改 IP 地址租期时间

背景 由于光猫默认设置的动态 IP 租期是 24 小时&#xff0c;所以每天都会断网一次&#xff0c;严重影响用网体验&#xff0c;所以打算通过修改动态 IP 租期为 一周&#xff08;最长就一周&#xff0c;没有永久的选项&#xff09;来改善。 需求 一台电脑&#xff08;已开启 …

网络信息安全:OpenSSH_7.4p1升级至OpenSSH_9.6p1 | ssh-agent远程代码执行漏洞(CVE-2023-38408)

网络&信息安全&#xff1a;OpenSSH_7.4p1升级至OpenSSH_9.6p1 | ssh-agent远程代码执行漏洞(CVE-2023-38408&#xff09; 1.1 风险详情1.2 操作环境1.3 漏洞处理&#xff1a;OpenSSH升级1、查看SSH客户端的版本信息2、列出系统中openssl、openssh的软件包3、启动telnet&…

idea:springboot项目搭建

目录 一、创建项目 1、File → New → Project 2、Spring Initializr → Next 3、填写信息 → Next 4、web → Spring Web → Next 5、填写信息 → Finish 6、处理配置不合理内容 7、注意事项 7.1 有依赖包&#xff0c;却显示找不到依赖&#xff0c;刷新一下maven 二…

SmartX 携手 openGauss 社区发布联合方案评测与性能最佳实践 | 附优化方法与测试数据

近日&#xff0c;北京志凌海纳科技有限公司&#xff08;以下简称 “SmartX”&#xff09;携手 openGauss 社区完成了 openGauss 数据库基于 SmartX 超融合平台&#xff08;SMTX OS&#xff09;和 SmartX 分布式存储平台&#xff08;SMTX ZBS&#xff09;的性能测试和调优。 结…

JVM(垃圾回收机制 ---- GC)

啥是垃圾? 不再使用的内存 啥是垃圾回收机制? 自动释放不用的内存 注意: GC 主要是针对 堆 进行的 GC的基本操作单位是 对象, 即GC’回收的是整个对象都不使用的情况 GC 的优缺点 好处: 省心, 写代码简单, 不易出错 缺点: 需要消耗额外资源, 有额外性能开销 , 此外, 易触发 S…

vue 内容渲染和属性绑定

内容渲染指令 1. 使用v-text指令&#xff0c;将数据采用纯文本方式填充其空元素中 <script setup>import { reactive } from vuelet student reactive({name: Jack,desc: <h3>我是来自中国的小朋友&#xff01;</h3>}) </script> <template><…

介绍下RabbitMQ的事务机制

想要保证发送者一定能把消息发送给RabbitMQ&#xff0c;一种是通过confirm机制&#xff0c;另外一种就是通过事务机制。 RabbitMQ的事务机制&#xff0c;允许生产者将一组操作打包一个原子事务单元&#xff0c;那么全部执行成功&#xff0c;要么全部失败。事务提供了一种确保消…

【开源】JAVA+Vue.js实现食品生产管理系统

目录 一、摘要1.1 项目介绍1.2 项目录屏 二、功能模块2.1 加工厂管理模块2.2 客户管理模块2.3 食品管理模块2.4 生产销售订单管理模块2.5 系统管理模块2.6 其他管理模块 三、系统展示四、核心代码4.1 查询食品4.2 查询加工厂4.3 新增生产订单4.4 新增销售订单4.5 查询客户 五、…

操作系统:初识操作系统

目录 1.冯诺依曼体系结构 2.操作系统 2.1什么是操作系统 2.2为什么需要操作系统 2.3怎么实现操作系统 1.冯诺依曼体系结构 对于上图&#xff1a; 输入设备完成的是写入工作&#xff0c;输出设备完成输出工作&#xff0c;这两部分包含磁盘这类的外存。 存储器一般指的是内存…

【C#杂谈】在 .NET Framework 中使用新的C#语言特性

前排提示&#xff1a;提出一个可以让 [^1] 这中语法可以在.NET Framework运行时中使用的方法 众所都周知&#xff0c;.NET Framework&#xff08;以下简称 .NF&#xff09;作为一个被微软官方确认不在继续发布新特性的运行时&#xff0c;它所对应的C#语言版本被&#xff08;官方…