引言
目前在做Arduino的知识学习,在使用OLED屏幕的时候为了实现精美的图片展示,需要将图像转化为位图数组的形式,网上的资源很多,可是自己想实现一下,本篇记录下探索的过程。
开发环境
Jupyter 永远的神,当然这只是编写代码的环境,具体使用Python3,涉及pillow(PIL)的模块。
准备素材
百度上找了一个图片(PS:如果侵权请及时联系我删除)
流程
我准备将该图片放入128*64分辨率的0.96屏幕,显然这个图片比例和尺寸都不合适,而且Oled使用的是二值化图像,所以整个流程分为:
1、裁剪尺寸;
导入图片,我的图片尺寸是640X480
img = Image.open('img.jpg') #读入图片
w,h=img.size # 查看图片尺寸
print(w,h) #打印图像尺寸,方便我们进行收东验证
裁剪图片,这里用的是128:64的宽高比例,当然可以自己自定义
w_h=128/64#宽:高
h_w=64/128#高:宽
w,h=img.size# 得到图片尺寸
if w>h*w_h:#宽比高值大,则保留高,裁剪宽
#我们准备从左上角开始裁剪,这里输入左上角和右下角坐标
#例如我是640X480图片,宽高比例128:64,因此640<480*128/64=960
#也就是说我们需要保留完整的宽度,而去对高度进行裁剪
new_img = img.crop((0, 0, h*w_h, h))
else:
#宽比高值小,则保留宽,裁剪高
new_img = img.crop((0, 0, w, h_w*w))
裁剪后的图像如下:
我们再进行等比例放缩
#进行图片缩放 这里用128*64的分辨率,即尺寸就是128X64
small_img=new_img.resize((128,64))
#保存一下,等下我们验证用的
small_img.save("small.jpg")
效果如下:
2、灰度处理
# 转化为黑白图片#进行灰度处理
black_img = small_img.convert("L")
效果如下:
3、二值化处理
获取图像的数据量,十进制的
#转换为像素数据 长度8192,128*64
bdata_list= list(black_img.getdata())
可以看到数据流的形式:
二值化,自己定义阈值(灰度界限)
# 接下来进行二值化
# 自定义灰度界限,大于这个值为黑色,小于这个值为白色,也可以翻过啦,实现背景色的反转
threshold = 128#阈值自己可以自定义
bvalue_list = [0 if i<threshold else 1 for i in bdata_list]
同样我们可以看到数据是这样的:
4、转化为位图数据也就是16进制数组
首先将数组进行分组,形成二进制字符串数组
#将8位一组放在一起
ob_list=[]
s="0b"
for i in range(1,len(bvalue_list)+1):
s+=str(bvalue_list[i-1])
if i%8==0:
ob_list.append(s)
s="0b"
我们可以看到数据是这样的:
最后将数据转化为16进制
#转换为16进制字符串 格式定位两位
ox_list=['0x'+'%02x' % int(i, 2) for i in ob_list]
#下面的代码 似乎不会保证固定的两位格式 比如0,会转化成0x0,而我们期望0x00
#ox_list=[hex(int(i, 2)) for i in ob_list]
我们16个一行输出
j=0
for i in ox_list:
j+=1
print(i,end=",")
if j%16==0:
print("")
处理之后的数据是这样的:
5、完整代码
from PIL import Image
img = Image.open('img.jpg')#读入图片
w,h=img.size# 查看图片尺寸
print(w,h)
#裁剪矩形,作为(左、上、右、下)元组,即左上角坐标和右下角坐标
w_h=128/64#宽:高
h_w=64/128#高:宽
#我们准备从左上角开始裁剪
w,h=img.size# 查看图片尺寸
if w>h*w_h:#宽比高值大,则保留高,裁剪宽
new_img = img.crop((0, 0, h*w_h, h))
else:#宽比高值小,则保留宽,裁剪高
new_img = img.crop((0, 0, w, h_w*w))
#####################################
small_img=new_img.resize((128,64))#进行图片缩放 这里用128*64的分辨率
small_img.save("small.jpg")
black_img = small_img.convert("L")# 转化为黑白图片#进行灰度处理
bdata_list= list(black_img.getdata())#转换为像素数据 长度8192
# 接下来进行二值化
# 自定义灰度界限,大于这个值为黑色,小于这个值为白色
threshold = 128
bvalue_list = [0 if i<threshold else 1 for i in bdata_list]
##############################################
#将8位一组放在一起
ob_list=[]
s="0b"
for i in range(1,len(bvalue_list)+1):
s+=str(bvalue_list[i-1])
if i%8==0:
ob_list.append(s)
s="0b"
#转换为16进制字符串 格式定位两位
ox_list=['0x'+'%02x' % int(i, 2) for i in ob_list]
#ox_list=[hex(int(i, 2)) for i in ob_list]
#########################################
j=0
for i in ox_list:
j+=1
print(i,end=",")
if j%16==0:
print("")
验证
这里我没有上板子验证,通过一个在线的图像转位图的网站进行对比,地址在这里
我们将裁剪的图像保存到本地
就是这行代码实现的保存,在上面的过程中我们已经保存了
small_img.save("small.jpg")
上传到网站看看效果
再看看生成的位图数据,这里选择一个字节,水平(一行一行读)存放:
上传我们的位图数据,进行图像生成看看效果,我们的数据也是水平保存的,白色背景,我们看看效果:
当如如果你选择 列读取会这样,图像乱了:
细节对比,上边是我们处理的,下边是网站处理的
总结
对比发现还是有些许差别的,这和我们灰度处理和处理的顺序都有关系,大家不妨自己尝试,寻找最佳的方案。如果有问题欢迎指出。