福州大学《嵌入式系统综合设计》 实验九:ROI视频编码

一、实验目的

ROI视频编码即感兴趣区域视频编码,即针对感兴趣区域进行重点编码,提高编码质量,而对非感兴趣区域采用低质量编码。通过这种方法可以降低码率。本实验即让同学们能够在算能的FFMPEG接口下实现基于ROI的视频编码。

二、实验内容

在实验8的基础上,进一步实现ROI编码。即对图像不同区域设置不同的QP进行视频编码。

三、开发环境

开发主机:Ubuntu 20.04.6 LTS

硬件:算能SE5

四、实验器材

开发主机 + 云平台(或SE5硬件)

五、实验过程与结论

ROI视频编码原理

整帧视频图像的视觉质量很大程度上取决于感兴趣区域ROI部分的视觉质量,而非ROI的图像质量下降不易被察觉,对整帧图像的视觉质量影响较小, 感兴趣区域并不局限于人眼的感兴趣区域,在某些具体的工程应用中,可以将某些具体的场景或者事物定义为感兴趣区域。

感兴趣区域ROI的视频编码的基本方法如图所示,对于输入视频帧序列,通过特定的算法和视频帧本身的特性,提取出感兴趣区域和不感兴趣区域掩膜,并将其作为控制信息输入到视频编码器中,用其控制编码器编码当前帧相应的编码参数,包括量化参数,运动估计时的搜索区域大小和参考帧数目,以及预测模式范围等。感兴趣区域ROI提取模块起到预处理和控制器的作用,感兴趣区域提取涉及视觉显著性等,显著性是指视频帧序列中的能够引起 HVS 注意的物体所具有的特性,一般感兴趣区域的显著性可以表现为纹理、亮度、色度、对比度、形态、对比度等特征。

码率控制的目的是在给定的码率条件下,保证良好稳定的输出码流。按照粒度大小码率控制可分为 GOP 层、帧层以及宏块层(或者CTU层)。编码器通过对不同层级中编码单元分配不同的码率比计算相应的QP值。QP 越大,编码单元丢失的细节越多,图像的失真度增加,质量下降,同时码率也降低,随着 QP 的减小,编码单元的细节保留的增多,在提高质量的同时增大了码率。

在固定目标比特率的 CBR 编码模式中,编码器通过编码结果对每帧、每个宏块或者CTU分配不同的码率,动态计算调整 QP 大小,让最终编码的整体码率与给定码率接近。在基于ROI的编码方法中,在ROI 分析与提取的基础上,基于CBR 的码率控制模型,对宏块级的 QP 进行调整,即对 ROI 降低 QP 提高编码质量,对非 ROI 相应提高 QP。通过这种方式,视觉关注区域得到了更多的码率,而非视觉关注区域节省了非必要的码率分配,进而达到在整体码率不变的条件下,提升视频的主观视觉质量的目的。

关键核心代码讲解

设置ROI有效

ROI的设置是在编码过程中通过设置不同区域的QP来设置ROI,在设置之前需要在初始化编码器时设置ROI有效,因此,我们需要在实验8的openEnc函数中设置ROI有效,具体如下:

//av_dict_set_int(&dict, "qp", 25, 0);
av_dict_set_int(&dict, "roi_enable", 1, 0);

注意,上式中的av_dict_set_int(&dict, "qp", 25, 0)是实验8的代码,更换为ROI后需要删除。

设置QP

我们在实验8的基础上,在编码前设置图像不同区域的编码QP。本次实验对编码图像分成四个区域,如图所示,其中不同区域设置成不同的量化参数。在视频编码中,量化参数QP是非常重要的参数,它直接影响着视频的编码比特率。对于某些应用场合,尤其是当传输速率受限时,灵活地控制量化 参数使得编码速率尽量接近给定速率尤为重要。图中右下角区域每个编码单元(宏块或者CTB)QP设为较小的值,QP=10,该区域经过编码后,保留较多的细节信息。而其他三个区域QP=40,这几个区域可以获得较小的码率和较少的计算资源。

H264中ROI代码:

对于H.264编码器,首先计算每个宏块的索引位置(i,j),当判断当前宏块位于右下角区域时,QP设置为40,其他则设置为10。

#define BM_ALIGN16(_x)             (((_x)+0x0f)&~0x0f)
#define BM_ALIGN32(_x)             (((_x)+0x1f)&~0x1f)
#define BM_ALIGN64(_x)             (((_x)+0x3f)&~0x3f)  
AVFrameSideData *fside = av_frame_get_side_data(picture, AV_FRAME_DATA_BM_ROI_INFO);
    if (fside) {
        AVBMRoiInfo *roiinfo = (AVBMRoiInfo*)fside->data;
        memset(roiinfo, 0, sizeof(AVBMRoiInfo));
        if (enc_ctx->codec_id  == AV_CODEC_ID_H264) {
             roiinfo->customRoiMapEnable = 1;
             roiinfo->customModeMapEnable = 0;
             for (int i = 0;i <(BM_ALIGN16(height) >> 4);i++) {
                 for (int j=0;j < (BM_ALIGN16(width) >> 4);j++) {
                     int pos = i*(BM_ALIGN16(width) >> 4) + j;
                     // test_1
                     if ( (j >= (BM_ALIGN16(width) >> 4)/2) && (i >= (BM_ALIGN16(height) >> 4)/2) ) {
                          roiinfo->field[pos].H264.mb_qp = 10;
                      }else{
                          roiinfo->field[pos].H264.mb_qp = 40;
                      }
                 }
             }
         } else if (enc_ctx->codec_id  == AV_CODEC_ID_H265) {
            roiinfo->customRoiMapEnable    = 1;
            roiinfo->customModeMapEnable   = 0;
            roiinfo->customLambdaMapEnable = 0;
            roiinfo->customCoefDropEnable  = 0;

            for (int i = 0;i <(BM_ALIGN64(height) >> 6);i++) {
                for (int j=0;j < (BM_ALIGN64(width) >> 6);j++) {
                    int pos = i*(BM_ALIGN64(width) >> 6) + j;

                    // test_1
                    if ( (j > (BM_ALIGN64(width) >> 6)/2) && (i > (BM_ALIGN64(height) >> 6)/2) ) {
                        roiinfo->field[pos].HEVC.sub_ctu_qp_0 = 10;
                        roiinfo->field[pos].HEVC.sub_ctu_qp_1 = 10;
                        roiinfo->field[pos].HEVC.sub_ctu_qp_2 = 10;
                        roiinfo->field[pos].HEVC.sub_ctu_qp_3 = 10;
                    } else {
                        roiinfo->field[pos].HEVC.sub_ctu_qp_0 = 40;
                        roiinfo->field[pos].HEVC.sub_ctu_qp_1 = 40;
                        roiinfo->field[pos].HEVC.sub_ctu_qp_2 = 40;
                        roiinfo->field[pos].HEVC.sub_ctu_qp_3 = 40;
                    }

                    roiinfo->field[pos].HEVC.ctu_force_mode = 0;
                    roiinfo->field[pos].HEVC.ctu_coeff_drop = 0;
                    roiinfo->field[pos].HEVC.lambda_sad_0 = 0;
                    roiinfo->field[pos].HEVC.lambda_sad_1 = 0;
                    roiinfo->field[pos].HEVC.lambda_sad_2 = 0;
                    roiinfo->field[pos].HEVC.lambda_sad_3 = 0;
                }

H265中ROI代码:

对于H.265/HEVC编码器,编码标准制定了一种非常灵活的QP控制机制,它引入了量化组(Quantization Group, QG)的概念,规定一个CTB可以包含一个或多个固定大小的QG,同一个QG内的所有含有非零系数的CU共享一个QP,不同的QG可以使用不同的QP。这样一来,编码器能够更灵活地进行速率控制。

QG是指将一幅图像分成的固定大小(NxN)的正方形像素块。其大小N由图像参数集(PPS)指定,且必须处于最大CU与最小CU之间(包含最大与最小CU)。图中给出了一个32x32 QG的示意图,其中粗实线为CTB边界,粗虚线表示CU划分方式,细线为QG分界线。CU与QG没有固定的大小关系。由于在一幅图像中,QG为固定大小,而CU是根据视频内容自适应划分出来的,因此可能出现一个QG包含一个或多个CU的情形,也可能存在一个CU包含多个QG的情形。

实验过程

生成可执行文件:

makefile的写法与前面的例程基本相同,按照前面实验1、实验2步骤,生成可执行文件并上传到算能盒子或者云平台中。如果是在云平台上测试,则可将编译好的执行文件通过云空间文件系统上传。

root@d11ae417e206:/tmp/test# ls

ffmpeg_encode_withRoi  1080p.yuv

给可执行文件赋权限并执行。

root@d11ae417e206:/tmp/test# chmod 777 ffmpeg_encode_withRoi

运行指令:

生成并上传编译文件后,根据如下指令在目标开发机终端运行。

./ffmpeg_encode_withRoi 1080.yuv output.h264

运行结果如下:

[h264_bm @ 0x429a90] width =1920

[h264_bm @ 0x429a90] height=1080 src/allocator.c:136 (ion_dmabufalloc_map)  

DEBUG: retrieved virtual address for physical memory

src/allocator.c:144 (ion_dmabufalloc_map)  

DEBUG: Invalidated physical memory

src/allocator.c:155 (ion_dmabufalloc_map)  

DEBUG: virtual address:  0x7f820c8000  aligned: 0x7f820c8000

src/allocator.c:176 (ion_dmabufalloc_unmap)  

DEBUG: shut down virtual address for 3133440 bytes of physical memory

[h264_bm @ 0x429a90] input frame: context=(nil), pts=100, dts=-9223372036854775808

[85fb6010] src/enc.c:2264 (Wave5BitIssueCommand)  

instanceIndex=0: cmd=0x100

[85fb6010] src/enc.c:1647 (enc_start_one_frame)  

instance Index: 0. instance Queue Count: 1, total Queue Count: 1

[85fb6010] src/enc.c:733 (VpuHandlingInterruptFlag)  

INTR Done: enc pic. reason=0x100

[85fb6010] src/enc.c:2264 (Wave5BitIssueCommand)  

instanceIndex=0: cmd=0x4000

[h264_bm @ 0x429a90] output frame: context=(nil), pts=91, dts=87

enc_pkt.pts=91, enc_pkt.dts=87

rescaled enc_pkt.pts=46592, enc_pkt.dts=44544

Muxing frame

The end of file!

Flushing video encoder

[85fb6010] src/enc.c:2264 (Wave5BitIssueCommand)  

instanceIndex=0: cmd=0x100

[85fb6010] src/enc.c:1647 (enc_start_one_frame)  

instance Index: 0. instance Queue Count: 1, total Queue Count: 1

[85fb6010] src/enc.c:733 (VpuHandlingInterruptFlag)  

INTR Done: enc pic. reason=0x100

[85fb6010] src/enc.c:2264 (Wave5BitIssueCommand)  

instanceIndex=0: cmd=0x4000 [h264_bm @ 0x429a90] encoding end!

src/encoder.c:804 (bmvpu_enc_close)  

DEBUG: closing encoder src/allocator.c:112 (ion_dmabufalloc_deallocate)  

DEBUG: deallocated 262144 bytes of physical memory

src/allocator.c:112 (ion_dmabufalloc_deallocate)  

DEBUG: deallocated 262144 bytes of physical memory

src/allocator.c:112 (ion_dmabufalloc_deallocate)  

DEBUG: deallocated 262144 bytes of physical memory

src/allocator.c:112 (ion_dmabufalloc_deallocate)  

DEBUG: deallocated 262144 bytes of physical memory

src/encoder.c:295 (bmvpu_enc_unload)  

DEBUG: unloaded VPU [AVIOContext @ 0x499190] Statistics: 2 seeks, 7 writeouts

encode finish!

#######VideoEnc_FFMPEG exit

注:上图展示为部分关键运行结果,详细运行结果请见运行界面。

Elecard StreamEye分析

对视频文件进行ROI编码后,用Elecard StreamEye分析码流。通过播放编码后的视频可以看出,右下角区域比其他区域的清晰度更高,细节保留的更多。左下角使用较大的QP,使得图像细节丢失,木板的间隙模糊,人工效应明显。而右下角区域使用较小的QP编码,则相对清晰。

同时可以调出MB信息查看每一个编码单元的QP值,可以看出,右下角的QP值更低,其他区域的QP值更高。

猜你喜欢

转载自blog.csdn.net/m0_52537869/article/details/134702369