本文主要介绍基于OpenCV contrib中的quality模块实现图像质量评价。图像质量评估Image Quality Analysis简称IQA,主要通过数学度量方法来评价图像质量的好坏。

本文需要OpenCV contrib库,OpenCV contrib库的编译安装见:

OpenCV_contrib库在windows下编译使用指南

本文所有代码见:

OpenCV-Practical-Exercise

1 OpenCV中图像质量评价算法介绍

1.1 相关背景

图像质量评价(IQA)算法以任意图像作为输入,输出质量分数作为输出。有三种类型的IQA:

  1. 全参考图像质量评价,适用情形:一个“干净”参考(非扭曲)图像以衡量扭曲图像的质量。此度量可用于评估图像压缩算法的质量。
  2. 半参考图像质量评价,适用情形:如果没有参考图像,而是具有一些选择性信息的图像(例如,水印图像)来比较和测量失真图像的质量。
  3. 无参考图像质量评价,适用情形:算法得到的唯一输入是要测量其质量的图像。

在OpenCV contrib的quality模块中一共有提供了5种图像质量评价算法,按上面的类别分仅提供全参考图像质量评价和无参考图像质量评价两种类别的算法,没有半参考图像质量评价算法。官方代码地址见quality,其中包含的5种图像质量评价算法具体如下:

  • 均方误差 Mean squared error (MSE)
  • 峰值信噪比 Peak signal-to-noise ratio (PSNR)
  • 结构相似性 Structural similarity (SSIM)
  • 梯度幅度相似性偏差 Gradient Magnitude Similarity Deviation (GMSD)
  • 盲/无参考图像空间质量评估器 Blind/Referenceless Image Spatial Quality Evaluation (BRISQUE)

这5种图像质量评价算法中,除了BRISQUE是无参考图像质量评价算法外,其他都是全参考图像质量评价。本文不具体介绍这些算法的原理,仅介绍这些算法的应用。想知道具体原理见链接:

事实上,各种图像质量评估算法都是寻找不同数学公式给出一个评判结果,差异并不那么大,仅知道使用即可。就全参考图像质量评价算法而言,一般情况下GMSD效果比其他全参考图像质量评价算法效果好。无参考图形质量评价以BRISQUE为代表。半参考图像质量评价更多用于发论文,实际应用不多。近年来也有深度学习应用于图像质量评估,但是效果还不错,但速度太慢。关于图像质量评估算法具体进一步研究可参考链接:图像质量评估指标(Image Quality Assessment,IQA)

1.2 OpenCV中图像质量评价算法接口介绍

OpenCV中图像质量评价算法接口分为静态方法和实例方法,静态方法固定快捷,实例方法灵活性强。其中全参考图像质量评价算法接口类似,只需要更改函数名即可,因为各种参考图像质量算法其实都数学公式应用变换数学公式即可。BRISQUE在[OpenCV实战]37 图像质量评价BRISQUE中已经提到如何使用,不过用起来相对opencv_contrib库中的quality模块麻烦,唯一好处[OpenCV实战]37 图像质量评价BRISQUE提到的方法不需要编译opencv_contrib库,但是实际建议使用opencv_contrib库的quality模块来实现图像质量评估算法。

1.2.1 opencv_contrib中全参考图像质量评价算法具体接口

C++/静态方法

// output quality map
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// compute MSE via static method
// cv::noArray() if not interested in output quality maps
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
cv::Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_map);

C++/实例方法

// alternatively, compute MSE via instance
cv::Ptr<quality::QualityBase> ptr = quality::QualityMSE::create(img1);
// compute MSE, compare img1 vs img2
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);

Python/静态方法

# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
result_static, quality_map = cv2.quality.QualityMSE_compute(img1, img2)

Python/实例方法

obj = cv2.quality.QualityMSE_create(img1)
result = obj.compute(img2)
quality_map = obj.getQualityMap()

1.2.2 opencv_contrib中无参考图像质量评价算法具体接口

C++/静态方法

// path to the trained model
cv::String model_path = "./model/brisque_model_live.yml";
// path to range file
cv::String range_path = "./model/brisque_range_live.yml";
// 静态计算方法
cv::Scalar result_static = quality::QualityBRISQUE::compute(img, model_path, range_path);

C++/实例方法

cv::Ptr<quality::QualityBase> ptr = quality::QualityBRISQUE::create(model_path, range_path);
// computes BRISQUE score for img
cv::Scalar result = ptr->compute(img)

Python/静态方法

# path to the trained model
model_path = "./model/brisque_model_live.yml"
# path to range file
range_path = "./model/brisque_range_live.yml"
# 静态计算方法
result_static = cv2.quality.QualityBRISQUE_compute(img, model_path, range_path)

Python/实例方法

obj = cv2.quality.QualityBRISQUE_create(model_path, range_path)
result = obj.compute(img)

1.2.3 opencv_contrib中图像质量评价算法输出参数介绍

对于静态方法和实例方法输出结果一样的,都是输出在不同颜色通道下的结果,比如对于全参考图像质量评价算法而言RGB图就是分别输出R、G、B三个通道的结果,所以最后需要求均值。对BRISQUE而言不管是彩色图还是灰度图都只输出一个0到100之间的数。各个算法的结果特点如下表所示:

算法 输出结果特点
MSE 结果越小,检测图像和基准图像的差距越小
PSNR 结果越小,检测图像和基准图像的差距越大
GMSD 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越大
SSIM 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越小
BRISQUE 结果为一个0到100之间的数,越小表示检测图像质量越好

2 代码实现与结果分析

2.1 代码实现

本文所提供的代码可以对者图像进行质量评价。本文提供C++和Python代码实现,但是MSE的Python实例计算代码可能有问题,可以用Python静态方法代替,所有代码如下:

C++

#include <opencv2/opencv.hpp>
#include <opencv2/quality.hpp> using namespace std;
using namespace cv; // 计算结果均值
double calMEAN(Scalar result)
{
int i = 0;
double sum = 0;
// 计算总和
for (auto val : result.val)
{
if (0 == val || isinf(val))
{
break;
}
sum += val;
i++;
}
return sum / i;
} // 均方误差 MSE
double MSE(Mat img1, Mat img2)
{
// output quality map
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// compute MSE via static method
// cv::noArray() if not interested in output quality maps
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
cv::Scalar result_static = quality::QualityMSE::compute(img1, img2, quality_map); /* 另外一种动态计算的方法
// alternatively, compute MSE via instance
cv::Ptr<quality::QualityBase> ptr = quality::QualityMSE::create(img1);
// compute MSE, compare img1 vs img2
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);
*/ return calMEAN(result_static);
} // 峰值信噪比 PSNR
double PSNR(Mat img1, Mat img2)
{
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
// 第四个参数为PSNR计算公式中的MAX,即图片可能的最大像素值,通常为255
cv::Scalar result_static = quality::QualityPSNR::compute(img1, img2, quality_map, 255.0); /* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualityPSNR::create(img1, 255.0);
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);*/ return calMEAN(result_static);
} // 梯度幅度相似性偏差 GMSD
double GMSD(Mat img1, Mat img2)
{
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
cv::Scalar result_static = quality::QualityGMSD::compute(img1, img2, quality_map);
/* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualityGMSD::create(img1);
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);*/
return calMEAN(result_static);
} // 结构相似性 SSIM
double SSIM(Mat img1, Mat img2)
{
// 质量结果图
// 质量结果图quality_map就是检测图像和基准图像各个像素点差值图像
cv::Mat quality_map;
// 静态方法,一步到位
// 如果不想获得质量结果图,将quality_map替换为noArray()
cv::Scalar result_static = quality::QualitySSIM::compute(img1, img2, quality_map);
/* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualitySSIM::create(img1);
cv::Scalar result = ptr->compute(img2);
ptr->getQualityMap(quality_map);*/
return calMEAN(result_static);
} // 盲/无参考图像空间质量评估器 BRISQUE
double BRISQUE(Mat img)
{
// path to the trained model
cv::String model_path = "./model/brisque_model_live.yml";
// path to range file
cv::String range_path = "./model/brisque_range_live.yml";
// 静态计算方法
cv::Scalar result_static = quality::QualityBRISQUE::compute(img, model_path, range_path);
/* 另外一种动态计算的方法
cv::Ptr<quality::QualityBase> ptr = quality::QualityBRISQUE::create(model_path, range_path);
// computes BRISQUE score for img
cv::Scalar result = ptr->compute(img);*/
return calMEAN(result_static);
} void qualityCompute(String methodType, Mat img1, Mat img2)
{
// 算法结果和算法耗时
double result;
TickMeter costTime; costTime.start();
if ("MSE" == methodType)
result = MSE(img1, img2);
else if ("PSNR" == methodType)
result = PSNR(img1, img2);
else if ("PSNR" == methodType)
result = PSNR(img1, img2);
else if ("GMSD" == methodType)
result = GMSD(img1, img2);
else if ("SSIM" == methodType)
result = SSIM(img1, img2);
else if ("BRISQUE" == methodType)
result = BRISQUE(img2);
costTime.stop();
cout << methodType << "_result is: " << result << endl;
cout << methodType << "_cost time is: " << costTime.getTimeSec() / costTime.getCounter() << " s" << endl;
} int main()
{
// img1为基准图像,img2为检测图像
cv::Mat img1, img2;
img1 = cv::imread("image/original-rotated-image.jpg");
img2 = cv::imread("image/noise-version.jpg"); if (img1.empty() || img2.empty())
{
cout << "img empty" << endl;
return 0;
} // 结果越小,检测图像和基准图像的差距越小
qualityCompute("MSE", img1, img2);
// 结果越小,检测图像和基准图像的差距越大
qualityCompute("PSNR", img1, img2);
// 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越大
qualityCompute("GMSD", img1, img2);
// 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越小
qualityCompute("SSIM", img1, img2);
// BRISQUE不需要基准图像
// 结果为一个0到100之间的数,越小表示检测图像质量越好
qualityCompute("BRISQUE", cv::Mat{}, img2);
system("pause");
return 0;
}

Python

# -*- coding: utf-8 -*-
"""
Created on Fri Oct 9 05:27:28 2020 @author: luohenyueji
""" import cv2
import numpy as np
import time # ----- 时间装饰器,打印运行结果和运行时间
def usetime(func):
def inner(*args, **kwargs):
time_start = time.time()
# 装饰的函数在此运行
result = func(*args, **kwargs)
time_run = time.time() - time_start
# 打印结果
print(func.__name__ + '_result is: {:.3f}'.format(result))
# 打印运行时间
print(func.__name__ + '_cost time is: {:.3f} s'.format(time_run)) return inner # ----- 均方误差 MSE
@usetime
def MSE(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
result_static, quality_map = cv2.quality.QualityMSE_compute(img1, img2)
# 另外一种动态计算的方法,但是MSE的计算可能有问题
# obj = cv2.quality.QualityMSE_create(img1)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score # ----- 峰值信噪比 PSNR
@usetime
def PSNR(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
# maxPixelValue参数为PSNR计算公式中的MAX,即图片可能的最大像素值,通常为255
result_static, quality_map = cv2.quality.QualityPSNR_compute(img1, img2, maxPixelValue=255)
# 另外一种动态计算的方法
# obj = cv2.quality.QualityPSNR_create(img1, maxPixelValue=255)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
return score # ----- 梯度幅度相似性偏差 GMSD
@usetime
def GMSD(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
result_static, quality_map = cv2.quality.QualityGMSD_compute(img1, img2)
# 另外一种动态计算的方法
# obj = cv2.quality.QualityGMSD_create(img1)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score # ----- 结构相似性 SSIM
@usetime
def SSIM(img1, img2):
# 静态方法,一步到位
# 质量结果图quality_map就是检测图像和基准图像各个像素点差值结果
result_static, quality_map = cv2.quality.QualitySSIM_compute(img1, img2)
# 另外一种动态计算的方法
# obj = cv2.quality.QualitySSIM_create(img1)
# result = obj.compute(img2)
# quality_map = obj.getQualityMap()
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score # ----- 盲/无参考图像空间质量评估器 BRISQUE
@usetime
def BRISQUE(img):
# path to the trained model
model_path = "./model/brisque_model_live.yml"
# path to range file
range_path = "./model/brisque_range_live.yml"
# 静态计算方法
result_static = cv2.quality.QualityBRISQUE_compute(img, model_path, range_path)
# # 另外一种动态计算的方法
# obj = cv2.quality.QualityBRISQUE_create(model_path, range_path)
# result = obj.compute(img)
# 计算均值
score = np.mean([i for i in result_static if (i != 0 and not np.isinf(i))])
score = 0 if np.isnan(score) else score
return score def main():
# img1为基准图像,img2为检测图像
img1 = cv2.imread("image/cut-original-rotated-image.jpg")
img2 = cv2.imread("image/cut-noise-version.jpg")
if img1 is None or img2 is None:
print("img empty")
return
# 结果越小,检测图像和基准图像的差距越小
MSE(img1, img2)
# 结果越小,检测图像和基准图像的差距越大
PSNR(img1, img2)
# 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越大
GMSD(img1, img2)
# 结果为一个0到1之间的数,越大表示检测图像和基准图像的差距越小
SSIM(img1, img2)
# 结果为一个0到100之间的数,越小表示检测图像质量越好
BRISQUE(img2) if __name__ == '__main__':
main()

2.2 结果分析

上面的代码实现了对不同图片的图像质量诊断,并输出各种方法在不同图像下的评分和方法检测速度。速度计算主要基于C++代码。
具体检测结果如下表所示,其中-nan(ind)表示结果出错,通常是两张图像一样。原图下的结果是原图和原图比,模糊图片和噪声图片是与原图为基准图片比较的结果。按清晰度而言,原图>模糊图片>噪声图片。
下面分别显示分辨率为612x816和中心裁剪分辨率305x305的结果。

结果 原图/分辨率612x816 模糊图片/分辨率612x816 噪声图片/分辨率612x816
方法
MSE -nan(ind) 1490.28 1734.03
PSNR -nan(ind) 16.3989 15.7454
GMSD -nan(ind) 0.209512 0.199491
SSIM 1 0.30256 0.482258
BRISQUE 53.3901 63.4859 71.2059
结果 原图/分辨率305x305 模糊图片/分辨率305x305 噪声图片/分辨率305x305
方法
MSE -nan(ind) 1303.96 984.486
PSNR -nan(ind) 16.9784 18.2243
GMSD -nan(ind) 0.111176 0.113035
SSIM 1 0.30256 0.687856
BRISQUE 56.1736 42.0616 73.3258

各个方法具体检测速度如下表所示:

速度/s 原图/分辨率612x816 模糊图片/分辨率612x816 噪声图片/分辨率612x816
方法
MSE 0.029 0.021 0.020
PSNR 0.017 0.019 0.019
GMSD 0.032 0.031 0.032
SSIM 0.084 0.086 0.084
BRISQUE 0.068 0.073 0.071
速度/s 原图/分辨率305x305 模糊图片/分辨率305x305 噪声图片/分辨率305x305
方法
MSE 0.006 0.005 0.005
PSNR 0.004 0.005 0.004
GMSD 0.012 0.011 0.012
SSIM 0.025 0.031 0.033
BRISQUE 0.027 0.028 0.028

从上面的结果可以得到如下分析:

  1. 对于612x816分辨率图片,结果正确的有MSE,GMSD,BRISQUE;对于305x305分辨率图片,如果从局部上来看,噪声图片和模糊图片清晰图差不太多,结果正确的有PSNR,GMSD。然而对于BRISQUE模糊图片的清晰度评分比原图高。所以通常情况下,有参考图片,GMSD准确率最高,其他方法并不靠谱,BRISQUE需要更加完整的大图才有好的效果。
  2. 就速度而言,图像分辨率越高,各个方法耗时也越多,毕竟都是靠图像像素点差值公式计算的,不过都能在1s以内获得结果。
  3. 如果有有参图像,最好用GMSD。BRISQUE更适合高分辨率图片,如果要低分辨率使用,建议自己重新训练模型,毕竟BRISQUE的模型太老了。关于BRISQUE模型训练见:quality/samples

总而言之,现在图像质量评价算法都只能针对某种特定环境使用,在实际最好针对每一种图像噪声情况设定一种判定算法,现在各个视频检测平台也都是这样做的。如果普通使用看看GMSD和BRISQUE即可。

3 参考

3.1 参考代码

3.2 参考文章

[OpenCV实战]48 基于OpenCV实现图像质量评价的更多相关文章

  1. [OpenCV实战]47 基于OpenCV实现视觉显著性检测

    人类具有一种视觉注意机制,即当面对一个场景时,会选择性地忽略不感兴趣的区域,聚焦于感兴趣的区域.这些感兴趣的区域称为显著性区域.视觉显著性检测(Visual Saliency Detection,VS ...

  2. [OpenCV实战]45 基于OpenCV实现图像哈希算法

    目前有许多算法来衡量两幅图像的相似性,本文主要介绍在工程领域最常用的图像相似性算法评价算法:图像哈希算法(img hash).图像哈希算法通过获取图像的哈希值并比较两幅图像的哈希值的汉明距离来衡量两幅 ...

  3. [OpenCV实战]28 基于OpenCV的GUI库cvui

    目录 1 cvui的使用 1.1 如何在您的应用程序中添加cvui 1.2 基本的"hello world"应用程序 2 更高级的应用 3 代码 4 参考 有很多很棒的GUI库,例 ...

  4. [OpenCV实战]38 基于OpenCV的相机标定

    文章目录 1 什么是相机标定? 2 图像形成几何学 2.1 设定 2.1.1 世界坐标系 2.1.2 相机坐标系 2.1.3 图像坐标系 2.2 图像形成方法总结 3 基于OpenCV的相机标定原理 ...

  5. [OpenCV实战]26 基于OpenCV实现选择性搜索算法

    目录 1 背景 1.1 目标检测与目标识别 1.2 滑动窗口算法 1.3 候选区域选择算法 2 选择性搜索算法 2.1 什么是选择性搜索? 2.2 选择性搜索相似性度量 2.3 结果 3 代码 4 参 ...

  6. [OpenCV实战]51 基于OpenCV实现图像极坐标变换与逆变换

    在图像处理领域中,经常通过极坐标与笛卡尔直角坐标的互转来实现图像中圆形转为方形,或者通过极坐标反变换实现方形转圆形.例如钟表的表盘,人眼虹膜,医学血管断层都需要用到极坐标变换来实现圆转方. 文章目录 ...

  7. [OpenCV实战]11 基于OpenCV的二维码扫描器

    目录 1 二维码(QRCode)扫描 2 结果 3 参考 在这篇文章中,我们将看到如何使用OpenCV扫描二维码.您将需要OpenCV3.4.4或4.0.0及更高版本来运行代码. 1 二维码(QRCo ...

  8. [OpenCV实战]15 基于深度学习的目标跟踪算法GOTURN

    目录 1 什么是对象跟踪和GOTURN 2 在OpenCV中使用GOTURN 3 GOTURN优缺点 4 参考 在这篇文章中,我们将学习一种基于深度学习的目标跟踪算法GOTURN.GOTURN在Caf ...

  9. [OpenCV实战]5 基于深度学习的文本检测

    目录 1 网络加载 2 读取图像 3 前向传播 4 处理输出 3结果和代码 3.1结果 3.2 代码 参考 在这篇文章中,我们将逐字逐句地尝试找到图片中的单词!基于最近的一篇论文进行文字检测. EAS ...

随机推荐

  1. day47-JDBC和连接池03

    JDBC和连接池03 8.事务 8.1事务介绍 基本介绍 JDBC程序中当一个Connection对象创建时,默认情况下是自动提交事务:每次执行一个SQL语句时,如果执行成功,就会向数据库自动提交,而 ...

  2. Linux实战笔记__Centos7上搭建DVWA网站(基于宝塔)

    安装宝塔套件 宝塔官网有远程安装代码https://www.bt.cn/bbs/thread-19376-1-1.html 下载DVWA并上传至/www/wwwroot目录 下载地址: 配置数据库连接 ...

  3. 一天十道Java面试题----第五天(spring的事务传播机制------>mybatis的优缺点)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 41.spring的事务传播机制 42 .spring事务什么时候会失效 43 .什么的是bean的自动装配.有哪些方式? ...

  4. 6.jmespath表达式

    jmespath是json的查询语言 可以从json文档中提取和转换元素,类似于jsonpath的另外一个库   字典取值 # 一层嵌套取值 {a:1,b:2,c:3} ""&qu ...

  5. $_SERVER['HTTP_USER_AGENT']:在PHP中HTTP_USER_AGENT是用来获取用户的相关信息的,包括用户使用的浏览器,操作系统等信息

    在PHP中HTTP_USER_AGENT是用来获取用户的相关信息的,包括用户使用的浏览器,操作系统等信息. 我机器:操作系统:WIN7旗舰版 64操作系统 以下为各个浏览器下$_SERVER['HTT ...

  6. SpringBoot简单快速入门操作

    项目类分为: dao层 server层 controller层 Mapper → Server→ controller mapper层(必须要用interface创建) 创建后,首先要在方法前加@Ma ...

  7. 从小白到架构师(4): Feed 流系统实战

    「从小白到架构师」系列努力以浅显易懂.图文并茂的方式向各位读者朋友介绍 WEB 服务端从单体架构到今天的大型分布式系统.微服务架构的演进历程.读了三篇万字长文之后各位想必已经累了(主要是我写累了), ...

  8. 论文笔记 - An Explanation of In-context Learning as Implicit Bayesian Inference

    这位更是重量级.这篇论文对于概率论学的一塌糊涂的我简直是灾难. 由于 prompt 的分布与预训练的分布不匹配(预训练的语料是自然语言,而 prompt 是由人为挑选的几个样本拼接而成,是不自然的自然 ...

  9. Linux下安装 SkyWalking 分布式追踪系统

    Linux下安装 SkyWalking 分布式追踪系统 1.SkyWalking简介 1.1 SkyWalking介绍 SkyWalking项目是由华为大牛吴晟开源的个人项目,目前已经加入Apache ...

  10. Debian Linux 的安装

    Debian Linux 的安装 作者:Grey 原文地址: 博客园:Debian Linux 的安装 CSDN:Debian Linux 的安装 说明 本安装说明是基于 Windows 10 下 V ...