做单片机嵌入式开发,数组C语言形式的图片,后缀如.c、.h的文件,用python读取显示并还原保存成普通图片格式文件,各种网络都搜不到这样的转换程序

在做单片机嵌入式开发,程序用的图片文件一般不是普通的jpg、png、gif等常规图片文件,而是像.c、.h这样种形式的文件。为了还原源代码中的这种C语言形式的数组图像,特意写了一个python程序,用来显示和还原保存数组图片文件。

注意:C语言形式的数组图片数据存储类似这样:

C像素数组图片数据结构

jpg二进制转换成字符串形式的图片数组结构

不同软件在处理从图片转换成C数组的时候,存储数据的格式会有不同,要查看具体的C或H头文件。几乎所有的C数组数据只有2种:一种是未经压缩的图片位图数据,另一种是压缩后的数据

首先处理未压缩的位图数据:

常规的图片转C数组头文件软件如image2lcd,转换出的图片都是位图形式的数组。

image2lcd位图数据,常见的格式类型如bmp,就是图片中的每一个像素对应于数组中的一个或两个、3个字节。这里要区分一下图片的存储大小。

常见的几种图片数据存储类型:1位(bit)黑白图像、16位(bit)真彩色图像、32位(bit)彩色图像

要解码C数组结构的图片数据,需要了解图像数据的存储规则。放大图片,会发现图片是由一个一个彩色小方块构成的,这个小方块就是像素。那么这个小方块怎么用数字来表示呢?

一个8位的字节,可以表示的数据大小为0~255,  像素类似一个灯泡,需要亮的时候就打开开关,需要熄灭的时候就关闭开关,我们用用数字“1”表示打开,数字“0”表示关闭。为了方便存储,图片数据是以字节为单位的,一个字节有8位,每个位每个位都代表着一个灯泡开关的状态,开关有“开”和“关”两个状态,所以一个位也只有两种状态,“1”和“0”状态。

前面说了,“1”表示开关闭合,点亮灯泡,“0表示”开关打开,熄灭灯泡,那么一个8位的字节就可以控制8盏灯泡。8盏灯泡全部熄灭就用‘000000’表示,8盏灯泡全部点亮就用‘11111111’表示。换成十进制数就是0和255,要控制哪盏灯的点亮和熄灭,就相应的在哪位上填“1”或“0”。

如果不需要色彩,只用黑白来表示像素,那就比较简单,需要显示像素的地方直接用“1”表示,不需要显示的地方直接用“0”表示。最终图像C数组的形式类似0,0,0,0,0,1,1,1,1,1,1这样一大串含有0和1形式的二进制数据。

图片数据存储的最小单位是字节,所以直接在C数组里显示的不是0,0,0,1,1,1这样的二进制数字,那样就太浪费存储空间了,实际存储的是0xff、0x00这样的十六进制字节数据。

如果存储的是0xff,它表示的是8个像素点都打开,如果用“1”表示白色,“0”表示黑色,那么0xff这8个像素点就发白光,显示的就是白色。把一副图像所有的像素都用黑白来表示,1位(bit)表示一个像素,这样就获得了黑白图像的数据。这就是1bit黑白图像数据。

在用python等语言显示图像数据时,读取图像的数据不是按照一位一位来读取的,而是按照字节来读取的,一个像素是用一个字节来显示的。这样原本是显示8个像素的,现在只能显示1个像素。所以在读取1bit黑白图像数据时,需要将每一位扩充成8字节数据,这样就能正常显示了。

解码就很简单了,将字节中每位“1”或“0”换成“255”或“0”的字节表示就可以了,代码如下:

def show_1bits_img(data):
    RGB=[]
    for i in data:
        for j in range(7,-1,-1):
            b=eval(i)>>j
            if b&1 == 1:
                RGB.append(0) 
            elif b&0==0:
                RGB.append(255)
    return RGB    

那么彩色像素怎么表示呢?

黑白图像用一位就可表示一个像素,彩色图像就不行了,因为含有色彩信息,一位明显不够,按照数据存储的最小单位来,那么一个彩色像素用一个字节来表示是合情合理的。

色彩是由红(R)、绿(G)、蓝(B)三种基本颜色合成的,那么一个彩色像素就可以用一个(R,G,B)数组来表示。每一个分量都用一个字节表示,那么一个彩色像素就需要3个字节24位来表示,可表示 16,777,216种颜色。彩色有16位和、24位、32位等的表示。

实际上,平常用不到那么多种颜色,那么可以踢掉不常用的颜色,就能减少图像存储的数据。于是就有了16位真彩色图像存储格式

16位彩色图像存储有565和555两种方案,我们以565方案说明。565也就是红色(R)用5位来表示,绿色(G)用6位来表示,蓝色(B)用5位来表示,刚好是两个字节的大小。555就是RGB每个分量都用5位来表示。

对以565方案存储的数据解码,还是类似的方法,把5位数据扩充为8位,左移3位即可,6位数据左移2位即可。这里要注意,一个像素是用2个字节表示的,这2个字节在数据存储中有顺序的变化,一般采用小端表示,即低位在前,高位在后。16位像素数据0xffd8,在数组中存储为0xd8,0xff这样两个字节,解码的时候需要重新合成0xffd8,这样才能表示一个像素。代码如下:

def show_16bits_img(data,w,h):
    RGB=[]
    data1=[eval(data[2*i+1])<<8|eval(data[2*i]) for i in range(w*h)]
    for i in data1:
        R=((i&0xf800)>>11)<<3
        RGB.append(R)
        G=((i&0x7e0)>>5)<<2
        RGB.append(G)
        B=(i&0x1f)<<3
        RGB.append(B)
    return RGB

24位真彩色图像数据解码就很简单了,每个像素分量是用一个字节表示的,按照RGB的顺序依次读取3个字节,并表示成一个像素,然后显示即可。代码如下:

def show_24bitsBGR_img(data):
    RGB=[]
    for i in range(len(data)//3):
        B=data[3*i]
        G=data[3*i+1]
        R=data[3*i+2]
        RGB.append(eval(R))
        RGB.append(eval(G))
        RGB.append(eval(B))
    return RGB

以上针对的是位图数据的数组图像,没有对像素值进行压缩处理。接下来我们直接处理压缩过的图像数据。

常见的图片格式jpg、png、gif等格式文件其实就是一种压缩过的像素数据。单片机等嵌入式开发中、二进制数据的图片直接用字符串表示,于是得到了c数组或h数组,这样的C数组图像就是原来的jpg、png等格式的二进制字符串表示。

解码很方便,将字符串图片数组还原成二进制数据,然后调用jpg、png等图片的解码库解码就得到了解码后的数据,然后用matplotlib显示就可。

这里使用openCV来解码,调用cv2.imdecode(imageArray, cv2.IMREAD_COLOR)函数来得到解码后的图像数据。

注意:用解码用位图表示的C数组图像时,需要获得图像的宽高,这些数据通常在数组的第一行,也可能是在/*...*/这样的注释里,源代码案例中宽高等数据都在注释里,具体如下:

第一个字节0x00表示扫描方式:自左向右;

第二个自己0x18表示图像存储的数据位数:0x18换成十进制是24位,也就是图像数据是24位存储的;

第三、四个字节两个字节表示图像的宽:0x28,0x00,表示0x28,换成十进制是28,宽就是28;

第五、六个字节两个字节表示图像的高:0x28,0x00,表示0x28,换成十进制是28,高就是28;

第七个字节表示图像是否是16位真彩色是否是565格式:0x00不是,16位图像下有效。

第八个字节是描述RGB颜色分量的排列顺序。

在源代码里,我已经添加了匹配注释里宽高的代码,在显示位图数据里不需要手动填入,另外制作C数组或H数组时,不要把图像宽高等数据放到数组中,这样反倒不能正确匹配到,显示数据的时候也麻烦。

def match_imgWH(pattern,one): 
    match=re.findall(pattern,str(one))
    print("匹配的行:",match)
    mlist=match[0].split(",")
    gray_code=eval(mlist[1])
    print("匹配的列表:",mlist)
    w=eval(mlist[3])+eval(mlist[2])
    h=eval(mlist[5])+eval(mlist[4])
    print("图像宽和高:",w,h)
    return gray_code,w,h

源代码里包含了完整的程序,把需要复显的C数组图片文件直接放到images文件夹内,保存的png格式图片在savedimages。以下是测试用例显示结果:

完整源代码如下所示,代码可识别.c和.h后缀的文件:

import os
import numpy as np
import cv2
import re
#import random
import matplotlib.pyplot as plt
#from PIL import Image
%matplotlib inline

image_list=[]
data_list=[]
jpeg_list=[]
jpeg_data=[]
path="./images"
savePath=path+"/savedImages"

def show_1bits_img(data):
    RGB=[]
    for i in data:
        for j in range(7,-1,-1):
            b=eval(i)>>j
            if b&1 == 1:
                RGB.append(0) 
            elif b&0==0:
                RGB.append(255)
    return RGB              
            
def show_16bits_img(data,w,h):
    RGB=[]
    data1=[eval(data[2*i+1])<<8|eval(data[2*i]) for i in range(w*h)]
    for i in data1:
        R=((i&0xf800)>>11)<<3
        RGB.append(R)
        G=((i&0x7e0)>>5)<<2
        RGB.append(G)
        B=(i&0x1f)<<3
        RGB.append(B)
    return RGB

def show_24bitsBGR_img(data):
    RGB=[]
    for i in range(len(data)//3):
        B=data[3*i]
        G=data[3*i+1]
        R=data[3*i+2]
        RGB.append(eval(R))
        RGB.append(eval(G))
        RGB.append(eval(B))
    return RGB

def match_imgWH(pattern,one): 
    match=re.findall(pattern,str(one))
    print("匹配的行:",match)
    mlist=match[0].split(",")
    gray_code=eval(mlist[1])
    print("匹配的列表:",mlist)
    w=eval(mlist[3])+eval(mlist[2])
    h=eval(mlist[5])+eval(mlist[4])
    print("图像宽和高:",w,h)
    return gray_code,w,h

def match_jpegIMG(pattern,jpegArray):
    match=re.findall(pattern,jpegArray)
    return match

def get_image_list(path):
    image_list=[]
    jpeg_list=[]
    check_folder_exists(path)
    for filename in os.listdir(path):
        if os.path.splitext(filename)[1] in ['.c','.C']:
            image_list.append(os.path.join(path,filename))
        elif os.path.splitext(filename)[1] in ['.h','.H']:
            jpeg_list.append(os.path.join(path,filename))
    print("C语言图片数组列表:",image_list)
    print("Hjpeg图片数组列表:",jpeg_list)
    return image_list,jpeg_list

def read_image_file(filename):
    img=[]
    with open(filename,'r') as f:
        file=f.readlines()
        oneLine=file[:1]   
        for line in file[1:]:
            for li in line.split(','):
                if li.strip()!="" and li.strip()!="};":
                    img.append(li.strip())
    print("图像总像素:",len(img))
    return oneLine,img

def read_jpeg_file(filename):
    with open(filename,'r') as f:
        file=f.read().replace('\n','')      
    return file

#根据图像色彩编码格式选则相应的显示解码函数
def convent_image_array(img,arg):
    RGB=[]
    gray_code=arg[0]
    w=arg[1]
    h=arg[2]
    if gray_code==1:
        RGB=show_1bits_img(img)
        data=np.array(RGB,dtype=np.uint8).reshape(h,w)
        
    elif gray_code==16:
        RGB=show_16bits_img(img,w,h)
        data=np.array(RGB,dtype=np.uint8).reshape(h,w,3)
        
    elif gray_code==24:
        RGB=show_24bitsBGR_img(img)
        data=np.array(RGB,dtype=np.uint8).reshape(h,w,3)
        
    return data

def plt_image_show(data,num,dataLea,imgName):
    plt.subplots_adjust(left=0.0,bottom=0.0,top=1,right=1,hspace=0.7,wspace=0.25)
    plt.subplot(dataLea//2,2,num)
    imgName=os.path.basename(imgName)
    plt.title(imgName)
    plt.axis('off')
    plt.imshow(data,cmap="gray")
    check_folder_exists(savePath)
    plt.imsave(savePath+"/{}.png".format(imgName),data,cmap='gray',format="png")

def check_folder_exists(folder_path):
    if not os.path.exists(folder_path):
        os.makedirs(folder_path)

def imshow_C_list(data_list,image_list):
    dataLea=len(data_list)
    for i in range(dataLea):
        plt_image_show(data_list[i],i+1,dataLea,image_list[i])
    plt.show()

def imshow_H_list(data_list,image_list):
    dataLea=len(data_list)
    for i in range(dataLea):
        plt_image_show(data_list[i],i+1,dataLea,image_list[i])
    plt.show()

    
image_list,jpeg_list=get_image_list(path) 

for imglist in image_list:  
    pattern="/\*.*?\*/"
    oneLine,img=read_image_file(imglist)
    gray_code,w,h=match_imgWH(pattern,oneLine)
    arg=[gray_code,w,h,imglist]
    print(arg)
    data=convent_image_array(img,arg)
    data_list.append(data)
    

for jpeglist in jpeg_list:
    pattern=r"{(.*)}"
    jpegfile=read_jpeg_file(jpeglist)
    match=match_jpegIMG(pattern,jpegfile)
    jpeg_img=[eval(i) for i in match]
    jpegimg=np.array(jpeg_img,dtype="uint8")
    image = cv2.imdecode(jpegimg, cv2.IMREAD_COLOR)
    print("图像(高,宽,位深)为:")
    print(np.array(image).shape)
    jpeg_data.append(image)

imshow_C_list(data_list,image_list)
imshow_H_list(jpeg_data,jpeg_list)






 

#cv2.imshow('URL2Image',image)
#cv2.waitKey() 
#plt.imshow(image)  

#plt.imshow(res,cmap="gray")
#kk=Image.fromarray(res)
#kk.show()
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值