利用FFMPEG与X264实现帧内预测模式的隐写算法(1)
前言
其实我以后并不打算着重研究视频编码,只是因为这次毕设选了这个题,所以稍微学了点相关的皮毛。关于FFMPEG的源码的分析我推荐雷宵骅前辈写的博客,需要学习的请点击传送门。我基本是看着他的博客学的,虽然现在ffmpeg版本有一定更新,但是大体框架基本是不变的,题目中需要用到的并且和他的博客中写的不同的我会稍微提一下。关于其他ffmpeg源码的研究如果没有特殊需要,我基本是不会考虑做的。最后,如果我有写错,欢迎指正。
正文
关于实现隐写算法我打算分两篇来写,这篇写铺垫一下理论知识,下一篇从代码实现角度上讲
1. 关于H.264编码的相关知识
关于H.264是什么东西这种问题这里就不多赘述了。我们直接讲一点干货。
1.1 I帧 P帧 B帧 与 GOP
任何一个视频,都可以看做是一个图片流,对于视频中每一张图片,我们一般称作帧。H.264格式视频流中的帧一般分为3类,即I帧,P帧,B帧(好像还有其他类别的帧,可以用于处理非常特殊的图片,这里不展开讲了)。这三类帧分别用不同的策略来压缩一张图片,从而降低视频大小。
从信息论的角度来讲,数据可以压缩是因为信息存在冗余,信息冗余存在的条件要么是出现频率不均等,要么是数据间存在相关性。对于出现频率不均等的数据我们可以运用熵编码(如哈夫曼编码)的手段将数据压缩;对于数据间存在相关性这个问题只能根据具体问题,试图将相关的数据转化成出现概率不均等的的数据。所以视频编码的核心就是挖掘视频画面中所有像素点之间的相关性。当然在必要时,也可以制造一些肉眼不可见的相关性进一步压缩。(比如直接砍去画面中高频分量)
理解了这点之后就比较容易理解这三种帧了:I帧主要挖掘单个画面中的像素点的相关性,P帧挖掘当前画面与前一个画面之间的相关性,B帧挖掘当前画面与前后两个画面之间的相关性。乍一眼看下去好像B帧比P帧厉害,也确实B帧的压缩率会比P帧更高(因为相关性挖掘更充分),但是在实时视频传输的过程中往往是不采用B帧的。
I帧相当于单张图片压缩,也是相关性挖掘最不充分的。同样一个画面用I帧会比用P帧和B帧压缩率低非常多!但是I帧也有个非常明显的优势:不受其他帧的干扰。由于P帧和B帧需要参考前后的帧,所以如果视频流中如果连续非常多的P帧与B帧,到后面的画质会大打折扣。这个时候我们就需要一个I帧来矫正前面的误差,使误差不继续向后传递。所以实际应用中,H264码流中的视频帧往往是按照三种帧的某一个排列不断重复。
比如:IPPPPPPPPPIPPPPPPPPPIPPPPPPPPP......... 或:IBPIBPIBPIBPIBP.......
前一个例子是IPPPPPPPPPP这样的帧结构不断重复,后一个例子是IBP这样的帧结构不断重复。
我们把每一个这样的帧结构叫做画面组(GOP),每一组中所包含的帧数叫做画面组大小(GOP size)
1.2 RGB与YUV
对图像有点了解的都知道一个像素点会有三个分量,如RGB。这里我们介绍另一种维度YUV,其中Y为亮度,U和V为色度。YUV分量与RGB分量之间可以通过简单的线性变换来实现。这样变换并不是无意义的,实验表明人的肉眼对YUV中Y的分量较为敏感,对UV则相对不那么敏感。这样我们在存储图片时,可以降低U分量和V分量的分辨率。在H264编码的视频中,yuv通常是4:2:0的采样,其实就是每2*2个像素共享同一个U和同一V,并分别拥有自己的Y。
在隐写算法中由于亮度的信息较多,编码规则也较复杂,更具有操作空间,所以一般都在亮度分量中嵌入信息。
1.3 I帧
我的课题基本上全部围绕I帧展开,其他P帧和B帧是如何实现的我就不赘述了。这里需要重新强调一下的是:I帧只挖掘画面内像素点的相关性。
I帧亮度分量(Y分量)处理的最大单元是一组16*16的像素点,我们把它叫做16*16宏块。注意这里强调的是最大单元,就是说会有更小的单元组成一个16*16宏块,但是16*16宏块就直接组成整个I帧了。
我们接着介绍每个16*16宏块的处理方式。
每个16*16宏块可能会被继续划分(也可能不划分了)成4个8*8宏块,或16个4*4宏块。其中8*8宏块和4*4宏块不会继续被划分了。16*16不划分的宏块一般组成图像较平坦的区域,4*4组成变化较快的区域,8*8则介于中间。我们通过两张图来对此有个较直观的认识。
注:I帧中的宏块不会被划分成16*8之类的块。
左图为原图,右图为经过划分后的图。最左上标出的红框大小为一个16*16宏块的大小。
回到要我们要挖掘像素点之间相关性这一点,由于画面之间的像素点是相关的,并且从普遍概率上说,一个像素点与其越近的相邻像素点相关性越强,也就是说周围的像素点可以一定程度上对一个特定像素点的值进行预测。
H264的编码中对于不切分的16*16的宏块一个有4中预测模式(IPM)
每种预测模式其实就是用与该宏块相邻的相应像素直接填充被预测的模块。
对于8*8宏块和4*4宏块,H264编码一共提供了9种IPM。对于一些边界上的宏块处理方法我会在后续具体分析代码的文章中分析。
当然仅仅通过预测我们是无法得到清晰图片的,我们能得到的图片大概只能是这样支离破碎的。
注:第一个宏块是灰色的是因为没有可用于预测的像素,所以亮度直接被设为128
这张图其实还是原图减去残差图得到的,其实相当于假设用于预测的像素都是无误差的,实际只通过IPM是无法还原图的。
有了IPM之后我们要做的事情就是矫正误差,由于误差的分布概率不同,误差接近0的点出现频率会明显比远离0的点高,于是这样我们就可以用熵编码压缩图片了。当然,在H264具体的编码过程中,后续还有通过DCT变换进一步消除相关性进行压缩。
上图是残差图像
在H264中,每个IPM其实也是先通过预测得到的,一般来说IPM的预测值通过紧邻的上边一个宏块的IPM和左边一个宏块的IPM得到。该宏块的IPM和预测的IPM相同,那么在编码时码率会有一定缩小。
2. 关于帧内预测模式隐写的相关知识与算法
2.1 隐写
隐写的目的就是将信息隐藏进载体,这样一旦载体被通讯了,被隐藏的信息实际也被通讯了,但是却无法被检测到隐藏信息通讯的发生,也就是将通信信道隐藏在载体中。举个简单的例子就是藏头诗,诗是载体,而诗句的头一个字就是被隐藏信息。
2.2 视频隐写的相关评价标准
1. 信噪比:对于隐写后的算法不能有过大噪声,过大的图像噪声肉眼能明显觉察出图像具有颗粒感。
2. 嵌入容量:对于不用算法用于同一段视频嵌入容量是不同的,任何方法嵌入过多数据可能会导致视频质量下降。
3. 比特率增加率:由于嵌入算法都是修改了原来最优方案选用次优方案替代之(不限于帧内预测模式隐写),所以一定会导致嵌入后的视频流大小大于原视频流,如果比特率增加过大,会增加原视频传送负担。
2.3帧内预测模式隐写
通过上文我们可以知道H264提供的预测模式还是比较丰富的,这就给嵌入信息提供了空间。这里我简单介绍一下一篇2012年关于帧内预测模式隐写的论文《H.264/AVC Data Hiding Based on Intra Prediction Modes for Real-time Applications》,作者为Samira Bouchama, Latifa Hamami, and Hassina Aliane。
由于肉眼对图像的高频信息不是特别敏感,也就是4*4宏块编码的部分,所以我们一般只对4*4宏块实行信息嵌入。
上面我们说到4*4宏块一共有9中IPM,而其中有很多IPM是非常近似的,如第四种与第二种,其预测所用到的像素只差一个像素M。于是论文就提出替换近似的预测模式来实现信息嵌入。当然如果该宏块的IPM与预测的IPM(MPM)相同,这个宏块是不用来信息嵌入的,否则会很大幅度增加编码负担。
论文中将IPM分为4组,修改条件是MPM不与IMP同组,并且修改后的IMP必须和最佳IMP同组
组一:1&8
组二:3&7
组三:2&4&5&6
组四:0&2
上图是论文提出的一种修改IPM的策略,这种算法浪费了0这个IPM,即如果遇到IPM=0时,不嵌入任何信息。
我们从解码的角度可能能更好地理解上面这张表格
2.4 对论文算法的一些简化
其实这篇论文的逻辑是有点复杂的,我用另一种相对逻辑简单一点的算法对其进行简化
对于一个宏块的MPM,与嵌入的比特,可以对应到四个可替代的IPM,在其中选择编码代价最小的替代原来的最佳IPM。
可以发现,其中MPM=1与MPM=7时不完全按照大小排序,是为了保证存在与最佳IPM近似方向的IPM可供选择。
运用这个隐写算法最大嵌入容量会比2.3中描述算法略小,而编码后码率增加率也相应减少。
注:我并不知道这个算法有没有和更早的论文撞重,好像很多关于帧内预测模式隐写的论文都是分两组,一组表示0,另一组表示1。
最后
关于H264和帧内预测模式隐写就介绍到这里,下一篇我们介绍X264与ffmpeg中对H264中I帧的的处理,然后实现2.4中描述的算法。