对一幅BMP格式的灰度图像进行二元霍夫曼编码和译码

信息论的实验终于结束了,才开始写python,写的比较乱,仅供参考

主要思想

霍夫曼编码关键在于树的构造,其构造要达到的目的为权重越大越靠近叶子结点,权重越小越靠近根,即使出现次数越多的值的码长越短。
构造时每次去权重最小的两个点合并为一个点n,这两个点为点n的左右子结点,这两个点的权重的和为结点n的权重,然后重复上述操作直至剩下一个点。如:

 

python二元霍夫曼编码_数组


python二元霍夫曼编码_python二元霍夫曼编码_02

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图像,所以示例如下)

python二元霍夫曼编码_Image_03

得到的两个文件,一个是像素值与编码的对应关系,一个为图像的编码结果

python二元霍夫曼编码_Image_04


python二元霍夫曼编码_python二元霍夫曼编码_05


python二元霍夫曼编码_Image_06