【原创】这次更新比较慢,译码过程比想象中复杂一些,更主要是译出来的DCT系数无法确定是否正确,要想验证就需要再进行正向压缩编码,再次形成jpeg图像验证正确,后续工作正在开展,这里就说一说译码的主要思路和过程。

说到译码过程,首先要了解jpeg图像数据流的组成:

数据流是以MCU(最小编码单元)为基本单位的,一个MCU又由若干个Y,Cr,Cb颜色分量单元组成,这里的颜色分量单元可以看作是一个8*8的矩阵,也就是译码过程中的最小单元了,可能读者对于这些结构的关系还不是很了解,OK,我们来看看下面这个图(word绘制,简洁易懂~)

一个MCU中的颜色分量单元个数由jpeg文件头中SOF0段中定义,SOF0段中有定义每个颜色分量的水平和垂直采样因子,这里假设水平,垂直采样因子分别为2:1:1,2:1:1。这个采样因子是什么意思呢,我们来看看上图。假设上图是一个16*16(像素)的图片,可以分成四个8*8的小矩阵,每个8*8小矩阵都是一个颜色分量单元,Y分量的水平采样因子为2则说明在一个MCU的水平方向上采样两次,垂直采样因子同理,因此在本例中一个MCU会有4个Y颜色分量单元,1个Cr颜色分量单元,1个Cb颜色分量单元。从这里我们也能得知一个MCU的大小是由水平、垂直采样因子的最大值决定的,假设分别为V_max和H_max,则MCU的大小为(V_max*8)*(H_max*8)。这些结构的关系如下:

颜色分量单元组成MCU,MCU组成数据流

现在再来看看图片中含有多个MCU的情况,假设有四个,允许我再盗用一下上面的图哈,

可以看到,一幅图像里MCU是按行排列的,在数据流中保存的顺序就是MCU1、MCU2、MCU3、MCU4。。。。每个MCU中的顺序为Y1、Y2、Y3、Y4、Cr、Cb(这里是按采样因子4:1:1,其他也类似,总之就是按Y、Cr、Cb的顺序)。

了解了数据流的组成,下面就要开始译码工作了,这也是个烦杂的过程。。。。

在JPEG压缩过程中,不仅使用了哈夫曼编码,还使用了RLE行程编码和差分编码,真是极尽压缩啊。。。这三种编码在JPEG中是综合运用的,可以达到很好的压缩效果。本人这里就从解码的角度来进行介绍了,个人觉得从解码角度来理解更为方便~

首先,JPEG中直流和交流系数是分开进行编码的,也就是说译码的过程有些许不同,而且采用的哈夫曼树也是不同的,其次,JPEG中的每个颜色分量也是分开进行编码的,不同颜色分量采用的哈夫曼树也是不同的,具体的每个颜色分量的交直流系数采用的哈夫曼树ID可以从文件头的SOF0段中得知,SOF0段的内容可以看我的另一篇博文

http://www.cnblogs.com/gungnir2011/p/3615273.html

在数据流中是以bit为单位存储信息的,每个DCT系数(8*8)矩阵都有1个直流分量和63个交流分量,每个矩阵的译码都先译直流分量,再译交流分量,具体的步骤如下:

1.按bit读取,对直流哈夫曼树进行搜索,直到搜索到叶子节点,说明这时命中了一个编码,哈夫曼树的权值代表还需继续读取多少bit,比如权值为0x04,则说明继续读取4bit,这4bit的值根据译码表(后面会给出)获取的值就是直流DCT系数。

2.继续按bit读取,这时是对交流哈夫曼树进行搜索,直到叶子节点,这里的权值和直流不一样,权值的高四位代表即将译出的交流DCT系数前面有多少个0,这里是行程编码,低四位代表还需继续读取多少bit,之后获得系数和直流一样。

3.不断重复步骤2,直到满足结束条件:

  • 读到的权值为0,则该矩阵中之后的所有值都为0,并结束该矩阵的译码;
  • 63个交流分量都已全部译完。

译码表:

编码数值 实际数值
0,1 -1,1
00,01,10,11 -3,-2,2,3
000,001,010,011,100,101,110,111 -7,-6,-5,-4,4,5,6,7
................. ......................

之后就是译完一个矩阵译下一个,译完一个颜色分量译下一个,直到所有译完~~

当然这只是理论,在实践过程中会遇到各种奇葩问题,下次更新中会详细说说我遇到的各种奇葩错误,现在还未完全写完译码部分,为了进度现在直接在研究libjpeg开源库,之后可能会找个时间把译码做完,未完待续~~

---------------------------------------------------分割线-------------------------------------------------------------

现在来说说我遇到的各种奇葩问题:

首先,关于权值问题,刚开始译码的时候没有考虑到交流权值的低四位和直流权值为0的情况,运行自然就挂掉了。。。遇到这种情况,就说明继续读取0bit,0bit的值就是0,所以遇到这种情况得到的系数值就应该是0。

其次,有些8*8的矩阵译码有时候会出现译出第65个值,按道理来说应该只有64个值的,这种情况我还没有弄清楚是什么原因,但是在没有出现这种情况以前译码的结果都是对的,当出现这种情况以后大概再过几个矩阵之后所有的译码结果基本上都不对。。。具体原因我会继续分析,因此在这里再次推荐使用库函数进行操作。

再来说说一些注意事项和建议吧:

  • 记录当前读取到第几个字节的第几位时,记录位数一定要计算好,一位出错,之后全错。。。像一些系数译出来要退出,以及其他退出情况时,退出之前要将位数往后移;
  • 对于继续读取的位数为0的情况最好单独进行处理,逻辑也容易清晰一些;
  • 读取出来的二进制要根据上述译码表进行译码,不能直接转换,否则结果不对;

其他就是一些编程时需要注意的小问题了,就不一一叙说了。

【原创】JPEG图像密写研究(三) 数据流译码的更多相关文章

  1. 【原创】JPEG图像密写研究(二) 哈夫曼树的建立

    [原创]记录自己研究的过程,仅供参考,欢迎讨论... 在根据JPEG图像文件结构读取完文件后,提取出其中DHT段,利用其中内容建立哈夫曼树,便于之后译码工作.这里需要注意的是文件中的哈夫曼表数量不固定 ...

  2. JPEG图像密写研究(一) JPEG图像文件结构

    [转载]转载自http://www.cnblogs.com/leaven/archive/2010/04/06/1705846.html JPEG压缩编码算法的主要计算步骤如下: (0) 8*8分块. ...

  3. JPEG图像压缩算法流程详解

    JPEG图像压缩算法流程详解 JPEG代表Joint Photographic Experts Group(联合图像专家小组).此团队创立于1986年,1992年发布了JPEG的标准而在1994年获得 ...

  4. 怎么用ABBYY将PDF转换为JPEG图像

    FineReader Mac版,全称ABBYY FineReader Pro for Mac,是一款流行的OCR图文识别软件,可快速方便地将扫描纸质文档.PDF文件和数码相机的图像转换成可编辑.可搜索 ...

  5. CUDA 实现JPEG图像解码为RGB数据

    了解JPEG数据格式的人应该easy想到.其对图像以8*8像素块大小进行切割压缩的方法非常好用并行处理的思想来实现.而其实英伟达的CUDA自v5.5開始也提供了JPEG编解码的演示样例.该演示样例存储 ...

  6. 多媒体(4):JPEG图像压缩编码

    (重要的事放前面)此JPEG的C++实现见 https://github.com/chencjGene/SoftEngineering/tree/master/JPEG 目录 多媒体(1):MCI接口 ...

  7. opencv IplImage各参数详细介绍以及如何从一个JPEG图像数据指针转换得到IplImage

    这篇文章里介绍得最清楚了.http://blog.chinaunix.net/uid-22682903-id-1771421.html 关于颜色空间  RGB颜色空间已经非常熟悉了.HSV颜色空间需要 ...

  8. 假设高度已知,请写出三栏布局,其中左栏、右栏各为300px,中间自适应的五种方法

    假设高度已知,请写出三栏布局,其中左栏.右栏各为300px,中间自适应的五种方法 HTML CSS 页面布局 题目:假设高度已知,请写出三栏布局,其中左栏.右栏各为300px,中间自适应 <!D ...

  9. python 在图像上写中文字体 (python write Chinese in image)

    本人处理图像的时候经常使用opencv的包,但是 cv2.putText 显示不了中文,所以查找了如何在python在图像上写中文的方法,在伟大的Stack Overflow上面找到一个方法,分享给大 ...

随机推荐

  1. Oracle EBS 如何月结[Z]

    概述应付模块的多数业务基于采购和库存的操作,因此应付模块的月结应该在采购模块和库存模块月结后才能关闭会计期.月结步骤在每个会计期末,应付模块的月结应遵循以下流程:1.检查业务是否全部录入;2.检查是否 ...

  2. mysql安装常见问题(系统找不到指定的文件、发生系统错误 1067 进程意外终止)

    在安装mysql时总是会遇到这样那样的问题,每次重新安装都会花很多时间来排查.在网上其实有很多相关的文章,但很多都只讲了方法,但没讲具体细节问题,导致无法解决问题.其实有时候知道问题的原因,但总是因为 ...

  3. EC读书笔记系列之5:条款9、条款10

    条款9 绝不在构造和析构过程中调用virtual函数 记住: ★在构造和析构期间不要调用virtual函数,∵这类调用从不下降至derived class ---------------------- ...

  4. Jexus 配置多个站点

    一:jexus配置站点的文件在 siteconf文件夹中,里面有多少个配置文件,就可以配置多少个站点 如我的里面有3个配置文件,其中default是原始文件,site1和siteconf就是我网站的配 ...

  5. Git 系列(三):建立你的第一个 Git 仓库

    现在是时候学习怎样创建你自己的 Git 仓库了,还有怎样增加文件和完成提交. 在本系列前面的文章中,你已经学习了怎样作为一个最终用户与 Git 进行交互:你就像一个漫无目的的流浪者一样偶然发现了一个开 ...

  6. Swift 基本数据类型

    Swift 1,Swift支持所有C和Objective-C的基本类型,支持面向过程和面向对象的编程机制. 2,Swift提供了两种功能强劲的集合类型:数组和字典. 3,元组. 4,可选类型. 5,S ...

  7. android应用程序的组成部分

    android 应用程序的组成部分 activity 应用表示层,应用程序中每一个UI都是通过activity类或者多个扩展实现的.activity使用fragment和视图来布局和显示信息,以及响应 ...

  8. Extjs springmvc session 超时 处理

    如果你的项目使用ExtJS作为表现层,你会发现,SESSION超时控制将是一个问题.本文将就自己的经验,来解决这一问题,当然,解决问题并非只有一种方法,我只是提出我的方法.首先,做超时控制,必需使用过 ...

  9. PHP fopen和fwrite函数实现创建html页面

    思路 用fopen函数和fread函数得到模板,然后用str_replace函数替换模板标签为变量,最后用fwrite函数输出新的HTML页面 index.html模板页面 <!DOCTYPE ...

  10. codeforces 21D. Traveling Graph 状压dp

    题目链接 题目大意: 给一个无向图, n个点m条边, 每条边有权值, 问你从1出发, 每条边至少走一次, 最终回到点1. 所走的距离最短是多少. 如果这个图是一个欧拉回路, 即所有点的度数为偶数. 那 ...