目录
1 问题描述
受童晶老师的启发,童晶老师使用字符串实现了字符画的效果,文章链接如下。
第14章 定制字符画(《Python趣味创意编程》教学视频)
现在我们使用英文/中文字符实现字符画的效果。下面以使用字符“LOVE”画lena图像为例(中文字符画与英文字符画原理相同)。
2 技术路线
2.1 抖动
像素画基础:抖动详细介绍 - 知乎该文章指出:“图像抖动(Dithering)是通过混合2种不同颜色的像素方块,获得更多颜色的技巧。”
图像抖动技术通常应用于打印机、显示器和图形处理器等领域。在这些应用中,由于资源的限制,通常需要在有限的颜色深度下处理图像。图像抖动技术可以用较少的灰度实现多种灰度的效果。
本文中字符画中通过字符的字模在画布中画字符,核心技术是抖动,用黑白两种灰度画出了“LOVE”4种字符,即4种灰度。具体请参见2-3和2-4小节。
2.2 获取灰度值
灰度图,又称为灰阶图。任何颜色都是由红、绿、蓝三原色组成,而灰度图只有一个通道,灰度图有256个灰度等级。灰度图的灰度值范围是0~255,灰度值为0代表全黑,灰度值为255代表全白。
如图2-1,这是“lena”图像的灰度图。该图像大小为512*512,有512*512个像素,我们可以获取灰度图像中每一个像素的灰度值,并且根据灰度值去做接下来的工作。
获取原图的灰度图中灰度值方法:
使用openCV库中的cv2模块的imread()函数(函数的参数是图片路径和cv2.IMREAD_GRAYSCALE),它的作用是可以将彩色图像转换为灰度图像,并返回一个二维数组(二维数组名字定义为img)。这个二维数组img的行数和列数和图片的长宽对应,例如lena图像的长宽分别为512、512,那么这个二维数组行列分别为512、512。数组中的每个元素代表了这个灰度图中这个位置像素的灰度值,img[x][y]所代表的值就是灰度图中坐标为(x,y)像素的灰度值。
图2-1
2.3 字符的字模
字符以字模的形式表示,字模可以以像素矩阵的形式表示,即一个二维数组,其中这个像素矩阵在这里定义的大小为20*20,矩阵中的值有0和1,0代表黑色块,1代表白色块。例如我们要用“LOVE”四个字符去画lena图像的字符画。那么对于字符“L”的字模就可以是下面这样的,如图2-2。
这是一个20*20的二维数组,其中,把值为0的位置连接,可以呈现一个“L”形状。同样的原理,可以画出字符“OVE”,通过4个字模可以抖动出4个字符,也代表了4种不同的灰度。
图2-2
要得到这个字模,即像素矩阵可以有这两种方式,
1.自定义。可以自己初始化一个二维数组,在二维数组中定义每个位置的值,即0或1,用这样的方式来表示一个字符的字模。
2.用字体文件转换为字模。
在这次实验中使用的字体为Raginy。具体过程为,
1)使用PIL库中ImageFont模块的truetype函数,创建字体对象,函数的参数为字体文件路径,和字体大小;
2)使用Image模块创建图片对象;
3)使用ImageDraw模块的text()函数,将字符写在图片上,函数的参数是,字体对象、字符、写字符的起始位置、字符的颜色。
3)最后将图片转换为二维数组。这里得到的二维数组中的值包括True和False。当图片中一个像素不是组成字符的像素时,那么该像素对应在二维数组中元素的值为False,反之,如果图片中一个像素是组成字符的像素,那么该像素对应在二维数组中的元素值为True。最后将False转换为1,True转换为0,即可得到一个字模(像图2-2那样)。
2.4 根据灰度值画字符
得到了原图的灰度图的像素灰度值,以及字模,就可以根据灰度值向图像中画字符。
怎么确定某个灰度值画哪个字符?这里使用了两种方法。