Найти - Пользователи
Полная версия: Ошибка ввода данных_Реализация кодирования двоичного файла
Начало » Центр помощи » Ошибка ввода данных_Реализация кодирования двоичного файла
1 2
asai9493
Здравствуйте, мне нужно реализовать кодирование и декодирование аудио файла: аудио файл (1)- закодировать с помощью квантования(2) - сохранить в двоичный файл (3)- закодировать кодом Хафмана(4)-сохранить поток битов в двоичный файл (5)-декодировать ( код Хафмана) (6)….

Шаги с 1 по 3, я реализовала. И застряла на шаге 4, реализование encoder Хафмана ( не испольхуя встроенные функции ). Не могу сообразить, как мне прописать этот шаг. Как я могу закодировать двочный файл кодом Хафмана? Как упаковать поток битов?

Я преобразовала пример из интернета под свою задачу:

import heapq  
from collections import Counter
from collections import namedtuple

class Node(namedtuple("Node", ["left", "right"])):
def walk(self, code, acc):
self.left.walk(code, acc + "0")
self.right.walk(code, acc + "1")

class Leaf(namedtuple("Leaf", ["char"])):
def walk(self, code, acc):

code[self.char] = acc or "0"

def huffman_encode(s):
h = []
for ch, freq in Counter(s).items():
h.append((freq, len(h), Leaf(ch)))
heapq.heapify(h)
count = len(h)
while len(h) > 1:
freq1, _count1, left = heapq.heappop(h)
freq2, _count2, right = heapq.heappop(h)

heapq.heappush(h, (freq1 + freq2, count, Node(left, right)))

count += 1
code = {}
if h:
[(_freq, _count, root)] = h
root.walk(code, "")
return code


s = open('en8bit.bin','r')
code = huffman_encode(s)
encoded = "".join(code[ch] for ch in s)
b = open('56.bin', 'wb')
pickle.dump(encoded, b)
b.close()


При запуске выводит в строке:
code = huffman_encode(s)

Ошибку:
UnicodeDecodeError: ‘charmap’ codec can't decode byte 0x98 in position 1992: character maps to <undefined>


Как мне ее исправить?
Как я понимаю моя ошибка состоит в в том, что я не правильно “читаю” двоичный файл en8bit.bin.
Как мне “запоковать” свой поток битов ( bitstream), чтобы ввести их в код?
py.user.next
asai9493
Как я могу закодировать двочный файл кодом Хафмана?
Тут разбор кода Хаффмана
https://www.youtube.com/watch?v=ypRWVYnXK7A
Построение дерева для кода Хаффмана
https://www.youtube.com/watch?v=KNVPFVG49Oc

Вот двоичный файл представляется как набор байт. У каждого из байт этого файла есть своя частота, их надо посчитать. Потом для этих байтов по их частотам составляешь дерево. Потом по составленному дереву составляешь таблицу из пар (байт, код байта), где код байта - это его адрес (путь) в дереве. А потом просто транслируешь все байты через эту таблицу.

Смысл в том, что адрес байта в дереве короче, чем битовое представление этого байта в символьной таблице. Если байт обычно кодируется восемью битами, то в дереве у него адрес может быть из трёх бит. Это обычно к текстам относится, потому что в текстах разных байт мало, поэтому дерево получается очень невысокое. И из-за этого потом текст на мегабайт запросто сжимается до двухсот килобайт.
asai9493
py.user.next

если я открою свой двоичный файл в режиме ‘rb’ - read binary, получю байтовую строку?
py.user.next
asai9493
если я открою свой двоичный файл в режиме ‘rb’ - read binary, получю байтовую строку?
Бинарные файлы нужно открывать в бинарном режиме. Просто в текстовом режиме чтения/записи могут выполняться невидимые преобразования и данные могут искажаться без вреда для чтения человеком. А в бинарном режиме данные будут читаться/писаться точно, без каких-либо преобразований. Любой файл можно считать бинарным и текстовым одновременно, нет разделения в файловой системе на виды файлов. Отличаются только способы работы с данными внутри файла.
Открывай файл в ‘rb’ и читай порциями через .read(block_size), например fin.read(1024).
MrPage
Код конечно далек от идеала, но для примера сойдет.

 import struct
class Node:
    def __init__(self, value, priority):
        self.value = value
        self.priority = priority
        self.side = None
        self.parent = None
        self.linked_l = None
        self.linked_r = None
    def link(self, node: 'Node', side: int):
        node.parent = self
        if side == 0:
            self.linked_l = node
            node.side = 0
        else:
            self.linked_r = node
            node.side = 1
class Pq:
    def __init__(self):
        self.elements = []
    def push(self, node: Node):
        self.elements.append(node)
    def pop(self):
        min = None
        for i in range(len(self.elements)):
            e = self.elements[i]
            if min is None or e.priority < min.priority:
                min = e
        if min is not None:
            self.elements.remove(min)
        return min
class HuffmansCode:
    @staticmethod
    def build_tree(bytes):
        alpha = {}
        for b in bytes:
            if b in alpha:
                alpha[b] += 1
            else:
                alpha[b] = 1
        tree = Pq()
        for k in alpha:
            tree.push(Node(k, alpha[k]))
        while len(tree.elements) > 1:
            n1 = tree.pop()
            n2 = tree.pop()
            node = Node(None, n1.priority + n2.priority)
            node.link(n1, 0)
            node.link(n2, 1)
            tree.push(node)
        return tree
    @staticmethod
    def build_table(tree):
        def walk_tree(root_node: Node, out_table):
            if root_node.value is not None:
                out_table.append(root_node)
            if root_node.linked_l is not None:
                walk_tree(root_node.linked_l, out_table)
            if root_node.linked_r is not None:
                walk_tree(root_node.linked_r, out_table)
        def trace_path(node, out_table):
            if node.parent is not None:
                out_table.insert(0, node.side)
                trace_path(node.parent, out_table)
        nodes = []
        walk_tree(tree.elements[0], nodes)
        table = {}
        for node in nodes:
            t = []
            trace_path(node, t)
            table[node.value] = t
        return table
    @staticmethod
    def get_bit_str(table, bytes):
        bitstr = ''
        for byte in bytes:
            for k in table:
                if int(byte) == int(k):
                    for bit in table[k]:
                        bitstr += str(bit)
        return bitstr
    @staticmethod
    def bitstr_to_bytes(bitstr):
        def align(str):
            l = len(str)
            if l % 8 != 0:
                l2 = l + 1
                while l2 % 8 != 0:
                    l2 += 1
                return str + '0' * (l2 - l), l2 - l
            return str, 0
        bitstr, diff = align(bitstr)
        bytes = []
        for i in range(0, len(bitstr), 8):
            byte = 0x00
            for j in range(8):
                if bitstr[i + j] == '1':
                    byte |= (0x01 << j)
            bytes.append(byte)
        return bytes, diff
    @staticmethod
    def bytes_to_bitstr(bytes):
        bitstr = ''
        for byte in bytes:
            for i in range(8):
                bitstr += '1' if (byte >> i) & 0x01 == 1 else '0'
        return bitstr
    @staticmethod
    def decode_bitstring(tree, bitstr):
        data = []
        node = tree.elements[0]
        for bit in bitstr:
            node = node.linked_l if bit == '0' else node.linked_r
            if node.value is not None:
                data.append(node.value)
                node = tree.elements[0]
        return data
def read_bytes(file):
    bytes = None
    with open(file, 'rb') as file:
        bytes = file.read()
    return bytes
def save_file(file, bytes):
    with open(file, 'wb') as file:
        for b in bytes:
            file.write(struct.pack('B', b))
def encode_file(file, target):
    bytes = read_bytes(file)
    # Build tree
    tree = HuffmansCode.build_tree(bytes)
    # Build table
    table = HuffmansCode.build_table(tree)
    # Code data to bit string
    bitstr = HuffmansCode.get_bit_str(table, bytes)
    # code bit string to bytes
    encoded_bytes, diff = HuffmansCode.bitstr_to_bytes(bitstr)
    save_file(target, encoded_bytes)
    return bytes, tree, table, bitstr
def decode_file(tree, file):
    bytes = read_bytes(file)
    bitstr = HuffmansCode.bytes_to_bitstr(bytes)
    return HuffmansCode.decode_bitstring(tree, bitstr)
bytes, tree, table, bitstr = encode_file('data.bin', 'enc.bin')
print("Original bytes: ", [b for b in bytes])
print("Coded bitstring: ", bitstr)
decoded_data = decode_file(tree, 'enc.bin')
print("Decoded data", decoded_data)
asai9493
MrPage
Код конечно далек от идеала, но для примера сойдет.


спасибо Вам.
Не могли бы Вы уточнить, что описывает class pq?

class node описывает узел / листья дерева ?
MrPage
Не могли бы Вы уточнить, что описывает class pq?

Priority Queue. Реализация очереди с приоритетом.

class node описывает узел / листья дерева ?

Да, только вдобавок куча “отсебятины”)) Вообще если алгоритм у вас уже есть, лучше используйте его. А для конвертирования последовательности бит в массив байт и обратно можете использовать методы bitstr_to_bytes и bytes_to_bitstr, если как раз с этим была загвоздка.

Ну и методы read_bytes/save_file
asai9493
MrPage

я разбираю ваш код и у меня возникли вопросы.

1) фукнция построение дерева. Первый оператор for. я не совсем понимаю вашу запись.
Что описывает ваш элемент alpha? Им Вы читате строку?

2) Не могли бы Вы дать комментарии к вашим функциям раздела pq?
функция рор убирает элементы, а push -
asai9493
MrPage

я разбираю ваш код и у меня возникли вопросы.

1) фукнция построение дерева. Первый оператор for. я не совсем понимаю вашу запись.
Что описывает ваш элемент alpha? Им Вы читате строку?

2) Не могли бы Вы дать комментарии к вашим функциям раздела pq?
функция рор убирает элементы, а push -
MrPage
фукнция построение дерева. Первый оператор for. я не совсем понимаю вашу запись.
Что описывает ваш элемент alpha? Им Вы читате строку?

alpha, это типа алфавит) при загрузке файла, он считывает из него все уникальные байты, и записывает их в словарь alpha в виде:

((байт(значение)) = (количество его повторений в файле (частота)))

Не могли бы Вы дать комментарии к вашим функциям раздела pq?
функция рор убирает элементы, а push -

Функция push добавляет элемент вида (значение, приоритет) в список элементов
Функция pop ищет в списке элементов тот, у которого значение “приоритет” меньше всего, убирает его из списка и возвращает.
This is a "lo-fi" version of our main content. To view the full version with more information, formatting and images, please click here.
Powered by DjangoBB