毫无疑问,编码端的QDCT和解码端的QDCT完全相同,下面从编码端提取QDCT. 为简便起见,仅提取第一帧第一个宏块第一个4*4块的QDCT.JM8.6编码器最核心的编码函数是encode_one_macroblock,该函数找到了残差并进行了整数DCT变换及量化,然后Zigzag scan和Run-Level编码. 在write_one_macroblock函数中进行了熵编码和写码流, 故在encode_one_macroblock和write_one_macroblock之间便可以提取QDCT.

首先看到变量img->cofAC[][][][],在global.h中是这么定义的:int ****cofAC;//AC coefficients[8x8block][4x4block][level/run][scan_pos]. 下面来具体探讨一下4个中括号里面内容的意思.img->cofAC[][][][]中第一个中括号中的值可为0,1,2,3,4,5,分别与下图红色标记位置(8*8的块)相对应: (下面数据中的像素值不用管,之所以列出来,是为了说明cofAC数组中各[]的对应位置)

====================== Y Data ======================

0                                                     1

+----------------+----------------+----------------+----------------+
| 43,216,254,249,|251,254,254,253,|251,252,254,254,|254,254,254,253,|
| 49,198,193,211,|228,205,213,185,|211,207,186,248,|198,203,208,183,|
| 48,194,177,171,|197,173,185,136,|191,195,138,179,|142,176,177,135,|
| 46,214,225,169,|177,189,198,160,|203,208,177,165,|173,196,191,156,|

+----------------+----------------+----------------+----------------+
|
41,185,208,180,|203,228,226,200,|214,226,225,227,|228,225,224,210,|
| 31,130,173,178,|215,230,221,212,|220,229,227,228,|229,227,226,226,|
| 29,119,194,216,|211,213,219,222,|225,223,220,219,|218,218,218,218,|
| 25,126,219,224,|217,224,227,227,|227,226,225,224,|220,220,221,222,|

            2                                                    
 3

+----------------+----------------+----------------+----------------+
| 26,131,215,223,|226,225,225,225,|225,226,223,219,|221,221,219,220,|
| 30,136,216,226,|223,224,225,225,|224,221,217,221,|222,219,220,226,|
| 30,136,216,227,|224,224,225,223,|221,218,221,216,|211,224,224,211,|
| 29,135,217,225,|222,221,222,222,|221,209,181,155,|186,210,186,164,|
+----------------+----------------+----------------+----------------+
| 29,134,216,224,|226,230,230,227,|206,177,146,113,|149,162,147,150,|
| 29,135,219,231,|225,201,190,185,|163,144,153,140,|127,143,165,184,|
| 30,139,210,192,|165,142,134,133,|143,141,129,138,|150,178,201,207,|
| 30,125,166,145,|144,154,132,111,|118,161,175,180,|204,214,213,209,|
+----------------+----------------+----------------+----------------+

====================== U Data
======================

4
+----------------+----------------+
|131,136,136,137,|137,136,137,137,|
|133,146,146,146,|146,148,147,147,|
|128,131,132,130,|131,130,128,129,|
|127,125,124,123,|123,121,120,121,|
+----------------+----------------+
|128,125,124,123,|122,120,120,119,|
|128,125,124,122,|117,115,116,114,|
|127,123,119,117,|113,115,118,118,|
|125,119,116,116,|119,120,121,118,|
+----------------+----------------+

====================== V Data
======================

                         
5

+----------------+----------------+
|120,106,109,104,|105,107,106,104,|
|115, 80, 81, 78,| 81, 75, 77, 78,|
|125,115,116,115,|116,116,117,115,|
|129,130,131,130,|130,131,131,131,|
+----------------+----------------+
|128,129,130,130,|131,131,131,131,|
|128,129,130,130,|132,132,132,133,|
|128,129,131,131,|133,133,133,133,|
|129,131,132,133,|133,133,133,133,|
+----------------+----------------+

img->cofAC[][][][]中第二个中括号中的值可为0,1,2,3,分别对应每个8*8块中的4个4*4的块,对应顺序为:

0                            1

+----------------+----------------+

|
43,216,254,249,|251,254,254,253,|
| 49,198,193,211,|228,205,213,185,|
| 48,194,177,171,|197,173,185,136,|
| 46,214,225,169,|177,189,198,160,|

2                           3

+----------------+----------------+

|
41,185,208,180,|203,228,226,200,|
| 31,130,173,178,|215,230,221,212,|
| 29,119,194,216,|211,213,219,222,|
| 25,126,219,224,|217,224,227,227,|

这样通过第一个中括号和第二个中括号的值就可以定位到每一个具体的4*4块. 假设第一个中括号为i,第二个中括号为j,那么下面的循环就能实现对24个4*4块的遍历:

for(i = 0; i < 6; i++)

for(j = 0; j < 4; j++)

接着看img->cofAC[][][][]的第三个中括号,里面的取值可为0,1.当取值为0的时候,这个数组表示的是Run
Level编码中的level,当取值为1的时候,这个数组表示的是Run Level编码中的run.这一部分涉及到游程编码的概念,下面举例说明:

假设这是一个4*4块的QDCT,现在进行zigzag
scan后就形成了:

10  9  0  3  0  1 
0  8  2  -1  -3  0  -5  0  0  0

现在进行Run Level编码后得到:

Level:10  9 
3  1  8  2  -1  -3  -5  0 
0  0  0  0  0  0

Run:   0   0  1  1  1 
0    0   0   1   0  0 
0  0  0  0  0

接着分析img->cofAC[][][][]的第四个中括号.第一个中括号和第二个中括号确定了具体的4*4块的位置,第三个中括号确定了是RunLevel还是Level,那么第四个中括号便是定位分量的具体位置了,其取值范围是0到15. 这样,所有的都一目了然了。

现在要在encode_one_macroblock和write_one_macroblock之间提取第一帧第一个宏块的第一个4*4块的QDCT.显然这是很容易的,只需给出img->cofAC[0][0][0][k]和img->cofAC[0][0][1][k]即可,代码如下:

[cpp] view
plain
 copy

  1. encode_one_macroblock ();
  2. if(1 == controlTimes) // controlTimes是static int型的变量,用于记录函数被调用的次数
  3. {
  4. for(i = 0; i < 16; i++)
  5. printf("%-4d", img->cofAC[0][0][0][i]);
  6. printf("\n\n");
  7. for(i = 0; i < 16; i++)
  8. 10.         printf("%-4d", img->cofAC[0][0][1][i]);
  9. 11.     printf("\n\n");

12. }

  1. 13.

14. write_one_macroblock (1);

提取的数据为:(实验发现,从编码端和解码端提取的数据完全一致)

9   -12 3   3   -3  -11 -5 
1   -1  -2  1   0   0  
0   0   0

0   
0   0   0    0   
0    0   0    0   2  
1   0   0   0   0   0

其中上一行为Level值,下一行为Run的值,于是可以得到Run Level编码前的zigzag scan的值,即为:

9  -12 
3  3  -3  -11  -5  1  -1  0  0 
-2  0  1  0  0

进一步可以得到zigzag扫描前的值,即为:

9   -12   -11   -5

3    -3     
1     0

3    -1     -2    
1

0    
0      0     0

这就是最后的QDCT矩阵, 为了进一步验证这个QDCT矩阵的正确性,我们可以用matlab来进行一个粗略的仿真:

第一帧的第一个宏块的第一个4*4块的原始像素值为:

| 43,216,254,249,|
| 49,198,193,211,|
| 48,194,177,171,|
| 46,214,225,169,|

下面用matlab计算QDCT,
matlab代码为:

[plain] view
plain
 copy

  1. clear
  2. clc
  3. org = [43 216 254 249;...
  4. 49 198 193 211;...
  5. 48 194 177 171;...
  6. 46 214 225 169];
  7. res = org - 128; % 残差矩阵
  8. % 下面对残差矩阵进行DCT变换并量化

10. T = [1 1 1 1 ; 2 1 -1 -2; 1 -1 -1 1; 1 -2 2 -1];

11. f = T * res * T';

12. a = 0.5;

13. b = 0.4 ^ 0.5;

14. X = [a*a a*b/2 a*a a*b/2;...

  1. 15.     a*b/2 b*b/4 a*b/2 b*b/4;...
  2. 16.     a*a a*b/2 a*a a*b/2;...
  3. 17.     a*b/2 b*b/4 a*b/2 b*b/4];

18. Y = f .* X / 16  % 从H.264visa查得QP=28,对应的量化步长刚好为16

所得结果为:

Y =

9.5156 
-12.4021  -10.7031   -5.5340
    2.7373   -2.9750   
1.1167   -0.4563
    2.7344   -1.5713  
-1.8594    0.9684
   -0.1383   -0.0813   
0.0395    0.0063

而JM8.6编码器得到的结果为:

9   -12  
-11   -5

3   
-3      1     0

3    -1     -2    
1

0    
0      0     0

对比结果,不言而喻.

如何在JM8.6编码端提取QDCT?的更多相关文章

  1. 会议更流畅,表情更生动!视频生成编码 VS 国际最新 VVC 标准

    阿里云视频云的标准与实现团队与香港城市大学联合开发了基于 AI 生成的人脸视频压缩体系,相比于 VVC 标准,两者质量相当时可以取得 40%-65% 的码率节省,旨在用最前沿的技术,普惠视频通话.视频 ...

  2. 【视频编解码·学习笔记】8. 熵编码算法:基本算法列举 & 指数哥伦布编码

    一.H.264中的熵编码基本方法: 熵编码具有消除数据之间统计冗余的功能,在编码端作为最后一道工序,将语法元素写入输出码流 熵解码作为解码过程的第一步,将码流解析出语法元素供后续步骤重建图像使用 在H ...

  3. Tomcat 中文乱码 设置UTF-8编码 问题解决办法

    在Java Web开发中,http请求带有中文字符的URI如果不处理容易出现乱码问题:这是因为Tomcat容器默认编码是iso-8859-1引起的,因此要避免出现乱码就要需要做相应的处理.解决办法如下 ...

  4. 将RegEx(正则表达式提取器)与JMeter一起使用

    JMeter的,最流行的开源性能测试工具,可以工作正则表达式,用正则表达式提取.正则表达式是一种用于通过使用高级操作提取文本的必需部分的工具.正则表达式在测试Web应用程序时很流行,因为它们可用于验证 ...

  5. ffmpeg实时编码解码部分代码

    程序分为编码端和解码端,两端通过tcp  socket通信,编码端一边编码一边将编码后的数据发送给解码端.解码端一边接收数据一边将解码得到的帧显示出来. 代码中的编码端编码的是实时屏幕截图. 代码调用 ...

  6. Jmeter正则提取请求响应数据

    前言 在测试时,我们经常需处理请求返回的响应数据,比如很多时候 cookie 或 token 或 Authorization授权码 会返回在 Response headers(响应头)中,这时我们便需 ...

  7. URLConnection 和 HttpClients 发送请求范例

    . java.net.URLConnection package test; import java.io.BufferedReader; import java.io.IOException; im ...

  8. Keras:基于Theano和TensorFlow的深度学习库

    catalogue . 引言 . 一些基本概念 . Sequential模型 . 泛型模型 . 常用层 . 卷积层 . 池化层 . 递归层Recurrent . 嵌入层 Embedding 1. 引言 ...

  9. URLConnection 和 HttpClients 发送请求范例【原】

    笔记,未完全标准. java.net.URLConnection package test; import java.io.BufferedReader; import java.io.IOExcep ...

随机推荐

  1. python16_day02【列表、字典】

    1.列表 names = ['Alex',"Tenglan",'Eric'] >>> names[0] 'Alex' >>> names[2] ...

  2. svn / git SourceTree

    开发使用SourceTree 忽略文件这块老弄错,这次专门博客一下,使用CocoaPods 开发项目, 忽略步骤如下:  忽略文件内容如下 *.xcworkspace xcuserdata *.loc ...

  3. 体系编程、SOC编程那些事儿

    转:https://blog.csdn.net/yueqian_scut/article/details/49968897 笔者将从芯片IC的系统设计的角度去诠释如何掌握体系编程和SOC编程.笔者有超 ...

  4. mysql 5.7快速部署

    目录 一:官网下载mysql二级制包.... 1 二:mysql二级制包解压.... 1 三:设置mysql库文件路径与授权... 1 四. 创建配置文件... 2 五:数据库初始化... 5 六: ...

  5. LeetCode——Word Break

    Question Given a string s and a dictionary of words dict, determine if s can be segmented into a spa ...

  6. EF Code-First 学习之旅 EntityTypeConfiguration<TEntity>

    之前我们配置的实体都都在OnModelCreating方法中,如果有很多实体的话,OnModelCreating方法管理很麻烦 我们可以用单独的类来管理配置,继承EntityTypeConfigura ...

  7. Bellman-Ford算法优化

    2017-07-27 16:02:48 writer:pprp 在BEllman-Ford算法中,其最外层的循环的迭代次数为n-1,如果不存在负权回路,需要迭代的次数是远远小于n-1; 如果在某一次迭 ...

  8. SEO提高网站排名快速见效的方法

    SEO如何提升网站排名?seo中文名称搜索引擎优化,是提升网站排名的一种技术手段,常用的手段就是优化标签,内链外链等等,最核心的优化方向,“关键词”.最常见的表现形式就是软文,但是有的时候就算你做了很 ...

  9. angularjs分页组件

    这是我第一次写博客,激动,首先,我也是个菜鸟,分享一下自己写的服务器端分页的代码,自己一步一步写的,其中也有参考别人的代码.技术比较渣,先这样了. // ====== 2019-1-3 ======/ ...

  10. pandas 选取数据 修改数据 loc iloc []

    pandas选取数据可以通过 loc iloc  [] 来选取 使用loc选取某几列: user_fans_df = sample_data.loc[:,['uid','fans_count']] 使 ...