matlab利用字符作画(汉字绘画)

本文介绍了如何使用MATLAB通过字符密度制作汉字画,包括算法原理、实现过程、动图与视频的生成,并探讨了如何利用较少字符提高分辨率以及边缘线条在字符作画中的应用。
摘要由CSDN通过智能技术生成

1 算法实现原理

算法的原理比较简单,就是利用不同字符具有不同的密度,比如字符“一”具有较低的密度,字符“曩”具有较高的密度。对于白底黑字来说,密度越高意味着该点附近具有较高的灰度值,这样文字就与图像的灰度值建立了一一对应的关系。

所以算法主要包括两个部分,一个是建立一个字符库,包含字符与其密度之间的关系;一个是实现字符密度和图像灰度值之间转换的方法。

2 实现

这里我使用的字符库为常用汉字5000个,图像采用cameraman作为要转换的图像。
常用汉字5000个地址:https://my.oschina.net/u/2510243/blog/716652

在这里插入图片描述
之后将5000个汉字输出在一个文字框中,统计汉字图像中的黑色部分的比例。
具体代码如下,其中str5000为之间保存的5000汉字,strH_gray为每个字符的密度值:

%字符转密度值
clear
load('str5000.mat')
strH=str5000;

N=length(strH);
strH_gray=zeros(N,1);
figure('color',[1 1 1])%白色背景
%循环5000个字
for j=1:N

%绘制文字
text(0,0,strH(j),'FontUnits','normalized','FontSize',0.9,'verticalAlignment','bottom');
xlim([0,0.5]);ylim([0,0.5]);
set(gcf,'Position',[488 342 375 420]);
set(gca,'Position',[0.0 0.0 1 1]);
axis off

%获取文字图像信息
F = getframe;
strdata=F.cdata;
imshow(F.cdata)

%计算文字所占密度
strdata1=strdata(:,:,1);
strdata1=uint8((strdata1~=255));
n=length(strdata1(:));
s=sum(strdata1(:));
strH_gray(j)=s/n;

clf%清空图像
end

%最后保存文字密度表strH_gray

之后拥有文字表和对应的文字密度表后,就可以创建文字密度和图像灰度值之间一一对应的关系了。具体代码如下:

clear
load('str5000_gray.mat')%文字密度表
load('str5000.mat')%文字表

strH=[str5000];
strH_gray=[str5000_gray];


x=imread('camera.jpg');
x=imresize(x,[32,32]);
x=double(x);%uint8格式不能表示0-1小数
strid=zeros(size(x(:,:,1)));

%将RGB转换为灰度。可以用rgb2gray简化处理
im_gray = ((x(:,:,1)).^2.2 * 0.2973 + (x(:,:,2)).^2.2 * 0.6274 + (x(:,:,3)).^2.2 * 0.0753).^(1/2.2);
im_gray=1-im_gray/255;

%由于文字最大密度也不能到100%,而图像却可以很暗,所以需要对文字的密度值进行一定缩放
enlarge=max(im_gray(:))/max(strH_gray(:));

%对每一个像素点的灰度值与文字密度进行匹配,记录下对应的文字编码
for j=1:length(im_gray(:))
im_g_j=im_gray(j);
[~,minid]=min(abs(im_g_j-strH_gray*enlarge));
strid(j)=minid;
end

%按照行进行每行的文字输出
n=size(im_gray,1);
str_img=cell(1,n);
figure('color',[1 1 1])
for j=1:n
    str_img=strH(strid(j,:));
    %由于默认matlab中text的行间距太大,这里人为约束固定每一行位置
    text(0,1-1/n*j,str_img,'FontUnits','normalized','FontSize',1/n,...
        'verticalAlignment','bottom','HorizontalAlignment','center');
end
xlim([-1,1]);%将文字居中
axis off

%获取gcf
F = getframe;
strdata=F.cdata;
imwrite(strdata,'A.png');

64×64个字符下的字符画:
在这里插入图片描述
32×32个字符下的字符画:
在这里插入图片描述

3 动图和视频的实现

动图的实现原理与普通图片的实现原理没有区别,但是细节上需要对格式进行整合,需要对每一帧都输出成字符画。

这里用猫和老鼠的一个动图作为具体的实现展示。

在这里插入图片描述
将这幅动图转换为字符画的代码如下:

clear
%载入文字库
load('str5000_gray.mat')
load('str5000.mat')
strH=[str5000];
strH_gray=[str5000_gray];
%载入gif动图
[A,map]=imread('E:\期末复习\研一下\寒假\文字作图\tomandjerry.gif', 'gif','frame','all'); 
C=squeeze(A);

%把动图变为灰度图,并调整尺寸
sizeimgnew=[116,160];
DRGB=zeros([size(C,1),size(C,2),3]);
D_gray2=zeros([sizeimgnew,size(C,3)]);
for k=1:size(C,3)
    RGB=ind2rgb(C(:,:,k),map);
    D_gray=rgb2gray(RGB);
    DRGB(:,:,1)=D_gray;
    DRGB(:,:,2)=D_gray;
    DRGB(:,:,3)=D_gray;
    DRGB2=imresize(DRGB,sizeimgnew);
    D_gray2(:,:,k)=DRGB2(:,:,1);
end

D_gray2(D_gray2>1)=1;
D_gray2=1-D_gray2;


%按比例线性填充至01区间
D_gray2=(D_gray2-min(D_gray2(:)))/(max(D_gray2(:))-min(D_gray2(:)));
strH_gray=(strH_gray-min(strH_gray(:)))/(max(strH_gray(:))-min(strH_gray(:)));

%逐个扫描像素,转换为对应标号
strid=zeros(size(D_gray2));
for j=1:length(D_gray2(:))
im_g_j=D_gray2(j);
[~,minid]=min(abs(im_g_j-strH_gray));
strid(j)=minid;
end

%逐帧输出每一副图像
n=size(D_gray2,1);
figure('color',[1 1 1])
for k=1:size(D_gray2,3)
    
    for j=1:n
        %逐行扫描文字
        str_img=strH(strid(j,:,k));
        
        text(0,1-1/n*j,str_img,'FontUnits','normalized','FontSize',1/n,...
            'verticalAlignment','bottom','HorizontalAlignment','center');
    end
    xlim([-1,1]);
    axis off
    pause(0.1)
    
    F = getframe;
    strdata=F.cdata;
    [I,map] = rgb2ind(strdata,32); %将真彩色图像转化为索引图像
    if k==2
        imwrite(I,map,'A.gif','gif','Loopcount',inf,'DelayTime',0.05);
    elseif k>2&&k<=23
        imwrite(I,map,'A.gif','gif','WriteMode','append','DelayTime',0.05);%DelayTime:帧与帧之间的时间间隔
    end

    clf
end

输出的结果如下:
在这里插入图片描述

3 尝试更少的字符表现更高的分辨率

由于文字具有不同的形状,所以根据这个原理可以减少使用文字的量。比如说“品”字上半部分具有较低的密度,下半部分具有较高的密度。比如文字“缸”的左半边密度就要大于右半边密度。
在这里插入图片描述

这样,就需要调整一下之前的算法,第一步需要将文字划分为4个部分,保存每个部分的密度;第二步需要根据4个图像像素和同一文字的4个密度,进行向量匹配,根据图像找到最接近的文字向量。

第一部分的文字密度向量的算法如下:

%字符转密度值4向量版
clear
load('str5000.mat');strH=str5000;
N=length(strH);
strH_gray=zeros(N,4);
figure('color',[1 1 1])

for j=1:N
text(0,0,strH(j),'FontUnits','normalized','FontSize',0.9, 'verticalAlignment','bottom','HorizontalAlignment','center');
xlim([-0.5,0.5]);ylim([0,0.5]);
set(gcf,'Position',[488 342 373 419]);set(gca,'Position',[0.0 0.0 1 1]);
axis off

F = getframe;strdata=F.cdata;
imshow(F.cdata)

%计算灰度值
strdata1=strdata(:,:,1);
strdata1=uint8((strdata1~=255));%其实可以不转换格式
[m,n]=size(strdata1);
%四个不同的密度向量
mn=m*n/4;
s1=sum(sum(strdata1(1:m/2,1:n/2)));%左上
s2=sum(sum(strdata1(1:m/2,n/2+1:end)));%右上
s3=sum(sum(strdata1(m/2+1:end,1:n/2)));%左下
s4=sum(sum(strdata1(m/2+1:end,n/2+1:end)));%右下

strH_gray(j,1)=s1/mn;
strH_gray(j,2)=s2/mn;
strH_gray(j,3)=s3/mn;
strH_gray(j,4)=s4/mn;

clf
end

将图片转换为文字的代码如下:

clear
load('str5000_gray4.mat')%之前的strH_gray保存的结果
load('str5000.mat')

strH=[str5000];
strH_gray=[str5000_gray4];


x=imread('camera.jpg');
x=imresize(x,[64,64]);
x=double(x);
%转换为灰度值
im_gray = ((x(:,:,1)).^2.2 * 0.2973 + (x(:,:,2)).^2.2 * 0.6274 + (x(:,:,3)).^2.2 * 0.0753).^(1/2.2);
im_gray=1-im_gray/255;
%按比例线性填充至01区间
im_gray=(im_gray-min(im_gray(:)))/(max(im_gray(:))-min(im_gray(:)));
strH_gray=(strH_gray-min(strH_gray(:)))/(max(strH_gray(:))-min(strH_gray(:)));
%匹配
%转换成4角格式
im_gray4=im2im4corner(im_gray);
[m,n]=size(im_gray4(:,:,1));
strid=zeros([m,n]);
%进行匹配
for j=1:m*n
    [I,J] = ind2sub([m,n],j);
    im_g_j=reshape(im_gray4(I,J,:),1,4);
    cospdist=pdist2(strH_gray,im_g_j,'euclidean');%利用欧拉距离值匹配
    [~,minid]=min(cospdist);
    strid(j)=minid;
end

%输出text
n=size(im_gray4,1);
str_img=cell(1,n);
figure('color',[1 1 1])
for j=1:n
    str_img{j}=strH(strid(j,:));
    
    text(0,1-1/n*j,str_img{j},'FontUnits','normalized','FontSize',1/n,...
        'verticalAlignment','bottom','HorizontalAlignment','center');
end
xlim([-1,1]);
axis off

%获取gcf
F = getframe;
strdata=F.cdata;
imwrite(strdata,'A2.png');

%脚本后置函数
function A4=im2im4corner(A)
%用于将图片转换为和文字密度匹配的4角格式
[m,n]=size(A);
A4=zeros(floor((m-2)/2)+1,floor((n-2)/2)+1,4);
for j=1:2:m-1
    for k=1:2:n-1
        
        A4((j+1)/2,(k+1)/2,1)=A(j,k);%左上
        A4((j+1)/2,(k+1)/2,2)=A(j,k+1);%右上
        A4((j+1)/2,(k+1)/2,3)=A(j+1,k);%左下
        A4((j+1)/2,(k+1)/2,4)=A(j+1,k+1);%右下
    end
end
end

和之前64字符的进行对比,可以看到向量法(左图)对边缘的刻画过渡更加平滑,但是对于灰度值的描述就不如直接灰度法描述的好。比如4角向量的方式,衣服内部的文字几乎相同,灰度值信息丢失较多。
在这里插入图片描述
向量法的细节图,在不同灰度变化较大的交接处,大量使用逐、罐、必、趟、茂等密度不对称的字,来增加边缘处整体的平滑度。
在这里插入图片描述

4 尝试利用边缘线条实现字符作画

利用edge函数可以提取边缘,进而利用边缘的图像来作画。
例如下图:
在这里插入图片描述
这里代码和之前第3节的代码几乎相同,就是文字库由汉字更换成为特殊符号,边缘检测利用matlab自带的approxcanny方法。具体代码就不放上了。其中edge的使用方法如下:

im_gray = edge(x2,'approxcanny')

详情参见官方文档:https://www.mathworks.com/help/images/ref/edge.html

这里要注意特殊符号库的选取,要统一全角、半角,不能混着搭配,否则会出现文字排版混乱。比如下图,就是不小心用了半角的空格“ ”而不是全角的空格“ ”。当然如果混用的话也可以,但是估计要修改 text函数的x坐标,固定每个符号的中心位置。
在这里插入图片描述

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值