5.3 Python图像处理之图像编码-算术编码

5.3 Python图像处理之图像编码-算术编码

1 算法原理

算术编码在图像数据压缩标准(如JPEG,JBIG)中扮演了重要的角色。在算术编码中,消息用0到1之间的实数进行编码,算术编码用到两个基本的参数:符号的概率和它的编码间隔。信源符号的概率决定压缩编码的效率,也决定编码过程中信源符号的间隔,而这些间隔包含在0到1之间。编码过程中的间隔决定了符号压缩后的输出。算术编码器的编码过程可用下面的例子加以解释。

[例1] 假设信源符号为{00, 01, 10, 11},这些符号的概率分别为{ 0.1, 0.4, 0.2, 0.3 },根据这些概率可把间隔[0, 1)分成4个子间隔:[0, 0.1), [0.1, 0.5), [0.5, 0.7), [0.7, 1),其中 表示半开放间隔,即包含 不包含 。上面的信息可综合在表1中。

如果二进制消息序列的输入为:10 00 11 00 10 11 01。编码时首先输入的符号是10,找到它的编码范围是[0.5, 0.7)。由于消息中第二个符号00的编码范围是[0, 0.1),因此它的间隔就取[0.5, 0.7)的第一个十分之一作为新间隔[0.5, 0.52)。依此类推,编码第3个符号11时取新间隔为[0.514, 0.52),编码第4个符号00时,取新间隔为[0.514, 0.5146),… 。消息的编码输出可以是最后一个间隔中的任意数。。

详细原理:https://zhuanlan.zhihu.com/p/390684936

2 代码

运行代码说明

1.要改变代码中的图片名称和格式(地址不能有中文)

更改def amcode():主函数的

filename = ["image1","image3","img5"]
filetype = [".jpg",".jpg",".jpg"]

filename和filetype要一一对应

#!/usr/bin/env python
# -*- coding: utf-8 -*-
# author:mgboy time:2021/6/29
# 算术编码

import math
import os
from fnmatch import fnmatch
from datetime import datetime
import matplotlib.pyplot as plt
plt.rcParams['font.sans-serif'] = ['SimHei']
plt.rcParams['axes.unicode_minus'] = False

# 以二进制的方式读取文件,结果为字节
def fileload(filename):
    file_pth = os.path.dirname(__file__) + '/' + filename
    file_in = os.open(file_pth, os.O_BINARY | os.O_RDONLY)
    file_size = os.stat(file_in)[6]
    data = os.read(file_in, file_size)
    os.close(file_in)
    return data


# 计算文件中不同字节的频数和累积频数
def cal_pr(data):
    pro_dic = {}
    data_set = set(data)
    for i in data_set:
        pro_dic[i] = data.count(i)  # 统计频数
    sym_pro = []  # 频数列表
    accum_pro = []  # 累积频数列表
    keys = []  # 字节名列表
    accum_p = 0
    data_size = len(data)
    for k in sorted(pro_dic, key=pro_dic.__getitem__, reverse=True):
        sym_pro.append(pro_dic[k])
        keys.append(k)
    for i in sym_pro:
        accum_pro.append(accum_p)
        accum_p += i
    accum_pro.append(data_size)
    tmp = 0
    for k in sorted(pro_dic, key=pro_dic.__getitem__, reverse=True):
        pro_dic[k] = [pro_dic[k], accum_pro[tmp]]
        tmp += 1
    return pro_dic, keys, accum_pro


# 编码
def encode(data, pro_dic, data_size):
    C_up = 0
    A_up = A_down = C_down = 1
    for i in range(len(data)):
        C_up = C_up * data_size + A_up * pro_dic[data[i]][1]
        C_down = C_down * data_size
        A_up *= pro_dic[data[i]][0]
        A_down *= data_size
    L = math.ceil(len(data) * math.log2(data_size) - math.log2(A_up))  # 计算编码长度
    bin_C = dec2bin(C_up, C_down, L)
    amcode = bin_C[0:L]  # 生成编码
    return C_up, C_down, amcode


# 译码
def decode(C_up, C_down, pro_dic, keys, accum_pro, byte_num, data_size):
    byte_list = []
    for i in range(byte_num):
        k = binarysearch(accum_pro, C_up * data_size / C_down)  # 二分法搜索编码所在频数区间
        if k == len(accum_pro) - 1:
            k -= 1
        key = keys[k]
        byte_list.append(key)
        C_up = (C_up * data_size - C_down * pro_dic[key][1]) * data_size
        C_down = C_down * data_size * pro_dic[key][0]
    return byte_list  # 返回译码字节列表


# 二分法搜索
def binarysearch(pro_list, target):
    low = 0
    high = len(pro_list) - 1
    if pro_list[0] <= target <= pro_list[-1]:
        while high >= low:
            middle = int((high + low) / 2)
            if (pro_list[middle] < target) & (pro_list[middle + 1] < target):
                low = middle + 1
            elif (pro_list[middle] > target) & (pro_list[middle - 1] > target):
                high = middle - 1
            elif (pro_list[middle] < target) & (pro_list[middle + 1] > target):
                return middle
            elif (pro_list[middle] > target) & (pro_list[middle - 1] < target):
                return middle - 1
            elif (pro_list[middle] < target) & (pro_list[middle + 1] == target):
                return middle + 1
            elif (pro_list[middle] > target) & (pro_list[middle - 1] == target):
                return middle - 1
            elif pro_list[middle] == target:
                return middle
        return middle
    else:
        return False


# 整数二进制转十进制
def int_bin2dec(bins):
    dec = 0
    for i in range(len(bins)):
        dec += int(bins[i]) * 2 ** (len(bins) - i - 1)
    return dec


# 小数十进制转二进制
def dec2bin(x_up, x_down, L):
    bins = ""
    while ((x_up != x_down) & (len(bins) < L)):
        x_up *= 2
        if x_up > x_down:
            bins += "1"
            x_up -= x_down
        elif x_up < x_down:
            bins += "0"
        else:
            bins += "1"
    return bins


# 保存文件
def filesave(data_after, filename):
    file_pth = os.path.dirname(__file__) + '/' + filename
    # 保存译码文件
    if (fnmatch(filename, "*_am.*") == True):
        file_open = os.open(file_pth, os.O_WRONLY | os.O_CREAT | os.O_BINARY)
        os.write(file_open, data_after)
        os.close(file_open)
    # 保存编码文件
    else:
        byte_list = []
        byte_num = math.ceil(len(data_after) / 8)
        for i in range(byte_num):
            byte_list.append(int_bin2dec(data_after[8 * i:8 * (i + 1)]))
        file_open = os.open(file_pth, os.O_WRONLY | os.O_CREAT | os.O_BINARY)
        os.write(file_open, bytes(byte_list))
        os.close(file_open)
        return byte_num  # 返回字节数


# 计算编码效率
def code_efficiency(pro_dic, data_size, bit_num):
    entropy = 0
    # 计算熵
    for k in pro_dic.keys():
        entropy += (pro_dic[k][0] / data_size) * (math.log2(data_size) - math.log2(pro_dic[k][0]))
    # 计算平均码长
    ave_length = bit_num / data_size
    code_efficiency = entropy / ave_length
    # print("The code efficiency is %.3f%%" % (code_efficiency * 100))


# 主函数
def amcode():
    filename = ["image1","image3","img5"]
    filetype = [".jpg",".jpg",".jpg"]
    for i in range(len(filename)):
        print(60 * "-")
        print("加载文件:", filename[i] + filetype[i])
        t_begin = datetime.now()
        # 读取源文件
        data = fileload(filename[i] + filetype[i])
        data_size = len(data)
        print("计算字节的概率..")
        # 统计字节频数
        pro_dic, keys, accum_pro = cal_pr(data)
        amcode_ls = ""
        C_upls = []
        C_downls = []
        byte_num = 1000  # 每次编码的字节数
        integra = math.ceil(data_size / byte_num)  # 迭代次数
        # print("\n编码开始.")
        # 编码
        for k in range(integra):
            C_up, C_down, amcode = encode(data[byte_num * k: byte_num * (k + 1)], pro_dic, data_size)
            amcode_ls += amcode
            C_upls.append(C_up)
            C_downls.append(C_down)
        # 保存编码文件,返回编码总字节数
        codebyte_num = filesave(amcode_ls, filename[i] + '.am')
        t_end = datetime.now()
        print("编码完成.")
        print("保存编码文件为: " + filename[i] + '.am')
        print("压缩比(原图大小除以压缩后大小)  %.3f%%" % ((data_size / codebyte_num) * 100))  # 压缩比,原图大小除以压缩后大小
        code_efficiency(pro_dic, data_size, len(amcode_ls))  # 编码效率
        print()

        decodebyte_ls = []
        # print("解码开始.")
        # 译码
        for k in range(integra):
            if (k == integra - 1) & (data_size % byte_num != 0):
                decodebyte_ls += decode(C_upls[k], C_downls[k], pro_dic, keys, accum_pro, data_size % byte_num,
                                        data_size)
            else:
                decodebyte_ls += decode(C_upls[k], C_downls[k], pro_dic, keys, accum_pro, byte_num, data_size)
        # 保存译码文件
        filesave(bytes(decodebyte_ls), filename[i] + '_am' + filetype[i])
        # print("解码完成.")
        print("保存解压文件: " + filename[i] + '_am' + filetype[i])
        # 计算误码率
        errornum = 0
        for j in range(data_size):
            if data[j] != decodebyte_ls[j]:
                errornum += 1
        print("误码率: %.3f%%" % (errornum / data_size * 100))  # 误码率
        import cv2
        org_img = cv2.imread(filename[i] + '_am' + filetype[i], 1)
        dep_img = cv2.imread(filename[i] + '_am' + filetype[i], 1)
        plt.figure()

        plt.suptitle('算术编码')
        plt.subplot(1, 2, 1)
        plt.title('原图')
        plt.imshow(cv2.cvtColor(org_img, cv2.COLOR_BGR2RGB))

        plt.subplot(1, 2, 2)
        plt.title('解压后')
        plt.imshow(cv2.cvtColor(dep_img, cv2.COLOR_BGR2RGB))
        plt.show()

if __name__ == "__main__":
    amcode()

3 效果

image-20210727225554778

image-20210727225733158

image-20210727225656584

image-20210727225748921

  • 5
    点赞
  • 50
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值