• 如何在JM8.6编码端提取QDCT?


    毫无疑问,编码端的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.         
    3. if(1 == controlTimes) // controlTimes是static int型的变量,用于记录函数被调用的次数  
    4. {  
    5.     for(i = 0; i < 16; i++)  
    6.         printf("%-4d", img->cofAC[0][0][0][i]);  
    7.     printf(" ");  
    8.   
    9.     for(i = 0; i < 16; i++)  
    10. 10.         printf("%-4d", img->cofAC[0][0][1][i]);  
    11. 11.     printf(" ");  

    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.   
    9. % 下面对残差矩阵进行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

               对比结果,不言而喻.

  • 相关阅读:
    报错:无法截断表 '某表',因为该表正由 FOREIGN KEY 约束引用
    如何选择使用结构或类
    C#交换两个变量值的多种写法
    什么是.Net, IL, CLI, BCL, FCL, CTS, CLS, CLR, JIT
    把数据库中有关枚举项值的数字字符串转换成文字字符串
    为参数类型一样返回类型不同的接口写一个泛型方法
    报错:System.NotSupportedException: LINQ to Entities does not recognize the method
    关于泛型类和扩展方法的一点思考
    在ASP.NET MVC下通过短信验证码注册
    ASP.NET MVC下实现前端视图页的Session
  • 原文地址:https://www.cnblogs.com/ranson7zop/p/7603773.html
Copyright © 2020-2023  润新知