首页 > 编程知识 正文

svg矢量图用什么编辑,位图变成矢量图

时间:2023-05-04 18:58:37 阅读:57426 作者:3819

使用matlab将位图转换为SVG矢量图0之前1算法的思路1.1图像1.2中值滤波器1.3中值滤波器1.4去除孤立像素1.5提取颜色1.6找到二进制图像中的所有连接体1.7提取的每个多边形的膨胀

惯例声明:本人没有相关的工程应用经验,只是单纯对相关算法感兴趣才写了这个博客。 所以如果有错误的话,欢迎在评论区指出。 我很感谢。 本文主要关注算法的实现,在实际应用等问题上本人没有任何经验,不再多说。

0前言

转换位图的方法有利用线条特征进行转换,也有利用颜色特征进行转换。 根据目的,各有优缺点。

线的特征变换主要根据线的运动,利用直线和贝塞尔曲线这样的线来拟合原始位图,多用于线框文字类的位图图像。 根据颜色特征关注位图的颜色分布,使用多边形和曲线包围的图形拟合由各色块构成的图像。

本文的主要算法是利用颜色特征进行变换。

算法的思路是1图像中值滤波、降噪。 2提取图像颜色,减少图形颜色。 3用图形分割不同颜色表示的图层,分割成多个独立的多边形。 4将这些多边形转换为矢量图的多边形,给出颜色。

1算法思路1.1读取图像,用matlab自带图像pepper

1.2中值滤波器去除噪声,去除细微的颜色分布,使得所产生的颜色可以表示整个图形。

因此,采用中值滤波器。 过滤效果如下。

1.3中值滤波器使用rgb2ind函数将图像颜色数量减少到指定数量。 现在生成x索引矩阵和RGB格式的映射图。 x的每个值表示颜色,具体颜色根据索引在map中。 然后,多边形的数量和颜色变为有限的值。

1.4去除孤立像素后,在1.3背景中可以看到干扰较多的形状的像素块。 例如,照片上的天鹅绒和照片下的天鹅绒。

这些像素块由于颜色介于两者之间,分布极为零散。

因此,为了减少多边形的数量,我们找到了这些孤立的像素,使其颜色与周围像素的颜色相等。

该方法为扫描每个像素,如果像素与周围八个像素中的小于两个相同,则该像素是孤立的并且应该与周围的像素同步。

去除的结果如下所示,可见幕布的颜色变为了几个完整的颜色。 水果中类似噪声的颜色也被去除了。

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格式,是因为使用了XML语言,可以直接用txt等文本软件读取,matlab的编程写入很容易。

基本语法如下。

sv

g 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代码 clearclcclose 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=(X==(k-1));%索引对应的分别为0到(N-1),所以要减1 C_BW_K=bwconncomp(BW_k);%进行区域分割识别 Pix_Area=C_BW_K.PixelIdxList; N_Area=numel(Pix_Area); for m=1:N_Area Pix_Area_m=Pix_Area{m};%找到对应的区域 [Pix_Area_m_X,Pix_Area_m_Y]=ind2sub([IH,IW],Pix_Area_m);%转化为坐标 B_id=boundary(Pix_Area_m_X,Pix_Area_m_Y,0.9);%提取边缘,找出边界索引 BD_Area_m_X=Pix_Area_m_X(B_id);%边界的x坐标 BD_Area_m_Y=Pix_Area_m_Y(B_id);%边界的y坐标 %保存 temp=temp+1;%临时计数用 PolySave(temp).AreaX=BD_Area_m_X; PolySave(temp).AreaY=BD_Area_m_Y; PolySave(temp).Color=cmap(k,:); PolySave(temp).AreaSum=numel(Pix_Area_m);%计算面积 endend%按照面积排序N_Polygon=numel(PolySave);Area_Max2Min=zeros(N_Polygon,1);Area_List=zeros(N_Polygon,1);for k=1:N_Polygon Area_List(k)=PolySave(k).AreaSum;%提取出面积end[~,Area_Max2Min]=sort(Area_List,'descend');%将每个多边形向外扩展1个像素for k=1:N_Polygon if ~isempty(PolySave(k).AreaX) if Area_List(k)<9 R=0.5; elseif Area_List(k)<16 R=1; else R=1.5; end [xE,yE]=PolyExpand(PolySave(k).AreaX,PolySave(k).AreaY,R); xE(xE<1)=1;xE(xE>IH)=IH;%防止超出画布边界 yE(yE<1)=1;yE(yE>IW)=IW; PolySave(k).AreaX=xE;%保存 PolySave(k).AreaY=yE; endend%模拟利用多边形绘制矢量图(实际结果图)figure()set(gca,'YDir','reverse');xlim([1,IW]);ylim([1,IH]);axis equalhold onfor k=1:N_Polygon ID_k=Area_Max2Min(k); fill(PolySave(ID_k).AreaY,PolySave(ID_k).AreaX,PolySave(ID_k).Color,'EdgeColor','none')endhold off%% 3保存为svg文件%创建文件f_id=fopen(filename,'w');s=['<svg width="',num2str(IW),'" height="',num2str(IH),'">'];fprintf(f_id,'%s rn',s);%中间添加各个多边形for k=1:N_Polygon ID_k=Area_Max2Min(k); xSVG_k=PolySave(ID_k).AreaY; ySVG_k=PolySave(ID_k).AreaX; cSVG_k=PolySave(ID_k).Color; if numel(xSVG_k)>3 %坐标转换 str_sum=''; for m=1:numel(xSVG_k) str_1=[num2str(xSVG_k(m)),',',num2str(ySVG_k(m))]; str_sum=[str_sum,str_1,' ']; end str_1=[num2str(xSVG_k(1)),',',num2str(ySVG_k(1))]; str_sum=[str_sum,str_1]; %颜色转换 str_1=['rgb(',num2str(cSVG_k(1)*255),',',num2str(cSVG_k(2)*255),',',num2str(cSVG_k(3)*255),')']; s=['<polygon fill="',str_1,'" stroke="',str_1,'" stroke-width="1" points="',str_sum,'"/>']; fprintf(f_id,'%s rn',s); endend%结束s=['</svg>'];fprintf(f_id,'%s rn',s);fclose(f_id);%% 其它用到的函数function IM2=IM_RGB_medfilt(IM1,windows)%RGB图片的中值滤波IM_R=medfilt2(IM1(:,:,1),[windows,windows]);%5×5的中值滤波IM_G=medfilt2(IM1(:,:,2),[windows,windows]);IM_B=medfilt2(IM1(:,:,3),[windows,windows]);IM2=uint8(zeros(size(IM1)));IM2(:,:,1)=IM_R;IM2(:,:,2)=IM_G;IM2(:,:,3)=IM_B;endfunction X3=Del_1_Pix(X)%去除孤立的像素。如果该像素周围只有2个和它一样的像素的话,就认为它是孤立的%耗时较长%图像基本信息IH=size(X,1);%图像的高度IW=size(X,2);%图像的宽度IN=IH*IW;%图像的像素总数%X=double(X);不能double,浮点数从1索引,整数从0索引,不一样X2=NaN(IH+2,IW+2);%X2只用于检索X2(2:end-1,2:end-1)=X;X3=X;%X3只用于更改for k=1:IN [k1,k2]=ind2sub([IH,IW],k); k1=k1+1;k2=k2+1;%由于X2在周围加了一圈,所以索引值也要加1 %读取周边的9个数值,NaN为辅助值 X_Id9=X2(k1-1:k1+1,k2-1:k2+1); X_IdC=X_Id9(2,2);%中央的点读取 X_Id9(2,2)=NaN; %删除所有的inf X_Id9_2=X_Id9(:); [NaN_Id9,~]=find(isnan(X_Id9_2)); X_Id9_2(NaN_Id9,:)=[]; %判断中间值和周围值是否只存在一个相等的 if sum(X_Id9_2==X_IdC)<=2 %如果是,把这个孤立像素替换为周围的某个点 X3(k)=mode(X_Id9_2); endendendfunction [xE,yE]=PolyExpand(x,y,R)%将每个多边形向外扩展1个像素%将每个顶点,沿着相邻两个边 所合成的矢量方向,移动。如果落在图形内部,则反向%多边形不能自相交x=x(:);y=y(:);%判断是否收尾相交封闭if x(1)==x(end) && y(1)==y(end) x2=x; y2=y;else x2=[x;x(1)]; y2=[y;y(1)];end%把点按照指定方向进行排列Area_xy=trapz(x2,y2);if Area_xy>0 x2=flipud(x2); y2=flipud(y2);endN=numel(x2);%计算每个点与下一个点相连的向量Dx=diff(x2);Dy=diff(y2);%开始按方向逐点移动x3=[];y3=[];for k=1:N-1 %计算向量a和向量b if k==1 Da=[Dx(1),Dy(1)]; Db=[Dx(N-1),Dy(N-1)]; else Da=[Dx(k),Dy(k)]; Db=[Dx(k-1),Dy(k-1)]; end %归一化 Da=Da/norm(Da); Db=Db/norm(Db); %如果向量a和向量b差积大于0,则证明是个凹陷点 Dc=cross([Da,0],[Db,0]); Dab_Sin=Dc(3); %进行扩展判断 if Dab_Sin>0 DR=Da-Db;%如果是凹点 xy3=[x2(k),y2(k)]+R*DR; %xy3=[x2(k),y2(k)]+R*DR/abs(Dab_Sin);保形膨胀,但是实际效果反而不好 x3=[x3;xy3(1)]; y3=[y3,xy3(2)]; elseif Dab_Sin<0 DR=Db-Da;%如果是凸点 xy3=[x2(k),y2(k)]+R*DR; %xy3=[x2(k),y2(k)]+R*DR/abs(Dab_Sin);保形膨胀,但是实际效果反而不好 x3=[x3;xy3(1)]; y3=[y3,xy3(2)]; elseif Dc(3)==0 ;%直接跳过 endendxE=x3;yE=y3;end

版权声明:该文观点仅代表作者本人。处理文章:请发送邮件至 三1五14八八95#扣扣.com 举报,一经查实,本站将立刻删除。