逻辑上而言,每个”一“的感受都来源于其形态,那么我们不禁要问,各个”一“带来的不同感受与它们的形态有何关联呢?为了探索”形态-感受“的关联性,需要对每个”一“的客观造型进行描述,提取特征,这里就需要用到图像处理的技术了。有兴趣的同学可以尝试完成下列任务:
任务主题:
针对”一“,定义一系列特征(可借鉴参考资料),并依此对每个”一“进行测量。测量可以分两个层次:1.手工测量;2.用算法实现测量;
任务要求:
(1)至少定义5个特征,需要图文结合地清晰描述出这些特征的定义;(2)至少要能够手工方式对十二个”一“测定所有定义好的特征;(3)尽可能用算法实现测量,并对比手工测量的结果;
任务用图
程序设计:
特征定义:
根据论文当中给的提示,我们做了如下特征的定义:
(1)笔画平均宽度
笔画的平均宽度,指在图像字体主体中,每一列的黑色像素数求均值。平均宽度,可以反映书写字体的雄浑成都,是非常基础的特征值。
(2)笔力
由于书法家在书写时的力度是不断变化的。这种力度的变化在一定的范围之类,体现出了书法家的书写 习惯,笔力用每个点的笔划宽度与平均的笔划宽度的方差表示。
(3)走势
根据笔划骨架起始点坐标位置和结束点坐标位置,可以得到每个笔划的走势情况。我们也以这两点之间的斜率大小近似定义为其走势信息。
(4)起笔处夹角及藏、露峰情况
偶然间知道了在书法的起笔当中,有这样两种形式——藏锋、露锋,而这两种形式反映在起笔处就是尖锐的程度,越是尖锐就越有可能是露锋,相反钝化的起笔就是藏锋,因而用起笔处的夹角进行近似比较。
(5)重心
书法字符图像中包含着丰富的信息,宽扁的书法字符几何重心偏低,瘦高的书法字符几何重心偏高。书法骨架矩阵。从某种角度上讲,重心是书法基础外在形态的象征。
(6)弯曲程度
通过相邻两列的斜率比较,求出方差,方差越是大,就表明这个笔画的弯曲程度大(变化大),方差越小表明这个笔画较为平直,弯曲程度小。
(7)最宽处与最细处的比
通过计算出每一列的黑色像素大小,取出最大值、最小值,进行比较,如若比率越大,则表明这个字体写的越洒脱豪放(宽度越一致越是严谨)。
(8)骨架上下墨迹的分布比
书法家创作时总喜欢用到一些特定动作,如轻佻,按压等,这些细微的变化会使得笔压在纸上也分布不均匀,最终在宣纸上同一笔画墨迹渗透范围不同。书法笔画骨架将笔划轮廓分为S1,S2两部分,统计这两部分墨迹分部量可以粗略感受书法家的挥笔走势。
手动测量
因为我们之后要使用matlab实现,而这些从论文里摘出来的变量是很难用尺子测量的,我选取了一些简单的变量进行手动测量,为之后图像处理做准备:
matlab代码书写
(一)图像的读取
(1)基础图像读取
function [Filename,I]=OpenFile()
%全部先转换为灰度图像所以无需考虑RGB三通道
[Filename,pathname]=uigetfile({'*.png'},'Select Your Picture');
if isequal(Filename,0)
msgbox('You have not selected any picture.','Warning');
else
filepath=[pathname,'\',Filename];
I=imread(filepath);
I=rgb2gray(I);
end
end
(2)利用ASCII码批量读取A~L“一”的图片
因为要统计现有的十二个“一”的图片,获取每张一的大小,从而用中位数限定出一个对每张图片都相对合适的大小,所以用了取巧的办法,利用ASCII码解决了文件名称为字符的问题。
Storage1=zeros(2,12);%先把A-L图片读一遍,记录下大小,找出最普遍出现的行与列作为缩放配准的图片大小
for i=1:12
str=char(64+i);%利用ASCII码,实现挨个读取,A开始的ASCII码为65
str=[str,'.png'];
temp=imread(str);
temp=rgb2gray(temp);%变为灰度图像
[row,col]=size(temp);%求一下图像的大小
Storage1(1,i)=row;
Storage1(2,i)=col;%第一行为行数;第二行为列数
end
Row=round(median(Storage1(1,:)));
Col=round(median(Storage1(2,:)));
(二)图像预处理
(1)OSTU大津法阈值分割(这里在实验四当中也做过)
Otsu(大津法或最大类间方差法)使用的是聚类的思想,把图像的灰度数按灰度级分成2个部分(背景和主体),使得两个部分之间的灰度值差异最大,每个部分之间的灰度差异最小,通过方差的计算来寻找一个合适的灰度级别来划分。 所以可以在二值化的时候采用Otsu算法来自动选取阈值进行二值化。Otsu算法被认为是图像分割中阈值选取的最佳算法,计算简单,不受图像亮度和对比度的影响。因此,使类间方差最大的分割意味着错分概率最小。
Otsu法的主要计算思路是:
function [Image] = OTSU(I)
%利用大津法进行图像阈值分割
[m,n]=size(I);
I=double(I);
count=zeros(256,1);
pcount=zeros(256,1);
for i=1:m
for j=1:n
pixel=I(i,j);
count(pixel+1)=count(pixel+1)+1;
end
end
dw=0;
for i=0:255
pcount(i+1)=count(i+1)/(m*n);
dw=dw+i*pcount(i+1);
end
Th=0;
Thbest=0;
dfc=0;
dfcmax=0;
while(Th>=0 && Th<=255)
dp1=0;
dw1=0;
for i=0:Th
dp1=dp1+pcount(i+1);
dw1=dw1+i*pcount(i+1);
end
if dp1>0
dw1=dw1/dp1;
end
dp2=0;
dw2=0;
for i=Th+1:255
dp2=dp2+pcount(i+1);
dw2=dw2+i*pcount(i+1);
end
if dp2>0
dw2=dw2/dp2;
end
dfc=dp1*(dw1-dw)^2+dp2*(dw2-dw)^2;
if dfc>=dfcmax
dfcmax=dfc;
Thbest=Th;
end
Th=Th+1;
end
for i=1:m
for j=1:n
if I(i,j)>=Thbest
I(i,j)=255;
else
I(i,j)=0;
end
end
end
Image=I;
end
(2)形态学处理
1.开运算(先腐蚀再膨胀)
开运算可以有效的去除毛刺、平滑边缘(但我觉得这个在这里有点鸡肋,毕竟最后处理了的形态还是有点毛躁)。
2.闭运算(先膨胀再腐蚀)
闭运算可以有效的填补孔洞(这个是真的,但是如果空洞太大的话,闭运算也会无能为力)。
当然最关键的还是腐蚀和膨胀两个算法,
腐蚀算法:
function [Image] = Corrosion(I)
%二值图像的腐蚀
[m,n]=size(I);
Image=zeros(m,n);
for i=2:m-1
for j=2:n-1
Image(i,j)=max(max(I(i-1:i+1,j-1:j+1)));
end
end
for j=1:n
Image(1,j)=255;
Image(m,j)=255;
end
%填充边界
for i=1:m
Image(i,1)=255;
Image(i,n)=255;
end
Image=uint8(Image);
end
膨胀算法:
function [Image] = Dilate(I)
%二值图像的膨胀
[m,n]=size(I);
Image=zeros(m,n);
for i=2:m-1
for j=2:n-1
Image(i,j)=min(min(I(i-1:i+1,j-1:j+1)));
end
end
for j=1:n
Image(1,j)=255;
Image(m,j)=255;
end
%填充边界
for i=1:m
Image(i,1)=255;
Image(i,n)=255;
end
Image=uint8(Image);
end
3.细化运算(骨架的提取)
我一开始用的是OPTA,但说实话,真的没什么效果,可能是我的算法理解的有问题,写出来,就是对着字体在一个方向上可劲削,8邻域完全没有作用,后来回归到了返璞归真的细化算法上。
对于8邻域,我们给出这样的标记:
如果p1满足以下条件,那么p1的删除不会影响到留下来的形状的连通性:
首先是八邻域当中黑色点数在2~6之间,可以保证p1既不是端点、孤立点也不是内部点;再来是对10模式Z0的计算;然后是上左右不全为黑色点或上方点不连通,以及上右下不全为黑色点或右侧点不连通的判断。
这里值得一提的是Z0的计算是按照10模式来推进(原始文献里是01,但是因为我的前景色是黑色,我把它改成了10),只要8邻域当中的10模式(pipj=10)不超过1个,则可以保证删除p1之后的连通性。
主迭代部分:
function [skeleton] = Skeleton(I)
[m,n]=size(I);
I=double(I);
J=double(size(I));
sum=0;
for i=1:m
for j=1:n
sum=sum+I(i,j);
end
end
T=sum/(m*n);
for i=1:m
for j=1:n
if I(i,j)>T
J(i,j)=1;
else
J(i,j)=0;
end
end
end
[m,n] = size(J);
K=J;
modified = true; % 控制迭代的标记
while(modified)
modified = false;
for i=2:m-1
for j=2:n-1
if J(i,j)==0
if flag1(J,i,j)&&flag2(J,i,j)&&flag3(J,i,j)&&flag4(J,i,j)
K(i,j) = 1;
modified = true; %判断是否继续迭代
end
end
end
end
J=K;
end
skeleton=zeros(m,n);
for i=1:m
for j=1:n
if i==1||j==1||i==m||j==n
I(i,j)=255;
else
if K(i,j)==1
skeleton(i,j)=255;
else
skeleton(i,j)=0;
end
end
end
end
end
flag1、2、3、4分别按照给出的条件去计算即可。
(3)图像配准
我思前想后了很久,图片的大小,不是标准化图片大小的最好方法,要让主体大小在同一衡量标准下,才是合理的,所以采取了首先裁切出最小矩形,再对准基准类缩放,使主体字达到长度430(基准初始列为44,末尾列为473),然后再填充至标准画布容器大小。
通过之前核算的基准列,统一规定十二个”一“在配准时的大小均为364*504,先通过遍历,获取第一行、最后一行、第一列、最后一列出现黑色像素的点(即为最小矩形),放缩得到长度为430的主体部分,再将放缩后的矩形放置到基准列之间,画布中线上,即完成配准。
(四)实验成果展示
预处理配准后,显示预处理图、骨架图、边缘图,以及各变量数据:
保存到Excel表格中,Excel表格中的数据:
点击Statistics按钮显示出十二个”一“某一变量的柱状图:(可以对照Original Picture,进行数据分析)
A~L分别对应从1号到12号。
(五)客观形态与主观感受的关联探索
(1)与情感之间的联系
通过集体数据可以先举例分析,对于9号(即I),弯曲程度在所有一当中最高,其实弯曲程度从某种角度可以看作是运笔的跳脱程度,越是洒脱(越大)越是能说明心情的愉悦程度很高。
(2)与运动感之间的关系
再次举例分析,笔力象征笔锋的前进态势,在集体数据中,5号(E)的笔力最大,再对照E的形态图,发现确实如此,笔力越大,笔画的风格越豁达有英气,感觉像是速度极大的扫过一般。
除此以外,还有最宽处与最细处之比,也可以看出一个笔画的力量程度,比值越大,意味着书写者书写时使用的按挑顿的技巧越强,对腕力的要求自然更高,力量之感油然而生。(如10号)
(3)和物理感受之间的关系
起笔角度可以判断起笔处的尖锐程度,藏锋的笔画相对比较具有钝感,露锋的笔画相对比较具有尖锐感。但其实我认为物理感受更倾向于墨迹的观察方面,如果是看量化的值或是处理后的图像,会错失很多细节,比如粗糙程度,处理前可以看到留白或是毛边,但是经过预处理,这些细节都会流失掉。
关联总结:其实客观上的数据很多时候只是我们主观感受的一种量化存在,有的时候有的特征值只是一个量化概念,过于抽象,不比我们用主观感受描述传达出的效果好,但是客观形态可以帮我们总结出一些客观规律,比如达到多少数据,我们会倾向于产生这样或那样的主观感受,就比如笔力小于100时,我可能会觉得运笔是平缓的,但是超过200的话,我就会觉得他是遒劲有力的。
实验心得
这个实验让我们学习的专业课程发生交融,增进了我们处理问题的综合能力,好快乐,终于写完报告啦!