基于Yolov8面部七种表情检测与识别C++模型部署
表情识别
七种表情识别是一个多学科交叉的研究领域,它结合了心理学、认知科学、计算机视觉和机器学习等学科的知识和技术。
基本概念
- 表情的定义:表情是人们在情绪体验时面部肌肉活动的结果,是人类情感交流的基本方式之一。
- 基本表情理论:心理学家Paul Ekman提出,人类有七种基本情绪,每种情绪都有其特定的面部表情模式。
七种基本表情
- 快乐:通常与积极情绪相关,特征是嘴角上扬,眼睛周围肌肉收缩。
- 悲伤:与失落或痛苦相关,特征是眉毛下垂,嘴角下拉。
- 愤怒:与愤怒或挫败相关,特征是眉毛下压,嘴唇紧闭。
- 惊讶:与意外或震惊相关,特征是眼睛和嘴巴张开。
- 恐惧:与害怕或焦虑相关,特征是眼睛瞪大,眉毛提升。
- 厌恶:与反感或不喜欢相关,特征是上唇提升,嘴角下拉。
- 轻蔑:与鄙视或不屑一顾相关,特征是嘴角一侧上扬。
应用领域
- 心理健康监测:通过表情识别来评估个体的情绪状态。
- 人机交互:使机器人或计算机系统能够理解和响应用户的情绪。
- 安全监控:在安全检查中识别可疑行为或情绪异常。
- 娱乐和媒体:在电影、游戏和虚拟现实中生成逼真的虚拟角色表情。
挑战和限制
- 个体差异:不同人表达相同情绪的方式可能不同。
- 文化差异:不同文化背景下,表情的表达和解读可能存在差异。
- 环境因素:光照、遮挡和面部表情的微妙变化可能影响识别的准确性。
- 实时性要求:在某些应用场景下,如视频监控或实时交互,对系统的处理速度有较高要求。
发展趋势
- 多模态识别:结合面部表情以外的信息,如语音、心率等,以提高识别的准确性。
- 迁移学习:利用预训练的深度学习模型来提高小样本学习的性能。
- 无监督和半监督学习:减少对大量标注数据的依赖,提高模型的泛化能力。
数据集
数据集内容:RAF-DB数据集是一个大规模面部表情数据库,由315名工作人员(大学的学生和教职员工)对表情进行标注。
在对表情的选择上,从一系列表情(例如:微笑,咯咯笑声,哭泣,愤怒,害怕,害怕,恐惧,震惊,惊讶,厌恶,无表情)中,挑选出六种基本情感以及中立情感,一共7种表情进行表情标注。
数据集数量:RAF-DB数据集,包含大约3万张面部图像。除了表情标注外,对每个人脸还有5个特征点标注,人脸边界框,种族,年龄范围和性别等属性的标注。
数据集功能:表情识别、人脸检测、年龄估计
下载链接:http://www.whdeng.cn/RAF/model1.html
Yolov8目标检测
YOLOv8是YOLO系列的最新迭代产品,它在目标检测领域带来了一系列创新和改进。以下是YOLOv8的一些关键特点和功能:
-
模型结构:YOLOv8采用了新的SOTA模型,包括不同分辨率的目标检测网络和基于YOLACT的实例分割模型。它在骨干网络和Neck部分可能参考了YOLOv7 ELAN的设计思想,将YOLOv5的C3结构换成了C2f结构,并对不同尺度模型调整了不同的通道数。
-
Head部分:与YOLOv5相比,YOLOv8的Head部分改动较大,换成了目前主流的解耦头结构,将分类和检测头分离,并且从Anchor-Based换成了Anchor-Free。
-
Loss计算:YOLOv8采用了TaskAlignedAssigner正样本分配策略,并引入了Distribution Focal Loss,这有助于提高检测过程的准确性和效率。
-
数据增强:在训练过程中,YOLOv8引入了YOLOX中的最后10个epoch关闭Mosaic增强的操作,这可以有效地提升精度。
-
训练策略:YOLOv8的模型训练总epoch数从300提升到了500,这导致训练时间增加,但可能有助于进一步提升模型性能。
-
性能和速度:YOLOv8专注于保持精度与速度之间的最佳平衡,适用于各种应用领域的实时目标检测任务。
-
预训练模型:YOLOv8提供一系列预训练模型,以满足各种任务和性能要求,从而更容易为您的特定用例找到合适的模型。
-
支持的任务和模式:YOLOv8系列提供多种模型,每种模型都专门用于计算机视觉中的特定任务,如物体检测、实例分割、姿态/关键点检测等。
-
性能基准测试:YOLOv8可以进行性能基准测试,以评估在不同导出格式下的速度和准确性。
-
实战应用:有教程提供了YOLOv8在LabVIEW中的部署,包括模型的导出、图片和视频推理的实现。
环境安装
安装环境:
conda create -n yolov8 python=3.8
activate ylolv8
pip install ultralytics
转onnx
安装完成之后,分割数据进行模型训练。训练完之后把模型转成onnx,使用以下命令将YOLO模型从PyTorch导出为ONNX格式,并设置opset为12:
yolo export model=yolov8s.pt format=onnx dynamic=False opset=12
命令的含义解释如下:
yolo export: 使用YOLO导出功能、
model=yolov8s.pt: 指定PyTorch模型的路径
format=onnx: 导出为ONNX格式
dynamic=False: 关闭动态输入
opset=12: 设置ONNX模型的opset版本为12
转ncnn模型
得到onnx模型之后要转成ncnn的模型,可以使用onnx2ncnn.exe进行模型转换,也可以使用在线的模型转换工具。
模型C++推理部署
ncnn库
从官方下载以编译好的ncnn库,我这里使用的IDE是vs2022,下载对应自己的库。导入lib和include,如果想要指用GPU进行推理,则要编译vulkan库。
C++ 推理代码
#include "FacialEmotion.h"static float fast_exp(float x)
{union {uint32_t i;float f;} v{};v.i = (1 << 23) * (1.4426950409 * x + 126.93490512f);return v.f;
}static inline float sigmoid(float x)
{return 1.0f / (1.0f + fast_exp(-x));
}
static float intersection_area(const Object& a, const Object& b)
{cv::Rect_<float> inter = a.rect & b.rect;return inter.area();
}static void qsort_descent_inplace(std::vector<Object>& faceobjects, int left, int right)
{int i = left;int j = right;float p = faceobjects[(left + right) / 2].prob;while (i <= j){while (faceobjects[i].prob > p)i++;while (faceobjects[j].prob < p)j--;if (i <= j){// swapstd::swap(faceobjects[i], faceobjects[j]);i++;j--;}}// #pragma omp parallel sections{// #pragma omp section{if (left < j) qsort_descent_inplace(faceobjects, left, j);}// #pragma omp section{if (i < right) qsort_descent_inplace(faceobjects, i, right);}}
}static void qsort_descent_inplace(std::vector<Object>& faceobjects)
{if (faceobjects.empty())return;qsort_descent_inplace(faceobjects, 0, faceobjects.size() - 1);
}static void nms_sorted_bboxes(const std::vector<Object>& faceobjects, std::vector<int>& picked, float nms_threshold)
{picked.clear();const int n = faceobjects.size();std::vector<float> areas(n);for (int i = 0; i < n; i++){areas[i] = faceobjects[i].rect.width * faceobjects[i].rect.height;}for (int i = 0; i < n; i++){const Object& a = faceobjects[i];int keep = 1;for (int j = 0; j < (int)picked.size(); j++){const Object& b = faceobjects[picked[j]];// intersection over unionfloat inter_area = intersection_area(a, b);float union_area = areas[i] + areas[picked[j]] - inter_area;// float IoU = inter_area / union_areaif (inter_area / union_area > nms_threshold)keep = 0;}if (keep)picked.push_back(i);}
}
static void generate_grids_and_stride(const int target_w, const int target_h, std::vector<int>& strides, std::vector<GridAndStride>& grid_strides)
{for (int i = 0; i < (int)strides.size(); i++){int stride = strides[i];int num_grid_w = target_w / stride;int num_grid_h = target_h / stride;for (int g1 = 0; g1 < num_grid_h; g1++){for (int g0 = 0; g0 < num_grid_w; g0++){GridAndStride gs;gs.grid0 = g0;gs.grid1 = g1;gs.stride = stride;grid_strides.push_back(gs);}}}
}static void generate_proposals(std::vector<GridAndStride> grid_strides, const ncnn::Mat& pred, float prob_threshold, std::vector<Object>& objects)
{const int num_points = grid_strides.size();const int num_class = 7;const int reg_max_1 = 16;for (int i = 0; i < num_points; i++) //out.h{const float* scores = pred.row(i) + 4 * reg_max_1;// find label with max scoreint label = -1;float score = -FLT_MAX;for (int k = 0; k < num_class; k++){float confidence = scores[k];if (confidence > score){label = k;score = confidence;}}float box_prob = sigmoid(score);if (box_prob >= prob_threshold){ncnn::Mat bbox_pred(reg_max_1, 4, (void*)pred.row(i));{ncnn::Layer* softmax = ncnn::create_layer(ncnn::layer_to_index("Softmax"));
// ncnn::layer_to_index("Softmax")ncnn::ParamDict pd;pd.set(0, 1); // axis
// pd.set(1, 1);softmax->load_param(pd);ncnn::Option opt;opt.num_threads = 1;opt.use_packing_layout = false;softmax->create_pipeline(opt);softmax->forward_inplace(bbox_pred, opt);softmax->destroy_pipeline(opt);delete softmax;}float pred_ltrb[4];for (int k = 0; k < 4; k++){float dis = 0.f;const float* dis_after_sm = bbox_pred.row(k);for (int l = 0; l < reg_max_1; l++){dis += l * dis_after_sm[l];}pred_ltrb[k] = dis * grid_strides[i].stride;}float pb_cx = (grid_strides[i].grid0 + 0.5f) * grid_strides[i].stride; float pb_cy = (grid_strides[i].grid1 + 0.5f) * grid_strides[i].stride;float x0 = pb_cx - pred_ltrb[0];float y0 = pb_cy - pred_ltrb[1];float x1 = pb_cx + pred_ltrb[2];float y1 = pb_cy + pred_ltrb[3];Object obj;obj.rect.x = x0;obj.rect.y = y0;obj.rect.width = x1 - x0;obj.rect.height = y1 - y0;obj.label = label;obj.prob = box_prob;objects.push_back(obj);}}
}//调用ncnn转置操作
static void transpose(const ncnn::Mat& in, ncnn::Mat& out)
{ncnn::Option opt;opt.num_threads = 1;opt.use_fp16_storage = false;opt.use_packing_layout = true;ncnn::Layer* op = ncnn::create_layer("Permute");// set paramncnn::ParamDict pd;pd.set(0, 1);// order_type=1op->load_param(pd);op->create_pipeline(opt);op->forward(in,out, opt);op->destroy_pipeline(opt);delete op;
}FacialEmotion::FacialEmotion()
{blob_pool_allocator.set_size_compare_ratio(0.f);workspace_pool_allocator.set_size_compare_ratio(0.f);
}int FacialEmotion::load(std::string parma_path,std::string bin_path,int _target_size,bool use_gpu)
{yolo.clear();blob_pool_allocator.clear();workspace_pool_allocator.clear();ncnn::set_cpu_powersave(2);ncnn::set_omp_num_threads(ncnn::get_big_cpu_count());yolo.opt = ncnn::Option();#if NCNN_VULKANyolo.opt.use_vulkan_compute = use_gpu;
#endifyolo.opt.num_threads = 2;yolo.opt.blob_allocator = &blob_pool_allocator;yolo.opt.workspace_allocator = &workspace_pool_allocator;yolo.load_param(parma_path.c_str());yolo.load_model(bin_path.c_str());target_size = _target_size;return 0;
}int FacialEmotion::detect(const cv::Mat& rgb, std::vector<Object>& objects, float prob_threshold, float nms_threshold)
{int width = rgb.cols;int height = rgb.rows;// pad to multiple of 32int w = width;int h = height;float scale = 1.f;if (w > h){scale = (float)target_size / w;w = target_size;h = h * scale;}else{scale = (float)target_size / h;h = target_size;w = w * scale;}ncnn::Mat in = ncnn::Mat::from_pixels_resize(rgb.data, ncnn::Mat::PIXEL_BGR, width, height, w, h);// pad to target_size rectangleint wpad = (w + 31) / 32 * 32 - w;int hpad = (h + 31) / 32 * 32 - h;ncnn::Mat in_pad;ncnn::copy_make_border(in, in_pad, hpad / 2, hpad - hpad / 2, wpad / 2, wpad - wpad / 2, ncnn::BORDER_CONSTANT, 0.f);in_pad.substract_mean_normalize(0, norm_vals);ncnn::Extractor ex = yolo.create_extractor();ex.input("images", in_pad);std::vector<Object> proposals;ncnn::Mat out;ex.extract("/model.22/Concat_3_output_0", out);ncnn::Mat out1;transpose(out, out1);std::vector<int> strides = {8, 16, 32}; // might have stride=64std::vector<GridAndStride> grid_strides;generate_grids_and_stride(in_pad.w, in_pad.h, strides, grid_strides);generate_proposals(grid_strides, out1, prob_threshold, proposals);qsort_descent_inplace(proposals);// apply nms with nms_thresholdstd::vector<int> picked;nms_sorted_bboxes(proposals, picked, nms_threshold);int count = picked.size();objects.resize(count);for (int i = 0; i < count; i++){objects[i] = proposals[picked[i]];// adjust offset to original unpaddedfloat x0 = (objects[i].rect.x - (wpad / 2)) / scale;float y0 = (objects[i].rect.y - (hpad / 2)) / scale;float x1 = (objects[i].rect.x + objects[i].rect.width - (wpad / 2)) / scale;float y1 = (objects[i].rect.y + objects[i].rect.height - (hpad / 2)) / scale;// clipx0 = std::max(std::min(x0, (float)(width - 1)), 0.f);y0 = std::max(std::min(y0, (float)(height - 1)), 0.f);x1 = std::max(std::min(x1, (float)(width - 1)), 0.f);y1 = std::max(std::min(y1, (float)(height - 1)), 0.f);objects[i].rect.x = x0;objects[i].rect.y = y0;objects[i].rect.width = x1 - x0;objects[i].rect.height = y1 - y0;}// sort objects by areastruct{bool operator()(const Object& a, const Object& b) const{return a.rect.area() > b.rect.area();}} objects_area_greater;std::sort(objects.begin(), objects.end(), objects_area_greater);return 0;
}int FacialEmotion::draw(cv::Mat& rgb, const std::vector<Object>& objects)
{static const char *class_names[] = {"surprise", "fear","disgust","happiness","sadness","anger","neutral"};static const unsigned char colors[19][3] = {{ 54, 67, 244},{ 99, 30, 233},{176, 39, 156},{183, 58, 103},{181, 81, 63},{243, 150, 33},{244, 169, 3},{212, 188, 0},{136, 150, 0},{ 80, 175, 76},{ 74, 195, 139},{ 57, 220, 205},{ 59, 235, 255},{ 7, 193, 255},{ 0, 152, 255},{ 34, 87, 255},{ 72, 85, 121},{158, 158, 158},{139, 125, 96}};int color_index = 0;for (size_t i = 0; i < objects.size(); i++){const Object& obj = objects[i];const unsigned char* color = colors[color_index % 19];color_index++;cv::Scalar cc(color[0], color[1], color[2]);cv::rectangle(rgb, obj.rect, cc, 2);char text[256];sprintf(text, "%s %.1f%%", class_names[obj.label], obj.prob * 100);int baseLine = 0;cv::Size label_size = cv::getTextSize(text, cv::FONT_HERSHEY_SIMPLEX, 0.5, 1, &baseLine);int x = obj.rect.x;int y = obj.rect.y - label_size.height - baseLine;if (y < 0)y = 0;if (x + label_size.width > rgb.cols)x = rgb.cols - label_size.width;cv::rectangle(rgb, cv::Rect(cv::Point(x, y), cv::Size(label_size.width, label_size.height + baseLine)), cc, -1);cv::Scalar textcc = (color[0] + color[1] + color[2] >= 381) ? cv::Scalar(0, 0, 0) : cv::Scalar(255, 255, 255);cv::putText(rgb, text, cv::Point(x, y + label_size.height), cv::FONT_HERSHEY_SIMPLEX, 0.5, textcc, 1);}return 0;
}
实现的效果如下:
源码地址:https://download.csdn.net/download/matt45m/89612957
相关文章:

基于Yolov8面部七种表情检测与识别C++模型部署
表情识别 七种表情识别是一个多学科交叉的研究领域,它结合了心理学、认知科学、计算机视觉和机器学习等学科的知识和技术。 基本概念 表情的定义:表情是人们在情绪体验时面部肌肉活动的结果,是人类情感交流的基本方式之一。基本表情理论&a…...
未确认融资费用含义及会计处理流程
文章目录 一、含义二、会计处理流程2.1、初始计量2.2、后续计量2.3、报表列式 三、实务中的注意事项 一、含义 未确认融资费用: 由于企业现有资金不足,购买资产时选择分期支付款项,导致实际支付的款项大于资产的购入价值,两者的差额就是由于…...
Linux配置go程序为service后台开机自启动
1.编写需要启动的项目路径以及简单配置 sudo nano /etc/systemd/system/go.service#定义服务的元数据和依赖关系。 [Unit] #这是对服务的简短描述。 DescriptionMy Go Service #network.target 是一个虚拟目标,它表示网络服务已经初始化完成。该指令告诉 systemd 在…...

汇舟问卷:完成16份调查,挣了40美金,换算后美滋滋
这个世界有太多的人30岁,35岁以后,当初没有去做自己想做的工作,没有花时间去坚持想做的工作,他们在选择这份想做的事业的前提被自己的父母朋友爱人阻断了。 他们告诉你,要努力的做好现在的工作,争取升职…...

Nacos 202407月RCE漏洞(0day)与复现
免责声明:请勿利用文章内的相关技术从事非法测试,由于传播、利用此文所提供的信息或者工具而造成的任何直接或者间接的后果及损失,均由使用者本人负责,所产生的一切不良后果与文章作者无关。该文章仅供学习用途使用。 一、背景与…...

Dynamo修改共享参数绑定的分组——群问题整理005
Hello大家好!我是九哥~ 今天继续给大家分享一些短平快的小教程,是来自群里面的问题。 问题005:Dynamo修改共享参数绑定的分组 今天看到群里询问如何修改参数所在的分组,查了下API,项目参数是不行的,不过共享参数是允许ReInsert()的,那么就好办了。 然后在Document下…...

聚焦汽车软件开发与测试:静态代码扫描、单元测试与集成测试等方面的实践应用
2024年7月18-19日,龙智携汽车软件开发及管理解决方案创新亮相2024 ATC汽车软件与安全技术周。龙智技术支持部负责人&Atlassian认证专家叶燕秀、龙智功能安全高级工程师景玉鑫在活动主会场联合发表了精彩演讲,分享推动汽车软件开发与功能安全的创新实…...

「队列」实现FIFO队列(先进先出队列|queue)的功能 / 手撕数据结构(C++)
概述 队列,是一种基本的数据结构,也是一种数据适配器。它在底层上以链表方法实现。 队列的显著特点是他的添加元素与删除元素操作:先加入的元素总是被先弹出。 一个队列应该应该是这样的: --------------QUEUE-------------——…...
C++ STL中 `set` 和 `multiset` 简单对比
在 C STL 中,set 和 multiset 都是用于存储唯一或重复元素的关联容器,但它们在处理元素的唯一性和特性方面有显著的区别。以下是这两个容器的详细比较: 1. 数据结构 set:基于红黑树(自平衡的二叉搜索树)实…...
代码随想录算法训练营Day20 | Leetcode 235 二叉搜索树的最近公共祖先 Leetcode 701 二叉搜索树中的插入操作
Leetcode 235 二叉搜索树的最近公共祖先 题目链接:235. 二叉搜索树的最近公共祖先 - 力扣(LeetCode) 代码随想录题解:代码随想录 (programmercarl.com) 思路:相比普通二叉树更简单,因为二叉搜索树的节点…...

第九届世界3D渲染大赛:赛程安排、赛事规则
第九届世界3D渲染大赛即将拉开帷幕,汇聚全球顶尖CG艺术家,展现最具有视觉盛宴的CG创作。那么该赛事的行程如何安排呢,赛事规则又是什么呢?本篇整理了赛事安排、赛事规则等内容,希望帮助大家。 赛事主题:Kin…...
RocketMQ5.0 Consumer Group
消费者分组的概念 消费者分组(Consumer Group)是指一组消费同一类消息的消费者实例。每个消费者分组有一个唯一的名称,用于标识该分组。消费者分组的设计使得消息能够被多个消费者实例并行消费,同时确保每条消息只被一个消费者实例…...

vulnhub之serial
这次我们来做这个靶场 项目地址https://download.vulnhub.com/serial/serial.zip 使用vm新建虚拟机 以下为注意事项 第一步,收集资产 扫描靶场ip netdiscover -i eth0 -r 192.168.177.0/24 抓个包 扫描目录 看到了cookie中有一个user Tzo0OiJVc2VyIjoyOntzOj…...
卷积神经网络(CNN)简单原理与简单代码实现
卷积神经网络(CNN)简单原理与简单代码实现 卷积神经网络(CNN)简单原理基本原理卷积层(Convolutional Layer):激活层(Activation Layer):池化层(Po…...

实时数仓分层架构详解
首先,我们从数据仓库说起。 数据仓库的概念可以追溯到20世纪80年代,当时IBM的研究人员提出了商业数据仓库的概念。数据仓库概念的提出,是为了解决和数据流相关的各种问题,特别是多重数据复制带来的高成本问题。 数据仓库之父Bill …...

计算机“八股文”在实际工作中是助力、阻力还是空谈?
“八股文”在实际工作中是助力、阻力还是空谈? 作为现在各类大中小企业面试程序员时的必问内容,“八股文”似乎是很重要的存在。但“八股文”是否能在实际工作中发挥它“敲门砖”应有的作用呢?有IT人士不禁发出疑问:程序员面试考…...

新160个crackme - 022-CM_2
运行分析 需破解Name和Serial,输入的小写字母都会变为大写字母 PE分析 C程序,32位,无壳 静态分析&动态调试 发现关键字符串 ida动态调试,发现Name和Serial长度需要大于5,且Serial前6位明文爆出,6287-A …...
在.c和.h 文件里定义数组的区别
在C语言开发中,掌握如何在.c文件和.h文件中合理定义数组,对于维护代码的模块化和避免不必要的编译错误至关重要。本文将探讨在这两种类型的文件中定义数组时需要注意的几个关键方面,包括定义性质、作用域、重复定义问题以及外部可见性等&…...

使用Step Functions运行AWS Backup时必备的权限要点
引言 在尝试从Step Functions执行AWS Backup的按需备份时,我在权限方面遇到了一些困难。为了备忘,我将这些经验写成这篇文章。 概述 从Step Functions执行AWS Backup时,需要分配以下权限: AWS Backup相关权限 执行备份的权限…...

强化JS基础水平的10个单行代码来喽!(必看)
目录 生成数组 数组简单数据去重 多数组取交集 重新加载当前页面 滚动到页面顶部 查找最大值索引 进制转换 文本粘贴 删除无效属性 随机颜色生成 生成数组 当你需要要生成一个0-99的数组 // 生成一个0-99的数组 // 方案一 const createArr n > Array.from(new A…...
Linux链表操作全解析
Linux C语言链表深度解析与实战技巧 一、链表基础概念与内核链表优势1.1 为什么使用链表?1.2 Linux 内核链表与用户态链表的区别 二、内核链表结构与宏解析常用宏/函数 三、内核链表的优点四、用户态链表示例五、双向循环链表在内核中的实现优势5.1 插入效率5.2 安全…...
java_网络服务相关_gateway_nacos_feign区别联系
1. spring-cloud-starter-gateway 作用:作为微服务架构的网关,统一入口,处理所有外部请求。 核心能力: 路由转发(基于路径、服务名等)过滤器(鉴权、限流、日志、Header 处理)支持负…...
Neo4j 集群管理:原理、技术与最佳实践深度解析
Neo4j 的集群技术是其企业级高可用性、可扩展性和容错能力的核心。通过深入分析官方文档,本文将系统阐述其集群管理的核心原理、关键技术、实用技巧和行业最佳实践。 Neo4j 的 Causal Clustering 架构提供了一个强大而灵活的基石,用于构建高可用、可扩展且一致的图数据库服务…...
MySQL用户和授权
开放MySQL白名单 可以通过iptables-save命令确认对应客户端ip是否可以访问MySQL服务: test: # iptables-save | grep 3306 -A mp_srv_whitelist -s 172.16.14.102/32 -p tcp -m tcp --dport 3306 -j ACCEPT -A mp_srv_whitelist -s 172.16.4.16/32 -p tcp -m tcp -…...

AI书签管理工具开发全记录(十九):嵌入资源处理
1.前言 📝 在上一篇文章中,我们完成了书签的导入导出功能。本篇文章我们研究如何处理嵌入资源,方便后续将资源打包到一个可执行文件中。 2.embed介绍 🎯 Go 1.16 引入了革命性的 embed 包,彻底改变了静态资源管理的…...

JVM虚拟机:内存结构、垃圾回收、性能优化
1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...
Java编程之桥接模式
定义 桥接模式(Bridge Pattern)属于结构型设计模式,它的核心意图是将抽象部分与实现部分分离,使它们可以独立地变化。这种模式通过组合关系来替代继承关系,从而降低了抽象和实现这两个可变维度之间的耦合度。 用例子…...

GitFlow 工作模式(详解)
今天再学项目的过程中遇到使用gitflow模式管理代码,因此进行学习并且发布关于gitflow的一些思考 Git与GitFlow模式 我们在写代码的时候通常会进行网上保存,无论是github还是gittee,都是一种基于git去保存代码的形式,这样保存代码…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

水泥厂自动化升级利器:Devicenet转Modbus rtu协议转换网关
在水泥厂的生产流程中,工业自动化网关起着至关重要的作用,尤其是JH-DVN-RTU疆鸿智能Devicenet转Modbus rtu协议转换网关,为水泥厂实现高效生产与精准控制提供了有力支持。 水泥厂设备众多,其中不少设备采用Devicenet协议。Devicen…...