三、使用HM进行简单的视频隐写demo


前言

在前两篇博客中介绍了基于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 ++){
   
   
	
评论 1
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值