对一幅BMP格式的灰度图像进行二元霍夫曼编码和译码
信息论的实验终于结束了,才开始写python,写的比较乱,仅供参考
主要思想
霍夫曼编码关键在于树的构造,其构造要达到的目的为权重越大越靠近叶子结点,权重越小越靠近根,即使出现次数越多的值的码长越短。
构造时每次去权重最小的两个点合并为一个点n,这两个点为点n的左右子结点,这两个点的权重的和为结点n的权重,然后重复上述操作直至剩下一个点。如:

程序说明
1、还原程序直接将图像大小写死了为256* 256
2、程序中编码效率的计算为信源熵除以平均码长
3、了解了python中的pillow库
Image.open(图像)打开图像
getpixel((x,y))得到图像x,y处像素值
Image.fromarry(数组,astype('uint8')).convert('L')将一个数组还原为图像,其中L表示灰度图
show()展示图像
save()保存图像
4、统计一个列表中各数值的个数
#需要导入from collections import Counter c = Counter() for num in list: c[num] = c[num] + 1 #list为一个列表,比如list=[11,11,22,22,22,33],则运行结果c={11:2,22:3,33:1}
5、np.arrry(矩阵)可以将矩阵变为数组
需要导入import numpy as np
程序还原时发现图像是镜像的,不知哪有问题,后来干脆把得到的二维数组转置一下,发现可以
转置有两种方法:
(1)T: 数组.T
(2)reshape:np.reshape(数组)
编码程序
import os import math from PIL import Image from collections import Counter #定义树的结构 class BinaryTree: def __init__(self,weight,name=None): self.name=name self.weight = weight self.leftChild = None self.rightChild = None #读取图像,将像素存入一个列表im_list image_name=input('请输入需要编码的图像') im = Image.open(image_name) width = im.width height = im.height im_list = [] for x in range(width): raw_list = [] for y in range(height): pixel = im.getpixel((x, y)) raw_list.append(pixel) im_list.extend(raw_list) sum=len(im_list)#计算像素个数总和 #统计各像素的的个数,将像素及对应次数存入字典c c = Counter() for num in im_list: c[num] = c[num] + 1 sum_v=[]#各像素的概率 H=0#信源熵 for i in range(0,256): if i in c: sum_v.append(c[i]/sum) H=H+c[i]/sum*math.log(sum/c[i],2) else: sum_v.append(0) #先将字典c中的各对元素转化为Binarytree类,即将各对元素变为结点,并存到Node列表中 node=[] for i in range(0,len(c)): temp_key=min(c,key=c.get) temp_num=c[temp_key] node.append(BinaryTree(temp_num,temp_key)) del c[temp_key] #对表内元素按照权重排序的函数 def sort_weight(elem): return elem.weight node.sort(key=sort_weight) #构造哈夫曼树 while len(node)!=1: small_1=node.pop(0) small_2=node.pop(0) newnode=BinaryTree(0) newnode.weight=small_1.weight+small_2.weight newnode.rightChild=small_1 newnode.leftChild=small_2 node.append(newnode) node.sort(key=sort_weight) tree=node.pop(0) #进行编码 huffcode={}#最后像素和所对应的的字典都存放在这里 bin_str=[]#想当于一个栈结构,用于存放编码 bin_str.append('') #迭代遍历函数 def coding (tree): Left(tree) Right(tree) bin_str.pop(-1) #左树遍历 def Left(tree): tree1=tree.leftChild bin_str.append(bin_str[-1] + '0') if tree1.name!=None: huffcode[tree1.name] = bin_str.pop(-1) return coding(tree1) #右树遍历 def Right(tree): tree2 = tree.rightChild bin_str.append(bin_str[-1] + '1') if tree2.name != None: huffcode[tree2.name]=bin_str.pop(-1) return coding(tree2) #对各像素编码,并将得到的编码表写入文件 coding(tree) filehuff=open('huffcode.txt',mode='w') filehuff.write(str(huffcode)) #对图像进行编码,并将编码写入文件imcode.txt im_code=[] while len(im_list)!=0 : im_code.append(huffcode[im_list.pop(0)]) filecode=open('imcode.txt',mode='w') filecode.write(str(im_code)) print('编码成功!') sum_l=[]#各像素码长 for i in range(0,256): if i in huffcode: sum_l.append(len(huffcode[i])) else: sum_l.append(0) L=0#平均码长 while(len(sum_l)!=0): L=sum_l.pop(0)*sum_v.pop(0)+L print('编码效率为') print(H/L)
还原程序
import os import re import numpy as np from PIL import Image #定义树的结构 class BinaryTree: def __init__(self,name=None): self.name=name self.leftChild = None self.rightChild = None #得到哈夫曼编码表,保存为一个列表 file=open('huffcode.txt') c={} for each_word in file: a=re.split(r'[\{\}\:\,\'\s+]+', each_word) a.pop(0) a.pop(-1) #构造哈夫曼编码的树 def encode(nodeser,tree_leave,strlen,str_len): if str[strlen] == '0': if str_len==1: nodeser.leftChild=tree_leave return elif nodeser.leftChild==None: newnode=BinaryTree() nodeser.leftChild =newnode encode(nodeser.leftChild,tree_leave,strlen+1,str_len-1) else: if str_len == 1: nodeser.rightChild = tree_leave return elif nodeser.rightChild == None: newnode = BinaryTree() nodeser.rightChild = newnode encode(nodeser.rightChild, tree_leave, strlen + 1, str_len - 1) return tree=BinaryTree() while len(a)!=0: name=a.pop(0) str=a.pop(0) tree_leave=BinaryTree(name) encode(tree,tree_leave,0,len(str)) #读取经过哈夫曼编码后的图片的文档 file2=open('imcode.txt') for each_word in file2: im=re.split(r'[\[\]\,\'\s+]+', each_word) im.pop(0) im.pop(-1) strcode='' #先转化为一串01串 while len(im)!=0: strcode=strcode+im.pop(0) tree_copy=tree reimg=[] #遍历树,得到相对应的像素值 for i in range(0,len(strcode)): if strcode[i]=='0': tree_copy=tree_copy.leftChild else: tree_copy=tree_copy.rightChild if tree_copy.name!=None: reimg.append(tree_copy.name) tree_copy=tree #变为二维列表存储 reimage=[] for i in range(0,256): reimage.append([]) for j in range(0,256): reimage[i].append(reimg.pop(0)) #变为数组形式,并转置 iii=np.array(reimage) iii=iii.T #变为图像 image=Image.fromarray(iii.astype('uint8')).convert('L') #展示并保存为dd.bmp image.show() image.save('dd.bmp')
运行结果
对如下图像编码(大小为256* 256,因为不能上传bmp图像,所以示例如下)
得到的两个文件,一个是像素值与编码的对应关系,一个为图像的编码结果