大学多媒体实验汇总(python)

霍夫曼编码(Huffman Coding)是一种广泛使用的无损数据压缩算法。它通过为数据中的每个符号分配一个变长的编码,使得频率高的符号使用较短的编码,而频率低的符号使用较长的编码,从而达到压缩数据的目的。霍夫曼编码的一般要求包括:

  1. 符号集合:首先需要确定数据流中的所有不同符号(例如,对于文本数据,符号可以是字符集)。
  2. 符号频率:计算每个符号在数据流中出现的次数,这些频率将决定霍夫曼编码树的构建。
  3. 优先队列:构建一个优先队列(通常是最小堆),以存储符号及其频率。
  4. 构建霍夫曼树:
    • 从优先队列中取出两个最小频率的节点。
    • 创建一个新的内部节点,其频率是两个取出节点频率的和。
    • 将这两个节点作为新内部节点的子节点,并将其重新加入优先队列。
  5. 生成编码:从根节点开始,向左子节点走分配“0”,向右子节点走分配“1”,直到达到叶节点(即符号)。此时,路径上的“0”和“1”序列构成了该符号的霍夫曼编码。
  6. 唯一性:确保所有符号的编码都是唯一的,以避免解码时的歧义。
  7. 前缀码:霍夫曼编码要求任何符号的编码都不是其他符号编码的前缀,这也称为前缀码(prefix code)性质。
  8. 编码数据:使用生成的霍夫曼编码替换原始数据中的每个符号。
  9. 存储编码表:为了能够解码,需要将编码表(即符号和其对应的霍夫曼编码)存储在压缩数据的前面或通过其他方式传达给解码器。
  10. 解码:解码过程与编码过程相反,它根据霍夫曼树或编码表将变长的编码转换回原始符号。
  11. 优化:在实际应用中,可能需要对霍夫曼树进行优化,以减少编码和解码的时间复杂度或压缩数据的大小。
  12. 自适应霍夫曼编码:在某些应用中,数据的统计特性可能随时间变化,因此会使用自适应霍夫曼编码,它在编码过程中动态更新霍夫曼树。

#初始化哈夫曼结点
class Huffmannode(object):
    def __init__(self):
        self.parent=0
        self.left=0
        self.right=0
        self.weight=0


#选择最小的结点下标
def select_node(huffman):
    #俩个结点直接返回不需要找最小俩个结点
    if len(huffman)==2:
        return 0,1
    min=semin=inf#初始化成无穷大
    f=s=-1
    for i in range(len(huffman)):
        if huffman[i].parent==0:
            if min>huffman[i].weight:
                semin=min
                s=f
                min=huffman[i].weight
                f=i
            elif semin>huffman[i].weight:
                semin=huffman[i].weight
                s=i
    return f,s


#编码
def Huffman_code(origin_dict):
    #给结点赋权重
    n=len(origin_dict)
    m=2*n-1
    huffman=[]
    for i in origin_dict:
        temp_huffmannode=Huffmannode()
        temp_huffmannode.weight=origin_dict[i]
        huffman.append(temp_huffmannode)
    # 构建Huffman树,选择俩个最小的结点合并
    for i in range(n,m):
        f , s=select_node(huffman)
        temp_huffmannode=Huffmannode()
        temp_huffmannode.weight = huffman[f].weight+huffman[s].weight
        temp_huffmannode.right = f  #小的放在右边
        temp_huffmannode.left = s
        huffman[f].parent = huffman[s].parent=i
        huffman.append(temp_huffmannode)

    #0,1编码,右1,左0
    codeing_dict = dict.fromkeys(origin_dict, None)
    for i in range(0,n):
        s=''
        k=i
        parent=huffman[i].parent
        while parent!=0:
            if huffman[parent].left==k:
                s+='0'
                k=parent
                parent=huffman[parent].parent
            else:
                s+='1'
                k=parent
                parent=huffman[parent].parent
        codeing_dict[list(origin_dict.keys())[i]]=list(reversed(s))
    for k in codeing_dict.items():
        codeing_dict[k[0]] = ''.join(k[1])

    return codeing_dict

def main():
    # 创建主窗口
    window = tk.Tk()
    window.title("Encoding and Decoding Tool")

    # 创建标签和输入框,用于输入字符串
    input_label = tk.Label(window, text="Input String:")
    input_label.pack()

    input_entry = tk.Entry(window)
    input_entry.pack()

    # 创建一个Frame容器,用于放置所有按钮
    button_frame = tk.Frame(window)
    button_frame.pack(pady=10)

    # 创建按钮,用于触发哈夫曼编码和解码操作
    def huffman_encode_decode():
        s = input_entry.get()
        if s:
            try:
                dic = {}
                for i in range(len(s)):
                    dic[s[i]] = dic.get(s[i], 0) + 1
                code_dict = Huffman_code(dic)
                show_success_message(f"Huffman Encoding: {code_dict}")
            except Exception as e:
                show_error_message(str(e))
        else:
            show_error_message("Please enter a valid input string.")

    huffman_button = tk.Button(button_frame, text="Huffman Encoding/Decoding", command=huffman_encode_decode)
    huffman_button.pack(side=tk.LEFT, padx=5)

算术编码是一种无损数据压缩技术,它通过将消息映射到一个0和1之间的分数区间来实现压缩。与霍夫曼编码等其他编码方法相比,算术编码通常能够提供更好的压缩比。以下是实现算术编码和解码的一般要求:

概率模型:需要为数据流中的每个符号建立一个概率模型。这些概率用于确定符号的编码区间。

初始区间:编码过程从0到1的区间开始,这个区间将根据符号的概率被分割成子区间。

区间分割:对于每个符号,根据其概率将当前区间分割成若干个子区间,每个子区间的长度与符号的概率成比例。

累积概率:对于每个符号,计算其累积概率,即该符号之前所有符号概率的和。

更新区间:当一个符号被编码时,当前区间将根据该符号的累积概率更新为对应的子区间。

编码结束:当所有符号都被编码后,将当前区间表示为一个二进制小数。

解码过程:解码过程与编码过程相反,它根据累积概率和当前区间来确定原始符号。

二进制表示:为了便于存储和传输,通常需要将算术编码的输出转换为二进制形式。

同步:在编码和解码过程中,需要确保发送方和接收方同步,以便正确地重建原始数据。

自适应算术编码:在一些应用中,数据的概率分布可能会随时间变化,因此需要使用自适应算术编码,它可以根据数据的实际分布动态调整概率模型。

防止下溢:由于算术编码涉及非常小的数,需要采取措施防止在编码和解码过程中发生下溢。

效率和精度:需要考虑编码和解码算法的效率和精度,以确保压缩和解压缩过程既快速又准确。

实现细节:算术编码的实现可能涉及一些技术细节,如使用特定的数据结构来表示区间,以及优化算法以减少计算量。

错误处理:需要考虑错误处理机制,以应对传输错误或数据损坏的情况。

3、实验结果分析:

def get_symbol_probabilities(text):
    # 统计输入文本中每个符号的概率分布
    symbol_probabilities = {}
    total_symbols = len(text)

    for symbol in text:
        symbol_probabilities[symbol] = symbol_probabilities.get(symbol, 0) + 1

    for symbol, count in symbol_probabilities.items():
        symbol_probabilities[symbol] = Decimal(count) / Decimal(total_symbols)

    return symbol_probabilities


def build_cumulative_probabilities(symbol_probabilities):
    # 构建累计概率表
    cumulative_probabilities = {}
    cumulative_prob = Decimal(0)

    for symbol, probability in symbol_probabilities.items():
        cumulative_probabilities[symbol] = (cumulative_prob, cumulative_prob + probability)
        cumulative_prob += probability

    return cumulative_probabilities


def encode(text, cumulative_probabilities):
    # 执行算术编码
    lower_limit = Decimal(0)
    upper_limit = Decimal(1)
    range_size = upper_limit - lower_limit

    for symbol in text:
        range_start, range_end = cumulative_probabilities[symbol]
        range_size *= range_end - range_start
        lower_limit += range_start * range_size
        upper_limit = lower_limit + range_size

    # 返回编码后的小数和编码范围大小
    return lower_limit, range_size


def decode(encoded_number, cumulative_probabilities, text_length):
    # 执行算术解码
    message = ''
    getcontext().prec = text_length  # 设置小数精度以确保准确解码

    while len(message) < text_length:
        for symbol, (range_start, range_end) in cumulative_probabilities.items():
            if range_start <= encoded_number < range_end:
                message += symbol
                range_size = range_end - range_start
                encoded_number = (encoded_number - range_start) / range_size
                break

    return message

def main():
    # 创建主窗口
    window = tk.Tk()
    window.title("Encoding and Decoding Tool")

    # 创建标签和输入框,用于输入字符串
    input_label = tk.Label(window, text="Input String:")
    input_label.pack()

    input_entry = tk.Entry(window)
    input_entry.pack()

    # 创建一个Frame容器,用于放置所有按钮
    button_frame = tk.Frame(window)
    button_frame.pack(pady=10)

# 创建按钮,用于触发算数编码和解码操作
def arithmetic_encode_decode():
    text = input_entry.get()
    if text:
        try:
            symbol_probabilities = get_symbol_probabilities(text)
            cumulative_probabilities = build_cumulative_probabilities(symbol_probabilities)
            encoded_number, range_size = encode(text, cumulative_probabilities)
            decoded_message = decode(encoded_number, cumulative_probabilities, len(text))
            show_success_message(f"arithmetic Encoding: {encoded_number}")
        except Exception as e:
            show_error_message(str(e))
    else:
        show_error_message("Please enter a valid input string.")

arithmetic_button = tk.Button(button_frame, text="Arithmetic Encoding/Decoding", command=arithmetic_encode_decode)
arithmetic_button.pack(side=tk.LEFT, padx=5)

LZW(Lempel-Ziv-Welch)编码是一种无损数据压缩算法,它通过构建一个可变长度的字典来逐步压缩数据。以下是实现LZW编码和解码的一般要求:

初始化字典:开始编码时,需要一个初始字典,其中包含输入数据集中所有可能的字符作为字典的初始条目。

字符编码:每个字符最初被单独编码,并逐步添加到输出中。

查找匹配串:算法尝试在输入数据中查找当前字符之后更长的匹配串,并检查该串是否已存在于字典中。

字典更新:如果找到匹配串,则将其编码添加到输出中,并从当前位置开始继续查找;如果没有找到,则将当前字符作为一个新条目添加到字典中。

编码输出:编码输出包括字典中条目的索引,这些索引对应于输入数据中的字符或字符串。

处理新词:当输入数据中出现一个新的字符或字符串(不在字典中),该串被分解为单个字符,并逐步添加到字典中。

结束条件:当输入数据完全被读取,编码过程结束。

解码过程:解码过程需要与编码过程相反,它使用相同的字典来将编码索引转换回原始字符或字符串。

字典共享:编码器和解码器必须使用相同的初始字典,并且在编码过程中以相同的方式更新字典。

数据结构:需要有效的数据结构来存储字典条目,以及快速查找和添加新条目。

压缩比:LZW算法的压缩比依赖于输入数据的重复模式。数据中重复模式越多,压缩比越高。

编码效率:LZW算法的效率取决于查找和插入字典条目的速度。

自适应性:LZW算法是自适应的,它可以根据输入数据的统计特性动态调整字典。

最大字典大小:在某些实现中,可能需要设置字典的最大大小,以防止内存使用过多。

清空字典:在一些特定情况下,如压缩非常大的文件时,可能需要清空字典并重新开始以避免性能问题。

3、实验结果分析:

# LZW编码实现
def lzw_encode(text):
    dictionary = {}
    next_code = 256
    encoded_text = []
    prefix = ""

    for c in text:
        if prefix + c in dictionary:
            prefix += c
        else:
            encoded_text.append(dictionary.get(prefix, 0))
            dictionary[prefix + c] = next_code
            next_code += 1
            prefix = c

    encoded_text.append(dictionary.get(prefix, 0))
    return encoded_text

def main():
    # 创建主窗口
    window = tk.Tk()
    window.title("Encoding and Decoding Tool")

    # 创建标签和输入框,用于输入字符串
    input_label = tk.Label(window, text="Input String:")
    input_label.pack()

    input_entry = tk.Entry(window)
    input_entry.pack()

    # 创建一个Frame容器,用于放置所有按钮
    button_frame = tk.Frame(window)
    button_frame.pack(pady=10)

# 创建按钮,用于触发LZW编码和解码操作
def lzw_encode_decode():
    text = input_entry.get()
    if text:
        try:
            code = lzw_encode(text)
            show_success_message(f"LZW Encoding: {code}")
        except Exception as e:
            show_error_message(str(e))
    else:
        show_error_message("Please enter a valid input string.")

lzw_button = tk.Button(button_frame, text="LZW Encoding/Decoding", command=lzw_encode_decode)
lzw_button.pack(side=tk.LEFT, padx=5)

WAV(Waveform Audio File Format)是一种无损音频文件格式,通常用于保存未压缩的音频数据。WAV文件由两部分组成:一个包含音频流的属性(如采样率、位深、声道数等)的头部信息,以及随后的实际音频数据。读取和显示WAV数据通常需要满足以下要求:

解析头部信息:读取WAV文件的头部信息,这通常包括以下几个关键字段:

音频格式(如PCM、IEEE float等)

采样率(每秒钟的样本数)

声道数(如单声道或立体声)

位深(每个样本的位数,常见的有8位、16位、24位或32位)

数据速率(每秒位数)

数据长度(音频数据的总字节数)

读取音频数据:根据头部信息中提供的参数,读取紧跟在头部之后的音频数据块。

数据格式转换:WAV文件的音频数据通常是以二进制形式存储的,需要根据位深和音频格式转换为适合处理的格式(如整数或浮点数)。

音频解码:对于未压缩的WAV文件,音频数据通常是原始样本值,可以直接用于播放或进一步处理。

音频播放:如果需要播放音频,可以使用音频播放库或API来播放解码后的音频数据。

显示波形:为了在界面上显示音频波形,需要将音频样本值映射到图形界面的坐标系中。通常,这涉及到绘制音频信号的波形图或频谱图。

同步播放与显示:如果同时进行播放和显示,需要确保播放进度和显示的波形同步。

错误处理:在读取和处理WAV文件时,应考虑错误处理机制,以应对文件损坏、格式不支持或读取权限等问题。

用户界面:如果需要用户交互,应设计直观的用户界面,提供播放控制(如播放、暂停、停止)和波形查看功能。

性能考虑:对于大型WAV文件或实时应用,需要考虑读取和处理数据的性能,避免延迟和卡顿。

内存管理:音频数据可能会占用大量内存,特别是在高采样率和多位深的情况下,需要合理管理内存使用。

跨平台兼容性:确保读取和显示WAV数据的程序或脚本在不同操作系统和平台上都能正常工作。

文件格式验证:在读取之前验证文件是否为有效的WAV格式,通常WAV文件以RIFF和WAVE作为文件头标识。

版权和许可:在使用WAV文件时,应确保遵守相关的版权和使用许可。

wav是一种标准数字音频文件。作为最常见的音频文件之一的wav格式,是由微软公司和IBM联合设计。

Python中主流的读取波形音频(.wav)的方法有四种,使用scipy库、soundfile库、ewave库、torchaudio库。

def open_wav_file(file_path):
    with wave.open(file_path, 'rb') as wav_file:
        # 获取音频文件的参数
        num_channels = wav_file.getnchannels()
        sample_width = wav_file.getsampwidth()
        sample_rate = wav_file.getframerate()
        num_frames = wav_file.getnframes()

        # 读取音频数据
        audio_data = wav_file.readframes(num_frames)

    return audio_data, num_channels, sample_width, sample_rate


def read_wav_file():
    # 读取WAV文件的逻辑
    try:
        filename = "audio.wav"  # WAV文件名
        wav = wave.open(filename, 'r')
        frames = wav.readframes(-1)
        signal = np.frombuffer(frames, dtype='int16')
        sample_rate = wav.getframerate()
        duration = len(signal) / sample_rate

        return signal, sample_rate, duration
    except Exception as e:
        show_error_message(str(e))


def display_wav_data():
    # 显示WAV数据的逻辑
    try:
        signal, sample_rate, duration = read_wav_file()
        time = np.linspace(0, duration, len(signal))

        plt.figure(figsize=(200, 54))
        plt.plot(time, signal)
        plt.xlabel('Time (s)')
        plt.ylabel('Amplitude')
        plt.title('WAV Data')
        plt.show()
    except Exception as e:
        show_error_message(str(e))

def main():
    # 创建主窗口
    window = tk.Tk()
    window.title("Encoding and Decoding Tool")

    # 创建标签和输入框,用于输入字符串
    input_label = tk.Label(window, text="Input String:")
    input_label.pack()

    input_entry = tk.Entry(window)
    input_entry.pack()

    # 创建一个Frame容器,用于放置所有按钮
    button_frame = tk.Frame(window)
    button_frame.pack(pady=10)

def read_bmp_data():
    try:
        bmp_file_path = filedialog.askopenfilename()
        bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, img_data = read_bmp(bmp_file_path)
        grayscale_data = convert_to_grayscale(img_data)
        show_success_message(
            f'bf_type = {bf_type},bf_size = {bf_size},bf_offbits = {bf_offbits},bi_width = {bi_width},bi_height = {bi_height},bi_bitcount = {bi_bitcount}')
        # 写入灰度图像到BMP文件
        output_filepath = r"C:\Users\sunao\Desktop\outbmp.bmp"
        write_bmp(output_filepath, bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, grayscale_data)
    except Exception as e:
        show_error_message(str(e))

read_bmp_button = tk.Button(button_frame, text="Read BMP File", command=read_bmp_data)
read_bmp_button.pack(side=tk.LEFT, padx=5)

BMP(Bitmap Image File Format)文件是一种位图图像文件格式,用于存储数字图像数据。BMP文件可以是压缩的,也可以是未压缩的,并且可以包含彩色图像或单色位图。进行BMP文件读写时,通常需要满足以下要求:

文件格式理解:理解BMP文件格式的组成,包括文件头、位图信息头、颜色数据和像素数据。

文件头解析:读取并解析BMP文件的文件头(BITMAPFILEHEADER),这通常包括文件类型、文件大小、位图数据的偏移量等信息。

位图信息头:读取位图信息头(BITMAPINFOHEADER),它包含图像的宽度、高度、位深(颜色位数)、压缩类型和图像大小等信息。

颜色数据:对于某些BMP文件,可能需要读取颜色数据(如调色板),特别是当位图使用索引颜色时。

像素数据读取:根据位图信息头中的信息,读取并解析像素数据。像素数据通常按行存储,每行可能需要字节对齐。

数据解析:根据位深解析像素数据,位深可以是1、4、8、16、24或32位。不同的位深意味着不同的颜色信息存储方式。

图像渲染:将解析后的像素数据渲染成图像,这可能涉及到颜色转换、位翻转等操作。

写入文件:在创建BMP文件时,需要构建正确的文件头、位图信息头和颜色数据,然后写入像素数据。

数据对齐:BMP文件要求每行像素数据的字节数必须是4的倍数,因此可能需要对数据进行填充。

压缩和解压:对于压缩的BMP文件,需要实现相应的压缩和解压算法。

错误处理:在读写过程中,需要考虑错误处理机制,以应对文件损坏、格式不正确或读写错误等问题。

性能考虑:对于大型图像文件,读写操作可能会消耗较多时间和资源,需要考虑性能优化。

用户界面:如果需要用户交互,设计直观的用户界面,允许用户选择文件、查看图像和保存结果。

跨平台兼容性:确保程序在不同的操作系统和平台上都能正确读写BMP文件。

版权和许可:在使用BMP文件时,需要确保遵守相关的版权和使用许可。

第三方库的使用:可以使用第三方图像处理库(如Python的Pillow库)来简化BMP文件的读写操作。

3、实验结果分析:

def read_bmp(filename):
    with open(filename, "rb") as fp:
        # 读取文件头
        file_header = fp.read(14)
        # 解析文件头数据
        bf_type = file_header[:2]
        bf_size = struct.unpack("<I", file_header[2:6])[0]
        bf_offbits = struct.unpack("<I", file_header[10:14])[0]

        # 读取图像信息头
        info_header = fp.read(40)
        # 解析图像信息头数据
        bi_width, bi_height = struct.unpack("<ii", info_header[4:12])
        bi_bitcount = struct.unpack("<H", info_header[14:16])[0]

        # 读取图像数据
        img_data = fp.read()

    return bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, img_data


def write_bmp(filename, bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, img_data):
    with open(filename, "wb") as fp:
        # 写入文件头
        fp.write(bf_type)
        fp.write(struct.pack("<I", bf_size))
        fp.write(struct.pack("<H", 0))  # bfReserved1
        fp.write(struct.pack("<H", 0))  # bfReserved2
        fp.write(struct.pack("<I", bf_offbits))

        # 写入图像信息头
        fp.write(struct.pack("<I", 40))  # biSize
        fp.write(struct.pack("<i", bi_width))
        fp.write(struct.pack("<i", bi_height))
        fp.write(struct.pack("<H", 1))  # biPlanes
        fp.write(struct.pack("<H", bi_bitcount))
        fp.write(struct.pack("<I", 0))  # biCompression
        fp.write(struct.pack("<I", 0))  # biSizeImage
        fp.write(struct.pack("<i", 0))  # biXPelsPerMeter
        fp.write(struct.pack("<i", 0))  # biYPelsPerMeter
        fp.write(struct.pack("<I", 0))  # biClrUsed
        fp.write(struct.pack("<I", 0))  # biClrImportant

        # 写入图像数据
        fp.write(img_data)

def convert_to_grayscale(img_data):
    grayscale_data = bytearray()
    i = 0
    while i < len(img_data):
        try:
            b, g, r = img_data[i:i+3]
            grayscale_value = int(0.299 * r + 0.587 * g + 0.114 * b)
            grayscale_data.extend([grayscale_value] * 3)
            i += 3
        except ValueError:
            # 如果当前像素数据不符合RGB格式,跳过该像素
            i += 1

    return bytes(grayscale_data)

def main():
    # 创建主窗口
    window = tk.Tk()
    window.title("Encoding and Decoding Tool")

    # 创建标签和输入框,用于输入字符串
    input_label = tk.Label(window, text="Input String:")
    input_label.pack()

    input_entry = tk.Entry(window)
    input_entry.pack()

    # 创建一个Frame容器,用于放置所有按钮
    button_frame = tk.Frame(window)
    button_frame.pack(pady=10)

def read_bmp_data():
    try:
        bmp_file_path = filedialog.askopenfilename()
        bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, img_data = read_bmp(bmp_file_path)
        grayscale_data = convert_to_grayscale(img_data)
        show_success_message(
            f'bf_type = {bf_type},bf_size = {bf_size},bf_offbits = {bf_offbits},bi_width = {bi_width},bi_height = {bi_height},bi_bitcount = {bi_bitcount}')
        # 写入灰度图像到BMP文件
        output_filepath = r"C:\Users\sunao\Desktop\outbmp.bmp"
        write_bmp(output_filepath, bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, grayscale_data)
    except Exception as e:
        show_error_message(str(e))

read_bmp_button = tk.Button(button_frame, text="Read BMP File", command=read_bmp_data)
read_bmp_button.pack(side=tk.LEFT, padx=5)

安装OpenCV库:确保你的开发环境中安装了OpenCV库,它是进行视频读取和处理的基础。

创建视频捕获对象:使用cv2.VideoCapture()函数创建一个视频捕获对象,该对象可以是视频文件的路径或摄像头的索引号。

检查视频是否成功打开:通过调用捕获对象的isOpened()方法,检查视频是否成功打开。

读取视频帧:在循环中使用read()方法从视频捕获对象中读取每一帧。该方法返回两个值:一个布尔值表示是否成功读取帧,以及读取的帧本身。

处理视频帧:对读取的每一帧进行所需的图像处理操作。

显示视频帧:使用cv2.imshow()函数显示视频帧。可以通过设置窗口名称和帧的标题来显示。

等待用户输入:使用cv2.waitKey()函数等待用户输入,以便在需要时退出视频读取循环。

释放视频捕获对象:完成视频读取后,使用release()方法释放视频捕获对象,释放与之关联的资源。

关闭所有OpenCV窗口:使用cv2.destroyAllWindows()关闭所有由OpenCV创建的窗口。

错误处理:在读取视频时,需要考虑错误处理,例如处理文件路径错误、文件格式不支持或文件损坏等问题。

视频属性获取:如果需要,可以使用get()方法获取视频的其他属性,如帧高度、宽度、帧率等。

支持的文件格式:确保视频文件的格式被OpenCV支持,常见的格式包括MP4、AVI等。

视频编解码器:了解和指定正确的视频编解码器,以确保视频可以被正确读取。

帧率和分辨率:根据需要处理的视频帧率和分辨率,可能需要对视频播放进行适当的调整。

实时视频处理:如果视频读取涉及到实时处理,需要考虑算法的效率和处理时间,以保持视频播放的流畅性。

def read_and_display_video(video_source=0):
    """
    读取视频源(视频文件或摄像头)并在窗口中实时显示
    :param video_source: 视频源,可以是视频文件路径或者0(表示默认摄像头)
    """
    # 打开视频文件或摄像头
    cap = cv2.VideoCapture(video_source)

    # 检查是否成功打开
    if not cap.isOpened():
        print("Cannot open video source")
        return

    while True:
        # 读取一帧视频
        ret, frame = cap.read()

        # 如果没有更多帧,退出循环
        if not ret:
            print("Can't receive frame (stream end?). Exiting ...")
            break

        # 显示帧
        cv2.imshow('Video', frame)

        # 按下q键退出循环
        if cv2.waitKey(1) == ord('q'):
            break

    # 释放视频捕获对象和关闭窗口
    cap.release()
    cv2.destroyAllWindows()

def main():
    # 创建主窗口
    window = tk.Tk()
    window.title("Encoding and Decoding Tool")

    # 创建标签和输入框,用于输入字符串
    input_label = tk.Label(window, text="Input String:")
    input_label.pack()

    input_entry = tk.Entry(window)
    input_entry.pack()

    # 创建一个Frame容器,用于放置所有按钮
    button_frame = tk.Frame(window)
    button_frame.pack(pady=10)

def read_mp4_data():
    try:
        mp4_file_path = filedialog.askopenfilename()  # 打开文件对话框选择 MP4 文件
        if mp4_file_path:
            read_and_display_video(mp4_file_path)
    except Exception as e:
        show_error_message(str(e))

read_mp4_button = tk.Button(button_frame, text="Read MP4 File", command=read_mp4_data)
read_mp4_button.pack(side=tk.LEFT, padx=5)

window.mainloop()

推流和拉流之间的主要区别如下:

推流模式(RTSP推流分发):

客户端将视频/音频数据主动推送到服务器。

服务器接收并转发这些数据流。

适用于客户端具有稳定上行带宽的情况。

拉流模式(RTSP拉流分发):

服务器主动向客户端拉取视频/音频数据流。

客户端被动接收服务器发来的数据流。

适用于客户端上行带宽较弱的情况。

直播软件OBS:

3、实验结果分析:

进入easydarwin页面localhost:10086

此页面可以上传媒体视频,也可以设置点播地址

打开obs直播软件,进行推流设置和音媒体配置设置。上传视频或者录像。

设置服务器地址和推流码

设置音视频码率

Easydarwin打开直播,点击播放

 附件:

import tkinter as tk
from tkinter import messagebox
import numpy as np
import matplotlib.pyplot as plt
import matplotlib
matplotlib.use('TkAgg')  # 在导入 pyplot 之前指定后端
import wave
from math import inf
from decimal import Decimal, getcontext
from tkinter import filedialog
import struct
import cv2


#初始化哈夫曼结点
class Huffmannode(object):
    def __init__(self):
        self.parent=0
        self.left=0
        self.right=0
        self.weight=0


#选择最小的结点下标
def select_node(huffman):
    #俩个结点直接返回不需要找最小俩个结点
    if len(huffman)==2:
        return 0,1
    min=semin=inf#初始化成无穷大
    f=s=-1
    for i in range(len(huffman)):
        if huffman[i].parent==0:
            if min>huffman[i].weight:
                semin=min
                s=f
                min=huffman[i].weight
                f=i
            elif semin>huffman[i].weight:
                semin=huffman[i].weight
                s=i
    return f,s


#编码
def Huffman_code(origin_dict):
    #给结点赋权重
    n=len(origin_dict)
    m=2*n-1
    huffman=[]
    for i in origin_dict:
        temp_huffmannode=Huffmannode()
        temp_huffmannode.weight=origin_dict[i]
        huffman.append(temp_huffmannode)
    # 构建Huffman树,选择俩个最小的结点合并
    for i in range(n,m):
        f , s=select_node(huffman)
        temp_huffmannode=Huffmannode()
        temp_huffmannode.weight = huffman[f].weight+huffman[s].weight
        temp_huffmannode.right = f  #小的放在右边
        temp_huffmannode.left = s
        huffman[f].parent = huffman[s].parent=i
        huffman.append(temp_huffmannode)

    #0,1编码,右1,左0
    codeing_dict = dict.fromkeys(origin_dict, None)
    for i in range(0,n):
        s=''
        k=i
        parent=huffman[i].parent
        while parent!=0:
            if huffman[parent].left==k:
                s+='0'
                k=parent
                parent=huffman[parent].parent
            else:
                s+='1'
                k=parent
                parent=huffman[parent].parent
        codeing_dict[list(origin_dict.keys())[i]]=list(reversed(s))
    for k in codeing_dict.items():
        codeing_dict[k[0]] = ''.join(k[1])

    return codeing_dict


def get_symbol_probabilities(text):
    # 统计输入文本中每个符号的概率分布
    symbol_probabilities = {}
    total_symbols = len(text)

    for symbol in text:
        symbol_probabilities[symbol] = symbol_probabilities.get(symbol, 0) + 1

    for symbol, count in symbol_probabilities.items():
        symbol_probabilities[symbol] = Decimal(count) / Decimal(total_symbols)

    return symbol_probabilities


def build_cumulative_probabilities(symbol_probabilities):
    # 构建累计概率表
    cumulative_probabilities = {}
    cumulative_prob = Decimal(0)

    for symbol, probability in symbol_probabilities.items():
        cumulative_probabilities[symbol] = (cumulative_prob, cumulative_prob + probability)
        cumulative_prob += probability

    return cumulative_probabilities


def encode(text, cumulative_probabilities):
    # 执行算术编码
    lower_limit = Decimal(0)
    upper_limit = Decimal(1)
    range_size = upper_limit - lower_limit

    for symbol in text:
        range_start, range_end = cumulative_probabilities[symbol]
        range_size *= range_end - range_start
        lower_limit += range_start * range_size
        upper_limit = lower_limit + range_size

    # 返回编码后的小数和编码范围大小
    return lower_limit, range_size


def decode(encoded_number, cumulative_probabilities, text_length):
    # 执行算术解码
    message = ''
    getcontext().prec = text_length  # 设置小数精度以确保准确解码

    while len(message) < text_length:
        for symbol, (range_start, range_end) in cumulative_probabilities.items():
            if range_start <= encoded_number < range_end:
                message += symbol
                range_size = range_end - range_start
                encoded_number = (encoded_number - range_start) / range_size
                break

    return message


# LZW编码实现
def lzw_encode(text):
    dictionary = {}
    next_code = 256
    encoded_text = []
    prefix = ""

    for c in text:
        if prefix + c in dictionary:
            prefix += c
        else:
            encoded_text.append(dictionary.get(prefix, 0))
            dictionary[prefix + c] = next_code
            next_code += 1
            prefix = c

    encoded_text.append(dictionary.get(prefix, 0))
    return encoded_text


def open_wav_file(file_path):
    with wave.open(file_path, 'rb') as wav_file:
        # 获取音频文件的参数
        num_channels = wav_file.getnchannels()
        sample_width = wav_file.getsampwidth()
        sample_rate = wav_file.getframerate()
        num_frames = wav_file.getnframes()

        # 读取音频数据
        audio_data = wav_file.readframes(num_frames)

    return audio_data, num_channels, sample_width, sample_rate


def read_wav_file():
    # 读取WAV文件的逻辑
    try:
        filename = "audio.wav"  # WAV文件名
        wav = wave.open(filename, 'r')
        frames = wav.readframes(-1)
        signal = np.frombuffer(frames, dtype='int16')
        sample_rate = wav.getframerate()
        duration = len(signal) / sample_rate

        return signal, sample_rate, duration
    except Exception as e:
        show_error_message(str(e))


def display_wav_data():
    # 显示WAV数据的逻辑
    try:
        signal, sample_rate, duration = read_wav_file()
        time = np.linspace(0, duration, len(signal))

        plt.figure(figsize=(200, 54))
        plt.plot(time, signal)
        plt.xlabel('Time (s)')
        plt.ylabel('Amplitude')
        plt.title('WAV Data')
        plt.show()
    except Exception as e:
        show_error_message(str(e))


def show_error_message(message):
    messagebox.showerror("Error", message)


def show_success_message(message):
    messagebox.showinfo("Success", message)


def read_bmp(filename):
    with open(filename, "rb") as fp:
        # 读取文件头
        file_header = fp.read(14)
        # 解析文件头数据
        bf_type = file_header[:2]
        bf_size = struct.unpack("<I", file_header[2:6])[0]
        bf_offbits = struct.unpack("<I", file_header[10:14])[0]

        # 读取图像信息头
        info_header = fp.read(40)
        # 解析图像信息头数据
        bi_width, bi_height = struct.unpack("<ii", info_header[4:12])
        bi_bitcount = struct.unpack("<H", info_header[14:16])[0]

        # 读取图像数据
        img_data = fp.read()

    return bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, img_data


def write_bmp(filename, bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, img_data):
    with open(filename, "wb") as fp:
        # 写入文件头
        fp.write(bf_type)
        fp.write(struct.pack("<I", bf_size))
        fp.write(struct.pack("<H", 0))  # bfReserved1
        fp.write(struct.pack("<H", 0))  # bfReserved2
        fp.write(struct.pack("<I", bf_offbits))

        # 写入图像信息头
        fp.write(struct.pack("<I", 40))  # biSize
        fp.write(struct.pack("<i", bi_width))
        fp.write(struct.pack("<i", bi_height))
        fp.write(struct.pack("<H", 1))  # biPlanes
        fp.write(struct.pack("<H", bi_bitcount))
        fp.write(struct.pack("<I", 0))  # biCompression
        fp.write(struct.pack("<I", 0))  # biSizeImage
        fp.write(struct.pack("<i", 0))  # biXPelsPerMeter
        fp.write(struct.pack("<i", 0))  # biYPelsPerMeter
        fp.write(struct.pack("<I", 0))  # biClrUsed
        fp.write(struct.pack("<I", 0))  # biClrImportant

        # 写入图像数据
        fp.write(img_data)

def convert_to_grayscale(img_data):
    grayscale_data = bytearray()
    i = 0
    while i < len(img_data):
        try:
            b, g, r = img_data[i:i+3]
            grayscale_value = int(0.299 * r + 0.587 * g + 0.114 * b)
            grayscale_data.extend([grayscale_value] * 3)
            i += 3
        except ValueError:
            # 如果当前像素数据不符合RGB格式,跳过该像素
            i += 1

    return bytes(grayscale_data)


def read_and_display_video(video_source=0):
    """
    读取视频源(视频文件或摄像头)并在窗口中实时显示
    :param video_source: 视频源,可以是视频文件路径或者0(表示默认摄像头)
    """
    # 打开视频文件或摄像头
    cap = cv2.VideoCapture(video_source)

    # 检查是否成功打开
    if not cap.isOpened():
        print("Cannot open video source")
        return

    while True:
        # 读取一帧视频
        ret, frame = cap.read()

        # 如果没有更多帧,退出循环
        if not ret:
            print("Can't receive frame (stream end?). Exiting ...")
            break

        # 显示帧
        cv2.imshow('Video', frame)

        # 按下q键退出循环
        if cv2.waitKey(1) == ord('q'):
            break

    # 释放视频捕获对象和关闭窗口
    cap.release()
    cv2.destroyAllWindows()

# 示例用法
# 读取默认摄像头
# read_and_display_video()


def main():
    # 创建主窗口
    window = tk.Tk()
    window.title("Encoding and Decoding Tool")

    # 创建标签和输入框,用于输入字符串
    input_label = tk.Label(window, text="Input String:")
    input_label.pack()

    input_entry = tk.Entry(window)
    input_entry.pack()

    # 创建一个Frame容器,用于放置所有按钮
    button_frame = tk.Frame(window)
    button_frame.pack(pady=10)

    # 创建按钮,用于触发哈夫曼编码和解码操作
    def huffman_encode_decode():
        s = input_entry.get()
        if s:
            try:
                dic = {}
                for i in range(len(s)):
                    dic[s[i]] = dic.get(s[i], 0) + 1
                code_dict = Huffman_code(dic)
                show_success_message(f"Huffman Encoding: {code_dict}")
            except Exception as e:
                show_error_message(str(e))
        else:
            show_error_message("Please enter a valid input string.")

    huffman_button = tk.Button(button_frame, text="Huffman Encoding/Decoding", command=huffman_encode_decode)
    huffman_button.pack(side=tk.LEFT, padx=5)

    # 创建按钮,用于触发算数编码和解码操作
    def arithmetic_encode_decode():
        text = input_entry.get()
        if text:
            try:
                symbol_probabilities = get_symbol_probabilities(text)
                cumulative_probabilities = build_cumulative_probabilities(symbol_probabilities)
                encoded_number, range_size = encode(text, cumulative_probabilities)
                decoded_message = decode(encoded_number, cumulative_probabilities, len(text))
                show_success_message(f"arithmetic Encoding: {encoded_number}")
            except Exception as e:
                show_error_message(str(e))
        else:
            show_error_message("Please enter a valid input string.")

    arithmetic_button = tk.Button(button_frame, text="Arithmetic Encoding/Decoding", command=arithmetic_encode_decode)
    arithmetic_button.pack(side=tk.LEFT, padx=5)

    # 创建按钮,用于触发LZW编码和解码操作
    def lzw_encode_decode():
        text = input_entry.get()
        if text:
            try:
                code = lzw_encode(text)
                show_success_message(f"LZW Encoding: {code}")
            except Exception as e:
                show_error_message(str(e))
        else:
            show_error_message("Please enter a valid input string.")

    lzw_button = tk.Button(button_frame, text="LZW Encoding/Decoding", command=lzw_encode_decode)
    lzw_button.pack(side=tk.LEFT, padx=5)

    # 创建按钮,用于读取和显示WAV数据
    def read_and_display_wav_data():
        try:
            wav_file_path = filedialog.askopenfilename()  # 打开文件对话框选择 WAV 文件
            audio_data, num_channels, sample_width, sample_rate = open_wav_file(wav_file_path)

            # 将二进制音频数据转换为 NumPy 数组
            audio_array = np.frombuffer(audio_data, dtype=np.uint8)

            # 如果音频数据是16位采样宽度,将其转换为有符号整数
            if sample_width == 2:
                audio_array = audio_array.astype(np.int16)

            # 计算音频数据的时间轴
            duration = len(audio_array) / sample_rate
            time = np.linspace(0, duration, len(audio_array))

            # 绘制波形图
            plt.plot(time, audio_array)
            plt.xlabel('Time (s)')
            plt.ylabel('Amplitude')
            plt.title('Waveform')
            plt.show()
        except Exception as e:
            show_error_message(str(e))

    read_wav_button = tk.Button(button_frame, text="Read WAV File", command=read_and_display_wav_data)
    read_wav_button.pack(side=tk.LEFT, padx=5)

    def read_bmp_data():
        try:
            bmp_file_path = filedialog.askopenfilename()
            bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, img_data = read_bmp(bmp_file_path)
            grayscale_data = convert_to_grayscale(img_data)
            show_success_message(
                f'bf_type = {bf_type},bf_size = {bf_size},bf_offbits = {bf_offbits},bi_width = {bi_width},bi_height = {bi_height},bi_bitcount = {bi_bitcount}')
            # 写入灰度图像到BMP文件
            output_filepath = r"C:\Users\sunao\Desktop\outbmp.bmp"
            write_bmp(output_filepath, bf_type, bf_size, bf_offbits, bi_width, bi_height, bi_bitcount, grayscale_data)
        except Exception as e:
            show_error_message(str(e))

    read_bmp_button = tk.Button(button_frame, text="Read BMP File", command=read_bmp_data)
    read_bmp_button.pack(side=tk.LEFT, padx=5)

    def read_mp4_data():
        try:
            mp4_file_path = filedialog.askopenfilename()  # 打开文件对话框选择 MP4 文件
            if mp4_file_path:
                read_and_display_video(mp4_file_path)
        except Exception as e:
            show_error_message(str(e))

    read_mp4_button = tk.Button(button_frame, text="Read MP4 File", command=read_mp4_data)
    read_mp4_button.pack(side=tk.LEFT, padx=5)

    window.mainloop()

# 调用主函数
main()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值