【H.264/AVC视频编解码技术详解】七、 熵编码算法(1):基础知识

简介: 《H.264/AVC视频编解码技术详解》视频教程已经在“CSDN学院”上线,视频中详述了H.264的背景、标准协议和实现,并通过一个实战工程的形式对H.

《H.264/AVC视频编解码技术详解》视频教程已经在“CSDN学院”上线,视频中详述了H.264的背景、标准协议和实现,并通过一个实战工程的形式对H.264的标准进行解析和实现,欢迎观看!

“纸上得来终觉浅,绝知此事要躬行”,只有自己按照标准文档以代码的形式操作一遍,才能对视频压缩编码标准的思想和方法有足够深刻的理解和体会!

链接地址:H.264/AVC视频编解码技术详解

GitHub代码地址:点击这里

本节视频免费

1. 熵编码概念

“熵”这一概念原本来自于化学和热力学,用于度量能量退化的指标,即熵越高,物体或系统的做功能力越低。后来香农将这一概念引入到信息论中,用于表示消息的平均信息量。信源的熵通常可以表示信源所发出信息的不确定性,即越是随机的、前后不相关的信息,其熵越高。

在信息论中,香农提出了信源编码定理。该定理说明了香农熵与信源符号概率之间的关系,说明信息的熵为信源无损编码后的平均码字长度的下限。任何的无损编码方法都不可能使编码后的平均码长小于香农熵,只能使其尽量接近。

基于此,对信源进行熵编码的基本思想,是使其前后的码字之间尽量更加随机,尽量减小前后的相关性,更加接近其信源的香农熵。这样在表示同样的信息量时所用的数据长度更短。

在实际使用中,常用的熵编码主要有变长编码和算术编码等方法。其中变长编码相对于算术编码较为简单,但平均压缩比可能略低。常见的变长编码方法有哈夫曼编码和香农-费诺编码等。

2. 熵编码的简单实现——哈夫曼编码

戴维·哈夫曼(David·A·Huffman)于1952年在麻省理工学院的罗伯特·费诺的指导下攻读博士学位时,发明了一种基于有序频率二叉树的编码方法,该方法的编码效率超过了他的导师和信息论之父香农的研究成果(香农-费诺编码),因此又称作“最优编码方法”。

哈夫曼编码时变长编码方法的一种,该方法完全依赖于码字出现的概率来构造整体平均长度最短的编码方法。进行哈夫曼编码的关键步骤是建立符合哈夫曼编码规则的二叉树,该树又称作哈夫曼树。

哈夫曼树是一种特殊的二叉树,其终端节点的个数与待编码的码元的个数等同,而且每个终端节点上都带有各自的权值。每个终端节点的路径长度乘以该节点的权值的总和称为整个二叉树的加权路径长度。在满足条件的各种二叉树中,该路径长度最短的二叉树即为哈夫曼树。

在使用哈夫曼编码执行对码元的实际编码过程时,码元的权值可设置为其概率值,那么可以根据其权值来构建哈夫曼树。我们假设使用哈夫曼编码对以下概率的码字进行编码:

码字 概率
A 0.1
B 0.1
C 0.15
D 0.2
E 0.2
F 0.25

根据概率表构建哈夫曼树的过程如下图所示:

最终我们可以得到如下图所示的哈夫曼树:

在哈夫曼树构建完成后,便可以得到每一个码元的哈夫曼编码的码字。具体方法是:从哈夫曼树的根节点开始遍历,直至每一个终端节点,当访问某个节点的左子树时赋予码字0,访问右子树时赋予一个码字1(反之亦可),直到遍历到终端节点时这一路径所代表的0和1的串便是该码元的哈夫曼编码码字。

例如上图的哈夫曼树,根节点访问左子树ABCF,赋予码字0;然后再访问左子树ABC,赋予码字0,此时整个码字为00,然后访问右子树得到终端节点C,赋予码字1,此时便可以得到C的哈夫曼编码码字001。以此规律,整个六个元素的码元集合的编码码表为:

  • A: 0000
  • B: 0001
  • C: 001
  • D: 10
  • E: 11
  • F: 01

从这个码表中还可以看出另外一个规律:哈夫曼编码的任意一个码字,都不可能是其他码字的前缀。因此通过哈夫曼编码的信息可以紧密排列连续传输,而不用担心解码时的歧义性。

3. 哈夫曼树的构建Demo

下面的程序段给出一个构建哈夫曼树,并生成对应码元的哈夫曼编码的过程:

#include "stdafx.h"
#include <iostream>
#include <fstream>
#include <queue>
#include <vector>
#include <string>

using namespace std;

//每一个符号定义为一个结构体,包括字符和出现频次
typedef struct
{
    unsigned char   character;
    unsigned int    frequency;
} CharNode;

static bool open_input_file(ifstream &input, const char *inputFileName)
{
    input.open(inputFileName);
    if (!input.is_open())
    {
        return false;
    }
    return true;
}

struct MinHeapNode
{
    char data;
    unsigned int freq;
    MinHeapNode *left, *right;
    MinHeapNode(char data, unsigned freq)
    {
        left = right = NULL;
        this->data = data;
        this->freq = freq;
    }
};
typedef struct MinHeapNode MinHeapNode;

struct compare
{
    bool operator()(MinHeapNode* l, MinHeapNode *r)
    {
        return (l->freq > r->freq);
    }
};

static void get_huffman_code(MinHeapNode *root, string code)
{
    if (!root)
    {
        return;
    }

    if (root->data != -1)
    {
        cout << root->data << " : " << code << endl;;
    }

    get_huffman_code(root->left, code + "0");
    get_huffman_code(root->right, code + "1");
}

int _tmain(int argc, _TCHAR* argv[])
{
    ifstream inputFile;
    if (!open_input_file(inputFile, "input.txt"))
    {
        cout << "Error: opening input file failed!" << endl;
        return -1;
    }

    char buf = inputFile.get();
    CharNode nodeArr[256] = { { 0, 0 } };
    while (inputFile.good())
    {
        cout << buf;
        nodeArr[buf].character = buf;
        nodeArr[buf].frequency++;
        buf = inputFile.get();
    }
    cout << endl;

    priority_queue<MinHeapNode*, vector<MinHeapNode*>, compare>  minHeap;
    for (int idx = 0; idx < 256; idx++)
    {
        if (nodeArr[idx].frequency > 0)
        {
            cout << "Node " << idx << ": [" << nodeArr[idx].character << ", " << nodeArr[idx].frequency << "]" << endl;
            minHeap.push(new MinHeapNode(nodeArr[idx].character, nodeArr[idx].frequency));
        }
    }

    MinHeapNode *leftNode = NULL, *rightNode = NULL, *topNode = NULL;
    while (minHeap.size() != 1)
    {
        leftNode = minHeap.top();
        minHeap.pop();

        rightNode = minHeap.top();
        minHeap.pop();

        topNode = new MinHeapNode(-1, leftNode->freq + rightNode->freq);
        topNode->left = leftNode;
        topNode->right = rightNode;
        minHeap.push(topNode);
    }

    get_huffman_code(topNode, "");

    inputFile.close();
    return 0;
}

程序的详细解释过程请到视频中观看。

目录
相关文章
|
1月前
|
机器学习/深度学习 人工智能 算法
【眼疾病识别】图像识别+深度学习技术+人工智能+卷积神经网络算法+计算机课设+Python+TensorFlow
眼疾识别系统,使用Python作为主要编程语言进行开发,基于深度学习等技术使用TensorFlow搭建ResNet50卷积神经网络算法,通过对眼疾图片4种数据集进行训练('白内障', '糖尿病性视网膜病变', '青光眼', '正常'),最终得到一个识别精确度较高的模型。然后使用Django框架开发Web网页端可视化操作界面,实现用户上传一张眼疾图片识别其名称。
64 9
【眼疾病识别】图像识别+深度学习技术+人工智能+卷积神经网络算法+计算机课设+Python+TensorFlow
|
28天前
|
存储 人工智能 算法
AI算法的道德与社会影响:探索技术双刃剑的边界
【8月更文挑战第22天】AI算法作为一把双刃剑,在推动社会进步的同时,也带来了诸多道德与社会挑战。面对这些挑战,我们需要以开放的心态、严谨的态度和创新的思维,不断探索技术发展与伦理规范之间的平衡之道,共同构建一个更加美好、更加公正的AI未来。
|
1月前
|
机器学习/深度学习 自然语言处理 负载均衡
揭秘混合专家(MoE)模型的神秘面纱:算法、系统和应用三大视角全面解析,带你领略深度学习领域的前沿技术!
【8月更文挑战第19天】在深度学习领域,混合专家(Mixture of Experts, MoE)模型通过整合多个小型专家网络的输出以实现高性能。从算法视角,MoE利用门控网络分配输入至专家网络,并通过组合机制集成输出。系统视角下,MoE需考虑并行化、通信开销及负载均衡等优化策略。在应用层面,MoE已成功应用于Google的BERT模型、Facebook的推荐系统及Microsoft的语音识别系统等多个场景。这是一种强有力的工具,能够解决复杂问题并提升效率。
42 2
|
1月前
|
机器学习/深度学习 监控 算法
目标检测算法技术
8月更文挑战第11天
|
1月前
|
机器学习/深度学习 算法 Java
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
这篇文章介绍了基于贪婪技术思想的Prim算法和Dijkstra算法,包括它们的伪代码描述、Java源代码实现、时间效率分析,并展示了算法的测试用例结果,使读者对贪婪技术及其应用有了更深入的理解。
算法设计(动态规划应用实验报告)实现基于贪婪技术思想的Prim算法、Dijkstra算法
|
1月前
|
机器学习/深度学习 数据采集 人工智能
理解并应用机器学习算法:从技术基础到实践应用
【8月更文挑战第10天】机器学习算法的应用已经深入到我们生活的方方面面,理解和掌握机器学习算法对于数据科学家、工程师乃至普通从业者来说都至关重要。通过本文的介绍,希望大家能够对机器学习有一个基本的认识,并学会如何将其应用于实际问题中。当然,机器学习是一个不断发展和演变的领域,只有不断学习和实践,才能跟上时代的步伐。
|
2月前
|
机器学习/深度学习 数据采集 人工智能
AI技术实践:利用机器学习算法预测房价
人工智能(Artificial Intelligence, AI)已经深刻地影响了我们的生活,从智能助手到自动驾驶,AI的应用无处不在。然而,AI不仅仅是一个理论概念,它的实际应用和技术实现同样重要。本文将通过详细的技术实践,带领读者从理论走向实践,详细介绍AI项目的实现过程,包括数据准备、模型选择、训练和优化等环节。
182 3
|
2月前
|
机器学习/深度学习 自然语言处理 算法
AIGC技术的核心算法与发展趋势
【7月更文第27天】随着人工智能技术的迅速发展,AIGC技术已经逐渐成为内容创造领域的一个重要组成部分。这些技术不仅能够帮助人们提高工作效率,还能创造出以往难以想象的新颖内容。本文将重点介绍几种核心算法,并通过一个简单的代码示例来展示如何使用这些算法。
54 7
|
2月前
|
算法
共识协议的技术变迁问题之Raft的选举算法进行如何解决
共识协议的技术变迁问题之Raft的选举算法进行如何解决
|
2月前
|
算法 安全 搜索推荐
AES(Advanced Encryption Standard)是一种广泛使用的对称密钥加密算法,由美国国家标准技术研究所(NIST)制定。
AES(Advanced Encryption Standard)是一种广泛使用的对称密钥加密算法,由美国国家标准技术研究所(NIST)制定。