对一幅BMP格式的灰度图像进行二 元Fano编码和译码
对一幅BMP格式的灰度图像进行二 元Fano编码和译码
才开始写python,写的比较乱,仅供参考,可参考我的另一篇文章对一幅BMP格式灰度图进行二元霍夫曼编码
主要思想
1、分组:
费诺编码构造树时设计对一组数进行分组,即将一组数分为两组,使这两组最接近平均。
这时需要用到背包算法的思想,我采取这样的方法,首先计算这组数的和为sum,对这一组数(这组数记为A)进行排序。每次取最小的一个数放入另一组B,直到放入一个数n时使B的和sum_b大于sum/2。这时进行判断,如果|sum_b-n-sum/2|>|sum_b-sum/2|则将n放入B,则分组完成,分为A与B。
2、树的构造:
不同于霍夫曼,树是从树根开始构造的。每次分为的两组,一组记为0,一组记为1,在分别对两组进行分组,如0组分组后,一组记为00,一组记为01。
程序说明
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 copy
import math
from PIL import Image
from collections import Counter
#定义树的结构
class BinaryTree:
def __init__(self,weight=0,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)#按权重从小到大排
#求结点列表的和的一个函数
def sum(L_ist):
sum_L=0
if len(L_ist)==0:
return 0
else:
for each in L_ist:
a=each.weight
sum_L=a+sum_L
return sum_L
#进行编码
bin_str=[]#可以看成有栈的意思
bin_str.append('')
d={}#最后的各像素及对应的编码保存于此
def encode(L):
sum_and = sum(L)
L1=copy.deepcopy(L)
L2=[]
while(1):
if (sum(L2)+L1[0].weight>sum_and/2)&(sum(L2)+L1[0].weight-sum_and/2 >= sum_and/2-sum(L2)):#此时分成了两个最优的组
break
else:
L2.append(L1.pop(0))
if len(L1)==1:
bin_str.append(bin_str[-1] + '0')
d[L1[0].name]=bin_str[-1]
bin_str.pop(-1)
else:
bin_str.append(bin_str[-1] + '0')
encode(L1)
if len(L2)==1:
bin_str.append(bin_str[-1] + '1')
d[L2[0].name]=bin_str[-1]
bin_str.pop(-1)
else:
bin_str.append(bin_str[-1]+ '1')
encode(L2)
bin_str.pop(-1)
return
#对各像素编码,并将得到的编码表写入fano文件
encode(node)
filehuff=open('fano.txt',mode='w')
filehuff.write(str(d))
#对图像进行编码,并将编码写入文件imcode.txt
im_code=[]
while len(im_list)!=0 :
im_code.append(d[im_list.pop(0)])
filecode=open('imcode2.txt',mode='w')
filecode.write(str(im_code))
print('编码成功!')
sum_l=[]#各像素码长
for i in range(0,256):
if i in d:
sum_l.append(len(d[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('fano.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('imcode2.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图像,所以示例如下)
得到的两个文件,一个是像素值与编码的对应关系,一个为图像的编码结果