干货 | 当 YOLOv5 遇见 OpenVINO,实现自动检测佩戴口罩
YOLOv5网络
YOLOv5代码链接:
https://github.com/ultralytics/yolov5
YOLOv5 于2020年6月横空出世!一经推出,便得到CV圈的瞩目,目前在各大目标检测竞赛、落地实战项目中得到广泛应用。
YOLOv5在COCO上的性能表现:
YOLOv5一共有4个版本,分别是Yolov5s、Yolov5m、Yolov5l、Yolov5x,其中性能依次增强。比如 YOLOv5s模型参数量最小,速度最快,AP精度最低;YOLOv5x模型参数量最大,速度最慢,AP精度最高。
其中YOLOv5网络结构如下:
图源:https://zhuanlan.zhihu.com/p/172121380
OpenVINO™工具套件介绍
OpenVINO™工具套件是英特尔针对自家硬件平台开发的一套深度学习工具库,包含推理库,模型优化等等一系列与深度学习模型部署相关的功能。同时可以兼容各种开源框架训练好的模型,拥有算法模型上线部署的各种能力,只要掌握了该工具,你可以轻松的将预训练模型在Intel的CPU、VPU等设备上快速部署起来。
重新训练YOLOv5
3.1 下载YOLOv5代码和权重文件
大家可以直接clone YOLOv5官方github代码:
git clone https://github.com/ultralytics/yolov5
也可以在官方github的releases中下载正式发布的版本:
https://github.com/ultralytics/yolov5/releases
我们这里下载YOLOv5 v3.1版本的源代码和yolov5s.pt权重文件。值得注意,目前YOLOv5已更新至 v5.0,但在实际转换OpenVINO™工具套件推理应用中遇到不少问题,为了方便使用,因此目前推荐较稳定的YOLOv5 v3.1版本。
3.2 数据集准备
数据集可以是自己标注的,也可以用网上开源的数据集。如果是标注自己的目标检测数据集,一般使用 labelImg工具(超好用!支持COCO等格式)。
这里我们下载使用roboflow开源的口罩检测数据集(Mask Wearing Dataset):https://public.roboflow.com/object-detection/mask-wearing,该数据集只有149幅图像,方便练手,而且格式直接支持 YOLOv5!
3.3 重新训练YOLOv5
3.3.1 修改参数
训练自定义数据集,一般需要修改两个参数:
nc:需要识别的类别 anchors:YOLOv5默认自适应
anchors计算,也可以自定义通过k-means算法计算
其中,nc是一定要修改的,比较每个数据集的类别会不一样,而anchors可以不用修改,即默认自适应计算。
比如Mask Wearing数据集只有两种类别:mask和no-mask,所以nc = 2。可见data.yaml中的信息:
这里使用YOLOv5s进行训练,所以需要同步修改yolov5/models/yolov5s.yaml 文件中的nc数值,设置为2:
3.3.2 训练YOLOv5
训练命令如下:
python train.py --data 数据集路径/data.yaml --cfg models/yolov5s.yaml --weights ''
--batch-size 64 --epochs 100
注:训练命名行的参数含义可参考:https://docs.ultralytics.com,比如batch size、epochs可以根据训练设备自行调整
训练完成后,权重文件会自动保存在runs文件夹中,自动生成last.pt和best.pt,如下图所示:
3.3.3 YOLOv5 Demo检测
对测试集中的图像进行检测,执行命令如下:
python detect.py --weight runs/exp6/weights/best.pt --source 数据集路
径/test/images/1288126-
10255706714jpg_jpg.rf.95f7324cbfd48e0386e0660b5e932223.jpg
输入图像:
口罩检测结果:
模型转换(YOLOv5—>OpenVINO™工具套件)
将YOLOv5的.pt训练权重文件转换成OpenVINO™工具套件调用的文件,主要的流程是:.pt 权重文件 —> ONNX 权重文件 —> IR 文件(.bin和xml)。其中利用ONNX(Open Neural Network Exchange,开放神经网络交换)进行文件格式转换。
使用版本说明:
Ubuntu 18.04
OpenVINO™ 2021.03
4.1 .pt 权重文件 —> ONNX 权重文件
先安装ONNX,然后运行脚本,实现转换。
4.1.1 安装ONNX
ONNX的安装方法相对简单,直接pip安装即可:
pip install onnx
4.1.2 ONNX转换
YOLOv5官方提供了转换成ONNX权重的脚本文件,位于yolov5/models/export.py,使用说明详见:
https://github.com/ultralytics/yolov5/issues/251
注意,这里需要将export.py脚本文件中的opset_version修改为10:
torch.onnx.export(model, img, f, verbose=False, opset_version=10, input_names= ['images'], output_names=['classes', 'boxes'] if y is None else ['output'])
然后再执行如下转换指令:
python models/export.py --weights runs/exp6/weights/best.pt --img 640 --batch 1
转换成功后,就会在runs/exp6/weights文件夹中生成best.onnx文件。
注:这里可以使用Netron打开yolov5s.onnx,进而可视化YOLOv5模型。
Netron在线可视化:https://netron.app/
Netron github:
https://github.com/lutzroeder/netron
4.2 ONNX 权重文件 —> IR 文件(.bin和.xml)
先安装、配置OpenVINO™工具套件,然后运行脚本,实现转换。
4.2.1 安装OpenVINO™
安装OpenVINO™工具套件的方法有很多,详见官网:
https://docs.openvinotoolkit.org/latest/index.html
这里我是使用APT的方式,具体参考:
https://docs.openvinotoolkit.org/latest/openvino_docs_install_guides_installing_openvino_apt.html
安装命名如下:
wget https://apt.repos.intel.com/openvino/2021/GPG-PUB-KEY-INTEL-OPENVINO-2021 apt-key add GPG-PUB-KEY-INTEL-OPENVINO-2021 apt-key list
touch /etc/apt/sources.list.d/intel-openvino-2021.list
echo "deb https://apt.repos.intel.com/openvino/2021 all main" >> /etc/apt/sources.list.d/intel-openvino-2021.list apt update
执行完上述命名后,可出现:
然后搜索可下载的包,要注意系统版本:
sudo apt-cache search intel-openvino-dev-ubuntu18
这里安装intel-openvino-dev-ubuntu18-2021.3.394版本
apt install intel-openvino-dev-ubuntu18-2021.3.394
安装成功后,输出内容如下图所示:
4.2.2 OpenVINO™工具套件转换
安装好OpenVINO™工具套件后,我们需要使用OpenVINO™工具套件的模型优化器(Model Optimizer)将ONNX文件转换成IR(Intermediate Representation)文件。
首先设置 OpenVINO™工具套件的环境和变量:
source /opt/intel/openvino_2021/bin/setupvars.sh
然后运行如下脚本,实现ONNX模型到IR文件(.xml和.bin)的转换:
python /opt/intel/openvino_2021/deployment_tools/model_optimizer/mo.py -- input_model runs/exp6/weights/best.onnx --model_name yolov5s_best -s 255 -- reverse_input_channels --output Conv_487,Conv_471,Conv_455
关于命令行的参数用法,更多细节可参考:https://docs.openvinotoolkit.org/cn/latest/openvino_docs_MO_DG_prepare_model_convert_model_Converting_Model_General.html
转换成功后,即可得到yolov5s_best.xml 和 yolov5s_best.bin文件。
使用OpenVINO™工具套件进行推理部署
5.1 安装Python版的OpenVINO工具套件
这里使用Python进行推理测试。因为我上面采用apt的方式安装OpenVINO™工具套件,这样安装后 Python环境中并没有OpenVINO™工具套件,所以我这里需要用pip安装一下OpenVINO™工具套件。
注:如果你是编译源码等方式进行安装的,那么可以跳过这步:
pip install openvino
另外,安装时要保持版本的一致性:
5.2 OpenVINO实测
OpenVINO™工具套件官方提供了YOLOv3版本的Python推理demo,可以参考:
https://github.com/openvinotoolkit/open_model_zoo/blob/master/demos/object_detection_demo/python/object_dete ction_demo.py
我们这里参考这个已经适配好的YOLOv5版本:
https://github.com/violet17/yolov5_demo/blob/main/yolov5_demo.py,该源代码的输入数据是camera或者video,所以我们可以将test数据集中的图像转 换成视频(test.mp4)作为输入,或者可以自行修改成图像处理的代码。
其中YOLOv5版本相对于官方YOLOv3版本的主要修改点:
1. 自定义letterbox函数,预处理输入图像:
def letterbox(img, size=(640, 640), color=(114, 114, 114), auto=True,scaleFill=False, scaleup=True):# Resize image to a 32-pixel-multiple rectangle
https://github.com/ultralytics/yolov3/issues/232shape = img.shape[:2] # current shape [height, width]w, h = size# Scale ratio (new / old)r = min(h / shape[0], w / shape[1])if not scaleup: # only scale down, do not scale up (for better test mAP)r = min(r, 1.0)# Compute paddingratio = r, r # width, height ratiosnew_unpad = int(round(shape[1] * r)), int(round(shape[0] * r))
dw, dh = w - new_unpad[0], h - new_unpad[1] # wh paddingif auto: # minimum rectangledw, dh = np.mod(dw, 64), np.mod(dh, 64) # wh paddingelif scaleFill: # stretchdw, dh = 0.0, 0.0new_unpad = (w, h)ratio = w / shape[1], h / shape[0] # width, height ratiosdw /= 2 # divide padding into 2 sidesdh /= 2if shape[::-1] != new_unpad: # resizeimg = cv2.resize(img, new_unpad,interpolation=cv2.INTER_LINEAR)top, bottom = int(round(dh - 0.1)), int(round(dh + 0.1))left, right = int(round(dw - 0.1)), int(round(dw + 0.1))img = cv2.copyMakeBorder(img, top, bottom, left, right, cv2.BORDER_CONSTANT,
value=color) # add bordertop2, bottom2, left2, right2 = 0, 0, 0, 0if img.shape[0] != h:top2 = (h - img.shape[0])//2bottom2 = top2img = cv2.copyMakeBorder(img, top2, bottom2, left2, right2,
cv2.BORDER_CONSTANT, value=color) # add borderelif img.shape[1] != w:left2 = (w - img.shape[1])//2right2 = left2img = cv2.copyMakeBorder(img, top2, bottom2, left2, right2,
cv2.BORDER_CONSTANT, value=color) # add borderreturn img
2. 自定义parse_yolo_region函数, 使用Sigmoid函数的YOLO Region层:
def parse_yolo_region(blob, resized_image_shape, original_im_shape, params,
threshold):# ------------------------------------------ Validating output parameters ------------------------------------------out_blob_n, out_blob_c, out_blob_h, out_blob_w = blob.shapepredictions = 1.0/(1.0+np.exp(-blob))assert out_blob_w == out_blob_h, "Invalid size of output blob. It sould be in NCHW layout and height should " \"be equal to width. Current height = {},current width = {}" \"".format(out_blob_h, out_blob_w)# ------------------------------------------ Extracting layer parameters -------------------------------------------orig_im_h, orig_im_w = original_im_shaperesized_image_h, resized_image_w = resized_image_shapeobjects = list()side_square = params.side * params.side# ------------------------------------------- Parsing YOLO Region output -------------------------------------------bbox_size = int(out_blob_c/params.num) #4+1+num_classesfor row, col, n in np.ndindex(params.side, params.side, params.num):bbox = predictions[0, n*bbox_size:(n+1)*bbox_size, row, col]x, y, width, height, object_probability = bbox[:5]class_probabilities = bbox[5:]if object_probability < threshold:continuex = (2*x - 0.5 + col)*(resized_image_w/out_blob_w)y = (2*y - 0.5 + row)*(resized_image_h/out_blob_h)if int(resized_image_w/out_blob_w) == 8 & int(resized_image_h/out_blob_h) == 8: #80x80,idx = 0elif int(resized_image_w/out_blob_w) == 16 & int(resized_image_h/out_blob_h) == 16: #40x40idx = 1elif int(resized_image_w/out_blob_w) == 32 & int(resized_image_h/out_blob_h) == 32: # 20x20idx = 2width = (2*width)**2* params.anchors[idx * 6 + 2 * n]height = (2*height)**2 * params.anchors[idx * 6 + 2 * n + 1]class_id = np.argmax(class_probabilities)confidence = object_probabilityobjects.append(scale_bbox(x=x, y=y, height=height,width=width,class_id=class_id, confidence=confidence,im_h=orig_im_h, im_w=orig_im_w,resized_im_h=resized_image_h, resized_im_w=resized_image_w))
return objects
3. 自定义scale_bbox函数,进行边界框后处理 :
def scale_bbox(x, y, height, width, class_id, confidence, im_h, im_w,resized_im_h=640, resized_im_w=640):gain = min(resized_im_w / im_w, resized_im_h / im_h) # gain = old / newpad = (resized_im_w - im_w * gain) / 2, (resized_im_h - im_h * gain) / 2 # wh paddingx = int((x - pad[0])/gain)y = int((y - pad[1])/gain)w = int(width/gain)h = int(height/gain)xmin = max(0, int(x - w / 2))ymin = max(0, int(y - h / 2))xmax = min(im_w, int(xmin + w))ymax = min(im_h, int(ymin + h))# Method item() used here to convert NumPy types to native types for compatibility with functions, which don't# support Numpy types (e.g., cv2.rectangle doesn't support int64 in color parameter)return dict(xmin=xmin, xmax=xmax, ymin=ymin, ymax=ymax,class_id=class_id.item(), confidence=confidence.item())
但在实际测试中,会出现:'openvino.inference_engine.ie_api.IENetwork' object has no attribute 'layers' 问题:
经过我调研后才得知,在OpenVINO™ 2021.02及以后版本, 'ie_api.IENetwork.layers' 就被官方删除了:
所以需要将第327、328行的内容:
out_blob = out_blob.reshape(net.layers[layer_name].out_data[0].shape)
layer_params = YoloParams(net.layers[layer_name].params, out_blob.shape[2])
修改为:
out_blob = out_blob.reshape(net.outputs[layer_name].shape)
params = [x._get_attributes() for x in function.get_ordered_ops() if
x.get_friendly_name() == layer_name][0]
layer_params = YoloParams(params, out_blob.shape[2])
并在第322行下面新添加一行代码:
function = ng.function_from_cnn(net)
最终在终端,输入下面命令:
python yolov5_demo.py -m yolov5/yolov5s_best.xml test.mp4
加上后处理,使用OpenVINO™工具套件的推理时间在220ms左右,测试平台为英特尔® 酷睿™ i5- 7300HQ,而使用PyTorch CPU版本的推理时间在1.25s,可见OpenVINO™工具套件加速明显!
最终检测结果如下:
参考:
https://img-bss.csdnimg.cn/202103311747047950.pdf
https://github.com/fb029ed/yolov5_cpp_openvino
https://www.cnblogs.com/shuimuqingyang/p/14260810.html
https://www.fatalerrors.org/a/c-implement-openvino-deployment-for-yolov5.html
https://colab.research.google.com/drive/1xla23daYYbTIfbdHF0nyHzHyoAvVtyaG#scrollTo=PO2uvGWy7oEi
https://github.com/OmniXRI/colab_openvino/blob/master/Colab_OpenVINO_mobilenet_v1.ipynb
https://www.bilibili.com/read/cv11455153/
https://github.com/TNTWEN/OpenVINO-YOLOV4
还想了解更多关于 OpenVINO™工具套件?了解更前沿的 AI 技术?
《大咖来了》第 14 期,邀请到了 Intel(中国)物联网事业部开发者赋能业务高级工程师邱丹和成都云图睿视科技有限公司创始人总经理孟莹,为你解读!
立即点击观看吧:
相关文章:

Ubuntu 16.04安装双显卡驱动方法收集
说明:不一定有效,要不断尝试。 http://www.linuxwang.com/html/2150.html http://blog.csdn.net/feishicheng/article/details/70662094>如有问题,请联系我:easonjim#163.com,或者下方发表评论。<

C#中的类型转换
C# 出来也有些日子了,最近由于编程的需要,对 C# 的类型转换做了一些研究,其内容涉及 C# 的装箱/拆箱/别名、数值类型间相互转换、字符的 ASCII 码和 Unicode 码、数值字符串和数值之间的转换、字符串和字符数组/字节数组之间的转换、各种数值…

解构 StyleCLIP:文本驱动、按需设计,媲美人类 P 图师
来源 | HyperAI超神经(ID:HyperAI)作者 | 神经三羊StyleCLIP 是一种新型「P 图法」,它结合了 StyleGAN 和 CLIP,可以仅依据文本描述,对图像进行修改和处理。提起 StyleGAN 大家都不陌生。这个由 NVIDIA 发布的新型生成…

nexus 4 下 DualBootInstallation 安装 ubuntu touch
最近折腾ubuntu for phone ubuntu也算是雷声大雨点小,从edge手机开始,到说兼容一大部分谷歌机,到现在缩水说只适配nexus 4 节操掉了一地啊,对付这种情况,ubuntu touch也就可以只装着玩玩了,还好ubuntu 官方…

我的家庭私有云计划-13
嗯,昨天算由感而发啊,大家看看就好了。 嗯,接着说咱们的云。 先说啊,我没打算在这个领域里面完全自研,我还没那么疯,这个呢属于一体化解决方案,我认为还是社会分工合作的结果,不强调…

C语言return函数
return函数 说到return,有必要提及主函数的定义。很多人甚至市面上的一些书籍,都使用了void main( )这一形式 ,其实这是错误的。 C/C 中从来没有定义过void main( ) 。C 之父 Bjarne Stroustrup 在他的主页上的 FAQ 中明确地写着: The defi…

怎样写出一个较好的高速排序程序
写出一个较好的高速排序程序 高速排序是经常使用的排序算法之中的一个,但要想写出一个又快又准的使用程序,就不是那么简单了须要注意的事项 首先要写正确。通常使用递归实现。其递归相当于二叉树展开,因此假设要用迭代实现的话须要使用一个队…

写代码时发现......还得是 SpringBoot !一篇拿下
关注了很多技术类公众号的读者肯定有这样一个感受,SpringBoot相关的文章铺天盖地,并且SpringBoot相关的文章阅读量、收藏量都很高,这也从侧面反映了SpringBoot技术的火爆。一切都在证明,SpringBoot已经成为了Java程序员必备的技能…

Python的 if .else.elif语句详解
If 语句 是用来判断的 Python 编程中 if 语句用于控制程序执行 用来检测一个条件:如果条件为 (真)true,就会运行这个语法块,如果为Fales 就跳过不执行。 elif是依附于if存在的,两者之间的运算逻辑相同&…

C#中string与byte[]的转换帮助类
在写C#程序时,string和byte[]之间的转换比较烦,在移植一些老程序时感觉很不好。我在C#中使用DES和TripleDES时移植一块老代码时也遇到了同样的情况。为了下次不为同样的事情烦恼,就写了下面的帮助类。 主要实现了以下…

鲲鹏入晋 万里腾飞,鲲鹏应用创新大赛2021山西赛区邀你来战!
2021 年 6 月 29 日,由山西省工业和信息化厅、山西转型综合改革示范区管理委员会为指导单位,华为技术有限公司主办,山西鲲鹏生态创新中心暨华为(山西综改区)DevCloud 创新中心承办,山西长河科技股份有限公司…

tcpdump-根据IP查看程序与服务都用了哪些端口
tcpdump -i em1 -tttt src 116.3.248.157 and port ! 6869 -nn -i 指定端口 -tttt 附带时间戳 -nn 解析域名与端口信息 ############################################# windows下可以使用netstat -nb |find “18999” 与 netstat -ao 结合使用,在通过pid号 查看进程…

快速构建Windows 8风格应用27-漫游应用数据
本篇博文主要介绍漫游应用数据概览、如何构建漫游应用数据、构建漫游应用数据最佳实践。 漫游应用数据概览 1.若应用当中使用了漫游应用数据,用户可以很轻松的在不同的设备间保持应用数据的同步。 2.Windows会将更新的漫游数据同步到云端,并将数据更新到…

jquery和css3打造超梦幻的三维动画背景
今天为大家带来的是一款由jquery和css3实现的超级梦幻的背景效果。绿色的小原点由远到近,由近到远一种飞跃效果。效果非常好看,我们一起看下效果图: 在线预览 源码下载 我们一起看下实现的代码。这是一款由jquey和css3实现的效果。这里引用…

C#时间函数扩展
//本周是本年第几周 private int DatePart(System.DateTime dt) { int weeknow Convert.ToInt32(dt.DayOfWeek);//今天星期几 int daydiff (-1) * (weeknow1);//今日与上周末的天数差 int days System.DateTime.Now.AddDays(daydiff).DayOfYear;//上周末是本年第几天 i…

“我被机器解雇了!”Amazon 63岁员工因算法评分太低被自动开除
整理 | Carol出品 | CSDN(ID:CSDNnews)“我被一个机器解雇了。”63岁“老司机”因跟踪算法被开除一觉醒来,63岁的斯蒂芬 诺曼丁(Stephen Normandin)发现自己居然被莫名其妙解雇了。斯蒂芬是Amazon Flex的一…

微信开放平台手机APP支付
PHP对接APP微信支付 微信开放平台手机APP支付总结 1. 微信开放平台手机APP支付总结 支付功能链接: https://pay.weixin.qq.com/wiki/doc/api/index.html APP支付功能文档: https://pay.weixin.qq.com/wiki/doc/api/app/app.php?chapter8_3 Demo下载地址: https://pay.weixin.q…

VS2005创建CLR自定义触发器
第一步:在Visual Studio 2005中编写代码 using System; using System.Data; using System.Data.Sql; using System.Data.SqlServer; using System.Data.SqlTypes; public partial class Triggers { // Enter existing table or view for the target and unc…

Adobe推出HTML5动画设计工具Edge
2019独角兽企业重金招聘Python工程师标准>>> HTML5和Flash,是敌对?是共存? 尽管Flash现在依然牢牢占据着网络动画的大半江山,但这种状况终将会被改变。 那么,Edge的推出是否意味着Adobe将放弃和屈服于Flash…

AI 算法给手画线稿自动上色指南来了
测试图片作者 | 叶庭云来源 | 修炼Python生成线稿图像手绘效果的特征:黑白灰色、边界线条较重、相同或相近色彩趋于白色、略有光源效果。手绘风格是在对图像进行灰度化的基础上由立体效果和明暗效果叠加而成的,灰度实际代表了图像的明暗变化,…

mysqldump和xtrabackup备份原理实现说明
MySQL数据库备份分为逻辑备份和物理备份两大类,犹豫到底用那种备份方式的时候先了解下它们的差异: 逻辑备份的特点是:直接生成SQL语句,在恢复的时候执行备份的SQL语句实现数据库数据的重现。物理备份的特点是:拷贝相关…

C语言100个经典的算法
POJ上做做ACM的题 语言的学习基础,100个经典的算法C语言的学习要从基础开始,这里是100个经典的算法-1C语言的学习要从基础开始,这里是100个经典的算法 题目:古典问题:有一对兔子,从出生后第3个月…

PornHub:修复百年前情色电影
全球最大不可描述网站 PornHub 最近在自己的官网上,注册了一个名为 「Remastured」的视频发布账号,中文意为「重制」。截止目前,这个账号已经上传了 21 个视频(包含一部项目介绍视频),共计两万的订阅用户和…

jquery 插件开发的作用域及基础
2019独角兽企业重金招聘Python工程师标准>>> 之前一直有开发jquery插件的冲动,所以一直想学习如何进行插件开发,最近一个项目需要使用图片上传组件及自动无限下拉组件,百度地图组件,所以趁着这次我就把他们全部插件化了…

WSUS Troubleshooting guide
Troubleshooting guide for issues where WSUS clients are not reporting in 来自于WSUS TEAM BLOG This guide is written to assist specifically in troubleshooting WSUS when clients are not reporting in. We will examine common troubleshooting considerations that…

在PHP语言中使用JSON
从5.2版本开始,PHP原生提供json_encode()和json_decode()函数,前者用于编码,后者用于解码。 一、json_encode() 该函数主要用来将数组和对象,转换为json格式。先看一个数组转换的例子: $arr array (a>1,b>2,c&g…

【动态规划】最长公共子序列与最长公共子串
1. 问题描述 子串应该比较好理解,至于什么是子序列,这里给出一个例子:有两个母串 cnblogsbelong比如序列bo, bg, lg在母串cnblogs与belong中都出现过并且出现顺序与母串保持一致,我们将其称为公共子序列。最长公共子序列ÿ…

限量!“Java成长笔记”Spring Boot/Sentinel/Nacos高并发
前言本文是为了帮大家快速回顾了Java中知识点,这套面试手册涵盖了诸多Java技术栈的面试题和答案,相信可以帮助大家在最短的时间内用作面试复习,能达到事半功倍效果。本来想将文件上传到github上,但由于文件太大有的都无法显示所以…

时区切换导致quartz定时任务没有触发问题
时区切换对Quartz的cron表达式有影响,切换的1小时内停止触发定时任务,导致sla没有定时清空内存计数,误发限流。 美国夏令时PST切换到冬令时PDT,会有时间跳变。不带时区跳变的,会出现时间重叠或不连续 问题复现 mac本机…

C#之消息队列的简要说明
using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; using System.Data; using System.Messaging ; using System.Threading ; namespace WinMsmq { /// <summary> /// Form1 的摘要说…