yolov5-7.0模型DNN加载函数及参数详解(重要)

yolov5-7.0模型DNN加载函数及参数详解(重要)

  • 引言
  • yolov5(v7.0)
    • 1,yolov5.h(加载对应模型里面的相关参数要更改)
    • 2,main主程序
      • (1)加载网络
      • (2)检测推理(forward)
        • (2.1)制作黑背景正方形,用于blobFromImage的无损缩放
        • (2.2)blobFromImage改变图像格式为网络出入格式
        • (2.3)输入setInput图像并forward推理
        • (2.4)结果解析
          • (2.4.0)方法0直接Mat读取解析(主要用于理解输出结构)
          • (2.4.1)方法1指针法Mat.data解析(快但不易理解)
        • (2.5)绘制结果

引言

  使用opencv的dnn模块对应yolov5(v7.0)的导出的onnx模型进行推理解析,明确各个参数的对应含义及结果。理论上,有了onnx模型,有了该网络模型的输入输出各个参数含义,就可以使用任意的可以读取onnx模型的部署框架进行部署推理。

yolov5(v7.0)

Yolov5(v7.0)
部署:使用opencv4.5.5(opencv4.5.4以上可以读取网络),dnn模块部署

已经导出的cpu、opset=12、640大小图像、默认静态
在这里插入图片描述

如上,输入输出层在后面的DNN模块推理会用到

1,yolov5.h(加载对应模型里面的相关参数要更改)

需要修改的相关参数如下:要根据自己的模型来进行
在这里插入图片描述
在这里插入图片描述

#include <fstream>
#include <sstream>
#include <iostream>
#include <opencv2/dnn.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/highgui.hpp>
using namespace cv;
using namespace dnn;
using namespace std;

class YOLO
{
public:
    struct Detection
    {
        int class_id;
        float confidence;
        Rect box;
    };
public:
    YOLO();
    ~YOLO();
    bool loadNet(string model_path, bool is_cuda);
    Mat formatYolov5(const Mat& source);
    void detect(Mat& image, vector<Detection>& output);
    void drawRect(Mat& image, vector<Detection>& output);
private:
    Net m_net;
    //修改为训练时自己模型Img大小
    float inputWidth = 640;
    float inputHeight = 640;
    //修改 dimensions = 类别数 + 5
    const int dimensions = 6;
    //修改 通过Netron可查看,图片大小320,rows为6300,图片大小640,rows为25200
    const int rows = 25200;

    float scoreThreshold = 0.2;
    float nmsThreshold = 0.4;
    float confThreshold = 0.4;
public:
    //修改为自己的类别数
    const vector<string> m_classNames = { "qrcode" };
    /*const std::vector<std::string> m_classNames = { "person", "bicycle", "car", "motorcycle", "airplane", "bus", "train", "truck", "boat", "traffic light",
        "fire hydrant", "stop sign", "parking meter", "bench", "bird", "cat", "dog", "horse", "sheep", "cow",
        "elephant", "bear", "zebra", "giraffe", "backpack", "umbrella", "handbag", "tie", "suitcase", "frisbee",
        "skis", "snowboard", "sports ball", "kite", "baseball bat", "baseball glove", "skateboard", "surfboard",
        "tennis racket", "bottle", "wine glass", "cup", "fork", "knife", "spoon", "bowl", "banana", "apple",
        "sandwich", "orange", "broccoli", "carrot", "hot dog", "pizza", "donut", "cake", "chair", "couch",
        "potted plant", "bed", "dining table", "toilet", "tv", "laptop", "mouse", "remote", "keyboard", "cell phone",
        "microwave", "oven", "toaster", "sink", "refrigerator", "book", "clock", "vase", "scissors", "teddy bear",
        "hair drier", "toothbrush" };*/

    const vector<Scalar> colors = { Scalar(255, 255, 0), Scalar(0, 255, 0), Scalar(0, 255, 255), Scalar(255, 0, 0) };
};

2,main主程序

int main(int argc, char** argv)
{
    Mat frame = imread("2.jpeg");
    YOLO yolov5;
    //加载模型
    yolov5.loadNet("qrcode.onnx", false);
    std::vector<YOLO::Detection> output;

    //进行检测
    //检测时间
    DWORD time_start, time_end;
    /* 获取开始时间 */
    time_start = GetTickCount(); //从操作系统启动经过的毫秒数

    yolov5.detect(frame, output);

    time_end = GetTickCount();
    int time = (time_end - time_start);
    cout << "Time = " << (time_end - time_start) << "ms\n ";
    //将所耗时间显示到图片上
    putText(frame, format("time:%dms", time), Point(20, 50), FONT_HERSHEY_SIMPLEX, 1.2, Scalar(255, 0,0), 2);
    
    //绘制结果
    yolov5.drawRect(frame, output);
    imshow("output", frame);
    waitKey(0);
    return 0;
}

(1)加载网络

加载已经导出的onnx网络,打印各个层级参数,此时网络读取成功(opencv要在4.5.4以上)
bool YOLO::loadNet(string model_path, bool is_cuda)
{
    try {
        m_net = readNet(model_path);

        //获取各层信息
        vector<string> layer_names = m_net.getLayerNames();		//此时我们就可以获取所有层的名称了,有了这些可以将其ID取出
        for (int i = 0; i < layer_names.size(); i++) {
            int id = m_net.getLayerId(layer_names[i]);			//通过name获取其id
            auto layer = m_net.getLayer(id);						//通过id获取layer
            printf("layer id:%d,type:%s,name:%s\n", id, layer->type.c_str(), layer->name.c_str());	//将每一层的id,类型,姓名打印出来(可以明白此网络有哪些结构信息了)
        }
    }
    catch (const std::exception&) {
        cout << "load faild" << endl;
        return false;
    }
    
    if (is_cuda)
    {
        cout << "Attempty to use CUDA\n";
        m_net.setPreferableBackend(DNN_BACKEND_CUDA);
        m_net.setPreferableTarget(DNN_TARGET_CUDA_FP16);
    }
    else
    {
        cout << "Running on CPU\n";
        m_net.setPreferableBackend(DNN_BACKEND_OPENCV);
        m_net.setPreferableTarget(DNN_TARGET_CPU);
    }
    return true;
}

在这里插入图片描述

(2)检测推理(forward)

推理前要对图像进行预处理,使其变为网络的输入格式

(2.1)制作黑背景正方形,用于blobFromImage的无损缩放

也可以不制作黑背景,但后面blobFromImage缩放计算可能结果有损失

//制作黑背景正方形,用于blobFromImage的无损缩放
//对于长宽比过大的图片,由于opencv的blobFromImage()函数在缩放的时候不是无损缩放,
//会导致图像变形严重导致结果错误或者漏检。虽然blobFromImage里面有个参数可以保持比例缩放,
//但是只会保留中间的部分,两边信息全部丢掉,所以如果你的目标全部在中间就可以无所谓,
//如果不是,那么需要简单的自己做个无损缩放,制作一张全黑的3通道正方形图片,边长为原图的长边,
//最后将原图放在(0,0)的位置上面,这样就可以直接输入blobFromImage里面就可以实现无损缩放了,
//而且不用对检测结果进行二次修正位置了。
//https ://blog.csdn.net/qq_34124780/article/details/116464727
Mat YOLO::formatYolov5(const Mat& source)
{
    int col = source.cols;
    int row = source.rows;
    int _max = MAX(col, row);
    Mat result = Mat::zeros(_max, _max, CV_8UC3);
    source.copyTo(result(Rect(0, 0, col, row)));
    return result;
}
(2.2)blobFromImage改变图像格式为网络出入格式
 Mat blob;
 auto input_image = formatYolov5(image);
 blobFromImage(input_image, blob, 1. / 255., Size(inputWidth, inputHeight), Scalar(), true, false);//1. / 255.数据归一化,图像大小变为输入格式(640*640)true进行通道交换,false不进行切片

此时经过blobFromImage的图像就变成4维了,在imagewatch中不显示
在这里插入图片描述
在这里插入图片描述

(2.3)输入setInput图像并forward推理
m_net.setInput(blob);
vector<Mat> outputs;
m_net.forward(outputs, m_net.getUnconnectedOutLayersNames());

Forward函数有很多,可以得到如下,有的是将所有的输出结果得到(有时输出层有多个),有的是将对应层的输出结果得到,这里直接得到所有最终输出结果就行了
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

实际这里推理只有1个输出结果

 cout << "outputs.size():" << outputs.size() << endl;

在这里插入图片描述

(2.4)结果解析

实际此时推理输出为[1252006]共三维的图像,使用指针的方法的话二维还是三维都会展开成一维指针进行读取
在这里插入图片描述

输出结果如下dims=3维度
在这里插入图片描述

将其转换成Mat可以显示的方式,用于查看对应的效果
在这里插入图片描述

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

(2.4.0)方法0直接Mat读取解析(主要用于理解输出结构)

在这里插入图片描述

与指针相比,通过mat数组获取对应的第4列的数据是对应的起来的,如下应该使用32F数据格式即可(要将其结果转为(25200*6格式的数据保存到mat中))
在这里插入图片描述

上图最右侧的第6列的数据是类的分数(虽然显示1,但实际上分值是浮点数接近1的)
在这里插入图片描述

三维的
在这里插入图片描述

//使用Mat进行相关数据的保存解析(这里主要通过mat用于理解对应的输出结构)
void YOLO::detect2(Mat& image, vector<Detection>& output) {
    Mat blob;
    auto input_image = formatYolov5(image);
    blobFromImage(input_image, blob, 1. / 255., Size(inputWidth, inputHeight), Scalar(), true, false);//1. / 255.数据归一化,图像大小变为输入格式(640*640)true进行通道交换,false不进行切片

    m_net.setInput(blob);
    vector<Mat> outputs;
    m_net.forward(outputs, m_net.getUnconnectedOutLayersNames());

    //结果解析
    //方法0、使用Mat进行解析
    //***********输出测试
    //几个输出结果
    cout << "outputs.size():" << outputs.size() << endl;

    /*Mat detect_res = m_net.forward("output0");
    cout << "detect_res.channels():" << detect_res.size() << endl;
    cout << "outputs[0].channels():" << outputs[0].size() << endl;*/

    //对其结果使用下方的重新复制一个MAT,这样的才能在imagewatch中看到,否则无法显示
    Mat d1 = outputs[0].clone();
    //重新定义一个Mat对象接收(将其转换成输出结构)(rows = 25200, dimensions=6)
    Mat detectionMat2(rows, dimensions, CV_32F, d1.ptr<float>());		//此处是初始化一个行为size[2],深度为浮点型,数据从detection中获取	
                                                                                        //使用imagewatch可以看到6*25200的数组,单通道32F深度
    cout << "detect_res.rows:" << detectionMat2.rows << endl;

    //先得到原始用于blobFromImage归一化的input_image图像与/640的比例,后面用于目标框的结果返回
    float x_factor1 = float(input_image.cols) / inputWidth;   //用于blobFromImage归一化的input_image图像/640,得到对应的比例
    float y_factor1 = float(input_image.rows) / inputHeight;  //用于blobFromImage归一化的input_image图像/640

    vector<int> class_ids1;
    vector<float> confidences1;
    vector<Rect> boxes1;
    //25200行的数据(对应的onnx的输出参数)
    for (int i = 0; i < detectionMat2.rows; i++) {
        float confidence1 = detectionMat2.at<float>(i, 4);         //置信度方框box的得分分值
        //cout << "confidence1:" << confidence1 << endl;
        if (confidence1 >= confThreshold)    //求置信度>某个值
        {
            //cout << "confidence1:" << confidence1 << endl;

            //求类别得分将第一个类别后面的所有的类别作为一个mat一行
            //制作一个vector对象,//循环从类别后面截取各个类的分值,并保存
            vector<float> classes_scores1;
            for (int j = 5; j < detectionMat2.cols; j++) {
                classes_scores1.push_back(detectionMat2.at<float>(i, j));
            }
            //求classes_scores1的最大值及对应的索引
            auto max_class_score = max_element(classes_scores1.begin(), classes_scores1.end());
            int idMax = max_class_score - classes_scores1.begin();
            //cout << "类别的分数值:" << *max_class_score << "id:" << idMax << endl;

            if (*max_class_score > scoreThreshold)
            {
                confidences1.push_back(confidence1);
                class_ids1.push_back(idMax);

                //前4行对应的(物体的x、y、w、h)(这里的x、y、w、h结果只是图像640*640的结果的,要返回)
                float x = detectionMat2.at<float>(i, 0);
                float y = detectionMat2.at<float>(i, 1);
                float w = detectionMat2.at<float>(i, 2);
                float h = detectionMat2.at<float>(i, 3);

                //将x、y、w、h结果返回(由640*640的结果返回到原图size()大小的结果)
                int left = int((x - 0.5 * w) * x_factor1);//(x_factor1*x)
                int top = int((y - 0.5 * h) * y_factor1);
                int width = int(w * x_factor1);             //宽高直接*对应比列即可
                int height = int(h * y_factor1);
                boxes1.push_back(Rect(left, top, width, height));   //得到最终原图对应的矩形框大小
            }
        }

    }

    cout << "boxes.size():" << boxes1.size() << endl;

    //NMS结果非极大值抑制(有些结果是重合的,此处通过nms去除)
    vector<int> nms_result; //保存对应的索引值
    NMSBoxes(boxes1, confidences1, scoreThreshold, nmsThreshold, nms_result);
    for (int i = 0; i < nms_result.size(); i++)
    {
        int idx = nms_result[i];
        Detection result;
        result.class_id = class_ids1[idx];
        result.confidence = confidences1[idx];
        result.box = boxes1[idx];
        output.push_back(result);
    }
}
(2.4.1)方法1指针法Mat.data解析(快但不易理解)
//使用指针进行相关的结果解析(更快速,但不易理解)(相关取值计算过程和上方一样,可以看detect2进行理解)
void YOLO::detect(Mat& image, vector<Detection>& output)
{
    Mat blob;
    auto input_image = formatYolov5(image);
    blobFromImage(input_image, blob, 1. / 255., Size(inputWidth, inputHeight), Scalar(), true, false);//1. / 255.数据归一化,图像大小变为输入格式(640*640)true进行通道交换,false不进行切片
    
    m_net.setInput(blob);
    vector<Mat> outputs;
    m_net.forward(outputs, m_net.getUnconnectedOutLayersNames());

     
    //方法1、使用指针进行解析(1*25200*6)
    float x_factor = float(input_image.cols) / inputWidth;   //用于blobFromImage归一化的input_image图像/640,得到对应的比例
    float y_factor = float(input_image.rows) / inputHeight;  //用于blobFromImage归一化的input_image图像/640
    float* data = (float*)outputs[0].data;  //获取输出结果的初始指针

    vector<int> class_ids;
    vector<float> confidences;
    vector<Rect> boxes;

    for (int i = 0; i < rows; ++i)
    {
        float confidence = data[4];          //置信度方框box的分值
        
        if (confidence >= confThreshold)    //求置信度
        {
            float* classes_scores = data + 5;   //求类别得分
            //求类别得分将第一个类别后面的所有的类别作为一个mat一行(x、y、w、h、confidence、class1、class2、、、)
            //一行,m_classNames.size()个长度
            Mat scores(1, m_classNames.size(), CV_32FC1, classes_scores);
            //求当前最大分数的类别及其索引
            Point class_id;
            double max_class_score;
            minMaxLoc(scores, 0, &max_class_score, 0, &class_id);

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

                //box
                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(Rect(left, top, width, height));
            }
        }
        data += dimensions;  //data隔着对应结果(类别数1+5(x、y、w、h))
    }

    //NMS结果非极大值抑制
    vector<int> nms_result;
    NMSBoxes(boxes, confidences, scoreThreshold, nmsThreshold, nms_result);
    for (int i = 0; i < nms_result.size(); i++)
    {
        int idx = nms_result[i];
        Detection result;
        result.class_id = class_ids[idx];
        result.confidence = confidences[idx];
        result.box = boxes[idx];
        output.push_back(result);
    }
}
(2.5)绘制结果

就是将矩形框、分值等数据绘制到对应的图像上

void YOLO::drawRect(Mat& image, vector<Detection>& output)
{
    int detections = output.size();
    for (int i = 0; i < detections; ++i)
    {
        auto detection = output[i];
        auto box = detection.box;
        auto classId = detection.class_id;
        const auto color = colors[classId % colors.size()];
        rectangle(image, box, color, 3);

        rectangle(image, Point(box.x, box.y - 40), Point(box.x + box.width, box.y), color, FILLED);
        string label = m_classNames[classId] + ":" + to_string(output[i].confidence);
        putText(image, label, Point(box.x, box.y - 5), FONT_HERSHEY_SIMPLEX, 1.5, Scalar(0, 0, 0), 2);
    }

}

下面是Yolov5s.onnx对应模型的结果

在这里插入图片描述

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

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

相关文章

QD1-P2 HTML 编辑器:HBuilderX

本节学习&#xff1a; HTML课程内容介绍HBuilderX编辑器的使用 本节视频 www.bilibili.com/video/BV1n64y1U7oj?p2 HTML 内容 基础语法 标签整体架构DOCTYPE 常用标签 标题和水平线段落和换行列表div 和 span格式化标签图片超链接标签表格表单字符实体 编辑器 HBuilder…

解决pyinstaller 打包 ddddocr 库方法

前言 ddddocr 库 在打包成 exe 文件后一直有各种各样的问题。无法运行。 总是提示缺少 onnxruntime_providers_shared.dll 等问题。例如下图: 所以这里总结一下打包解决方法。 方法 1、 第一步,先使用命令打包一次 pyinstaller -F demo.py -p D:\Python38\Lib\site-pac…

登录注册静态网页实现(HTML,CSS)

实现效果图 实现效果 使用HTML编写页面结构&#xff0c;CSS美化界面&#xff0c;点击注册&#xff0c;跳转到注册界面&#xff0c;均为静态网页&#xff0c;是课上的一个小作业~ 使用正则表达式对输入进行验证&#xff0c;包括邮箱格式验证&#xff0c;用户名格式验证。 正则…

【Java】类型转换与类型提升

目录 1.类型转换 1.1自动类型转换&#xff08;隐式&#xff09; 1.2强制类型转化&#xff08;显式&#xff09; 2.类型提升 3.字符串类型 1.类型转换 Java作为一个强类型编程语言,当不同类型之间的变量相互赋值的时候,会有教严格的校验. 在Java中&#xff0c;当参与运算数…

【C++】——继承(下)

【C】——继承&#xff08;下&#xff09; 5 继承与友元6 继承与静态成员7 多继承7.1 继承模型7.2 菱形继承的问题7.3 虚继承7.4 多继承中的指针偏移问题 8 组合与继承 5 继承与友元 友元关系不能被继承。即一个函数是父类的友元函数&#xff0c;但不是子类的友元函数。也就是说…

组合模式详解

1、组合模式基本介绍 1) 组合模式&#xff08;Composite Pattern&#xff09;&#xff0c;又叫部分整体模式&#xff0c;它创建了对象组的树形结构&#xff0c;将对象组合成树状结构以 表示“整体-部分”的层次关系。 2) 组合模式依据树形结构来组合对象&#xff0c;用来表示部…

UE5 武器IK瞄准系统

创建空项目 创建基础蓝图类My_GameMode&#xff0c;My_HUD&#xff0c;My_PlayChar&#xff0c;My_PlayController 项目设置地图模式 近裁平面 0.1 My_PlayChar蓝图中添加摄像机&#xff0c;角色骨骼网格体&#xff0c;武器骨骼网格体 编辑角色骨骼&#xff0c;预览控制器使用…

动静态IP地址多方面对比分析

“静态IP地址”和“动态IP地址”是互联网通信基础中的重要概念&#xff0c;两者作为IP地址分配的两种基本机制&#xff0c;各自适应不同的应用场景和需求。 我们可以从定义、地址分配机制、网络管理和运维、服务与应用兼容性等角度来分析有什么不同。 首先是定义。 从概念上来…

快速入门Tomcat服务(业务发布基础技能)

文章目录 1 Tomcat简介 2 安装tomcat 2.1 安装jdk 2.2 安装Tomcat 3 Tomcat目录结构 4 Tomcat重要配置文件 1 Tomcat简介 Tomcat是Sun公司官方推荐的Servlet和JSP容器&#xff0c;在中小型系统和并发访问用户不是很多的场合下&#xff0c;其作为轻量级应用服务…

解决无法安装“vue.volar“扩展,跟vscode版本不兼容问题

问题&#xff1a;安装volar插件的时候提示跟vscode版本不兼容 解决方案 1、进入VSCode插件市场&#xff0c;搜索Vue.volar&#xff08;直达链接&#xff1a;volar下载界面&#xff09; 2、点击download Extension&#xff08;下载插件&#xff09; 3、下载.vsix文件完成后&a…

Axure PR 9 开关切换 设计交互

大家好&#xff0c;我是大明同学。 这期内容&#xff0c;我们来探讨Axure开关按钮设计与交互技巧​。 创建切换开关所需的元件 1.打开一个新的 RP 文件并在画布上打开 Page 1。 2.将“圆形”元件拖到画布上&#xff0c;在样式窗格中将高度和宽度设置为35&#xff0c;线段宽度…

HTMLCSS练习

1) 效果如下 2) 代码如下 2.1) HTML <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport" conte…

图像处理(二)——MDPI特刊推荐

特刊征稿 01 期刊名称&#xff1a; Computer Vision and Image Processing, 2nd Edition 截止时间&#xff1a; 投稿截止日期&#xff1a;2024年12月31日 目标及范围&#xff1a; 感兴趣的主题包括但不限于&#xff1a; 用于图像分类和识别的深度学习 对象检测和跟…

浙江省发规院产业发展研究所调研组莅临迪捷软件考察调研

2024年10月10日下午&#xff0c;浙江省发展与规划院产业发展研究所调研组一行莅临迪捷软件考察调研&#xff0c;绍兴市府办、区发改、区经信、迪荡街道等相关领导陪同。 调研组一行参观了迪捷软件的展厅与办公区&#xff0c;深入了解了迪捷软件的公司发展历程、运营状况、产品…

ECCV`24 | 新加坡国立华为提出Vista3D: 实现快速且多视角一致的3D生成

文章链接&#xff1a;https://arxiv.org/pdf/2409.12193 gitbub链接&#xff1a;https://github.com/florinshen/Vista3D 亮点直击 提出了Vista3D&#xff0c;一个用于揭示单张图像3D darkside 的框架&#xff0c;能够高效地利用2D先验生成多样的3D物体。开发了一种从高斯投影到…

tauri开发Mac电脑Safari浏览器一个很奇怪的问题:在 input 输入框输入的是全小写英文字母,会自动将首字母转换为大写解决办法

问题原因 在 Mac 系统中默认使用 Safari 的内核 WKWebView 作为渲染引擎&#xff0c;而 Safari 浏览器的一些 “人性化” 机制&#xff1a;如果输入框中输入的是全小写英文&#xff0c;会自动将首字母转换为大写。 解决办法 我只需要禁止这个默认的行为&#xff0c;即可解决这…

【js逆向学习】极志愿 javascript+python+rpc

JSRPC使用方式 逆向目标逆向过程逆向分析1、什么是 websocket2、websocket的原理3、总体过程3.1 环境说明3.2 python服务端代码3.3 python客户端代码 4、Sekiro-RPC4.1 执行方式4.2 客户端环境4.3 参数说明4.4 SK API4.5 python代码调试4.6 代码注入流程 逆向总结 逆向目标 网…

物联网智能项目(含案例说明)

物联网&#xff08;Internet of Things&#xff0c;简称IoT&#xff09;智能项目是指利用物联网技术将各种物理设备、传感器、软件、网络等连接起来&#xff0c;实现设备之间的互联互通&#xff0c;并通过数据采集、传输、处理和分析&#xff0c;实现智能化管理和控制的项目。以…

三品PLM系统赋能中小企业实现数字化转型迈向管理智能化

在全球化的浪潮中&#xff0c;发达国家的企业在管理体系上更具优势&#xff0c;常采用先进的PLM体系提升运营和战略规划效率。相较之下&#xff0c;国内中小企业在PLM系统的应用上明显滞后&#xff0c;中高层管理人员普遍缺乏相应的认知与实践经验&#xff0c;这限制了企业的创…

【命令操作】查看和分析系统各类日志--journalctl

原文链接&#xff1a;【命令操作】查看和分析系统各类日志–journalctl | 统信 | 麒麟 | 方德 Hello&#xff0c;大家好啊&#xff01;今天给大家带来一篇关于Linux系统上journalctl命令详解的文章。journalctl是systemd的日志查看工具&#xff0c;用于查看和管理系统日志&…