这个系列项目代码实现:
https://github.com/liangwq/Chatglm_lora_multi-gpu/tree/main/APP_example
Lean to 矢量生成
可微分渲染文章:
平滑矢量图形综述:表示、创建、光栅化和图像矢量化的最新进展
无偏扭曲区域采样在可微分渲染中的应用
可微分矢量图形光栅化用于编辑和学习
图片逐层矢量
矢量图
类人笔触的模型风格化绘画
CLIPDraw:通过语言-图像编码器探索文本到绘图合成
根据参考风格进行矢量图绘制
基于语义感知的对象草图绘制
具有不同类型和抽象级别的场景素描
通过潜在扩散模型实现文本引导矢量草图合成
使用扩散模型进行文本引导的SVG生成
使用神经实现路径表示的文本到向量生成
矢量文字
前面部分回顾了下通过learn的方式来求解图片矢量。现在的learn方式更多是体现的给定光栅图、控制条件来求解矢量模型参数。目前还没有看到类似文本生成图片的生成大模型的,直接通过文本引导生成矢量图的大模型。
在接下来的系列会介绍上面论文的代码实践。先介绍论文主要算法实现,然后介绍代码的应用实例。下面以Diffvg作为这个系列的开场。
Diffvg+html矢量图生成
核心类实现
optimize_svg
这份代码是一个复杂的Python脚本,用于解析和优化SVG(可缩放矢量图形)文件。它包含多个类和方法,用于处理SVG文件的不同部分,如形状、颜色、变换和渐变。下面是对代码的主要部分的解释:
- 导入模块:
- 代码开始时导入了多个Python标准库和第三方库,如
json
,copy
,xml.etree.ElementTree
,torch
,numpy
,pydiffvg
等,这些库用于处理文件、数据复制、XML解析、机器学习、数值计算和图形渲染。
- SvgOptimizationSettings类:
- 这个类定义了SVG优化的默认参数,如颜色优化、透明度优化、变换优化等。它还包含一个字典
optims
,用于存储不同优化器的实现。
- OptimizableSvg类:
- 这个类是主要的SVG处理类,它包含了多个子类和方法,用于解析和优化SVG文件。它包括处理SVG节点、形状、变换、渐变等的子类和方法。
- SVG节点类:
- 包括
SvgNode
,GroupNode
,RootNode
,ShapeNode
,PathNode
,RectNode
,CircleNode
,EllipseNode
,PolygonNode
,GradientNode
等。这些类用于表示SVG文件中的不同类型的节点,如根节点、组节点、形状节点等。
- 优化器类:
- 包括
ColorOptimizer
,StopOptimizer
,GradientOptimizer
,TransformOptimizer
等。这些类用于优化SVG中的颜色、渐变停止点、渐变和变换。
- 解析和写入方法:
- 代码中包含了许多方法,用于解析SVG文件的不同部分(如形状、变换、渐变等),并将优化后的结果写回到XML格式。
- 优化和渲染:
- 代码提供了方法来优化SVG文件中的元素,并通过
pydiffvg
库渲染优化后的SVG图形。
解析SVG文件,还能够对其进行优化,并通过机器学习方法(如PyTorch)来调整SVG元素的属性,最终生成优化后的SVG图形。这对于需要处理和优化大量SVG文件的应用场景非常有用。
它定义了一个名为OptimizableSvg
的类,用于处理和优化SVG(Scalable Vector Graphics)图像。程序使用了PyTorch库来执行数值计算和优化。以下是代码的主要组成部分和功能的详细解释:
类定义和属性
SvgOptimizationSettings
:这个类用于存储SVG优化的设置,例如颜色优化、透明度优化、变换优化等。它包含了默认参数和优化器的映射。OptimizableSvg
:这是主要的类,用于表示和优化SVG图像。它包含了一系列方法来解析SVG文件、构建场景、执行优化步骤以及渲染图像。
核心功能
parseRoot(root)
:解析SVG文件的根元素,初始化画布尺寸、转换和外观。parseShape(shape, parent)
:根据SVG中的不同形状(如路径、圆形、矩形等),调用相应的解析函数。parseTransform(node)
:解析SVG节点中的变换属性。parseAppearance(node, defs, device)
:解析SVG节点的外观属性,如填充颜色、透明度等。build_scene()
:根据解析的SVG数据构建场景,准备进行渲染或优化。render(scale=None, seed=0)
:渲染场景,生成图像。step()
:执行优化步骤,更新SVG属性。
辅助功能
TransformTools
:一个嵌套类,提供了一系列静态方法来处理SVG变换,如解析矩阵、应用变换到点等。ColorOptimizer
、StopOptimizer
、GradientOptimizer
、TransformOptimizer
:这些嵌套类用于优化SVG的不同属性,如颜色、渐变停止点、渐变属性和变换。
SVG节点
SvgNode
:一个抽象基类,定义了SVG节点的基本结构和行为。GroupNode
、RootNode
、ShapeNode
、PathNode
、RectNode
、CircleNode
、EllipseNode
、PolygonNode
、GradientNode
:这些类继承自SvgNode
,代表SVG中的不同类型节点,并实现了具体的构建场景和写入XML的方法。
写入和报告
write_xml()
:将优化后的SVG节点写入XML格式。write_defs(root)
:写入SVG定义(如渐变、样式等)。reportSkippedAttribs(node, non_skipped=[])
:报告在解析过程中跳过的SVG属性。reportSkippedChildren(node, skipped)
:报告在解析过程中跳过的子节点。
其他
unit_dict
:一个字典,用于将SVG中的尺寸单位转换为像素。parseLength(s)
、parseOpacity(s)
、parse_color(s)
:这些静态方法用于解析SVG中的尺寸、透明度和颜色。
整体来看,这个程序提供了一个完整的框架来加载SVG文件,对其进行解析、优化和重新生成。它特别适用于需要对SVG图像进行数值优化的场景,例如在计算机图形学和机器学习应用中。
render_pytorch
代码是一个基于PyTorch框架的渲染库,使用了diffvg和pydiffvg库来实现矢量图的前向渲染和反向梯度传播。它主要用于生成和操作具有艺术效果的图像,例如绘画风格或者设计图案。下面是代码的主要功能和实现逻辑的详细解释:
主要功能
- 矢量图渲染:将矢量图形(如圆形、椭圆形、路径、多边形和矩形)渲染成位图图像。
- 多种输出类型:支持颜色图像和符号距离场(SDF)两种输出类型。
- 渐变填充:支持线性渐变和径向渐变作为填充颜色。
- 笔触宽度和样式:可以设置矢量图形的笔触宽度,并选择是否使用距离近似。
- 分组和混合规则:支持将多个形状分组,并使用非零规则或偶奇规则进行渲染。
- 前向渲染和梯度传播:能够进行前向渲染以生成图像,并计算梯度以进行反向传播,这对于训练生成模型或优化图形参数非常有用。
实现逻辑
- RenderFunction 类:这是一个PyTorch Function,它封装了与diffvg库交互的逻辑。
- serialize_scene 方法:将场景中的图形和形状分组序列化为一个参数列表,以便在PyTorch中使用。
- forward 方法:执行前向渲染,生成图像或SDF。
- render_grad 方法:在给定梯度图像的情况下,计算场景中各个元素的梯度。
- backward 方法:执行反向传播,计算图形参数的梯度。
- 场景构建:在forward和render_grad方法中,首先构建一个diffvg.Scene对象,该对象包含了渲染所需的所有信息,如图形、形状分组、滤镜等。
- 渲染过程:使用diffvg.render函数执行实际的渲染操作。这个函数处理了光线追踪、栅格化等底层细节。
- 参数序列化与反序列化:为了与PyTorch的自动微分系统兼容,需要将场景中的图形和参数序列化为一个扁平化的参数列表,并在前向和后向传递中进行反序列化。
- 梯度传播:在backward方法中,根据diffvg库提供的梯度信息,计算并返回每个图形参数的梯度。
代码结构
- 类和枚举定义:定义了OutputType枚举和RenderFunction类。
- 全局变量和函数:print_timing全局变量控制是否打印计时信息,set_print_timing函数用于设置这个变量。
- 形状和颜色处理:代码中处理了多种形状和颜色类型,包括圆形、椭圆形、路径、多边形、矩形以及线性渐变和径向渐变。
- 渲染和梯度计算:实现了前向渲染和梯度计算的逻辑,包括场景构建、渲染调用和梯度传播。
类和函数解释
set_print_timing
:一个函数,用于设置是否打印渲染过程中的时间信息。OutputType
:一个枚举类,定义了输出类型,可以是颜色图像或符号距离场(SDF)。RenderFunction
:一个类,实现了PyTorch的Function
接口,用于封装diffvg
库的渲染功能。
RenderFunction
类的主要方法:
serialize_scene
:序列化场景,将场景中的图形和颜色信息转换为可以由PyTorch处理的线性参数列表。forward
:前向渲染方法,根据提供的参数和场景设置,执行渲染操作,生成图像。render_grad
:梯度渲染方法,计算给定图像梯度的翻译梯度。backward
:后向渲染方法,计算场景参数对图像的梯度。
逻辑流程
- 序列化场景:在
serialize_scene
中,将场景的图形和颜色信息转换为一系列参数,这些参数随后被用于渲染过程。 - 前向渲染:在
forward
方法中,首先构造一个diffvg
场景对象,然后调用diffvg.render
函数执行实际的渲染操作。如果有背景图像,它会被合并到输出图像中。 - 梯度渲染:在
render_grad
方法中,计算给定梯度图像的平移梯度,这有助于神经网络训练中的场景优化。 - 后向渲染:在
backward
方法中,计算图像对场景参数的梯度,这对于自动微分和优化场景参数至关重要。
实现细节
- 使用
IntEnum
定义输出类型,提高代码的可读性。 - 通过
RenderFunction
类封装diffvg
库的功能,使其与PyTorch的自动微分系统兼容。 - 通过
forward
和backward
方法,实现了自定义的PyTorch函数,可以自动计算梯度。 - 代码中包含了大量的断言(
assert
语句),以确保输入数据的正确性。
用于在PyTorch框架中进行矢量图形的渲染和自动微分,可以用于图形设计、计算机视觉和机器学习等领域。
代码通过以下步骤实现图形渲染:
- 场景序列化 (
serialize_scene
函数):
- 这个函数将场景中的所有元素(如形状、形状组、颜色、变换等)转换为可以在PyTorch中处理的格式。它将每个元素的参数(如位置、大小、颜色等)转换为张量,并将它们存储在一个列表中,以便在渲染时使用。
- 前向传播 (
RenderFunction
的forward
方法):
- 在这个方法中,首先从输入参数中解包出场景的序列化参数。
- 然后,根据这些参数创建一个
diffvg.Scene
对象,这个对象包含了渲染所需的所有信息。 - 接着,根据输出类型(颜色或SDF)创建一个空的渲染图像张量。
- 如果提供了背景图像,将其转换为PyTorch张量并确保其具有正确的形状和设备。
- 使用
diffvg.render
函数执行实际的渲染操作,这个函数会根据场景和渲染设置生成图像。 - 渲染完成后,将渲染图像和场景信息存储在上下文中,以便在反向传播时使用。
- 渲染细节:
diffvg.render
函数是渲染的核心,它使用GPU加速来计算图像。这个函数会根据场景中的形状和颜色信息,以及渲染设置(如分辨率、采样数等),计算每个像素的颜色或SDF值。- 渲染过程中,会考虑到形状的遮挡关系、颜色渐变、光照效果等,以生成最终的图像。
- 输出:
- 渲染完成后,返回的渲染图像是一个PyTorch张量,其形状取决于输出类型。如果是颜色图像,形状通常是
(height, width, 4)
,表示每个像素的RGBA值;如果是SDF图像,形状可能是(height, width, 1)
或(num_positions, 1)
,取决于是否指定了评估位置。
通过结合PyTorch的自动求导能力和diffvg库的图形渲染功能,实现了高效的图形渲染和梯度计算。这使得它非常适合用于需要通过梯度下降来优化图形参数的应用,例如生成对抗网络(GANs)中的图像生成任务。
DiffVG图渲染例子
光栅图转矢量SVG
编译&安装Diffvg
git clone https://github.com/BachiLi/diffvg.git
cd diffvg
git submodule update --init --recursive
conda install -y -c anaconda cmake
conda install -y -c conda-forge ffmpeg
pip install svgwrite svgpathtools cssutils torch-tools
python setup.py install
光栅图转SVG
#1. 上传一张要处理的图片
#2. 设定需要多少条曲线拟合这幅图、曲线的宽度、用什么loss计算迭代生成图和实际图差异
#3. 把参数设置好执行以下脚本
python painterly_rendering.py imgs/fallingwater.jpg --num_paths 2048 --max_width 4.0 --use_lpips_loss
SVG动画可视化
#1. 把上面生成的svg文件拿出
#2. 用html脚本对生成svg做动画播发可视化
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>svg path animation</title>
<style type="text/css">
.svgContent {
width: 100%;
max-width: 960px;
margin: 0 auto;
padding: 0 20px;
}
</style>
</head>
<body>
<div class="svgContent">
<svg xmlns="http://www.w3.org/2000/svg" version="1.1" width="1344" height="768"></svg>
<svg xmlns="http://www.w3.org/2000/svg" id="svg" class="svg" viewBox="0 0 1344 768" preserveAspectRatio="xMinYMin meet">
<path d="M 199.27186584472656 -1.17560875415802 C 200.66622924804688 126.04058837890625 193.59605407714844 133.11273193359375 189.6529998779297 86.62046813964844 C 146.4644012451172 53.55277633666992 56.82194900512695 23.302818298339844 89.3664321899414 66.16450500488281" stroke-width="8.0" fill="none" stroke="rgb(0, 255, 255)" stroke-opacity="0.012530866" stroke-linecap="round" stroke-linejoin="round"/>
</svg>
</div>
<script src="../cobrasvg.js"></script>
<script src="https://unpkg.com/html2canvas"></script>
<script src="https://unpkg.com/@ffmpeg/ffmpeg"></script>
<script src="https://unpkg.com/@ffmpeg/core"></script>
<script>
/**
* cobrasvg
*
*
* 一个用来使用SVG的路径(path)来制作动画效果的插件
*
* Licensed under the MIT license.
* http://www.opensource.org/licenses/mit-license.php
*
* Copyright 2015,janily
*
*/
(function(window) {
'use strict';
/**
* 针对不支持SVG的浏览器的检测,如果不支持svg的话,会在html元素上添加一个noSvg的class,反之则添加一个svg的class
* 可以使用这个class来做一些降级处理,如不支持svg的浏览器,则直接显示一张png或者是jpg图片
* 参考:http://stackoverflow.com/questions/654112/how-do-you-detect-support-for-vml-or-svg-in-a-browser
*/
cobrasvg.prototype._supportSvg = function() {
return !!document.createElementNS && !! document.createElementNS('http://www.w3.org/2000/svg', "svg").createSVGRect;
}
/* 探测浏览器种类
* 使用JavaScript来检测浏览器动画事件是否完成
* 参考http://davidwalsh.name/css-animation-callback
*/
function whichTransitionEvent() {
var t;
var el = document.createElement('fakeelement');
var transitions = {
'transition': 'transitionend',
'OTransition': 'oTransitionEnd',
'MozTransition': 'transitionend',
'WebkitTransition': 'webkitTransitionEnd'
}
for (t in transitions) {
if (el.style[t] !== undefined) {
return transitions[t];
}
}
}
var transitionEvent = whichTransitionEvent();
/**
* 扩展
*/
function extend(a, b) {
for (var key in b) {
if (b.hasOwnProperty(key)) {
a[key] = b[key];
}
}
return a;
}
/**
* 构造函数
*/
function cobrasvg(options) {
this.options = extend({}, this.options);
extend(this.options, options);
this._init();
}
/**
* 配置选项
* 指定要产生动画效果SVG元素的ID,默认为svg
* 是否开启填充动画效果
*/
cobrasvg.prototype.options = {
elementId: "svg", //指定要产生path动画效果的SVG元素的ID
fillPath: true //是否开启填充的动画效果
}
/**
* cobrasvg _init
* 初始化方法
*/
cobrasvg.prototype._init = function() {
if (!this._supportSvg()) {
document.documentElement.className = "noSvg";
} else {
document.documentElement.className = "svg";
}
this.svg = document.getElementById(this.options.elementId);
this.fillDraw = this.options.fillPath;
this.paths = this.svg.querySelectorAll("path");
this._initAnimation();
}
/**
* cobrasvg _initAnimation()
* 动画方法,主要是初始化一些属性的值,首先是获取path元素的长度;
* 然后设置path的透明度。
*/
cobrasvg.prototype._initAnimation = function() {
for (var i = 0; i < this.paths.length; i++) {
var path = this.paths[i];
var length = path.getTotalLength();
// 重置透明度
path.style.fillOpacity = 0;
path.style.strokeOpacity = 1;
// 重置transition
path.style.transition = path.style.transitionEvent = "none";
// 重置path的strokeDasharray和strokeDashoffset属性
path.style.strokeDasharray = length + " " + length;
path.style.strokeDashoffset = length;
path.getBoundingClientRect();
// 应用transition
path.style.transition = path.style.transitionEvent = "stroke-dashoffset 23s ease-in-out";
// 设置strokeDashoffset的值为0
path.style.strokeDashoffset = 0;
// 是否填充路径
if(this.fillDraw == true) {
this._fillPath(path);
}
}
}
/**
* cobrasvg _fillPath()
*
* 重置transition并设置路径的透明度
*
*/
cobrasvg.prototype._fillPath = function(path) {
path.addEventListener(transitionEvent, function() {
// 重置transition
path.style.transition = path.style.transitionEvent = "none";
path.style.transition = path.style.transitionEvent = "fill-opacity 3s ease-in-out, stroke-opacity 3s ease-in-out";
// 修改透明度
path.style.fillOpacity = 1;
path.style.strokeOpacity = 0;
});
}
/**
* 添加命名空间
*/
window.cobrasvg = cobrasvg;
})(window);
(function() {
var myAnimation = new cobrasvg({
elementId: "svg"
});
})();
</script>
</body>
</html>