目标检测评估指标mAP:从Precision,Recall,到AP50-95

这篇具有很好参考价值的文章主要介绍了目标检测评估指标mAP:从Precision,Recall,到AP50-95。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1. TP, FP, FN, TN

True Positive

满足以下三个条件被看做是TP

        1. 置信度大于阈值(类别有阈值,IoU判断这个bouding box是否合适也有阈值)

        2. 预测类型与标签类型相匹配(类别预测对了)

        3. 预测的Bouding Box和Ground Truth的IoU大于阈值(框 打对了)。 当存在多个满足条件的预选框,则选择置信度最大的作为TP,其余的作为FP

False Positive

       1. 预测的 类别  和 真实的标签类型不匹配(分类错误)

        2. 预测的Bounding box和Ground Truth的IoU小于阈值(框  打的不是那么好, 定位错误)

False Negative

        分类争取,定位正确,但是被检测成了负样本

True Negative

        负样本被检测出的数量,太多了,绝大多数的框都是这个类型。后面计算precision和recall的时候用不到,所以这个东西也就不统计数量了。

2. Precision和Recall

Precision = TP/(TP+FP) = TP/(所有被我判定为正例的个数)

Recall = TP/(TP+FN) = TP/(所有世界是正例的个数)

3. PR曲线

Precision-Recall曲线是根据阈值 从0到1这个区间的变动,每个阈值下模型的precision和recall的值分别作为纵坐标和横坐标来连线绘制而成的。(每个阈值θ对应于一个(Precision,Recall)点,把这些点连起来就是PR曲线)

比如假设我们收集了20个sample的数据,他们的真实标签和置信度如下

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

 此时我们为了绘制PR曲线,计算出了PR曲线上下面这些点的坐标

阈值=0.9——TP=len([ #1, ]) = 1; FP=0; FN=len([#2, #4, #5, #6, #9, #11, #13, # 17, #19])=9——Precision=TP/(TP+FP)=1/(1+0)=1——Recall=TP/(TP+FN)=1/(1+9)= 0.1

阈值0.8——TP=len([#1,#2])=2; FP=0; FN=len([#4, #5, #6, #9, #11, #13, # 17, #19])=8——Precision=2/(2+0)=1; Recall=2/(2+8)=0.2

阈值0.7——TP=len([#1,#2])=2; FP=len([#3])=1, #_of_True=10 ——Precision=2/(2+1)=0.67;Recall=2/10=0.2

阈值0.6——TP=len([#1, #2,#4])=3FP=len([#3])=1, #_of_True=10——Precision=3/(3+1)=0.75; Recall = 3/10=0.3

阈值0.5——TP=len([#1, #2, #4, #5, #6, #9])=6FP=len([#3, #7, #8, #10])=4,#_of_True=10——Precision=6/(6+4)=0.6; Recall = 6/10=0.6

阈值0.4——TP=len([#1, #2,#4, #5, #6, #9,  #11])=7; FP=len([#3, #7, #8, #10])=4, #_of_True=10——Precision=7/(7+4)=0.64; Recall = 7/10=0.7

阈值0.3——TP=len([#1, #2,#4, #5, #6, #9,  #11, #13, #17, #19])=10;FP=len([#3, #7, #8, #10, #12, #14, #15, #16, #18])=9; #_of_True=10; ——Precision=10/(10+9)=0.53; Recall = 10/10=1

用sklearn的下面这段代码就可以计算出答案

import numpy as np
from sklearn.metrics import precision_recall_curve

# 导入数据
y_true = np.array([1,1,0,1,1,1,0,0,1,0,1,0,1,0,0,0,1,0,1,0])
y_scores = np.array([0.9,0.8,0.7,0.6,0.55,0.54, 0.53,0.52,0.51,0.505, 0.4, 0.39,0.38, 0.37, 0.36, 0.35, 0.34, 0.33, 0.30, 0.1])

# 计算出每个阈值下,precision和recall的值
precision, recall, thresholds = precision_recall_curve(y_true, y_scores)

# 写上这两行,中间的列就不会被用省略号省略,都显示出来
pd.options.display.max_rows = None
pd.options.display.max_columns = None


#整理成横向的dataframe,方便大家查看
precision = pd.DataFrame(precision).T
recall = pd.DataFrame(recall).T
thresholds= pd.DataFrame(thresholds).T
#纵向拼接
results = pd.concat([thresholds, recall,precision], axis=0)
# 仅仅保留2位小数
results = round(results, 2)
#行名改一下
results.index = ["thresholds", "recall", "precision"]
print(results)

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

绘制出来的precision-recall曲线是下面这样

import matplotlib.pyplot as plt
def plot_pr_curve(recall, precision):
    plt.plot(recall, precision, label='Precision-Recall curve')
    plt.xlabel('Recall')
    plt.ylabel('Precision')
    plt.title('PR Curve')
    plt.legend()
plot_pr_curve(recall, precision)

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

对于PR图中这种类似锯齿状的图形,我们一般采用平滑锯齿的操作。所谓平滑锯齿操作就是在Recall轴上,对于每个阈值θ计算出的Recall点,看看它的右侧(包含它自己)谁的Precision最大,然后这个区间都使用这个Precision值,在下图这个例子中,Recall=(0,0.4] 都使用 Precision=1, Recall=(0.4,0.8] 都使用 Precision=0.57, Recall=(0.8,1) 都使用 Precision=0.5

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

因此我们这个例子中的图形经过锯齿平滑化以后应该是下面这个样子

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

Precision和Recall之间的此消彼长的矛盾关系

        上图的右下角,recall高,说明所有所有的杀人犯中99%的都被抓住了,那就会造成一个结果,抓的人特别多。和这桩杀人案有一丁点关系的人都被逮捕了。这必然导致,所有被抓的人中,实际是罪犯的比例变得很低,也就是precision低。

        OK,如果你希望Police抓的人中,实际是罪犯的比例高一些,(也就是precision高一些)。那Police就不敢乱抓人了,没有十足的证据就不敢去逮人。那结果是啥?那就是大量隐藏额的很好的罪犯都被漏掉了,也就是说所有的罪犯中实际被抓住的比例降低了。也就是recall低了。这种情形对应的就是上图左上角的那个位置。

如何评估一个不同模型的好坏关系

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

        既然一个模型的precision和recall是此消彼长的关系,不可能两个同时大,那怎么判断哪个模型更优呢?答案是,P-R曲线越往右上角凸起的曲线对应的模型越优秀。正如上图中红线A和黑线B都是比模型C要优秀的

        但是问题来了,模型A和模型B之间,孰优孰劣呢?那就引出了平衡点("Balanced Error Point" (BEP))这个概念。平衡点就是曲线上的Precision值=Recall值的那个点,也就是上图中那三个点,平衡点的坐标越大,模型越好。

        除了平衡点,也可以用F1 score来评估。F1 = 2 * P * R /( P + R )。F1-score综合考虑了P值和R值,是精准率和召回率的调和平均值, 同样,F1值越大,我们可以认为该学习器的性能较好。

4. 从PR曲线到AP

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

AP的公式如下

AP这个指标的实际意义:

        AP是,在“”阈值不同“”引发的“Recall值不同”的情况下,各种Precision值的均值。AP summarizes a precision-recall curve as the weighted mean of precisions achieved at each threshold, with the increase in recall from the previous threshold used as the weight:

        AP值可以理解为PR曲线向下、向左到X轴和Y轴这片面积之和

        这个阈值到前一个阈值之间的差是长方形的宽,precision值是长方形的高。宽乘以高就是长方形柱子的面积。

        AP这就是P-R曲线的积分,也就是面积

用sklearn的公式这样算

from sklearn.metrics import average_precision_score
AP = average_precision_score(y_true, y_scores, average='macro', pos_label=1, sample_weight=None)
print(AP)

# 0.7357475805927818

AP=(0.2-0)× 1 + (0.5-0.2)× 0.83 + (0.6-0.5) × 0.67 + (0.7-0.6)×0.64 +(0.8-0.7)×0.62 +(1-0.8)×0.53=0.7480

和上面那个程序算出来来的有0.1的误差,但是大体区别不大就不去追究是哪个小地方算错了

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

5. 从AP到mAP

把各个类别(比如汽车、行人、巴士、自行车)的AP值取平均

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

6. 从mAP到 mAP50, mAP75, mAP95

mAP中也有个阈值, mAP50这个也有IoU阈值,这两个阈值之间是什么关系?有哪些区别

        mAP里面那个阈值,是指的分类问题上的阈值(即判断这个目标是人还是汽车还是摩托车 这个分类),如拿着预测某个类别(如:汽车)的置信度  和这个IoU阈值比较,如果置信度大于阈值,则predicted label是True;反之如果predicted confidence低于这个IoU阈值,则predicted label是False

        mAP50里面这个IoU指的是定位(打bouding box识别框) 里面 这个定位问题的阈值。 判断predicted bbox和ground truth bbox之间的IoU大于   这个IoU阈值   才能被算作  定位上的True。反之,如果predicted bbox和ground truth bbox之间的IoU 小于 这个IoU阈值  定位上 这两个框之间的匹配关系 就是False

        在mAP50里面,这个阈值是针对 定位问题的    交并比(IoU)     的。mAP中的阈值是针对分类问题的。

所以这里的mAP50, mAP75, mAP95里面这些50、75、95是针对 定位的bouding box而言的,阈值分别为0.50、0.75、0.95。

我们在目标检测中常说的mAP实际指的是mAP50-95。这个mAP50-95是mAP阈值为50%到mAP阈值为95,间隔5%,取得10个mAP值,然后对这十个值取平均。后面是这10个IoU阈值[0.5 0.55 0.6 0.65 0.7 0.75 0.8 0.85 0.9 0.95]

        mAP50的阈值最低,要求最宽松,因此一般用于人脸识别的性能评估

        mAP80到mAP95,要求比较严格,因此多用于一些对准确性和安全性要求比较高的陈景中如自动驾驶

7. 目标检测中mAP50-95是如何计算得出的

Step1 拿到原始数据

定位数据(bouding box)

        真实标签 truth label

                下面这个矩阵是一张图片的数据

                第一列数据,t代表 truth,表示有真实标签中有N个,一行就是一条真实的标注

                第二列表示是的是这一个方框里面的object  的 真实类别 

                第三列和第四列是, 真实的bouding box 左下角点的横坐标 和 纵坐标

                第五列和第六列是,真实bouding box右上角点的横坐标和纵坐标

                

                通过这这些数据,图片中的N个object的位置和类别就都表示出来了

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

        你的模型预测的标签 predicted label

                这里仍然是一张照片上所有的bouding box的数据和对object类别的分类结果

                与true label不同的是,这里的第三列 score是true label所没有的。这就是对于一条数据所框处的这个object属于这个类别的置信度

                ground truth对这张图片打了N个方框,predicted 对这张图片打了M个方框。

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

Step2 定位  构建IoU_mask

这一步专注在定位(打方框)这里

Step2.1 计算真实框和预测框之间的IoU交并比

计算N个真实框和这M个预测边界框之间的IoU值,共有N×M个匹配的IoU值

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

 Step2.2 根据mAP设定的 IoU 阈值来每个预测框和每个真实框 之间的True或False

假定你的mAP是mAP50,那么IoU阈值就是0.5,上面所有的IoU值 只要大于0.5这个阈值,就会变成True,反之就是False。

iou_mask = (iou_between_target_and_pred > 0.5)

也就是说根据你设定的IoU不同,比如说mAP75, mAP95,同样的IoU值就可能从True变成False或者反过来。因此,不同IoU阈值,下面这掩码矩阵mask matrix都是有些许不同的。

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

需要注意:

  • 一个预测框可能与多个真实框的 IOU 都大于等于 0.5,比如第一列
  • 一个真实框可能与多个预测框的 IOU 都大于等于 0.5,比如第一行

Step3 分类 构建cls_mask

正如前文讲的mAP是一个分类的评估指标,是Precision-Recall曲线下和X轴、Y轴组成的这篇区域的面积。而Precision-Recall曲线是你设定不同的阈值的情况下,Recall作为横坐标、Precision作为纵坐标形成的点,这些点所连成的曲线。

我们都知道Recall和Precision的计算需要三个东西的个数(1)True Positive, (2)False Negative, (3)False Positive

要拿到这三个数字,你首先得有一些 True和False吧?怎么拿到True和False呢?

        看下面这个矩阵第三列这个分类的置信度,如果这个置信度大于所设定的阈值就用True 来代替这个置信度, 小于阈值就用False来代替这个置信度

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

          然后你拿着“predicted ”随着阈值改变的这个由True或False组成的,每个框对应类别的True或False, 和“ground truth”的 类别这一列,都是同一个标签,就赋值为True,反之有一点不同就赋值为False

                不管这个predicted的方框是不是距离这个ground truth近,都一一匹配,标注True和False

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

        这样就拿到了下面这个矩阵cls_mask

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

Step4 综合定位和分类,拿到

对于iou_mask和cls_mask这两个N×M的矩阵 每个元素 之间 做  and 运算,

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

也就是 只有 两个都为True的时候,matched_mask上这个位置的元素才为True.其他的情况,一个True、一个False,   两个False  的情况  这位位置上的元素都为False

matched_mask = ( (iou_mask == True) and (cls_mask == True) )

得出下面这样的match_mask矩阵 

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

Step 5 按照Step4中匹配为True的位置,得到 target-predicted 匹配的pairs

从上面这个matched_mask里面把值为True的元素取出来,列出 target(matched_mask的横坐标)的序号和predicted的序号(matched_mask的纵坐标)拿出来列在下面

我们这里为了简单,下面的这个配对数据是编造的,假设只有6组配对,计算的是mAP50。下面编造的这组数据和上文的iou_mask, cls_mask, matched_mask里面那些数据无关。

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

你看到上面这6组配对,你可能一时摸不着头脑,不知道这些数据代表什么含义,又为什么是这些id之间进行配对,而不是一些其他id的target和predicted进行配对。下面我就一一为你揭晓答案。

        第一列和第二列写的是index的数字。如第一列和第二列的第一行和第二行表示的是和匹配是一对,和匹配是一对。这两列想表达的就是target中第几个和pred中第几个是可以匹配成一对的。

        第三列iou是predicted bouding box和target bouding box之间的IoU(重合面积除以总面积)

        第四列是target bbox和 predicted bbox框处的那个object的类别。

        第五列是predicted bbox里面是这个类别的置信度。

                而对于target bbox里面是这个类别,是数据标注的时候都给好的,那就是这个类别,非要写置信度那就1.00,所以写这个置信度就没啥意义了,所以没有这一列

上图这些配对是根据matched_mask矩阵中为True的元素匹配来的。这样的设计,可以匹配的target和predicted必须同时满足下列两个条件,才会被放进表格里

(条件一):定位方面达标:因为我们这里计算的是mAP50,所以predicted bouding box和target boudingbox这两个方框之间的IoU都必须大于50%的。所以你看上面这个pairs的第三列,全部的IoU都是大于0.50的。

(条件二):类别方面达标:所有配对的这些predicted和target的类别都必须完全相同的,起码是在这一条匹配中,二者的类别标签是完全一样的。比如前三行,target 的index=0和它匹配得上的predicted id=0, id=1, id=2的类别都是 “79”这个类别。(应是用的COCO数据集的列表标签有80个类别,从0到79,所以才会有79这个类别)。但是尽管predicted id=0, id=1, id=2的类别都是 “79”这个类别,他们对于分类到“79”这个类别的置信度是不同的,有的是0.89,有的是0.3,有的是0.15。

目标检测评估指标mAP:从Precision,Recall,到AP50-95,目标检测,人工智能,计算机视觉,深度学习,YOLO

 这里需要注意,在上面这些配对里面

        (1)同一个真实框可能对应多个预测框相匹配,比如和。因此下面我们要去重,使得一个真实框只对应一个预测框。

        (2)同一个预测框可能对应多个真实框,比如,它一个预测框在配对中就有两次分别和

        ————还没写完,有时间,再继续写————

目前还不懂的知识

        ROC曲线是什么?表示什么含义?怎么用?

        AUC曲线同样的问题

Reference

PR曲线与ROC曲线_roc曲线和pr曲线_THE@JOKER的博客-CSDN博客

为什么平均精准度(Average Precision,AP)就是PR曲线的线下面积? - Mark Lue的回答 - 知乎 https://www.zhihu.com/question/422868156/answer/1523130474

sklearn.metrics.average_precision_score — scikit-learn 1.3.0 documentation

[CV] 目标检测中的map计算 - 知乎

COCO - Common Objects in Context

https://www.cnblogs.com/ywheunji/p/13376090.html

准确率、召回率和mAP、AP50/75_map和ap50_dagongji10的博客-CSDN博客文章来源地址https://www.toymoban.com/news/detail-703449.html

到了这里,关于目标检测评估指标mAP:从Precision,Recall,到AP50-95的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 目标检测的评估指标

    在训练阶段是不需要nms处理的,只有在验证或者是测试阶段才需要将预测结果进行非极大值抑制处理, Precision(精确率/查准率):是指在所有被预测为正的样本中,确实是正样本的占比。当Precision越大时,FP越小,此时将其他类别预测为本类别的个数也就越少,可以理解为预测

    2024年02月13日
    浏览(34)
  • 目标检测评估指标

    评估指标是评价目标检测算法方法好坏的重要依据,目标检测有:IoU(交并比)、Precision(精确度)、Recall(召回率)、AP(平均正确率)、mAP(平均类别AP)等多种评价指标。 1.IoU IoU:用来评价目标检测算法的对象定位精度,IoU是目标检测的预测框和标签框之间的重叠面积

    2024年02月06日
    浏览(35)
  • YOLO等目标检测模型的非极大值抑制NMS和评价指标(Acc, Precision, Recall, AP, mAP, RoI)、YOLOv5中mAP@0.5与mAP@0.5:0.95的含义

    YOLOv5正负样本定义 yolov5输出有3个预测分支,每个分支的每个网格有3个anchor与之对应。 没有采用IOU最大的匹配方法,而是通过计算该bounding-box和当前层的anchor的宽高比,如果最大比例大于4(设定阈值),则比例过大,则说明匹配度不高,将该bbox过滤,在当前层认为是背景

    2024年02月03日
    浏览(37)
  • 【计算机视觉 | 目标检测】术语理解3:Precision、Recall、F1-score、mAP、IoU 和 AP

    在图像目标检测中,常用的评估指标包括以下几项: 精确率(Precision):也称为查准率,表示被分类为正类别的样本中真正为正类别的比例。计算公式为:Precision = TP / (TP + FP),其中TP是真正例(模型正确预测为正类别的样本数),FP是假正例(模型错误预测为正类别的样本数

    2024年02月13日
    浏览(35)
  • 如何快速水出人生中的第一篇SCI系列:深度学习目标检测算法常用评估指标——一文读懂!

    详细的改进教程以及源码,戳这!戳这!!戳这!!!B站:AI学术叫叫兽 源码在相簿的链接中,动态中也有链接,感谢支持!祝科研遥遥领先! 截止到发稿,B站YOLOv8最新改进系列的源码包已更新了22种! 排列组合2-4种后,约有6000-7000种! 部分改进教程视频在这:详细的改进

    2024年02月07日
    浏览(35)
  • 最新目标跟踪评估指标汇总

    前段时间接触了一些目标跟踪的场景,本文主要汇总目标跟踪的常用评估指标,主要包括下面几类: 容易理解的概念:FP、FN、TP、id switch、ML、MT 更加综合的概念:MOTA、IDF1、MOTP、HOTA 主要的介绍集中在HOTA ,因为这个评估指标比较新,我能看到的讲解都比较少一点,所以展开

    2024年02月04日
    浏览(28)
  • 损失函数与模型评估指标、目标函数的区别

    对于这样的简单的线性回归问题: x y_true 1 2 3 4 我们可以假设方程为 y = w x + b y= wx+b y = w x + b 当w =1,b=-1时 (即y=x-1,称为模型A) x y_true y_pred 1 2 0 3 4 2 当w =1,b=0时 (即y=x,称为模型B) x y_true y_pred 1 2 1 3 4 3 我们可以用SSE(残差平方和)来评估模型A和B哪个输出结果更好,当然对于回

    2024年01月20日
    浏览(35)
  • 基于sklearn计算precision、recall等分类指标

    在前面的文章中,我们已经介绍了分类指标Precision,Recall,F1-Score的定义和计算公式:详解分类指标Precision,Recall,F1-Score 我们可以知道, 精度(precision)、查全率(recall)、F1的计算,是针对于二分类器来定义的。他们的计算,只与y_true和y_pred有关,要求y_true和y_pred中只含有0和

    2024年02月16日
    浏览(27)
  • 机器学习系列(二)——评价指标Precision和Recall

    Precision 和 Recall 是常考的知识点,就其区别做一个详细总结 1. Precision  中文翻译 “精确率”,“查准率”。 “查准率”这个名字更能反应其特性,就是 该指标关注准确性。  计算公式如下: 这里TP,FP的概念来自统计学中的混淆矩阵,TP指 “预测为正(Positive), 预测正确(

    2024年02月16日
    浏览(31)
  • 深度学习应用篇-计算机视觉-OCR光学字符识别[7]:OCR综述、常用CRNN识别方法、DBNet、CTPN检测方法等、评估指标、应用场景

    【深度学习入门到进阶】必看系列,含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、序列模型、预训练模型、对抗神经网络等 专栏详细介绍:【深度学习入门到进阶】必看系列,含激活函数、优化策略、损失函数、模型调优、归一化算法、卷积模型、

    2024年02月09日
    浏览(44)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包