利用OSG和GLSL实现彩色图转为灰度图

news2024/11/16 1:55:04

目录

1. 前言

2. 开发环境说明 

3. 预备知识

4. 功能实现

4.1. 代码

4.2. 代码说明

5. 附加说明


1. 前言

       灰色图片其rgb值是一样的,比如(0.5, 0.5, 0.5)就是一张灰度图。彩色转黑白算法有很多种。因此由彩色转黑白关键就是由彩色的rgb算出灰度gray,然后最终的颜色就是(gray, gray , gray)。网上搜索到RGB转gray的算法有很多种,其中最常见的几种如下:

浮点算法:Gray = R * 0.3 + G * 0.59 + B * 0.11
整数方法:Gray = (R * 30 + G * 59 + B * 11) / 100
移位方法:Gray = (R * 76 + G * 151 + B*28) >> 8;
平均值法:Gray = (R + G + B) / 3;
仅取绿色:Gray = G;

       利用编程语言如C、C++等自己实现彩色图转为灰度图也是很容易的。本博文仅仅只是说明如何利用OSG和GLSL实现彩色图转为灰度图。下面是要实现的功能效果:

 

2. 开发环境说明 

          本次用到的开发环境说明如下:

  • OpenSceneGraph 3.6.2。
  • Visual studio 2022 64位社区版。
  • Windows 11 家庭中文版。
  • Qt  5.14.1。

3. 预备知识

       因为本博文用到了GLSL,如果不了解GLSL编程,请参考本人博客GLSL专栏或者自行问度妈。本文用到了OSG和GLSL混合编程,不懂的可以参考如下博文:

  • OSG与opengl的shader结合。
  • 利用GLSL和OSG进行三维渲染项目实战。

4. 功能实现

4.1. 代码

  直接上代码,main.cpp

#include "imageGrayByQt.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    imageGrayByQt w;
    w.show();
    return a.exec();
}

imageGrayByQt.cpp:

#include "imageGrayByQt.h"
#include<osgViewer/ViewerEventHandlers>
#include<osgDB/FileUtils>
#include<QMessageBox>
#include"shaderScript.h"

class MVPCallback : public osg::Uniform::Callback
{
public:
	MVPCallback(osg::Camera* camera, osg::MatrixTransform* pMatTransform)
		:mCamera(camera), _pMatTransform(pMatTransform)
	{
	}
	virtual void operator()(osg::Uniform* uniform, osg::NodeVisitor* nv)
	{
		const osg::Matrix& mat = _pMatTransform->getMatrix();
		osg::Matrixf modelView = mCamera->getViewMatrix();
		osg::Matrixf projectM = mCamera->getProjectionMatrix();
		uniform->set(mat * modelView * projectM); // 要乘以_pMatTransform的矩阵,否则不正确
	}

private:
	osg::Camera* mCamera;
	osg::MatrixTransform* _pMatTransform;
};

imageGrayByQt::imageGrayByQt(QWidget *parent)
    : QWidget(parent)
    , ui(new Ui::imageGrayByQtClass())
{
    ui->setupUi(this);

    this->setWindowState(Qt::WindowMaximized);

    connect(ui->showOrigImageBtn, &QAbstractButton::clicked, this, &imageGrayByQt::showOrigImage);
    connect(ui->showGrayImageBtn, &QAbstractButton::clicked, this, &imageGrayByQt::showGrayImage);

	osg::ref_ptr<osg::Group> spRoot = new osg::Group;

    // 画四边形
    auto spQuadGeo = drawQuadGeomtry();
	
	spRoot->addChild(spQuadGeo);

	ui->viewWnd->setSceneData(spRoot);
	ui->viewWnd->setCameraManipulator(new osgGA::TrackballManipulator);
	ui->viewWnd->addEventHandler(new osgViewer::WindowSizeHandler);
	ui->viewWnd->addEventHandler(new osgViewer::StatsHandler);

	createProgramAndShader();

    showOrigImage();
}

imageGrayByQt::~imageGrayByQt()
{
    delete ui;
}

void imageGrayByQt::showOrigImage()
{
	if (!osgDB::fileExists("test.png"))
	{
		QMessageBox::warning(this, "test.png is not exist !", "file exist check");
		return;
	}

	removeShader();

	auto pTexture2D = new osg::Texture2D();
	auto pImage = osgDB::readImageFile("test.png");
	pTexture2D->setImage(pImage);
	pTexture2D->setFilter(osg::Texture::FilterParameter::MAG_FILTER, osg::Texture::FilterMode::LINEAR);
	pTexture2D->setFilter(osg::Texture::FilterParameter::MIN_FILTER, osg::Texture::FilterMode::LINEAR);
	pTexture2D->setWrap(osg::Texture::WRAP_S, osg::Texture::CLAMP);
	pTexture2D->setWrap(osg::Texture::WRAP_T, osg::Texture::CLAMP);
	auto pGeode = _matTransform->getChild(0)->asGeode();
	pGeode->getOrCreateStateSet()->setTextureAttributeAndModes(0, pTexture2D);
}

// 显示灰度图
void imageGrayByQt::showGrayImage()
{
	if (nullptr == _spTextureCoordArray)
	{
		_spTextureCoordArray = new osg::Vec2Array();
	}
	_spTextureCoordArray->clear();

	//_spTextureCoordArray->push_back(osg::Vec2d(0.0, 0.0));
	//_spTextureCoordArray->push_back(osg::Vec2d(0.0, 1.0));
	//_spTextureCoordArray->push_back(osg::Vec2d(1.0, 1.0));
	//_spTextureCoordArray->push_back(osg::Vec2d(1.0, 0.0));

	auto spGeode = _matTransform->getChild(0)->asGeode();
	auto pQuadGeo = spGeode->getChild(0)->asGeometry();
	
#ifdef _SHADER_VER_120
	pQuadGeo->setTexCoordArray(0, _spTextureCoordArray);
#endif
#ifdef _SHADER_VER_430
	_spTextureCoordArray = dynamic_cast<osg::Vec2Array*>(pQuadGeo->getTexCoordArray(0));
	pQuadGeo->setVertexAttribArray(0, pQuadGeo->getVertexArray());
	pQuadGeo->setVertexAttribBinding(0, osg::Geometry::BIND_PER_VERTEX); // 必须是BIND_PER_VERTEX,否则四边形不会显示,下同
	pQuadGeo->setVertexAttribArray(1, _spTextureCoordArray);
	pQuadGeo->setVertexAttribBinding(1, osg::Geometry::BIND_PER_VERTEX);

	// 必须用osg::Matrixf,而不能用osg::Matrixd或osg::Matrix,否则报错,场景不会绘制出来
	osg::Matrixf modelView = ui->viewWnd->getCamera()->getViewMatrix(); 
	osg::Matrixf projectM = ui->viewWnd->getCamera()->getProjectionMatrix();
	osg::Uniform* pMVPUniform = new osg::Uniform("mvp", modelView * projectM);
	osg::StateSet* ss = spGeode->getOrCreateStateSet();
	ss->addUniform(pMVPUniform);//对应的Program和Uniform要加到同一个Node下的StateSet中
	pMVPUniform->setUpdateCallback(new MVPCallback(ui->viewWnd->getCamera(), _matTransform));
#endif // _SHADER_VER_430

	spGeode->getOrCreateStateSet()->setAttributeAndModes(_spProgram);
	spGeode->getOrCreateStateSet()->addUniform(new osg::Uniform("colorMap", 0));

	_spProgram->addShader(_spVertexShader);
	_spProgram->addShader(_spFragShader);
}

// 移除着色器
void imageGrayByQt::removeShader()
{
	if (nullptr != _spProgram)
	{
		if (nullptr != _spVertexShader)
		{
			_spProgram->removeShader(_spVertexShader);
		}

		if (nullptr != _spFragShader)
		{
			_spProgram->removeShader(_spFragShader);
		}
	}
}

// 创建着色器相关对象
void imageGrayByQt::createProgramAndShader()
{
	if (nullptr == _spProgram)
	{
		_spProgram = new osg::Program;
	}

	if (nullptr == _spVertexShader)
	{
		_spVertexShader = new osg::Shader(osg::Shader::VERTEX, vs());
	}

	if (nullptr == _spFragShader)
	{
		_spFragShader = new osg::Shader(osg::Shader::FRAGMENT, fs());
	}
}

// 画四边形
osg::ref_ptr<osg::MatrixTransform> imageGrayByQt::drawQuadGeomtry()
{
	if (nullptr == _matTransform)
	{
		_matTransform = new osg::MatrixTransform;

		// 转动60度,便于观察
		_matTransform->setMatrix(osg::Matrix::rotate(osg::inDegrees(60.0), osg::X_AXIS));
	}

	auto pChildNum = _matTransform->getNumChildren();
	if (0 == pChildNum)
	{
		osg::ref_ptr<osg::Geode> spGeode = new osg::Geode;
		_matTransform->addChild(spGeode);

		auto pQuadGeo = osg::createTexturedQuadGeometry(osg::Vec3d(-1.0, -1.0, 0.0), osg::Vec3d(1.0, 1.0, 0.0), osg::Vec3d(-1.0, 1.0, 0.0));
		spGeode->addDrawable(pQuadGeo);
	}

	return _matTransform;
}

imageGrayByQt.h: 

#pragma once

#include <QtWidgets/QWidget>
#include "ui_imageGrayByQt.h"
#include<osg/matrixTransform>
QT_BEGIN_NAMESPACE
namespace Ui { class imageGrayByQtClass; };
QT_END_NAMESPACE

class imageGrayByQt : public QWidget
{
    Q_OBJECT

public:
    imageGrayByQt(QWidget *parent = nullptr);
    ~imageGrayByQt();

private:

    void showOrigImage();

    void showGrayImage();

    // 画四边形
    osg::ref_ptr<osg::MatrixTransform> drawQuadGeomtry();

    // 创建着色器相关对象
    void createProgramAndShader();

    // 移除着色器
    void removeShader();
private:

    osg::ref_ptr <osg::MatrixTransform> _matTransform{nullptr};
    osg::ref_ptr <osg::Program> _spProgram{nullptr};  // 着色器程序
    osg::ref_ptr<osg::Shader>_spFragShader;           // 片段着色器
    osg::ref_ptr<osg::Shader>_spVertexShader;         // 顶点着色器
    osg::ref_ptr<osg::Vec2Array>_spTextureCoordArray;  // 纹理数组

private:
    Ui::imageGrayByQtClass *ui;
};

shaderScript.h:

#ifndef _SHADERSCRIPT_H
#define _SHADERSCRIPT_H

//#define _SHADER_VER_120
#define _SHADER_VER_430

// 顶点着色器
#ifdef _SHADER_VER_120
static const char* vs() {
	static const char* p = "#version 120\n"
		"varying  vec2 vTexCoord; \n"
		"void main()\n"
		"{\n"
		"gl_Position = ftransform();\n"
		"vTexCoord = gl_MultiTexCoord0.xy;\n"
		"}\n";

	return p;
}

// 片段着色器
static const char* fs() {
	static const char* p = "#version 120\n"
		"uniform sampler2D colorMap;\n"
		"varying  vec2 vTexCoord;\n"
		"void main(void)\n"
		"{\n"
		"vec3 W = vec3(0.2125, 0.7154, 0.0721);\n"
		"vec4 mask = texture2D(colorMap, vTexCoord);\n"
		"float luminance = dot(mask.rgb, W);\n"
		"gl_FragColor = vec4(vec3(luminance), 1.0);\n"
		"}\n";

	return p;
}
#endif

#ifdef _SHADER_VER_430

static const char* vs() {
	static const char* p = "#version 430\n"
		"layout (location=0) in vec3 VertexPosition; \n"
		"layout (location=1) in vec2 vTexCoord; \n"
		"out vec2 texCoord;\n"
		"uniform mat4 mvp;\n"
		"void main()\n"
		"{\n"
		"    gl_Position = mvp * vec4(VertexPosition, 1.0);\n"
		"    texCoord = vTexCoord;\n"
		"}\n";

	return p;
}

// 片段着色器
static const char* fs() {
	static const char* p = "#version 430 \n"
		"in vec2 texCoord;\n"
		"out vec4 FragColor; \n"
		"uniform sampler2D colorMap;\n"
		"void main(void)\n"
		"{\n"
		"vec3 W = vec3(0.2125, 0.7154, 0.0721);\n"
		"vec4 mask = texture2D(colorMap, texCoord);\n"
		"float luminance = dot(mask.rgb, W);\n"
		"FragColor = vec4(vec3(luminance), 1.0);\n"
		"}\n";

	return p;
}
#endif // _SHADER_VER_430

#endif

   ui->viewWnd是QtOsgView类型指针,QtOsgView.h、QtOsgView.cpp文件参见:osg嵌入到Qt窗体,实现Qt和osg混合编程 博文。

4.2. 代码说明

        shaderScript.h文件实现了顶点着色器和片段着色器。着色器分为1.2.0版本和4.3.0版本。1.2.0版本中用到了ftransform函数,该函数理论上可以用如下的GLSL内置函数等效:

gl_Position =gl_ModelViewProjectionMatrix * gl_Vertex;

关于ftransform函数的更多知识,请参考:opengl ftransform 。

      注意:

gl_FragColor、varying类型、gl_ModelViewProjectionMatrix 、gl_Vertex、gl_MultiTexCoord0在GLSL 1.40之后的版本中移除了,因此在4.30版本使用这些关键字会报错。

       在GLSL 4.3.0版本当向顶点着色器的uniform mat4类型的mvp传入模型视图投影矩阵时,传入的矩阵类型必须是osg::Matrixf,而不能是osg::Matrix或osg::Matrixd,否则会报如下错误,且四边形不会绘制出来:  

Warning: detected OpenGL error 'invalid operation' at after pcp->apply(Unfiorm&) in GLObjectsVisitor::apply(osg::StateSet& stateset), unifrom name: mvp

      在GLSL 4.3.0版本必须像下面那样对uniform设置回调,否则四边形绘制的位置不对,可能会看不见。

pMVPUniform->setUpdateCallback(new MVPCallback(ui->viewWnd->getCamera(), _matTransform));

 且在MVPCallback类的operator函数中乘以_pMatTransform包含的矩阵,即如下那样:

virtual void operator()(osg::Uniform* uniform, osg::NodeVisitor* nv)
	{
	     .......................// 其它代码略
		uniform->set(mat * modelView * projectM); // 要乘以_pMatTransform的矩阵,否则不正确
	}

否则当点击界面“显示灰度图”按钮时,场景也即四边形位置会变化,如下为没乘以_pMatTransform的矩阵的现象:

可以看到点击界面“显示灰度图”按钮后,四边形位置发生了变化,需要鼠标拖动后才看到四边形,这可不是我们想要的结果(只是灰度图片,怎么会改变位置呢) 

       彩图转为灰度图由showGrayImage函数实现。需要说明的是:图片的纹理坐标必须通过如下代码设置:

_spTextureCoordArray = dynamic_cast<osg::Vec2Array*>(pQuadGeo->getTexCoordArray(0));

而不能通过人为设置,即不能像该函数第90~93行注释那样设置,否则会出现转为灰度图时,四边形位置发生改变或图片反向了,如下为取消90~93行注释,在GLSL 1.2版本对图片灰度化的现象:

可以看到,纹理图片反向了。事实上,通过第102行的如下代码:

 _spTextureCoordArray = dynamic_cast<osg::Vec2Array*>(pQuadGeo->getTexCoordArray(0));

返回的纹理坐标依次是:(0.0, 1.0)、(0.0, 0.0)、(1.0, 0.0)、(1.0, 1.0),即人为预想的纹理坐标和真实的纹理坐标可能会不一样,即将第90~93行改为如下那样则纹理图片不会反向:

    _spTextureCoordArray->push_back(osg::Vec2d(0.0, 1.0));
  	_spTextureCoordArray->push_back(osg::Vec2d(0.0, 0.0));
	_spTextureCoordArray->push_back(osg::Vec2d(1.0, 0.0));
	_spTextureCoordArray->push_back(osg::Vec2d(1.0, 1.0));

总之:出于安全便捷考虑,不要人为设置纹理坐标,只需要按照第102行那样,调用osg的接口获取纹理坐标就行。另外如下代码可以去掉,不影响功能,留下来仅仅是为了验证刚才说的纹理坐标问题的。

#ifdef _SHADER_VER_120
	pQuadGeo->setTexCoordArray(0, _spTextureCoordArray);
#endif

5. 附加说明

     代码中读取了png图片,故请保证png插件存在,否则读取png会失败。关于怎么编译png插件到osg,请参见:osg第三方插件的编译方法(以jpeg插件来讲解)

     参考链接:第13节 实例-彩色转灰度(做假红外)。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

7+共病思路。WGCNA+多机器学习+实验简单验证,易操作

今天给同学们分享一篇共病WGCNA多机器学习实验的生信文章“Shared diagnostic genes and potential mechanism between PCOS and recurrent implantation failure revealed by integrated transcriptomic analysis and machine learning”&#xff0c;这篇文章于2023年5月16日发…

数据结构与算法解析(C语言版)--搭建项目环境

本栏目致力于从0开始使用纯C语言将经典算法转换成能够直接上机运行的程序&#xff0c;以项目的形式详细描述数据存储结构、算法实现和程序运行过程。 参考书目如下&#xff1a; 《数据结构C语言版-严蔚敏》 《数据结构算法解析第2版-高一凡》 软件工具&#xff1a; dev-cpp 搭…

调试AOSP源码的官方神器-Android Studio for Platform(ASfP)

文章目录 下载安装启动AOSP导入调试不足 欢迎关注微信公众号ZZH的Android 下载 下载地址平台版 Android Studio 由于该工具在调试源码时需要对AOSP进行编译&#xff0c;所以目前只有Ubuntu版本&#xff0c;后续应该也只会有Ubuntu版本。 Ubuntu环境下显示可下载 Windows系统…

【Redis】认识Redis-特点特性应用场景对比MySQL重要文件及作用

文章目录 认识redisredis的主要特点redis的特性&#xff08;优点&#xff09;redis是单线程模型&#xff0c;为什么效率这么高&#xff0c;访问速度这么快redis应用场景redis不可以做什么MySQL和Redis对比启动RedisRedis客户端Redis重要文件及作用 认识redis redis里面相关的小…

SHCTF2023 山河CTF Reverse Week3 --- ststst easyre WP详解

文章目录 [WEEK3]ststst[WEEK3]easyre [WEEK3]ststst 64 bit 的 ELF 文件 sub_400763点进去看看 mprotect &#xff0c;这个 这一题是SMC TEA的考察&#xff0c;我写过一篇关于 SMC学习网鼎杯jocker 可以使用idapython写脚本自动修复&#xff0c;也可以使用动态调试&#x…

吉他、班卓琴和贝斯吉他降分器:Arobas Music Guitar 8.1.1

Arobas Music Guitar 是一款专业的吉他、班卓琴和贝斯吉他降分器。在熟练的手中&#xff0c;它不仅可以让您创作&#xff0c;还可以编辑、聆听和录制&#xff0c;以及导入和导出乐谱。如果有人感兴趣的话&#xff0c;录音是在八个轨道上进行的&#xff0c;你可以为每个轨道单独…

1.8 网络安全模型

思维导图&#xff1a; 1.8 网络安全模型笔记&#xff1a; 网络安全模型核心概念: 消息在Internet上从发送方传送至接收方&#xff0c;涉及到源地址、目的地址、通信协议&#xff08;如TCP/IP&#xff09;的使用。信息交换的双方需要合作保证交换的可靠性。 安全技术核心组成…

发布一款CAN总线接口的GPS模块

一、模块硬件介绍 GNSS模块&#xff1a;u-blox M9N&#xff0c;支持北斗、GPS、格洛纳斯和伽利略四系统并发处理陶瓷天线&#xff1a;太盟PA025AZ009&#xff0c;支持北斗、GPS、格洛纳斯三系统。根据太盟的回复&#xff0c;25*25*4尺寸的陶瓷天线无法做到完美的四系统兼容&am…

软考系统架构师知识点集锦十:计算机网络、数学与经济管理、知识产权与标准化

一、计算机网络 1.1、考情分析 2.1 TCP/IP协议簇 2.1.1常见协议及功能 网际层是整个TCP/IP体系结构的关键部分,其功能是使主机可以把分组发往任何网络并使分组独立地传向目标。 POP3: 110 端口&#xff0c;邮件收取SMTP: 25 端口&#xff0c;邮件发送FTP: 20数据端口/21控制…

最优值函数

一、最优状态值函数 解决强化学习任务大致上意味着找到一种政策&#xff0c;能够在长期内实现很多奖励。对于有限MDPs&#xff0c;我们可以精确地定义一种最优政策&#xff0c;其定义如下。值函数定义了政策的一种部分排序。如果一个政策的预期回报大于或等于另一个政策π0在所…

VS Code2023安装教程(最新最详细教程)附网盘资源

目录 一.简介 二.安装步骤 三.VS Code 使用技巧 网盘资源见文末 一.简介 VS Code是一个由微软开发的跨平台的轻量级集成开发环境&#xff08;IDE&#xff09;&#xff0c;被广泛用于编写各种编程语言的代码。它支持多种编程语言&#xff0c;并且可以通过插件扩展功能。 以…

读图数据库实战笔记04_路径与图变异

1. Groovy 1.1. Java编程语言的一个超集 1.2. Gremlin Console的一个特性是能和Groovy配合使用 1.2.1. Gremlin Console会自动地迭代结果 1.3. 从技术上说&#xff0c;Gremlin Console就是Groovy交互式解释器&#xff08;read-eval-print loop&#xff0c;REPL&#xff09;…

一篇博客理解Recyclerview的使用

从Android 5.0开始&#xff0c;谷歌公司推出了RecylerView控件&#xff0c;当看到RecylerView这个新控件的时候,大部分人会首先发出一个疑问&#xff0c;recylerview是什么&#xff1f;为什么会有recylerview也就是说recylerview的优点是什么&#xff1f;recylerview怎么用&…

图像视觉特效处理工具:Boris FX Optics 2024.0.1

BorisFX光效插件Optics首发2024版&#xff1a;3大新功能详解 2023年9月15日&#xff0c;全球领先的视觉后期软件开发公司BorisFX推出了旗下知名软件Boris FX Optics的全新2024版本&#xff0c;这款备受后期处理爱好者喜爱的Photoshop插件和独立程序再次升级&#xff0c;为您的…

【PC】特殊空投-2023年10月

亲爱的玩家朋友们&#xff0c;大家好&#xff01; 10月特殊空投活动来袭。本月我们也准备了超多活动等着大家来体验。快来完成任务获得丰富的奖励吧&#xff01;签到活动&#xff0c;每周一次的PUBG空投节&#xff0c;还有可以领取PGC2023免费投票劵的活动等着大家&#xff01;…

聊聊统一认证中的四种安全认证协议(干货分享)

大家好&#xff0c;我是陈哈哈。单点登录SSO的出现是为了解决众多企业面临的痛点&#xff0c;场景即用户需要登录N个程序或系统&#xff0c;每个程序与系统都有不同的用户名和密码。在企业发展初期&#xff0c;可能仅仅有几个程序时&#xff0c;管理账户和密码不是一件难事。但…

软考系统架构师知识点集锦九:数据库系统

一、考情分析 二、考点精讲 2.1数据库概述 2.1.1数据库模式 (1)三级模式:外模式对应视图&#xff0c;模式(也称为概念模式)对应数据库表&#xff0c;内模式对应物理文件。(2)两层映像:外模式-模式映像&#xff0c;模式-内模式映像;两层映像可以保证数据库中的数据具有较高的…

linux查看系统版本、内核信息、操作系统类型版本

1. 使用 uname 命令&#xff1a;这将显示完整的内核版本信息&#xff0c;包括内核版本号、主机名、操作系统类型等。 uname -a2. 使用 lsb_release 命令&#xff08;仅适用于支持 LSB&#xff08;Linux Standard Base&#xff09;的发行版&#xff09;&#xff1a;这将显示包含…

reactos 可调试光盘映像

链接&#xff1a;https://pan.baidu.com/s/13M9BZN4IDrWLc3bjnHO79g?pwd0gst 提取码&#xff1a;0gst

【计算机网络笔记】传输层——多路复用和多路分用

系列文章目录 什么是计算机网络&#xff1f; 什么是网络协议&#xff1f; 计算机网络的结构 数据交换之电路交换 数据交换之报文交换和分组交换 分组交换 vs 电路交换 计算机网络性能&#xff08;1&#xff09;——速率、带宽、延迟 计算机网络性能&#xff08;2&#xff09;…