对一幅BMP格式的灰度图像进行二元霍夫曼编码和译码
信息论的实验终于结束了,才开始写python,写的比较乱,仅供参考
主要思想
霍夫曼编码关键在于树的构造,其构造要达到的目的为权重越大越靠近叶子结点,权重越小越靠近根,即使出现次数越多的值的码长越短。
构造时每次去权重最小的两个点合并为一个点n,这两个点为点n的左右子结点,这两个点的权重的和为结点n的权重,然后重复上述操作直至剩下一个点。如:
1558795626981.drawio.html
52.85 KB
程序说明
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图像,所以示例如下)
得到的两个文件,一个是像素值与编码的对应关系,一个为图像的编码结果