在TMC2中获取占位图(附TMC2-v8.0 patch及运行过程)


博客背景:

最近看到一篇论文Occupancy Map Guided Fast Video-Based Dynamic Point Cloud Coding有受到启发,想在自己的实验中加入类似的方法,需要获取占位图并进行一些判断,本博客记录这一过程。之前已经写过目前最新版v15.0的配置过程,而为与论文实验结果对比采用v8.0,老版本的TMC2有参数videoEncoderPath、videoEncoderAuxPath、videoEncoderOccupancyMapPath可以调用外部可执行文件来编码,只需要修改external中的文件即可,由于v8.0需要patch并且部署方法与15.0略有不同,这里再在第一部分记录一下运行过程,第二部分为占位图引导的CU快速决策方法。

本篇文章修修改改也在完善中,如果有错误请指出。




0 材料准备

0.1 依赖及工具

材料名称链接及版本
TMC2TMC2-v8.0
HMHM-16.20+SCM-8.8
HDRToolsHDRTools-v0.18
GitGit
CMakeCMake
第二部分的文件压缩包CSDN资源下载

0.2 解压及安装

在某位置下建立一个TMC2_v8.0的文件夹,并且下文均以此目录为根目录,用 / 开头表示此目录下的路径,在此文件夹下解压下载的 mpeg-pcc-tmc2-release-v8.0,并建立文件夹external和ply,最终路径截图如下
​​在这里插入图片描述

接下来进入external,在这里解压刚才下载的HDRTools-v0.18和HM-HM-16.20+SCM-8.8+3DMC,最终路径截图如下:在这里插入图片描述

0.3 测试序列和法线数据准备

在/ply下存放TMC2的输入测试序列,具体的下载方法参考这篇博客的2.1和2.2,下文使用soldier这个测试序列,最终包结构如下:
在这里插入图片描述
法线文件在此处下载,下载后解压至/ply/soldier,解压后的包结构如下:
在这里插入图片描述


# 1 TMC2-v8.0的部署

1.1 构建及生成

进入/mpeg-pcc-tmc2-release-v8.0,build得到解决方案,使用CMake也好用cmd执行build也好这里不在赘述,build及生成过程如有需要可以参考这篇博客的1.1和1.2部分,注意下文中均以默认路径为例填写参数,如果修改了生成路径需要修改对应位置的参数内容。生成的编码可执行文件及路径如下:
在这里插入图片描述

打开/external/HDRTools.sln,对HDRTools-v0.18生成,生成过程可以参考这篇博客的1.3部分,注意仍建议使用默认路径。生成的可执行文件及路径如下:​

HM同样需要生成,但首先需要进行patch因此先执行下一节的内容。

1.2 patch过程

进入/mpeg-pcc-tmc2-release-v8.0/dependencies/hm-modification,这里有两个对HM的patch文件,我们只使用pcc_me-ext_for_HM-16.20+SCM-8.8.patch。注意这两个patch文件不可以都打,只打pcc_me-ext_for_HM-16.20+SCM-8.8.patch就可以了。
在这里插入图片描述
将此patch文件复制到/external/HM-HM-16.20+SCM-8.8/source下,并在此处打开Git Bash(在0.1 依赖及工具中下载安装好就可以通过在文件夹空白处右键→Git Bash打开使用了,如果是win11则在“显示更多选项”中,有需要的话Git Bash安装过程可以参考这篇文章),执行以下代码:

git apply pcc_me-ext_for_HM-16.20+SCM-8.8.patch --reject

在这里插入图片描述

如果不需要对HM进行修改的话,现在已经可以去生成HM并在TMC2中填写外部调用参数来使用了,HM的生成过程同HDRTools。本博客以原始的HM作为Anchor,本章以运行Anchor为例。

1.3 TMC2-v8.0的编码执行

首先假设已按照1.1 的步骤build并生成好了TMC2。

1.3.1 修改输入配置文件

我们使用soldier这个测试序列,打开/mpeg-pcc-tmc2-release-v8.0/cfg/sequence/soldier_vox10.cfg 并做如下修改:(这里为了呈现对比效果因此注释了原代码,修改时直接改掉内容即可,尽量不要改变行数)在这里插入图片描述
如果你的点云测试序列不是放在本文相同地方(/ply)下,或修改了目录结构与本文0.3中最终包结构不同,需在这里对应修改。

1.3.2 填写TMC2运行参数

打开/mpeg-pcc-tmc2-release-v8.0/build/TMC2.sln,将PccAppEncoder设为启动项,并在属性页的配置属性→调试,修改命令参数为:

--configurationFolder=cfg/ --config=cfg/common/ctc-common.cfg --config=cfg/condition/ctc-random-access.cfg --config=cfg/sequence/soldier_vox10.cfg --config=cfg/rate/ctc-r3.cfg --uncompressedDataFolder=D:/00Workbench/TMC2_V8.0/ply/ --frameCount=1 --videoEncoderPath=../external/HM-HM-16.20+SCM-8.8/bin/vc2015/x64/Release/TAppEncoder.exe --videoEncoderAuxPath=../external/HM-HM-16.20+SCM-8.8/bin/vc2015/x64/Release/TAppEncoder.exe --videoEncoderOccupancyMapPath=../external/HM-HM-16.20+SCM-8.8/bin/vc2015/x64/Release/TAppEncoder.exe --colorSpaceConversionPath=../external/HDRTools-v0.18/bin/HDRConvert.exe --reconstructedDataPath=S26C03R03_rec_%04d.ply --compressedStreamPath=S26C03R03.bin --keepIntermediateFiles=1 --normalDataPath=D:/00Workbench/TMC2_v8.0_Thesis/ply/soldier/soldier_n/soldier_vox10_%04d_n.ply

参数含义写在本文1.4小节,如有需求或文件路径的变化,请对应更改参数。

修改工作目录为:…/…/…/…/
调试器截图如下:
在这里插入图片描述

1.3.3 正确运行结果

在这里插入图片描述

占位图编码结果

在这里插入图片描述

几何图编码结果

在这里插入图片描述

纹理图编码结果

在这里插入图片描述

TMC2整体运行结果

1.4 参数解释

--configurationFolder=cfg/ 										// 指定配置文件的默认路径
--config=cfg/common/ctc-common.cfg 								// 指定通用配置文件
--config=cfg/condition/ctc-random-access.cfg 					// 指定选用编码模式的配置文件
--config=cfg/sequence/soldier_vox10.cfg 						// 指定选用测试序列的配置文件
--config=cfg/rate/ctc-r3.cfg 									// 指定选用QP的配置文件
--uncompressedDataFolder=D:/00Workbench/TMC2_V8.0/ply/ 			// 指定输入点云帧的默认路径,这一段文字会被添加到soldier_vox10.cfg的uncompressedDataPath参数前面,即1.3.1中修改的部分,这两条内容组合起来是输入.ply文件的绝对路径
--frameCount=1 													// 指定编码帧数
--videoEncoderPath=../external/HM-HM-16.20+SCM-8.8/bin/vc2015/x64/Release/TAppEncoder.exe 
--videoEncoderAuxPath=../external/HM-HM-16.20+SCM-8.8/bin/vc2015/x64/Release/TAppEncoder.exe 
--videoEncoderOccupancyMapPath=../external/HM-HM-16.20+SCM-8.8/bin/vc2015/x64/Release/TAppEncoder.exe 
																// 这三个参数都是指定使用的外部编码器
--colorSpaceConversionPath=../external/HDRTools-v0.18/bin/HDRConvert.exe 
																// 指定使用的外部color space conversion
--reconstructedDataPath=S26C03R03_rec_%04d.ply 					// 在指定位置生成重建.ply文件
--compressedStreamPath=S26C03R03.bin							// 在指定位置生成中间文件,即TMC2生成的输入给编码器编码的文件,其中去掉.bin后缀的部分也将是占位图、几何图、纹理图会用到的部分文件名,这个文件名将在本文第二章提到
--keepIntermediateFiles=1 										// 保留中间文件,v8.0默认删除,如果不需要用到TMC2生成的占位图几何图纹理图的yuv文件也可以不设置这个参数
--normalDataPath=D:/00Workbench/TMC2_v8.0_Thesis/ply/soldier/soldier_n
																// 参考的法线文件,如果不输入最后这两个参数TMC2得不到p2plane等反映编码质量的值

2 给HM添加占位图快速决策

可直接在本博客0.1或2.3处下载文件并按说明解压到本地使用。

2.1获取占位图

我们通过获取TMC2传递给HM的中间文件的方法来获取占位图的原文件,当然在占位图编码之前将输入文件另存某处,在几何图和纹理图编码时再去调用也可以,这样的方法会增加读写文件各一次,而本文采用获取占位图的方法只会读一次,个人希望尽可能减少文件读取次数对程序增加的负担。

2.1.1 修改TMC2代码使其保留中间文件

如果在TMC2的运行参数中添加:–keepIntermediateFiles=1
就不必修改原代码,直接跳至2.1.2即可

由于TMC2_v8.0默认删除中间文件(15.0是默认保留的,但15.0使用本方法获取占位图会有另外的问题出现,这里不赘述),即HM的输入文件,可以通过修改TMC2中的“keepIntermediateFiles”参数使其保留,TMC2_v8.0不提供输入参数的修改,需要到代码中修改定义部分。

用Ctrl+F搜索“keepIntermediateFiles”,只需要修改Encoder部分的两处,Decoder部分同样有两处定义,可以不修改。
在/mpeg-pcc-tmc2-release-v8.0/source/lib/PccLibEncoder/source/PCCEncoderParameters.cpp的第122行修改第一处为true:
在这里插入图片描述


在/mpeg-pcc-tmc2-release-v8.0/source/lib/PccLibEncoder/include/PCCVideoEncoder.h的第66行修改第二处为true:
在这里插入图片描述

修改文件前后对比:
在这里插入图片描述

默认只保留S26C03R03.bin其他中间文件在调用HM后会删除

在这里插入图片描述

将两处改为true后,中间文件被保留,同v15.0

2.1.2 添加全局变量保存关键信息

我们用C++全局变量来将占位图信息保存到数组使用,在/external/HM-HM-16.20+SCM-8.8/source/Lib/TLibEncoder下,创建occupancyData.h和occupancyData.cpp保存全局变量和初始化方法

// occupancyData.h
#ifndef __occupancyData__
#define __occupancyData__

unsigned char** occupancyData;		// 保存占位图信息,第一维表示帧数,第二维表示当前帧内从左至右从上至下第i个像素上的占位图信息
									// 如占位图上第0帧第i行第j列个像素,用occupancyData[0][(i-1)*occupancyWidth+j]表示
int occupancyHeight;				// 占位图帧的高度
int occupancyWidth;					// 占位图帧的宽度

void occupancyDciInit(Int height, Int width, int frameNum, string occupancyName);
									// 初始化方法,frameNum表示占位图帧数
void checkData();					// 输出occupancyData中的数据

#endif
// occupancyData.cpp
#include "occupancyData.h"

extern unsigned char** occupancyData;
extern int occupancyHeight;
extern int occupancyWidth;

void occupancyDciInit(Int height, Int width, int frameNum, string occupancyName) {
    occupancyHeight = height/4;							// 初始化占位图高度
    occupancyWidth = width/4;							// 初始化占位图宽度

    int npos_GOF0_, npos_x;
    npos_GOF0_ = occupancyName.find("_GOF0_");
    npos_x = occupancyName.find_last_of("/");
    char* temp = new char;
    string occupancyName = name.substr(npos_x + 1, npos_GOF0_ - npos_x - 1) + "_GOF0_occupancy_" +
        itoa(occupancyWidth, temp, 10) + "x" + itoa(occupancyHeight, temp, 10) +
        "_8bit_p420.yuv";
														// 生成占位图文件的文件名
    occupancyData = new unsigned char* [frameNum/2];	// 对occupancyData第一维初始化
    for (int i = 0; i < frameNum/2; i++)				// 对occupancyData第二维初始化,占位图帧数是几何图和纹理图帧数的一半
        occupancyData[i] = (unsigned char*)malloc(sizeof(unsigned char) * occupancyHeight * occupancyWidth * 3 / 2);
        
    FILE* oriOccupancy = fopen(occupancyName.data(), "rb+");
    if (oriOccupancy != nullptr)
        for (int i = 0; i < frameNum / 2; i++)
            fread(occupancyData[i], sizeof(unsigned char), occupancyWidth * occupancyHeight * 3 / 2, oriOccupancy);
    if (oriOccupancy != NULL) fclose(oriOccupancy);
														// 将占位图的亮度信息提取,YUV文件的总大小为文件宽×高×帧数×1.5,亮度信息Y分量存在0~宽×高位上,剩余的1/5是Cb,4/5是Cr
}

void checkData(int frameNum, int height, int width) {	// 输入帧数、帧高、帧宽
    for (int i = 0; i < frameNum; i++) {
        for (int j = 0; j < height; j++) {
            for (int k = 0; k < width; k++) {
                cout << occupancyData[i][j * occupancyWidth + k] - 0 << " ";
            }
            cout << endl;
        }
        cout << "-========================-" << endl;	// 分隔不同帧
    }
}

获取YUV文件的Y, U, V分量的方法可以参考这篇博客,非常详细。

然后就需要在进入主编码循环之前将所需的三个信息保存到全局变量(实际上只需要在TAppEncTop::encode() 初始化编码参数之后调用occupancyDciInit()就可以了)。首先打开/external/HM-HM-16.20+SCM-8.8/source/App/TAppEncoder,首先引入occupancyData.h和occupancyData.cpp,如果是在VS的项目中新建的occupancyData.h和occupancyData.cpp则只引入头文件即可,或者在CMakeList中添加,这里假设采取的方法是在文件夹中新建文本文件后改名的方法或从本博客所附资源中解压建立的occupancyData.h和occupancyData.cpp,那么由于没有添加到项目中去所以需要引入.cpp的定义部分。

#include "TLibEncoder/occupancyData.h"
#include "TLibEncoder/occupancyData.cpp"

在这里插入图片描述
然后找到同一文件中TAppEncTop::encode()定义部分,在“allocate original YUV buffer”之前对全局变量初始化,可以用checkData()检查保存的占位图数据是否正确。

  occupancyDciInit(m_iSourceWidth, m_iSourceHeight, m_framesToBeEncoded, m_inputFileName);
  checkData(m_framesToBeEncoded/2,m_iSourceHeight/4,m_iSourceWidth/4);`

在这里插入图片描述
这里对TAppEncTop类的个别属性做一些解释:

m_iSourceWidth		// 记录了输入文件的像素宽度
m_iSourceHeight		// 记录了输入文件的像素高度
m_framesToBeEncoded	// 记录了输入文件的编码帧数,注意如果是占位图编码这里是等于点云帧数的,而如果是几何图纹理图编码,这里是等于点云帧数两倍的,本博客介绍几何图和纹理图的快速决策判断,因此只在编码几何图纹理图时需要用到occupancyData,故它的初始化过程中用到的是几何图输入文件帧数的一半。
m_inputFileName		// 记录了输入文件的文件名,同样因为是在几何图纹理图编码过程中获取到的,因此这个值为几何图的输入文件名或纹理图的输入文件名,并不是占位图的文件名,在初始化过程中剥离出相同的部分并根据宽高做了判断,生成出占位图编码过程中的中间文件名

2.1.3 检验occupancyData的值是否正确

至此,成功将/mpeg-pcc-tmc2-release-v8.0 下生成的占位图中间文件保存到全局变量occupancyData中,重新生成HM的TAppEncoder,然后运行一遍TMC2,可以看到控制台输出打印了occupancyData的信息,与/mpeg-pcc-tmc2-release-v8.0/S26C03R03_GOF0_occupancy_320x320_8bit_p420.yuv比较,数值相同则成功获取占位图信息。(注意使用测试序列为soldier,使用其他测试序列由于宽高不同,文件名也会不同)
在这里插入图片描述

红框部分是占位图第一行的320个Y值

在这里插入图片描述

可以检验是否与占位图的Y值相同

2.2 占位图引导的CU快速决策

打开/external/HM-HM-16.20+SCM-8.8/source/Lib/TLibEncoder/TEncCu.cpp,首先引入全局变量:

extern unsigned char** occupancyData;
extern int occupancyHeight;
extern int occupancyWidth;

并添加函数用以判断是否跳过SKIP/MERGE模式后的CU编码模式,返回值为false则跳过

bool occupancyDci(int WH, int Y, int X, int nowPOC) {
    for (int i = 0; i < WH; i++) 
        for (int j = 0; j < WH; j++) 
            if (occupancyData[nowPOC/2][(Y + i) * occupancyWidth + X + j] - 0 != 0)return true; 
        
    return false; 
} 

在这里插入图片描述

解释:占位图上一个像素代表对应几何图或纹理图对应的4×4的像素块是否存在V-PCC过程patch的投影块,若不存在即为0,否则为1。那么对于CU选用编码模式的过程中,如果当前CU中的对应占位图位置上的值全为0则选用SKIP/MERGE模式。

在TEncCu::xCompressCU() 计算SKIP/MERGE模式的RDCost后,添加判断的调用及跳过flag:

        bool fastOccupancyDci = false;   // 控制占位图判断的开闭,true打开,false关闭
        bool SKIPME = true;
        if (fastOccupancyDci&& rpcBestCU->getQP(0)>10)  
            SKIPME = occupancyDci(uiWidth / 4, uiTPelY / 4, uiLPelX / 4, rpcBestCU->getSlice()->getPOC()); 

		// 注意在下一行的if语句中需要添加SKIPME

在这里插入图片描述

2.3 资源使用

以上需要修改的HM中四个文件可在此处下载,解压后

将TEncCu.cpp、occupancyData.h和occupancyData.cpp放置在/external/HM-HM-16.20+SCM-8.8/source/Lib/TLibEncoder/,并且TEncCu.cpp覆盖原文件;
将TAppEncTop.cpp放置在/external/HM-HM-16.20+SCM-8.8/source/Lib/TLibEncoder,并覆盖原文件;

评论 23
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值