精华内容
下载资源
问答
  • 哈夫曼编码压缩图像

    千次阅读 2020-06-15 10:09:47
    最近在上图像处理的课,要完成的一些作业就顺便分享到这上面来了。本来是编辑好了,就贴代码就好了,但由于新手操作,一放代码就出问题导致心态崩了。所以就不讲原理了,试试看代码怎么放吧。真的难受,我打了这么多...

    最近在学相关的东西,本来是编辑好了贴代码就行了,但由于新手操作,一放代码就出问题导致心态崩了。所以就不讲原理了,试试看代码怎么放吧。枯了。

    原理就看下图吧。(感觉变糊了)
    在这里插入图片描述

    代码思路大概就是,我认为只有出现在最后两位最小的概率值需要加码字0或者1。所以就可以正向直接求每一个概率值的码字。比如说第一个概率a1,排序后判断是否是最后一个,是的话就加一个码字0;如果是倒数第二个,就加码字1;如果两种情况都不是,就保持码字不变。

    值得注意的是代码中出现的(find(S(j,:)==loc)==L)、(find(S(j,:)==loc)==L-1)其实是想要找到当前求的概率的位置,其实就是想要实现(find(ai)==L-1)。但是由于每次最后两个概率相加之后都要重新排序,所以a1的位置不可能总是在第一个(即ai不可能总是在第i个),所以每次排序都要记下排序前ai的位置loc(第一次排序的位置就是i),然后根据这个loc来判断下一次排序后ai的位置是否在最后两个。

    loc:第一次排序时是i,因为哈夫曼编码先将概率从大到小排序;当不是第一次时,就是上一次排序的位置

    find(S(j,:)==loc):S保存的是每次排序前的位置,所以这一命令可以找到排序后,即当前该概率值ai的位置

    find(S(j,:)==loc)==L:判断是否在最后一个

    下面就是写的代码(尝试了几次,不知道怎么办我就只能贴图了):

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述

    展开全文
  • 哈夫曼编码实现图像压缩,是自己整理的讲稿,希望对大家有用
  • 在计算机信息处理中,“哈夫曼编码”是一种一致性编码法(又称“熵编码法”),用于数据的无损压缩。本文主要介绍了基于哈 夫曼编码图像压缩技术的原理、算法、过程,并利用VB6.0作为编程开发工具,开发了一个对256...
  • 利用四叉树与哈夫曼编码,实现分形压缩的快速算法的matlab程序
  • 图像编码哈夫曼压缩编码
  • 数据结构大作业——哈夫曼编码压缩BMP格式文件

    千次阅读 多人点赞 2020-04-17 10:21:06
    数据结构大作业——哈夫曼编码压缩BMP格式文件 首先需要了解BMP图像格式 BMP图像格式详解 其次需要了解哈夫曼编码如何对BMP文件进行压缩 哈夫曼压缩与解压缩 编程部分 从BMP文件中读取需要的内容 首先是自定义图像...

    数据结构大作业——哈夫曼编码压缩BMP格式文件

    首先需要了解BMP图像格式

    BMP图像格式详解

    其次需要了解哈夫曼编码如何对BMP文件进行压缩

    哈夫曼压缩与解压缩

    编程部分

    使用的头文件

    虽然这里用了using namespace std;,实际上C语言更多一点,只是为了使用类似于Python的字符串变量的加和功能(C语言本身的字符串拼接很多功能自己不熟练……

    #include <stdio.h>
    #include <windows.h>
    #include <string> 
    #include <bitset>
    
    using namespace std;
    

    从BMP文件中读取需要的内容

    首先是自定义图像文件头结构:
    需要BMP图像文件的文件头数据、信息头数据、BMP调色板数据(RGB)

    struct BMPHeader {
        BITMAPFILEHEADER BF;   		//文件头
        BITMAPINFOHEADER BI;		//信息头
        int rgb[256];				//BMP调色板
    };
    

    需要知道的数据有:
    图像的大小:
    图像的高度(以像素为单位)——biHeight
    图像的宽度(以像素为单位)——biWidth
    像素点占位数(以比特位/像素位单位)——biBitCount

    读取BMP图片文件

    int ReadBMP(string filename, BMPHeader & ih, unsigned char ** & data) 
    {
        FILE * fp;
        fp = fopen(filename.c_str(), "rb");
        if (fp == NULL) {
            return 0;
        } 
    //为所需信息赋值:
        fread(&ih.BF, sizeof(BITMAPFILEHEADER), 1, fp);			//如果没有读取头文件会导致 biBitCount直接成为40—— 位图信息头大小(40byte) 
        fread(&ih.BI, sizeof(BITMAPINFOHEADER), 1, fp);
        fread(&ih.rgb, sizeof(int), 256, fp);
    //只能打开256色位BMP图像:
        if (ih.BI.biBitCount != 8) {
            printf("文件打开失败,请选择正确文件格式!\n");
            return 0;
        }
    
        data = new unsigned char*[ih.BI.biHeight];
        int row_width = ih.BI.biWidth + (4 - ih.BI.biWidth % 4);
        for (int i = 0; i < ih.BI.biHeight; i++) {
            data[i] = new unsigned char[ih.BI.biWidth];
        }
        for (int i = 0; i < ih.BI.biHeight; i++) {
            for (int j = 0; j < ih.BI.biWidth; j++) {
                fread(&data[i][j], 1, 1, fp);//data赋值——将位图中每一个像素点的信息提取保存起来,作为数据的一部分。
            }
            if (ih.BI.biWidth % 4 > 0) {//偏移量定位
                fseek(fp, 4 - ih.BI.biWidth % 4, SEEK_CUR);
            }
        }
        fclose(fp);
        return 1;
    }
    

    使用BMP中的数据创建哈夫曼树并生成哈夫曼编码

    首先创建树节点:

    typedef struct node {
        int weight;					//权重
        int left;					//左子树
        int right;					//右子树
        int inrange;				//是否在检索范围内(接下来会解释)
    } HFTNode;
    
    

    生成哈夫曼树

    int CreateHFTree(HFTNode* HFTNodes, int* weights) {
        for (int i = 0; i < 256; i++) {
            HFTNodes[i].left =HFTNodes[i].right = -1;
            HFTNodes[i].weight = weights[i];
            HFTNodes[i].inrange = 0;
        }//结点初始化
        int range = 256;//初始化搜索范围:前256个叶子结点
    
        while (1) {		//循环构树
            int lc = SelectMinW(HFTNodes, range);	//寻找构树节点
            if (lc == -1) {		//说明到达了根结点处,哈夫曼树已经构建完毕
                break;
            }
            int rc = SelectMinW(HFTNodes, range);
            if (rc == -1) {
                break;
            }
            HFTNodes[range].left = lc;
            HFTNodes[range].right = rc;
            HFTNodes[range].weight = HFTNodes[lc].weight + HFTNodes[rc].weight;					//将左右子树的权值赋予根节点,该节点代替左右子树加入搜索范围进行下一轮搜索
            HFTNodes[range].inrange = 0;//边缘结点加入搜索
            range++;
            //很容易理解:每一轮循环将一个新的结点加入森林,而这个结点的编号恰好是range,搜索范围需要加1
        }
        return range;	//改点返回根结点的标号,用于确定后续过程的循环用条件
    }
    

    使用的另一个功能是针对该类型的选择函数:

    int SelectMinW(HFTNode* HFTNodes, int range) {
        int node = -1;
        for (int i = 0; i < range; i++) {		//只在范围内寻找
    	if(!HFTNodes[i].inrange&&HFTNodes[i].weight > 0)//判断是否在搜索范围以内(去除了权重为0的结点,因为用不到)
        	if (node == -1 || HFTNodes[i].weight < HFTNodes[node].weight)//判断是否是根节点或者是否满足较小条件
          	 	{node = i;}
            }
        //得到最小值的序号了!
        if (node != -1) {
            HFTNodes[node].inrange = 1;//把这个结点排出搜索范围之中
        }
        return node;//返回该较小值的序号
    }
    

    创建哈夫曼编码结构:

    由哈夫曼树构造哈夫曼编码:(从叶子结点到根节点)

    typedef struct
    {
    	int weight;		//权重
    	int parent;		//双亲结点
    	int lc;		//左孩子结点
    	int rc;		//右孩子结点
    } HFNode;
    
    typedef struct
    {
    	char cd[N];		//存放哈夫曼码
    	int start;
    } HCode;
    
    void CreateHCode(HFNode* HFNodes,HCode* hcode,int range)	//由哈夫曼树HFNodes构造哈夫曼编码hcode
    {
    	int i,f,c;
    	HCode hc;
    	for (i=0;i<range;i++)	//根据哈夫曼树构造所有叶子结点的哈夫曼编码
    	{
    		hc.start=range;c=i;
    		f=HFNodes[i].parent;
    		while (f!=-1)	//循环直到树根结点
    		{
    			if (HFNodes[f].lc==c)	//处理左孩子结点
    				hc.cd[hc.start--]='0';
    			else					//处理右孩子结点
    				hc.cd[hc.start--]='1';
    			c=f;f=HFNodes[f].parent;		//找到双亲结点向上查找
    		}
    		hc.start++;		//start指向哈夫曼编码最开始字符
    		hcode[i]=hc;		//由于并不需要记录中间结点,只对叶子结点进行处理
    	}
    }
    

    递归生成哈夫曼编码:(从根结点到叶子结点)

    void CHFTCode(HFTNode* HFTNodes, int pos, string bits, string * Code) {
    
    /********************函数初始条件********************/
        int l = HFTNodes[pos].left;
        int r = HFTNodes[pos].right;
        
    /********************递归终止条件********************/
        if (HFTNodes[pos].left == -1 && HFTNodes[pos].right == -1) {//左右孩子均为空->已经是叶子结点了,编码已经可以结束了
            Code[pos] = bits;			//赋值哈夫曼编码! 
            return;
        }
    
    /********************递归嵌套语句********************/
        CHFTCode(HFTNodes, r, bits + "1", Code);//将函数推向右孩子
        CHFTCode(HFTNodes, l, bits + "0", Code);//将函数推向左孩子
        //递归过程中哈夫曼编码的“前缀”得以保留。
    }
    

    这里的Code实际上是一个字符串数组,将每一结点的哈夫曼编码转化为一串字符串(仅看叶子结点:Code[i]=HCode[i].cd)。

    使用哈夫曼编码对文件进行压缩

    void HuffmanEncode(unsigned char * data[], int height, int width, const char *filename) 
    {
        int weights[256];						//256种点的权重
        memset(weights, 0, sizeof(int) * 256);	//初始化
        for (int i = 0; i < height; i++) 
    	{
            for (int j = 0; j < width; j++) 
    		{
                weights[data[i][j]]++;			//循环求每一种点的权重
            }
        }
    
        HFTNode HFTNodes[256 * 2-1];			//开辟需要的节点个数(哈夫曼树最多需要如此多个结点)
         string Code[256];						//为每一个叶子结点开辟哈夫曼编码空间
         
        int range = CreateHFTree(HFTNodes, weights);	
    											//返回根点值——确定查找范围
    
        CHFTCode(HFTNodes, range - 1, "", Code);
    
    											//开辟缓冲区
        int BuffLength = 0;
        for (int i = 0; i < 256; i++) {
            BuffLength += weights[i] * Code[i].size();
    											//计算所有哈夫曼编码的总长度以确定缓冲区的大小
        }
        char * Buff = new char[BuffLength];
        
       	//将压缩后的文件读入“缓冲区”——这个意义不严格
        int cur = 0;
        for (int i = 0; i < height; i++) {
            for (int j = 0; j < width; j++) {
                for (int k = 0; k < Code[data[i][j]].size(); k++) {
                    Buff[cur] = Code[data[i][j]][k];		//data的数据是像素点的信息
                    cur++;
                }
            }
        }
        //生成新的压缩文件 
        FILE* fp;
        fp = fopen(filename, "wb");			//新建文件,二进制写入
        int times = BuffLength / 32 + 1;	//int——>32位
        string total = "";
        total = total + Buff;				//一次性写入数据区
        for (int i = 0; i < 32 * times - BuffLength; i++) {
            total = total + "0";			//尾缀“000000000”——>存在不足现象——哈夫曼编码长度不确定
            								//在解压缩的时候每一个像素点对应的哈夫曼编码实际是确定的
            								//尾缀生成的像素可以在解压缩图的像素矩阵中排除
        }
        fwrite(&BuffLength, sizeof(int), 1, fp); //写数据的长度
        for (int i = 0; i < times; i++) 
    	{
            bitset<32> byte(total.substr(32 * i, 32));
    					//每次取total的32位,并以i为计数器向后推移
            unsigned long tmp = byte.to_ulong();
            fwrite(&tmp, sizeof(int), 1, fp);
            			//写入,写入……
        }
        fclose(fp);
    }
    

    生成新的文件

    int main() 
    {
    	//初始化 
        char readpath[50];
        printf("请输入BMP文件名(全英、不带文件类型后缀):");
        scanf("%s", readpath);
        unsigned char ** data;				//老二维数组了
    	//读取文件信息 
    	//char path1[]="";
        //strcat(path1, readpath);
    	//strcat(path1, ".bmp");//文件名的拼接
    	string path1 = "";
        path1 = path1 + readpath + ".bmp";
    	BMPHeader ih;
        if (ReadBMP(path1, ih, data)) {
            printf("图片 %s 读取成功.\n", readpath);
        } else {
            printf("图片 %s 读取失败.\n", readpath);
            return 0;
        }
    	//生成压缩后的哈夫曼文件
        string path2="";
        path2 = path2 + readpath + ".bmp.huf";
        HuffmanEncode(data, ih.BI.biHeight, ih.BI.biWidth, path2.c_str());
        printf("文件压缩成功.\n");
    }
    

    验证部分

    对压缩文件进行解压,并将得到的数据重新生成BMP文件

    展开全文
  • ​在上一期,我们介绍了一种特殊的数据结构 “哈夫曼树”,也被称为最优二叉树。没看过的小伙伴可以点击下方链接:漫画:什么是 ...因此,我们在计算机上看到的一切文字、图像、音频、视频,底层都是用二进制来存...

    ​在上一期,我们介绍了一种特殊的数据结构 “哈夫曼树”,也被称为最优二叉树。没看过的小伙伴可以点击下方链接:

    漫画:什么是 “哈夫曼树” ?

    那么,这种数据结构究竟有什么用呢?我们今天就来揭晓答案。

    34b012fdb75bf7a3592986a85ca2d977.png

    3d0d722fbeb9705cea9e1c345fcb4e8d.png

    cac9f49676283481b1a84c0320f2d9e8.png

    e108609efbea75fc18f0b4e04ff1b5a6.png

    计算机系统是如何存储信息的呢?

    计算机不是人,它不认识中文和英文,更不认识图片和视频,它唯一“认识”的就是0(低电平)和1(高电平)。

    因此,我们在计算机上看到的一切文字、图像、音频、视频,底层都是用二进制来存储和传输的。

    6ddbde89168065d9c6e050f3252be6c2.png

    从狭义上来讲,把人类能看懂的各种信息,转换成计算机能够识别的二进制形式,被称为编码

    编码的方式可以有很多种,我们大家最熟悉的编码方式就属ASCII码了。

    在ASCII码当中,把每一个字符表示成特定的8位二进制数,比如:

    5fba7c95e4caa7292ba67bedd01d7041.png

    显然,ASCII码是一种等长编码,也就是任何字符的编码长度都相等。

    3ec49dfa575f94ece1ec71ab325b0895.png

    954140f5c5634e68d956052fb2fd9d24.png

    5eea712f944be901c21708d247e7c095.png

    462578b3c895d7ad50449415d704d559.png

    为什么这么说呢?让我们来看一个例子:

    假如一段信息当中,只有A,B,C,D,E,F这6个字符,如果使用等长编码,我们可以把每一个字符都设计成长度为3的二进制编码:

    86053af3d077fe63e9cf685c63242c70.png

    如此一来,给定一段信息 “ABEFCDAED”,就可以编码成二进制的 “000 001 100 101 010 011 000 100 011”,编码总长度是27。

    79dcb94565e2d73c34d07c38664d3619.png

    但是,这样的编码方式是最优的设计吗?如果我们让不同的字符对应不同长度的编码,结果会怎样呢?比如:

    4ded0eda7b02edfc61743f841c4b1aa9.png

    如此一来,给定的信息 “ABEFCDAED”,就可以编码成二进制的 “0 00 10 11 01 1 0 10 1”,编码的总长度只有14。

    782d06ee9b33e69cf68b92526d17f6eb.png

    8d88f074d5ce1e41942ab0af0d7d3c8b.png

    aa872b79797b5b0424b1895a8ce19290.png

    2b5020f8be7447bf874c834981f352ba.png

    哈夫曼编码(Huffman Coding),同样是由麻省理工学院的哈夫曼博所发明,这种编码方式实现了两个重要目标:

    1.任何一个字符编码,都不是其他字符编码的前缀。

    2.信息编码的总长度最小。

    92f34ae131890484d6e3a91ca8c6e80b.png

    2b7b3e5dddc28289d5a8692da0ad3f56.png

    d6053d5851e6e11177adfe1cd426054b.png

    哈夫曼编码的生成过程是什么样子呢?让我们看看下面的例子:

    假如一段信息里只有A,B,C,D,E,F这6个字符,他们出现的次数依次是2次,3次,7次,9次,18次,25次,如何设计对应的编码呢?

    我们不妨把这6个字符当做6个叶子结点,把字符出现次数当做结点的权重,以此来生成一颗哈夫曼树:

    83f82e5ab35114d665b04c6489a136ae.png

    这样做的意义是什么呢?

    哈夫曼树的每一个结点包括左、右两个分支,二进制的每一位有0、1两种状态,我们可以把这两者对应起来,结点的左分支当做0,结点的右分支当做1,会产生什么样的结果?

    646e2248c8292838d1b1b9afbe9027e7.png

    这样一来,从哈夫曼树的根结点到每一个叶子结点的路径,都可以等价为一段二进制编码:

    1f9576ff79edb56a0a7e3fed6c40476d.png

    上述过程借助哈夫曼树所生成的二进制编码,就是哈夫曼编码

    现在,我们面临两个关键的问题:

    首先,这样生成的编码有没有前缀问题带来的歧义呢?答案是没有歧义。

    因为每一个字符对应的都是哈夫曼树的叶子结点,从根结点到这些叶子结点的路径并没有包含关系,最终得到的二进制编码自然也不会是彼此的前缀。

    其次,这样生成的编码能保证总长度最小吗?答案是可以保证。

    哈夫曼树的重要特性,就是所有叶子结点的(权重 X 路径长度)之和最小。

    放在信息编码的场景下,叶子结点的权重对应字符出现的频次,结点的路径长度对应字符的编码长度。

    所有字符的(频次 X 编码长度)之和最小,自然就说明总的编码长度最小。

    4020fadb0574bb10343d43d5853f43ca.png

    d65c758db4c7d2620a55f073c0472f64.png

    b410b010f07b4384b4e898875509ee36.png

    6715932d4b127d6c96c1b6b3bba9fc3c.png

    817ac492e4712d74078f4c81a347d4a4.png

    a022254b16a663c76127d1fbebac1b53.png

    6d3b1f50406d7880364e7de07382a62a.png

    30e8da103d58c1cc66ff401f764f5324.png
    1. private Node root;
    2. private Node[] nodes;
    3. //构建哈夫曼树
    4. public void createHuffmanTree(int[] weights) {
    5. //优先队列,用于辅助构建哈夫曼树
    6. Queue<Node> nodeQueue = new PriorityQueue<>();
    7. nodes = new Node[weights.length];
    8. //构建森林,初始化nodes数组
    9. for(int i=0; i<weights.length; i++){
    10. nodes[i] = new Node(weights[i]);
    11. nodeQueue.add(nodes[i]);
    12. }
    13. //主循环,当结点队列只剩一个结点时结束
    14. while (nodeQueue.size() > 1) {
    15. //从结点队列选择权值最小的两个结点
    16. Node left = nodeQueue.poll();
    17. Node right = nodeQueue.poll();
    18. //创建新结点作为两结点的父节点
    19. Node parent = new Node(left.weight + right.weight, left, right);
    20. nodeQueue.add(parent);
    21. }
    22. root = nodeQueue.poll();
    23. }
    24. //输入字符下表,输出对应的哈夫曼编码
    25. public String convertHuffmanCode(int index) {
    26. return nodes[index].code;
    27. }
    28. //用递归的方式,填充各个结点的二进制编码
    29. public void encode(Node node, String code){
    30. if(node == null){
    31. return;
    32. }
    33. node.code = code;
    34. encode(node.lChild, node.code+"0");
    35. encode(node.rChild, node.code+"1");
    36. }
    37. public static class Node implements Comparable<Node>{
    38. int weight;
    39. //结点对应的二进制编码
    40. String code;
    41. Node lChild;
    42. Node rChild;
    43. public Node(int weight) {
    44. this.weight = weight;
    45. }
    46. public Node(int weight, Node lChild, Node rChild) {
    47. this.weight = weight;
    48. this.lChild = lChild;
    49. this.rChild = rChild;
    50. }
    51. @Override
    52. public int compareTo(Node o) {
    53. return new Integer(this.weight).compareTo(new Integer(o.weight));
    54. }
    55. }
    56. public static void main(String[] args) {
    57. char[] chars = {'A','B','C','D','E','F'};
    58. int[] weights = {2,3,7,9,18,25};
    59. HuffmanCode huffmanCode = new HuffmanCode();
    60. huffmanCode.createHuffmanTree(weights);
    61. huffmanCode.encode(huffmanCode.root, "");
    62. for(int i=0; i<chars.length; i++){
    63. System.out.println(chars[i] +":" + huffmanCode.convertHuffmanCode(i));
    64. }
    65. }

    这段代码中,Node类增加了一个新字段code,用于记录结点所对应的二进制编码。

    当哈夫曼树构建之后,就可以通过递归的方式,从根结点向下,填充每一个结点的code值。

    e22e65fa19672e02ac0f79f578b1f7fc.png

    22176b6b29b4c0d60025bf4aa1dc913f.png

    在公众号后台回复“学习视频”,可以获得海量免费的IT视频课程哦~~

    展开全文
  • 哈夫曼编码和分形编码图像压缩技术初探。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。。
  • IMAGE_DCT.m //基于dct变换和哈夫曼编码图像压缩函数,并计算压缩率,返回解码图像。 img_block.m //图像分块操作函数 main_.m //交互式图形界面代码,也是测试运行的主要入口 pingYi.m //图像平移函数 sf.m /...
  • 字符编码压缩哈夫曼树的应用很广,哈夫曼编码就是其在电讯通信中的应用之一。广泛地用于数据文件压缩的十分有效的编码方法,其压缩率通常在 20% ~ 90% 之间。在电讯通信业务中,通常用二进制编码来表示字母或其他...
    e0fb3340-4a13-eb11-8da9-e4434bdf6706.png

    引言

    在上一期,我们介绍了什么是哈夫曼树以及哈夫曼树的构建过程,本期我们接着介绍哈夫曼树的用途。

    字符编码压缩

    哈夫曼树的应用很广,哈夫曼编码就是其在电讯通信中的应用之一。广泛地用于数据文件压缩的十分有效的编码方法,其压缩率通常在 20% ~ 90% 之间。

    在电讯通信业务中,通常用二进制编码来表示字母或其他字符,并用这样的编码来表示字符序列。

    在计算机当中,因为计算机不是人,不能识别图像、声音、视频等内容,对于计算机来讲,它只能认识二进制的 0 和 1 ,在数字电子电路中,逻辑门的实现直接应用了二进制,因此现代的计算机和依赖计算机的设备里都用到二进制。

    我们在计算机上看到的一切的图像、声音、视频等内容,都是由二进制的方式进行存储的。

    简单来讲,我们把信息转化为二进制的过程可以称之为编码,在计算机的世界里,编码有很多种格式,比如我们常见的:ASCII 、 Unicode 、 GBK 、 GB2312 、 GB18030 、 UTF-8 、 UTF-16 等等。

    编码方式从长度上来分大致可以分为两个大类:

    • 定长编码:定长仅表明段与段之间长度相同,但没说明是多长。
    • 变长编码:变长就是段与段之间的长度不相同,同样也不定义具体有多长。

    在最初的设计中, ASCII 编码就是采用的定长编码的方案,使用定长一字节来表示一个字符。

    举个栗子,假如我们对 「hello」 进行编码,使用定长编码,为了方便,采用了十进制,主要是因为我懒,原理与二进制是一样的。

    字符编码
    h00
    e01
    l02
    o03

    假设我们现在有个文件,内容是 00000001 ,假如定长 2 位(这里的位指十进制的位)是唯一的编码方案,用它去解码,就会得到 「hhhe」 (可以对比上面的编码, 00 代表 h ,所以前 6 个 0 转化成 3 个 h ,后面的 01 则转化成 e )。

    但是,如果定长 2 位不是唯一的编码方案呢?如上图中的定长 4 位方案,如果我们误用定长 4 位去解码,结果就只能得到「he」( 0000 转化为 h , 0001 转化为 e )

    e3fb3340-4a13-eb11-8da9-e4434bdf6706.png

    随着时代的发展,不仅老美要对他们的英文进行编码,我们中国人也要对汉字进行编码,而早期的 ASCII 码的方案只有一个字节,对我们汉字而言是远远不够的,所以在我们的汉字编码方案 GB2312 中,汉字是使用两个字节来表示的(这也是迫不得已的事,一字节压根不够用) 。

    再多说一句,实际上我们的 GB2312 是一种变长的编码方案,主要是为了兼容一个字节的 ASCII 码。

    随着计算机在全世界的推广,各种编码方案都出来了,彼此之间的转换也带来了诸多的问题。采用某种统一的标准就势在必行了,于是乎天上一声霹雳, Unicode 粉墨登场!

    不过 Unicode 对于只需要使用到一个字节的 ASCII 码来讲,让他们使用 Unicode ,多少还是不是很愿意的。

    比如 「he」 两个字符,用 ASCII 只需要保存成 6865 ( 16 进制),现在则成了 00680065 ,前面多的那两个 0 (用作站位) ,基本上可以说是毫无意义,用 Unicode 编码的文本,原来可能只有 1KB 大小,现在要变成 2KB ,体积成倍的往上涨。

    最终, Unicode 编码方案逐渐演化成了变长的 UTF-8 编码方案,并且 UTF-8 是可以和 ASCII 码进行兼容。

    UTF-8 因为能兼容 ASCII 而受到广泛欢迎,但在保存中文方面,要用 3 个字节,有的甚至要 4 个字节,所以在保存中文方面效率并不算太好,与此相对, GB2312 , GBK 之类用两字节保存中文字符效率上会高,同时它们也都兼容 ASCII ,所以在中英混合的情况下还是比 UTF-8 要好,但在国际化方面及可扩展空间上则不如 UTF-8 了。

    所以如果有进军国际的想法,那么最好还是使用 UTF-8 编码。

    哈弗曼编码

    哈弗曼编码是一种不定长的编码方式,是由麻省理工学院的哈夫曼博所发明,这种编码方式实现了两个重要目标:

    • 任何一个字符编码,都不是其他字符编码的前缀。

    • 信息编码的总长度最小。

    干巴巴的,还是接着举例子:

    如果我们对 「ABACCDA」 进行编码,假设 A, B, C, D 的编码分别为 00, 01,10, 11。

    那么 「ABACCDA」 编码后的结果是 「00010010101100」 (共 14 位),我们解码的时候只需要每两位进行拆分,就可以恢复编码前的信息了。

    那我们如果用哈弗曼编码的方式进行编码呢?

    第一件事情是要确定每个字母的权值(出现频率), 「ABACCDA」 这个字符串中 A, B, C, D 的权值(出现的频率)分别为 0.43, 0.14, 0.29, 0.14 。

    有了权值,我们可以构造一个哈弗曼树了,感兴趣的同学可以自己画一下,下面这个是我画的:

    e5fb3340-4a13-eb11-8da9-e4434bdf6706.png

    编码的结果就显而易见了:A:0, C:10, B:110, D:111 。

    刚才那个 「ABACCDA」 编码后的结果就是 「0110010101110」 (共 13 位)。

    上面我们知道了哈夫曼编码如何编码,那么我们拿到了一个经过哈弗曼编码后的代码,如何进行译码工作呢?

    首先还是要知道每个字母的权重是多少,然后画出来这个哈弗曼树,接下来,就可以对照着这个哈弗曼树进行译码工作了。

    在译码的过程中,若编码是 「0」 ,则向左走。若编码是 「1」 ,则向右走,一旦到达叶子结点,则译出一个字符。然后不停的重复,直到这个编码的结束,就是我们需要的原内容了。

    参考

    https://www.cnblogs.com/wkfvawl/p/9783271.html

    https://my.oschina.net/goldenshaw/blog/307708

    感谢阅读e7fb3340-4a13-eb11-8da9-e4434bdf6706.png

    eafb3340-4a13-eb11-8da9-e4434bdf6706.gif

    展开全文
  • 哈夫曼编码,又称为霍夫曼编码,是一种字符编码。 在计算机数据压缩处理中,霍夫曼编码使用变长编码表()对源符号(如文件中的一个字母)进行编码,其中变长编码表是通过一种评估来源符号出现几率的方法得到的,出现...
  • 代码实现huffman图像压缩的过程:从内存中读出图像数据,计算各数据概率;...对图像数据就行huffman编码压缩;将压缩后的数据存入内存;再将该数据进行解码解压。可将解压后的数据与原数据进行对比,是完全一致的。
  • 监控摄像机的工作流程数字摄像机工作原理是通过图像传感器(CMOS或CCD)将视频图像转为模拟信号,模拟信号经过模数转换器(A/D)转换为数字信号,而后经过编码压缩,通过网络接口输出到存储显示设备。视频的压缩方式目前...
  • 图像压缩编码哈夫曼树)

    千次阅读 2019-01-16 12:13:28
    1.首先图片压缩编码对不同文件的压缩效率是不一样的  这也是我在最后发现...哈夫曼编码压缩解压缩实现&amp;不同类型文件压缩比的测试 https://blog.csdn.net/to_be_better/article/details/50431352   ...
  • 哈夫曼编码 26 [M,N] = size(Iq); 27 I1 = Iq(:); 28 P = zeros( 1 , 256 ); 29 for i = 0 : 255 30 P(i+ 1 ) = length(find(I1 == i))/(M* N); 31 end 32 k = 0 : 255 ; 33 dict = ...
  • 本实例是用Matlab编写的对图像进行无损压缩的.m文件,里面要处理的文件是comp你可以换成你想要压缩的文件,功能不是很强的,但很实用,供初级人员学习用
  • 图像、电影、音乐,数据不仅仅限制于文本,绝大多数数据会有冗余,例如在文本文件中,很多字符出现的频率远高于其他字符,图片编码中也存在大片的相同区域,电影、声音等类似信号的文件都含有大量重复模式。...
  • Huffmann哈夫曼编码实现,经典图像压缩算法
  • 哈夫曼编码

    2017-03-24 21:06:58
    1.利用程序huff_enc和huff_dec进行以下操作(在每种情况下,利用由被压缩图像生成的码本) (a)对Sena、Sensin和Omaha图像进行编码。 (b)编写一段程序,得到相邻像素之差,然后利用huffman对差值图像进行编码。...
  • 哈夫曼(Huffman)编码是一种常用的压缩编码方法,是 Huffman 于 1952 年为压缩文本文件建立的。它的基本原理是频繁使用的数据用较短的代码代替,较少使用的数据用较长的代码代替,每个数据的代码各不相同。这些代码都...
  • Matlab-数字图像编码实验-无损编码/压缩算法实验 问题 实现哈夫曼压缩, 计算原图和压缩以后的尺寸,计算压缩率并比较分析 结果???? Matlab代码???? clear; clear all; A=imread('01.jpg'); I=rgb2gray(A);...
  • 哈夫曼编码应用于数据文件和图像压缩的编码方式。其压缩率通常在20%~90%之间、在进行远距离通信时,通常需要把将要传送的文字转换为由二进制字符组成的字符串,并使要传送的电文总长度尽可能的短。显然只要将点文章...
  • 1.利用程序huff_enc和huff_dec进行以下操作(在每种情况下,利用由被压缩图像生成的码本)。 对sena、sensin和omaha图像进行编码如下:  压缩前  压缩后  压缩比   文件名称 文件大小 文件名称
  • 作者 孟欢 包海燕 潘飞 电子科技大学 微电子与固体电子学院(四川 成都 610054)本文...摘要:在图像处理、文件传真、视频压缩编码中,哈夫曼编码是最常用的一种编码方式。本文设计并实现了对一段数字序列进行哈夫曼...
  • 算法学习-哈夫曼编码(c++实现)

    千次阅读 2014-11-27 02:14:20
    哈夫曼编码虽然简单,但是是一个非常重要的编码方式,它解决了数据存储的一个非常重要的问题:压缩!它的编码方式是最优的,无损的,尤其在图像中使用的非常多。下面讲下它的原理。 编码方式 哈夫曼编码的构造是...
  • 首先咱们先聊聊什么是数据压缩:信息时代,数值、文字、语言、音乐、图形、动画、静图像、电视视频图像等多种媒体信息的数量惊人,数据的表达、组织、存储和传输都有很大难度。大数据量的图像信息会给存储器的存储...
  • 通过“图片压缩编码”的编程实践,学习树、二叉树、哈夫曼树、哈夫曼编码及其应用。 (1)掌握二叉树的存储结构 (2) 掌握并理解Huffman树、Huffman编码等知识和应用 (3)掌握文件的操作 (4)使用Huffman算法实现图像压缩...
  • 哈夫曼编码是一种高效的编码方式,在信息存储和传输过程中,用于对信息进行压缩。 计算机系统是如何存储信息的呢? 计算机不是人,它不认识中文和英文,更不认识图片和视频,它唯一“认识”的就是0(低电平)和1...

空空如也

空空如也

1 2 3 4 5 ... 7
收藏数 124
精华内容 49
关键字:

哈夫曼编码压缩图像