对一幅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。
enter description here

程序说明

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


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

posted @ 2019-05-26 00:55  启林O_o  阅读(521)  评论(0编辑  收藏  举报