当前位置: 首页 > 知识库问答 >
问题:

Python图像压缩

申屠黎昕
2023-03-14

我正在使用Python的Pillow库来读取图像文件。如何使用哈夫曼编码进行压缩和解压缩?以下是说明:

您已经获得了一组示例图像,您的目标是在不丢失任何可感知信息的情况下尽可能压缩它们——解压后,它们应该看起来与原始图像相同。图像本质上存储为一系列颜色点,其中每个点表示为红色、绿色和蓝色(rgb)的组合。rgb值的每个分量范围在0-255之间,因此例如:(100, 0, 200)表示紫色。使用固定长度编码,rgb值的每个分量需要8位进行编码(28=256),这意味着整个rgb值需要24位进行编码。您可以使用像霍夫曼编码这样的压缩算法来减少更常见值所需的位数,从而减少编码图像所需的总位数。

# For my current code I just read the image, get all the rgb and build the tree
from PIL import  Image
import sys, string
import copy

codes = {}
def sortFreq(freqs):
    letters = freqs.keys()
    tuples = []
    for let in letters:
       tuples.append (freqs[let],let)
    tuples.sort()
    return tuples

def buildTree(tuples):
    while len (tuples) > 1:
        leastTwo = tuple (tuples[0:2])    # get the 2 to combine
        theRest  = tuples[2:]    # all the others
        combFreq = leastTwo[0][0] + leastTwo[1][0]    # the branch points freq
        tuples   = theRest + [(combFreq, leastTwo)]    # add branch point to the end
    tuples.sort()       # sort it into place
    return tuples[0]    # Return the single tree inside the list

def trimTree(tree):
    # Trim the freq counters off, leaving just the letters
    p = tree[1]    # ignore freq count in [0]
    if type (p) == type (""):
        return p    # if just a leaf, return it
    else:
        return (trimTree (p[0]), trimTree (p[1]) # trim left then right and recombine

def assignCodes(node, pat=''):
    global codes
    if type (node) == type (""):
        codes[node] = pat                # A leaf. Set its code
    else:
        assignCodes(node[0], pat+"0")    # Branch point. Do the left branch
        assignCodes(node[1], pat+"1")    # then do the right branch.

dictionary = {}
table = {}
image = Image.open('fall.bmp')
#image.show()
width, height = image.size
px = image.load()
totalpixel = width*height
print ("Total pixel: "+ str(totalpixel))

for x in range (width):
    for y in range (height):
        # print (px[x, y])
        for i in range (3):

            if dictionary.get(str(px[x, y][i])) is None:
                dictionary[str(px[x, y][i])] = 1
            else:
                dictionary[str(px[x, y][i])] = dictionary[str(px[x, y][i])] +1
table = copy.deepcopy(dictionary)
#combination = len(dictionary)
#for value in table:
#    table[value] = table[value] / (totalpixel * combination) * 100
#print(table)

print(dictionary)
sortdic = sortFreq(dictionary)
tree = buildTree(sortdic)
trim = trimTree(tree)
print(trim)
assignCodes(trim)
print(codes)

共有1个答案

郗福
2023-03-14

HuffmanCoding类将要压缩的文本文件的完整路径作为参数。(因为它的数据成员存储特定于输入文件的数据)。

函数的作用是:返回输出压缩文件的路径。

函数decompress()需要解压缩文件的路径。(从为压缩创建的同一个对象调用decompress(),以便从其数据成员获得代码映射)

import heapq
import os

class HeapNode:
    def __init__(self, char, freq):
        self.char = char
        self.freq = freq
        self.left = None
        self.right = None

    def __cmp__(self, other):
        if(other == None):
            return -1
        if(not isinstance(other, HeapNode)):
            return -1
        return self.freq > other.freq


class HuffmanCoding:
    def __init__(self, path):
        self.path = path
        self.heap = []
        self.codes = {}
        self.reverse_mapping = {}

    # functions for compression:

    def make_frequency_dict(self, text):
        frequency = {}
        for character in text:
            if not character in frequency:
                frequency[character] = 0
            frequency[character] += 1
        return frequency

    def make_heap(self, frequency):
        for key in frequency:
            node = HeapNode(key, frequency[key])
            heapq.heappush(self.heap, node)

    def merge_nodes(self):
        while(len(self.heap)>1):
            node1 = heapq.heappop(self.heap)
            node2 = heapq.heappop(self.heap)

            merged = HeapNode(None, node1.freq + node2.freq)
            merged.left = node1
            merged.right = node2

            heapq.heappush(self.heap, merged)


    def make_codes_helper(self, root, current_code):
        if(root == None):
            return

        if(root.char != None):
            self.codes[root.char] = current_code
            self.reverse_mapping[current_code] = root.char
            return

        self.make_codes_helper(root.left, current_code + "0")
        self.make_codes_helper(root.right, current_code + "1")


    def make_codes(self):
        root = heapq.heappop(self.heap)
        current_code = ""
        self.make_codes_helper(root, current_code)


    def get_encoded_text(self, text):
        encoded_text = ""
        for character in text:
            encoded_text += self.codes[character]
        return encoded_text


    def pad_encoded_text(self, encoded_text):
        extra_padding = 8 - len(encoded_text) % 8
        for i in range(extra_padding):
            encoded_text += "0"

        padded_info = "{0:08b}".format(extra_padding)
        encoded_text = padded_info + encoded_text
        return encoded_text


    def get_byte_array(self, padded_encoded_text):
        if(len(padded_encoded_text) % 8 != 0):
            print("Encoded text not padded properly")
            exit(0)

        b = bytearray()
        for i in range(0, len(padded_encoded_text), 8):
            byte = padded_encoded_text[i:i+8]
            b.append(int(byte, 2))
        return b


    def compress(self):
        filename, file_extension = os.path.splitext(self.path)
        output_path = filename + ".bin"

        with open(self.path, 'r+') as file, open(output_path, 'wb') as output:
            text = file.read()
            text = text.rstrip()

            frequency = self.make_frequency_dict(text)
            self.make_heap(frequency)
            self.merge_nodes()
            self.make_codes()

            encoded_text = self.get_encoded_text(text)
            padded_encoded_text = self.pad_encoded_text(encoded_text)

            b = self.get_byte_array(padded_encoded_text)
            output.write(bytes(b))

        print("Compressed")
        return output_path


    """ functions for decompression: """

    def remove_padding(self, padded_encoded_text):
        padded_info = padded_encoded_text[:8]
        extra_padding = int(padded_info, 2)

        padded_encoded_text = padded_encoded_text[8:] 
        encoded_text = padded_encoded_text[:-1*extra_padding]

        return encoded_text

    def decode_text(self, encoded_text):
        current_code = ""
        decoded_text = ""

        for bit in encoded_text:
            current_code += bit
            if(current_code in self.reverse_mapping):
                character = self.reverse_mapping[current_code]
                decoded_text += character
                current_code = ""

        return decoded_text


    def decompress(self, input_path):
        filename, file_extension = os.path.splitext(self.path)
        output_path = filename + "_decompressed" + ".txt"

        with open(input_path, 'rb') as file, open(output_path, 'w') as output:
            bit_string = ""

            byte = file.read(1)
            while(byte != ""):
                byte = ord(byte)
                bits = bin(byte)[2:].rjust(8, '0')
                bit_string += bits
                byte = file.read(1)

            encoded_text = self.remove_padding(bit_string)

            decompressed_text = self.decode_text(encoded_text)

            output.write(decompressed_text)

        print("Decompressed")
        return output_path

运行程序:将上述代码保存在文件huffman中。py。

创建示例文本文件。或者从sample.txt下载示例文件(右键单击,另存为)

将下面的代码保存在与上述代码相同的目录中,并运行此python代码(运行前编辑下面的路径变量。将其初始化为文本文件路径)

Usehufman。py公司

from huffman import HuffmanCoding

#input file path
path = "/home/ubuntu/Downloads/sample.txt"

h = HuffmanCoding(path)

output_path = h.compress()
h.decompress(output_path)
The compressed .bin file and the decompressed file are both saved in the same directory as of the input file.

结果在上面链接的示例文本文件上运行:

初始大小:715.3 kB压缩文件大小:394.0 kB Plus,解压后的文件与原始文件完全相同,没有任何数据丢失。

这就是霍夫曼编码实现的全部内容,包括压缩和解压缩。编码很有趣。

上述程序要求解压缩函数使用创建压缩文件的同一对象运行(因为代码映射存储在其数据成员中)。我们还可以使压缩和解压缩函数独立运行,如果以某种方式,在压缩期间我们将映射信息也存储在压缩文件中(在开始)。然后,在解压缩期间,我们将首先从文件中读取映射信息,然后使用该映射信息解压缩其余文件。

 类似资料:
  • 对于我的图像压缩,我使用枕头库获得rgb中的每个像素(例如:(100,0200)。使用哈夫曼编码,我已经将其转换为二进制,以减少比特数。现在,我必须将位序列保存到文本或二进制文件中。压缩文件始终比原始文件小,但目前,我的txt文件比原始文件大。我该怎么办?在那之后,我如何读取文件并解压缩它。以下是说明: 您的代码应该读取图像文件,计算固定长度编码需要多少位,然后应用压缩算法创建较小的编码-您需要实

  • 我一直在计算图像的未压缩和压缩文件大小。这对我来说总是导致压缩图像比我预期的未压缩图像小。如果图像包含大量不同的颜色,则存储调色板会占用大量空间,还需要更多位来存储每个代码。然而,我的问题是,压缩方法是否可能导致比未压缩的RGB图像更大的文件。对于这种压缩方法仍然有用的、总共包含k种不同颜色的最小正方形RGB图像,其大小(像素)是多少?因此,我们想要找到,对于给定的k值,找到最小整数n,对于该整数

  • 问题内容: 我正在通过wifi或移动网络通过网络发送图像,以将其存储在服务器中并再次检索。我已经这样做了,但是由于相机拍摄的图像太大,这使我的应用程序变慢,只是要指出我正在打开图库并从那里拍摄照片,而不是直接从应用程序拍摄照片。我注意到,从相机和图库中获取的来自whatsapp的图像已被压缩到大约50%。100kb。 目前,我的代码获取一个文件,并将其转换为字节,然后发送。这是获取文件并将其转换为

  • 我想上传图像到服务器。将我的图像转换为位图,但仍然出错。位图太大,无法上传到纹理中 如果我得到图片使用画廊意味着我得到错误的位图太大,不能上传到纹理 如果我得到图片使用相机意味着得到错误的 造成:java.lang.安全例外:权限拒绝:阅读<-plhd--1/>MediaProvider uri内容://media/外部/图像/媒体从pid=18253,uid=10257需要android.per

  • 问题内容: 我要求压缩文件大小小于500kb的所有已上传图像,我已经在google上搜索了,我只能看到: 如果我采用这种方法,则必须检查压缩后图像是否小于500kb,如果不是,则选择较低的质量和尺寸。 有更好的方法吗? 问题答案: JPEG压缩事先无法预测。您所描述的方法,压缩和测量并重试,是我知道的唯一方法。 您可以尝试压缩具有不同质量设置的许多典型图像,以了解最佳起点,以及猜测设置的更改将如何

  • 从pagespeed中,我只获得了图像链接和可能的优化(以字节为单位) 例如,我有大小为300kb的图像,对于该图像,pagespeed显示100kb 这只是一个图像,但我相信我会有很多图像压缩。所以,我如何通过传递字节或百分比作为参数或使用java中的任何其他计算(通过使用API或图像处理工具)来压缩图像,以便获得google建议的图像压缩版本。 提前谢谢。