三、使用HM进行简单的视频隐写
前言
在前两篇博客中介绍了基于HEVC/H.265视频编解码器的视频隐写的基本思路,本文将详细记录我在使用HM做视频隐写的简单实验中遇到的各种问题和解决的思路等。
本次实验参考的博客是dy学长的两篇博客:第一篇博客、第二篇博客
感谢dy学长、另外一位网友和徐老师对我实验过程中的帮助和解答。
一、实验环境
在Windows10 x64的笔记本上,用VS2010对HM12.0进行实验。
HM12.0网盘分享(提取码:zdeu)
二、实验思路
本次实验是我对视频隐写的第一个实验,意在熟悉视频隐写的整个流程,以便将来进行其它的视频隐写实验。所以,这次实验选择了比较简单的对4x4块使用LSB算法,对帧内预测过程中的IPM进行隐写,且不论最终实验评价指标(即PSNR、SSIM等)的优劣。
本次实验是在上一篇博客的基础上进行的,按照上一篇博客的思路,将使用三个HM编解码器:
1、在第一个编码器HM_1中提取出所有的原始载体original_cover涉及到的相关信息——深度depth、划分模式partitionsize和original_IPM值——并保存下来;
2、在外部编写LSB隐写算法,对4x4块的original_IPM进行隐写。这里需要注意的是:并不是所有的4x4块的IPM都适合隐写,对IPM=0、1和34的块,不进行隐写——IPM为0或1时,块是没有预测方向的角度的,此时如若进行隐写修改,则将导致从无角度变成有角度,产生较大失真。IPM为34时,使用LSB进行隐写,若secret_msg==“1”,会使得隐写后的stego_IPM变成35,而在HM中,IPM的取值范围是[0,34],导致越界。为避免这些影响,简单地将这三种情况排除,不进行隐写。最终,得到经过LSB算法隐写好后的stego_IPM;
3、在第二个编码器HM_2中将stego_IPM放回,并编码视频,得到嵌密视频。这里需要注意的是:HM_1和HM_2的视频参数应该是一模一样的;
4、在第三个解码器HM_3中对嵌密视频进行提取,提取所有隐写后的载体以及相关信息——深度depth、划分模式partitionsize和extract_IPM值;
5、在外部编写LSB提取算法,对4x4块的IPM进行提取,得到extract_msg,并与original_msg进行比对;
三、实验过程
3.1 提取原始载体
使用码流分析器我们可以得知:8x8的块与4x4的块它们的深度都为3,而其它更大的块的深度为2、1、0等,8x8的块与4x4的块之间的差别在于,8x8的块的划分模式是2Nx2N的,而4x4的块的划分模式是NxN的,而其它更大的块的划分模式都是2Nx2N。所以,对于所有深度depth=3、划分模式partitionsize=NxN的块,就是4x4的块,即就是我们要找的原始载体original_cover。
接下来查看TComDataCU.h中的TComDataCU类,这个类中定义了一个CU的各种属性,如该CU在Slice中的位置、Z-order、横坐标、纵坐标、深度、PU类型、编码模式和帧内预测模式等等。(TComDataCU类解释)
class TComDataCU
{
private:
// 绝大部分已经省略
UInt m_uiCUAddr; ///< CU address in a slice 在 slice 中的位置,用的是 raster 扫描(从左到右,从上到下)
UInt m_uiAbsIdxInLCU; ///< absolute address in a CU. It's Z scan order 当前 CU 在 LCU 中的位置,位置用 Z 扫描顺序
UInt m_uiCUPelX; ///< CU position in a pixel (X) CU 的横坐标
UInt m_uiCUPelY; ///< CU position in a pixel (Y) CU 的纵坐标
UChar* getDepth () {
return m_puhDepth; } //返回深度信息
UChar getDepth ( UInt uiIdx ) {
return m_puhDepth[uiIdx]; } //返回深度信息
Char* getPartitionSize () {
return m_pePartSize; } //返回划分模式信息
PartSize getPartitionSize ( UInt uiIdx ) {
return static_cast<PartSize>( m_pePartSize[uiIdx] ); } //返回划分模式信息
UChar* getLumaIntraDir () {
return m_puhLumaIntraDir; }//返回帧内预测模式
UChar getLumaIntraDir ( UInt uiIdx ) {
return m_puhLumaIntraDir[uiIdx]; }//返回帧内预测模式
};
// TComDataCU类定义结束
再看一下PartSize类,这是一个枚举类型的类,其中2Nx2N的值为0,NxN的值为3。
/// supported partition shape
enum PartSize
{
SIZE_2Nx2N, ///< symmetric motion partition, 2Nx2N 0
SIZE_2NxN, ///< symmetric motion partition, 2Nx N 1
SIZE_Nx2N, ///< symmetric motion partition, Nx2N 2
SIZE_NxN, ///< symmetric motion partition, Nx N 3
SIZE_2NxnU, ///< asymmetric motion partition, 2Nx( N/2) + 2Nx(3N/2) 4
SIZE_2NxnD, ///< asymmetric motion partition, 2Nx(3N/2) + 2Nx( N/2) 5
SIZE_nLx2N, ///< asymmetric motion partition, ( N/2)x2N + (3N/2)x2N 6
SIZE_nRx2N, ///< asymmetric motion partition, (3N/2)x2N + ( N/2)x2N 7
SIZE_NONE = 15 // 15,指的是没有了
};
所以,把所有块的深度depth、划分模式partitionsize和预测模式IPM保存下来,那些depth=3&&partitionsize=3的IPM就是可供隐写的载体。
for(int i = 0 ; i < pcCU->getTotalNumPart() ; i ++){


941

被折叠的 条评论
为什么被折叠?



