流程图渲染方式:Canvas vs SVG
我们是袋鼠云数栈 UED 团队,致力于打造优秀的一站式数据中台产品。我们始终保持工匠精神,探索前端道路,为社区积累并传播经验价值。
本文作者:霁明
背景
我们产品中会有一些流程图应用,例如审批中心的审批流程图:

我们数栈产品内的流程图,基本都是使用的 mxGraph 实现的,mxGraph 使用了SVG来渲染图形。
流程图组件库除了 mxGraph,还有其他一些流行的库,例如:ReactFlow、G6、X6等等,各个库的特点、具体实现原理各有不同,但图形渲染方式却主要都是这两种:Canvas 和 SVG。
本文会通过绘制流程图(只是简单绘制,不涉及图表库的实现),来介绍 Canvas 和 SVG 的使用方式、动画实现以及两者之间的一些差异。
Canvas
简介
MDN 对 Canvas 的介绍:
Canvas API 提供了一个通过 JavaScript 和 HTML的 <canvas>元素来绘制图形的方式。它可以用于动画、游戏画面、数据可视化、图片编辑以及实时视频处理等方面。
目前所有主流的浏览器都支持 Canvas。
使用
基本用法
创建蓝白红3个色块:
import { useEffect } from 'react';
function Page() {
useEffect(() => {
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
if (canvas?.getContext) {
const ctx = canvas.getContext('2d');
ctx.fillStyle = '#002153';
ctx.fillRect(10, 10, 50, 100);
ctx.fillStyle = '#ffffff';
ctx.fillRect(60, 10, 50, 100);
ctx.fillStyle = '#d00922';
ctx.fillRect(110, 10, 50, 100);
}
}, []);
return <canvas id="canvas"></canvas>;
}
export default Page;
效果如下图:

绘制流程图
绘制一个开始节点、一个中间节点和一个结束节点,节点之间用有向线条进行连接,如下图:

前置知识:
devicePixelRatio:设备像素比,返回当前显示设备的物理像素分辨率与 _CSS _ 像素分辨率之比,它告诉浏览器应使用多少屏幕实际像素来绘制单个 CSS 像素。比如屏幕物理像素是2000px,css 像素是1000px,则设备像素比为2。
实现代码如下:
import { useEffect } from 'react';
import styles from '../../styles/canvas.module.css';
function Page() {
useEffect(() => {
const canvas = document.getElementById('canvas') as HTMLCanvasElement;
if (canvas?.getContext) {
// 处理图像模糊问题
const ratio = window.devicePixelRatio || 1;
const { width, height } = canvas;
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
const ctx = canvas.getContext('2d');
// 放大(处理图像模糊问题)
ctx.scale(ratio, ratio);
ctx.font = '12px sans-serif';
// 开始节点
ctx.beginPath();
ctx.arc(300, 125, 25, Math.PI / 2, (Math.PI * 3) / 2, false); // 左边框
ctx.lineTo(350, 100);
ctx.arc(350, 125, 25, (Math.PI * 3) / 2, (Math.PI * 5) / 2, false); // 右边框
ctx.lineTo(300, 150);
ctx.lineWidth = 3;
ctx.stroke();
ctx.fillStyle = '#FFF';
ctx.fill();
ctx.fillStyle = '#000';
ctx.fillText('开始', 312, 130);
// 中间节点
ctx.beginPath();
ctx.arc(280, 230, 5, Math.PI, (Math.PI * 3) / 2, false); // 左上圆角
ctx.lineTo(370, 225);
ctx.arc(370, 230, 5, (Math.PI * 3) / 2, Math.PI * 2, false); // 右上圆角
ctx.lineTo(375, 270);
ctx.arc(370, 270, 5, 0, Math.PI / 2, false); // 右下圆角
ctx.lineTo(280, 275);
ctx.arc(280, 270, 5, Math.PI / 2, Math.PI, false); // 左下圆角
ctx.lineTo(275, 230);
ctx.lineWidth = 3;
ctx.stroke();
ctx.fillStyle = '#FFF';
ctx.fill();
ctx.fillStyle = '#000';
ctx.fillText('中间节点', 300, 254);
// 结束节点
ctx.beginPath();
ctx.arc(300, 400, 25, Math.PI / 2, (Math.PI * 3) / 2, false); // 左边框
ctx.lineTo(350, 375);
ctx.arc(350, 400, 25, (Math.PI * 3) / 2, (Math.PI * 5) / 2, false); // 右边框
ctx.lineTo(300, 425);
ctx.stroke();
ctx.fillStyle = '#FFF';
ctx.fill();
ctx.fillStyle = '#000';
ctx.fillText('结束', 312, 405);
// 线条1
ctx.beginPath();
ctx.moveTo(325, 150);
ctx.lineTo(325, 225);
ctx.lineWidth = 1;
ctx.stroke();
// 箭头1
ctx.beginPath();
ctx.moveTo(320, 215);
ctx.lineTo(330, 215);
ctx.lineTo(325, 225);
ctx.fill();
// 线条2
ctx.beginPath();
ctx.moveTo(325, 275);
ctx.lineTo(325, 375);
ctx.stroke();
// 箭头2
ctx.beginPath();
ctx.moveTo(320, 365);
ctx.lineTo(330, 365);
ctx.lineTo(325, 375);
ctx.fill();
}
}, []);
return (
<div className={styles.container}>
<canvas id="canvas" width="800" height="600"></canvas>
</div>
);
}
export default Page;
绘制图形可以通过绘制矩形、绘制路径的方式来绘制图形,还可以使用 Path2D 对象来绘制,具体使用方法可以查看MDN。
样式和颜色
给节点加上样式,效果如下:

对比上一步,可以发现给节点内容和边框填充了颜色,以开始节点为例:
...
// 开始节点
ctx.beginPath();
ctx.arc(300, 125, 25, Math.PI / 2, (Math.PI * 3) / 2, false); // 左边框
ctx.lineTo(350, 100);
ctx.arc(350, 125, 25, (Math.PI * 3) / 2, (Math.PI * 5) / 2, false); // 右边框
ctx.lineTo(300, 150);
ctx.lineWidth = 3;
ctx.strokeStyle = '#82b366';
ctx.stroke();
ctx.fillStyle = '#d5e8d4';
ctx.fill();
ctx.fillStyle = '#000';
ctx.fillText('开始', 312, 130);
...
canvas 支持绘制许多样式,例如:颜色、透明度、线条样式、阴影等,具体使用可查看 MDN
动画实现
实现线条流动动画,实现效果如下图所示:

实现原理:将线条设置为虚线,然后设置偏移量,每间隔一定时间渲染一次,每次的偏移量都递增,便实现了线条流动的动画效果。
原理了解了,但在开发之前有两个点要考虑一下:
- 动画是有执行频率的,要控制的话用哪种方式好一点?
- 每次动画执行时,一般是整个画布都刷新,考虑到性能问题,是否可以局部刷新?
带着这两个问题,我们看下代码实现:
import { useEffect } from 'react';
import styles from '../../styles/page.module.css';
const rAFSetInterval = (handler: (timer: number) => void, timeout?: number) => {
let timer = null;
let startTime = Date.now();
const loop = () => {
let currentTime = Date.now();
if (currentTime - startTime >= timeout) {
startTime = currentTime;
handler(timer);
}
timer = requestAnimationFrame(loop);
};
loop();
return timer;
};
function Page() {
let canvas: HTMLCanvasElement;
let ctx: CanvasRenderingContext2D;
let offset = 0;
useEffect(() => {
canvas = document.getElementById('canvas') as HTMLCanvasElement;
if (canvas) {
const ratio = window.devicePixelRatio || 1;
const { width, height } = canvas;
canvas.width = Math.round(width * ratio);
canvas.height = Math.round(height * ratio);
canvas.style.width = `${width}px`;
canvas.style.height = `${height}px`;
ctx = canvas.getContext('2d');
ctx.scale(ratio, ratio);
ctx.font = '12px sans-serif';
draw();
rAFSetInterval(run, 50);
}
}, []);
const run = () => {
offset++;
if (offset > 1000) {
offset = 0;
}
drawAnimateLine();
};
const draw = () => {
// 初始化
ctx.clearRect(0, 0, canvas.width, canvas.height);
ctx.setLineDash([]);
ctx.lineDashOffset = 0;
// 开始节点
ctx.beginPath();
ctx.arc(300, 125, 25, Math.PI / 2, (Math.PI * 3) / 2, false); // 左边框
ctx.lineTo(350, 100);
ctx.arc(350, 125, 25, (Math.PI * 3) / 2, (Math.PI * 5) / 2, false); // 右边框
ctx.lineTo(300, 150);
ctx.lineWidth = 3;
ctx.strokeStyle = '#82b366';
ctx.stroke();
ctx.fillStyle = '#d5e8d4';
ctx.fill();
ctx.fillStyle = '#000';
ctx.fillText('开始', 312, 130);
// 中间节点
ctx.beginPath();
ctx.arc(280, 230, 5, Math.PI, (Math.PI * 3) / 2, false); // 左上圆角
ctx.lineTo(370, 225);
ctx.arc(370, 230, 5, (Math.PI * 3) / 2, Math.PI * 2, false); // 右上圆角
ctx.lineTo(375, 270);
ctx.arc(370, 270, 5, 0, Math.PI / 2, false); // 右下圆角
ctx.lineTo(280, 275);
ctx.arc(280, 270, 5, Math.PI / 2, Math.PI, false); // 左下圆角
ctx.lineTo(275, 230);
ctx.lineWidth = 3;
ctx.strokeStyle = '#6c8ebf';
ctx.stroke();
ctx.fillStyle = '#dae8fc';
ctx.fill();
ctx.fillStyle = '#000';
ctx.fillText('中间节点', 300, 254);
// 结束节点
ctx.beginPath();
ctx.arc(300, 375, 25, Math.PI / 2, (Math.PI * 3) / 2, false); // 左边框
ctx.lineTo(350, 350);
ctx.arc(350, 375, 25, (Math.PI * 3) / 2, (Math.PI * 5) / 2, false); // 右边框
ctx.lineTo(300, 400);
ctx.strokeStyle = '#82b366';
ctx.stroke();
ctx.fillStyle = '#d5e8d4';
ctx.fill();
ctx.fillStyle = '#000';
ctx.fillText('结束', 312, 380);
// 线条1
ctx.beginPath();
ctx.moveTo(325, 150);
ctx.lineTo(325, 223);
ctx.setLineDash([4, 4]);
ctx.lineDashOffset = -offset;
ctx.lineWidth = 1.5;
ctx.strokeStyle = '#000';
ctx.stroke();
// 箭头1
ctx.beginPath();
ctx.moveTo(320, 215);
ctx.lineTo(325, 218);
ctx.lineTo(330, 215);
ctx.lineTo(325, 225);
ctx.fill();
// 线条2
ctx.beginPath();
ctx.moveTo(325, 275);
ctx.lineTo(325, 348);
ctx.stroke();
// 箭头2
ctx.beginPath();
ctx.moveTo(320, 340);
ctx.lineTo(325, 343);
ctx.lineTo(330, 340);
ctx.lineTo(325, 350);
ctx.fill();
};
const drawAnimateLine = () => {
// 清空线条
ctx.clearRect(324, 150, 2, 67);
ctx.clearRect(324, 275, 2, 67);
// 绘制线条1
ctx.beginPath();
ctx.moveTo(325, 150);
ctx.lineTo(325, 223);
ctx.setLineDash([4, 4]);
ctx.lineDashOffset = -offset;
ctx.lineWidth = 1.5;
ctx.strokeStyle = '#000';
ctx.stroke();
// 绘制线条2
ctx.beginPath();
ctx.moveTo(325, 275);
ctx.lineTo(325, 348);
ctx.stroke();
};
return (
<div className={styles.container}>
<canvas id="canvas" width="800" height="600"></canvas>
</div>
);
}
export default Page;
针对前面的两个问题,这里总结一下:
- 使用 requestAnimationFrame 实现一个 setInterval 方法,做到定时控制和性能兼顾
- 针对动画区域,通过坐标和区域宽高,进行 canvas 的局部刷新
SVG
简介
引用 MDN 对 SVG 的介绍:
可缩放矢量图形(Scalable Vector Graphics,SVG)基于 XML 标记语言,用于描述二维的矢量图形。
和传统的点阵图像模式(如 JPEG 和 PNG)不同的是,SVG 格式提供的是矢量图,这意味着它的图像能够被无限放大而不失真或降低质量,并且可以方便地修改内容,无需图形编辑器。通过使用合适的库进行配合,SVG 文件甚至可以随时进行本地化。
目前所有主流的浏览器都支持SVG(IE部分支持)。
使用
常用标签
流程图中主要用到的几种标签:
<svg>
SVG 容器元素,SVG 的代码都包裹在该元素下,可以作为根元素(一般是 svg 图片),也可以内嵌在HTML文档中。如果 svg 不是根元素,svg 元素可以用于在当前文档内嵌套一个独立的 svg 片段。这个独立片段拥有独立的视口和坐标系统。
<g>
元素 g 是用来组合对象的容器。添加到 g 元素上的变换会应用到其所有的子元素上。添加到 g 元素的属性会被其所有的子元素继承。
<rect>
rect元素是 SVG 的一个基本形状,用来创建矩形,基于一个角位置以及它的宽和高。它还可以用来创建圆角矩形。
<path>
path 元素是用来定义形状的通用元素。所有的基本形状都可以用 path 元素来创建。
<foreignObject>
foreignObject 元素允许包含来自不同的 XML 命名空间的元素。在浏览器的上下文中,很可能是 XHTML / HTML。在我们的流程图中,通过 HTML 渲染的节点一般都渲染在这个标签内。
基本用法
使用svg渲染图片
function Page() {
return (
<svg width="150" height="100">
<rect width="50" height="100" x="0" fill="#002153" />
<rect width="50" height="100" x="50" fill="#ffffff" />
<rect width="50" height="100" x="100" fill="#d00922" />
</svg>
);
}
export default Page;
上面代码渲染效果如下图:

绘制流程图
使用svg绘制流程图:

代码实现如下:
import styles from '../../styles/page.module.css';
function Page() {
return (
<svg width="800" height="600" className={styles.container}>
<g>
<path
d="M 320 110 C 286 110, 286 160, 320 160 L 370 160 C 404 160, 404 110, 370 110 Z"
stroke="#82b366"
strokeWidth="2"
fill="#d5e8d4"
/>
<text x="332" y="140" style={{ fontSize: 12 }}>
开始
</text>
</g>
<g>
<rect
x="295"
y="235"
width="100"
height="50"
rx="5"
fill="#dae8fc"
stroke="#6c8ebf"
strokeWidth="2"
></rect>
<text x="320" y="264" style={{ fontSize: 12 }}>
中间节点
</text>
</g>
<g>
<path
d="M 320 360 C 286 360, 286 410, 320 410 L 370 410 C 404 410, 404 360, 370 360 Z"
stroke="#82b366"
strokeWidth="2"
fill="#d5e8d4"
/>
<text x="332" y="390" style={{ fontSize: 12 }}>
结束
</text>
</g>
<g>
<path d="M 345 160 L 345 235" stroke="#000"></path>
<path d="M 340 225 L 345 228 L 350 225 L 345 235 Z" fill="#000"></path>
</g>
<g>
<path d="M 345 285 L 345 360" stroke="#000"></path>
<path d="M 340 350 L 345 353 L 350 350 L 345 360 Z" fill="#000"></path>
</g>
</svg>
);
}
export default Page;
以开始节点为例,主要看下path元素:
<path
d="M 320 110 C 286 110, 286 160, 320 160 L 370 160 C 404 160, 404 110, 370 110 Z"
stroke="#82b366"
strokeWidth="2"
fill="#d5e8d4"
/>
d 属性定义了要绘制的路径,路径定义是一个路径命令组成的列表,其中的每一个命令由命令字母和用于表示命令参数的数字组成。每个命令之间通过空格或逗号分隔。
M 表示 move to,即移动到某个坐标;L 表示 line to,即连线到某个坐标。
C 表示使用三次方贝塞尔曲线,后面跟随3个坐标点,分别是起始控制点、终点控制点、终点。
Z 表示 ClosePath,将从当前位置绘制一条直线到路径中的第一个点。上面只用到了4种命令,而命令总共有20种,具体可以查看MDN。
stroke、strokeWidth、fill 则分别指定了边框颜色、宽度,以及填充颜色。
动画实现
实现线条流动动画,实现效果如下图:

实现原理:先将线条设置为虚线,然后通过 css 动画,修改虚线的偏移量并无限循环,从而实现线条流动效果。
代码实现如下:
.animate-path {
stroke-dasharray: 5;
animation: dashdraw 0.5s linear infinite;
}
@keyframes dashdraw {
0% {
stroke-dashoffset: 10;
}
}
svg 可以通过 css、js 或者 animate 标签来实现动画,适用于需要高质量矢量图形、可缩放和交互性强的场景
对比
使用方式
Canvas 是比 SVG 更低级别的 API,绘制图形需要通过 JS 来操作。Canvas 提供了更大的灵活性,但复杂度也更高,理论上任何使用 SVG 绘制的图形,都可以通过 Canvas绘制出来。相反,由于 SVG 是比 Canvas 更高级别的 API,可以当作 HTML 元素去使用,也可以结合 JS、CSS 去操作,使用 SVG 创建一些复杂的图形会比使用 Canvas 更加简单。
交互性
SVG 位于 DOM 中,和普通 DOM 元素一样支持响应事件。Canvas 也可以响应交互事件,但需要额外的代码去实现。
性能
Canvas 和 SVG 性能的影响因素主要有两个:绘制图形的数量、绘制图形的大小。
下图是微软 MSDN 上给的一个对比图。

Canvas 的性能受画布尺寸影响更大,而 SVG 的性能受图形元素个数影响更大。网络上的对于性能及使用相关的建议是:如果绘制图像面积大或者绘制元素数量小时,建议使用SVG,如果绘制图像面积小或者绘制元素数量较大时,则建议使用 Canvas。
总结
本文介绍了 Canvas 和 SVG 的一些基本概念和使用方式,在我们日常开发中,有时会碰到需要绘制图形的场景,对于 Canvas 和 SVG,分别有其适合的场景:
- 需要绘制的图像简单、交互性强或者是矢量图(例如图标),建议使用 SVG。
- 需要支持像素级别的操作,或者复杂的动画和交互(例如数据可视化、交互式游戏),建议使用Canvas。
大多数流程图组件库都是使用 Canvas 或 SVG 来绘制图形,流程图一般图形简单,节点数量不多,会有一些简单的交互,因而大多数流程图组件库都使用 SVG 来进行渲染,例如 ReactFlow、draw.io、mxGraph、X6、XFlow 等,都是使用 svg 来进行渲染。
链接
https://developer.mozilla.org/zh-CN/docs/Web/API/Canvas_API
https://developer.mozilla.org/zh-CN/docs/Web/SVG
最后
欢迎关注【袋鼠云数栈UED团队】~
袋鼠云数栈 UED 团队持续为广大开发者分享技术成果,相继参与开源了欢迎 star
- 大数据分布式任务调度系统——Taier
- 轻量级的 Web IDE UI 框架——Molecule
- 针对大数据领域的 SQL Parser 项目——dt-sql-parser
- 袋鼠云数栈前端团队代码评审工程实践文档——code-review-practices
- 一个速度更快、配置更灵活、使用更简单的模块打包器——ko
- 一个针对 antd 的组件测试工具库——ant-design-testing
流程图渲染方式:Canvas vs SVG的更多相关文章
- 前端绘图方式Canvas和SVG的区别
Canvas和SVG是html5中支持2种可视化技术,都是可以在画布上绘制图形和放入图片.下面来介绍和分析一下他们. 一.Canvas 和 SVG 简介 1.什么是Canvas? Canvas 是H5 ...
- canvas与svg特性和使用对比
什么是 Canvas? HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像. 画布是一个矩形区域,您可以控制其每一像素. canvas 拥有多种绘制路径.矩形.圆形.字符以 ...
- canvas与svg
canvas与svg都是用于在网页上绘制图形(位图). canvas是HTML5新出来的一个标签,用来定义一块画图的区域(canvas本身没有绘制能力),用JavaScript来画图,可以绘制路径.矩 ...
- Canvas 与 SVG
什么是SVG? 引用w3c的一段话就是: SVG 指可伸缩矢量图形 (Scalable Vector Graphics) SVG 用来定义用于网络的基于矢量的图形 SVG 使用 XML 格式定义图形 ...
- HTML5新特性——HTML 5 Canvas vs. SVG
Canvas 和 SVG 都允许您在浏览器中创建图形,但是它们在根本上是不同的. SVG SVG 是一种使用 XML 描述 2D 图形的语言. SVG 基于 XML,这意味着 SVG DOM 中的每个 ...
- 【HTML5】Canvas和SVG的区别
* SVG SVG 是一种使用 XML 描述 2D 图形的语言. SVG 基于 XML,这意味着 SVG DOM 中的每个元素都是可用的.您可以为某个元素附加 JavaScript 事件处理器. 在 ...
- Canvas 和 SVG 都允许您在浏览器中创建图形,但是它们在根本上是不同的
SVG SVG 是一种使用 XML 描述 2D 图形的语言. SVG 基于 XML,这意味着 SVG DOM 中的每个元素都是可用的.您可以为某个元素附加 JavaScript 事件处理器. 在 SV ...
- HTML5 Canvas、内联 SVG、Canvas vs. SVG
canvas 元素用于在网页上绘制图形. 什么是 Canvas? HTML5 的 canvas 元素使用 JavaScript 在网页上绘制图像. 画布是一个矩形区域,您可以控制其每一像素. canv ...
- 列表总结Canvas和SVG的区别
参考链接: 菜鸟教程 HTML5 内联SVG 经典面试题(讨论canvas与svg的区别) Canvas | SVG ---|--- 通过 JavaScript 来绘制 2D 图形|是一种使用 XML ...
- H5 Canvas vs. SVG
HTML 5 Canvas vs. SVG HTML5 SVG HTML5 地理定位 Canvas 和 SVG 都允许您在浏览器中创建图形,但是它们在根本上是不同的. SVG SVG 是一种使用 XM ...
随机推荐
- 关于sql server导出csv格式文件的身份证号乱码问题处理办法
1.使用SQL Server数据库经常会遇到导出大量数据的情况,例如导出40万条数据,虽然EXCL支持可以放入百万的数据,但是使用数据库复制,粘贴到EXCL表格时,数据库会提示溢出的情况,如下图所示: ...
- 远程控制软件 TeamViewer 的局限性和替代方案
TeamViewer 公司创建于2005年,总部位于德国,客户遍及全球,其中企业用户居多,其各方面性能都很不错,但价格却非常贵.针对个人用户,TeamViewer 提供免费版软件,但时不时会提示&qu ...
- NumPy 数组复制与视图详解
NumPy 数组的复制与视图 NumPy 数组的复制和视图是两种不同的方式来创建新数组,它们之间存在着重要的区别. 复制 复制 会创建一个包含原始数组相同元素的新数组,但这两个数组拥有独立的内存空间. ...
- 地理数据可视化的神奇组合:Python和Geopandas
本文分享自华为云社区<Python与Geopandas:地理数据可视化与分析指南>,作者:柠檬味拥抱. 地理数据可视化在许多领域都是至关重要的,无论是研究地理空间分布.城市规划.环境保护还 ...
- 【漏洞复现】用友NC-Cloud PMCloudDriveProjectStateServlet接口存在JNDI注入漏洞
阅读须知 花果山的技术文章仅供参考,此文所提供的信息只为网络安全人员对自己所负责的网站.服务器等(包括但不限于)进行检测或维护参考,未经授权请勿利用文章中的技术资料对任何计算机系统进行入侵操作.利用此 ...
- 模拟重装Kubernetes(k8s)集群:删除k8s集群然后重装
目录 一.系统环境 二.前言 三.重装Kubernetes集群 3.1 环境介绍 3.2 删除k8s所有节点(node) 3.3 kubeadm初始化 3.4 添加worker节点到k8s集群 3.5 ...
- Flyway简单迁移失败问题
因为我是初学者,所以问题 只是一些细节性问题,现在看看V开头创建数据库迁移时的错误 问题一: flyway 命名规则问题这个绝对的细节中的细节问题 问题一解决方案: 命名规则:一定要遵循flyway的 ...
- Android 13 - Media框架(18)- CodecBase
关注公众号免费阅读全文,进入音视频开发技术分享群! 从这一节开始我们会回到上层来看ACodec的实现,在这之前我们会先了解ACodec的基类CodecBase.CodecBase.h 中除了声明有自身 ...
- FFmpeg开发笔记(二十六)Linux环境安装ZLMediaKit实现视频推流
<FFmpeg开发实战:从零基础到短视频上线>一书在第10章介绍了轻量级流媒体服务器MediaMTX,通过该工具可以测试RTSP/RTMP等流媒体协议的推拉流.不过MediaMTX的功能 ...
- 【论文笔记】SegNet
[深度学习]总目录 SegNet是Cambridge提出旨在解决自动驾驶或者智能机器人的图像语义分割深度网络,开放源码,基于caffe框架.SegNet运用编码-解码结构和最大池化索引进行上采样,最主 ...