利用matlab将位图转为SVG矢量图


惯例声明:本人没有相关的工程应用经验,只是纯粹对相关算法感兴趣才写此博客。所以如果有错误,欢迎在评论区指正,不胜感激。本文主要关注于算法的实现,对于实际应用等问题本人没有任何经验,所以也不再涉及。

0 前言

位图转矢量图的方法有很多,有的是利用线条特征转换,有的是利用颜色特征转换。根据目的来说,各有优缺点。
线条特征转换主要根据线条的走势,利用直线或贝塞尔曲线之类的线条拟合原位图,多用于线框文字类的位图图片。基于颜色特征的则关注位图的颜色分布,利用多边形或曲线围成的图形来拟合各个色块组成的图像。

本文主要算法为利用颜色特征进行转换。
算法思路为:1图片中值滤波,减少噪声。2将图片的颜色提取,并减少图形的颜色。3将不同颜色代表的图层进行图形分割,分割为若干个独立的多边形。4将这些多边形转换为矢量图的多边形,赋予颜色。

1 算法思路

1.1 读取图片

以matlab自带图片pepper
在这里插入图片描述

1.2 中值滤波

滤掉噪声,去除某些零星的颜色分布,使得生成的颜色能够代表整个图形。
因此,采用中值滤波。滤波效果如下:
在这里插入图片描述

1.3 中值滤波

利用rgb2ind函数,将图片的颜色数量减少到指定的数量。此时生成一个X索引矩阵,和RGB格式的map地图。X中每一个值代表一个颜色,具体颜色可以根据索引在map里找到。使得之后多边形的数量和颜色为一个有限值。
在这里插入图片描述

1.4 去除孤立的像素

可以看到1.3背景还有很多噪点形状的像素块,比如图片上方的绒布和图片下方的绒布。
这些像素块是由于颜色介于两者之间,导致分布极为零星破碎。

因此,为了减少多边形数量,我们找到这些孤立的像素,使得它的颜色等于周边像素的颜色。
方法为遍历每一个像素,如果该像素与周边8个像素中少于2个相同,则代表该像素比较孤立,应该同化为周围的像素。

去除之后的结果如下,可以看到幕布的的颜色变为了几个整颜色。水果上的某些噪点类似的颜色也被剔除。
在这里插入图片描述

1.5 提取单独的颜色

将1.4得到的新的X索引进行单独的颜色提取。以BW=(X==1)为例,此时提取出来了一个只有0和1的图像。
根据1.4的结果对比,可以看到是深红色的图形被提取了出来。

这也可以看做是图像分割。

在这里插入图片描述

1.6 找出二值图像中所有的连接体

将上面的图形区域分割,把每一个连通区域都找到,并进行边缘的记录。
当然,matlab有专门的函数来处理这个问题,分别为bwconncomp进行分割,boundary进行边缘的提取。

然而boundary提取边缘的时候,只能进行凸包提取,中间的孔洞和凹陷很容易忽略掉。

所以为了将这种影响减少到最小,我们把面积大的图形先绘制,面积小的图形后绘制。这样如果出现孔洞没有被算作边缘时,先画大图当背景,之后面积小的孔洞在后面绘制时,就会显示在这个大图图层的上方,不被影响。

之后boundary提取边缘得到的就是最终绘制矢量图所需的多边形了。

在这里插入图片描述

1.7 将提取出来的每个多边形膨胀

之后,按照多边形面积大小依次绘制多边形,并填充颜色。

可以发现,每个多边形之间会出现缝隙。这是因为在提取多边形的时候,是按照像素点中心处的坐标提取,和实际位图像素的边界相差约0.5个像素。相邻的多边形每个都相差0.5个像素,就会导致最终出现大约1个像素左右宽的缝隙。

如下图所示:
在这里插入图片描述
因此,需要对每一个多边形进行膨胀,在不改变图形的外形条件下,向四周均匀膨胀1个像素左右的距离。

原理采用基于多边形顶点来进行膨胀,如下图所示:
在这里插入图片描述
把图形言逆时针依次标号,蓝色向量为沿着标号方向,指向下一个点的向量。红色向量为上一个蓝色向量平移过来的向量,也就是说,红色向量2等由蓝色向量1平移得到,红色向量3等由蓝色向量2平移得到。

我们规定膨胀的方向为,向着红色向量方向走一步,再向着蓝色向量方向相反的方向走一步,如点4处的示意。

如果遇到图形凹陷,膨胀的方向与凸点相反。判断凹陷点和凸出点的方法为,计算红色向量与蓝色向量之间的夹角(逆时针),超过180°则为凹点。这里可以用叉乘来间接计算出sin值,来进行判断。

当然最终结果不能保证边缘与原图像平行。如果想要平行,需要在最终合成的向量那里除以sin值。但是后来发现实际效果并不好,它会把某些特别尖的尖点过于放大。

matlab中实际效果大致如下:
在这里插入图片描述

实际应用到图形中的效果如下:
在这里插入图片描述
可以看到基本做到了严丝合缝的效果。

1.8 输出为SVG格式

之所以选择SVG格式,是因为它利用XML语言,可以直接用txt等文本软件读取,方便matlab的编程写入。
基本语法为:

<svg width="500" height="500"> 
具体内容
</svg> 

这里用到的主要对象为多边形polygon,fill代表填充颜色,stroke代表线条颜色,stroke-width代表线条宽度,points代表组成多边形的点,x,y一组,首尾相连。

<polygon fill="rgb(186,157,45)" stroke="rgb(186,157,45)" stroke-width="1" points="326,220 326,222 328,222 328,220 326,220"/> 

2 最终结果展示

前面的例子中,利用matlab自带的图片尝试了svg转换。虽然不是卡通风格,但是也验证了程序的通用性。

这里采用https://getavataaars.com/网站随机头像生成器,作为另一个示例。这张相比于前面的照片案例,更适合转换为位图。
在这里插入图片描述
标准的png格式位图如上所示。
然而CSDN不支持SVG格式的图像,所以采用截图导出的方式展示SVG格式。
在这里插入图片描述
这里背景是黑色,是因为程序暂时还没有识别透明色的这个功能。所以把透明的背景默认为了黑色。

有些棱角、尖点有些失真,这是由于之前采用了中值滤波造成的。对于这种画风简单,色块清晰的图片,没必要采用中值滤波。

下图为不采用中值滤波处理后,生成的图片。可以看到原本的细节几乎都能够较好的保存下来。眼睛处的圆形有些不光滑,这是多边形拟合的缺陷。骷髅嘴那里有些内部锐角消失,这是由于boundary函数进行边缘提取的时候,难以识别凹陷所导致的。
在这里插入图片描述
总体来说,效果还是达到了理想中的效果。

3 完整的Matlab代码

clear
clc
close all
%把位图转换为矢量图

%% 1初始设置
%导入图片
IM_Origin=imread('peppers.png'); 

%导出名称
filename='peppers_hyh.svg';

%输出的颜色数量(约多约接近原图,不过一般卡通图本身颜色数量就不多,所以也没必要太多)
ColorNum=27;

%原始图片
figure()
imshow(IM_Origin)

%中值滤波(对于简单图案,可以不进行滤波)
IM_Origin=IM_RGB_medfilt(IM_Origin,5);%中值滤波,去除噪点

%% 2图像处理
%图像基本信息
IH=size(IM_Origin,1);%图像的高度
IW=size(IM_Origin,2);%图像的宽度
IN=IH*IW;%图像的像素总数

%图片颜色量化
%[X,cmap]=rgb2ind(IM_Origin,0.25,'nodither');
[X,cmap]=rgb2ind(IM_Origin,ColorNum,'nodither');

%去除掉周围只有2个像素的图像
X=Del_1_Pix(X);
X=Del_1_Pix(X);

%生成去除单独像素后的图像(预想结果图)
figure()
IM_Reduce2 = ind2rgb(X,cmap);
imshow(IM_Reduce2)

%对每一个颜色进行分割,保存轮廓与颜色信息
temp=0;
for k=1:size(cmap,1)
    BW_k=<
  • 18
    点赞
  • 40
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值