帧间预测分为运动估计ME和运动补偿MV,其中用到了MV的亚像素搜索,需要使用filterHor和filterVer进行插值。
这里使用的是HM16,在之前版本中分为filterHorLuma、filterHorChroma和filterHorLuma、filterVerChroma。HM16中已经将亮度和色度插值滤波器整合。

我们来从最底层插值滤波器函数filter看起。filter函数代码如下,对同一亚像素位置(如1/4处)处进行插值,插值后的MV存在dst中。

/** 插值滤波器* \brief Apply FIR filter to a block of samples  ** \tparam N          Number of taps  抽头数* \tparam isVertical Flag indicating filtering along vertical direction* \tparam isFirst    Flag indicating whether it is the first filtering operation* \tparam isLast     Flag indicating whether it is the last filtering operation* \param  bitDepth   Bit depth of samples* \param  src        Pointer to source samples  源MV地址* \param  srcStride  Stride of source samples* \param  dst        Pointer to destination samples   插值后的MV地址* \param  dstStride  Stride of destination samples* \param  width      Width of block* \param  height     Height of block* \param  coeff      Pointer to filter taps  抽头系数*/
template<Int N, Bool isVertical, Bool isFirst, Bool isLast>
Void TComInterpolationFilter::filter(Int bitDepth, Pel const *src, Int srcStride, Pel *dst, Int dstStride, Int width, Int height, TFilterCoeff const *coeff)
{Int row, col;Pel c[8];   //系数数组c[0] = coeff[0];   //抽头0系数c[1] = coeff[1];   //抽头1系数if ( N >= 4 )    //N≥4时,取抽头2和3系数(UV分量){c[2] = coeff[2];c[3] = coeff[3];}if ( N >= 6 )    //N≥6时,取抽头4和5系数{c[4] = coeff[4];c[5] = coeff[5];}if ( N == 8 )    //N=8时,取抽头6和7系数(Y分量){c[6] = coeff[6];c[7] = coeff[7];}Int cStride = ( isVertical ) ? srcStride : 1;   //垂直情况下步长为srcStride,水平情况下为1src -= ( N/2 - 1 ) * cStride;   //找到前面N/2-1个整数点Int offset;Pel maxVal;Int headRoom = std::max<Int>(2, (IF_INTERNAL_PREC - bitDepth));Int shift    = IF_FILTER_PREC;// with the current settings (IF_INTERNAL_PREC = 14 and IF_FILTER_PREC = 6), though headroom can be// negative for bit depths greater than 14, shift will remain non-negative for bit depths of 8->20assert(shift >= 0);if ( isLast ){shift += (isFirst) ? 0 : headRoom;offset = 1 << (shift - 1);offset += (isFirst) ? 0 : IF_INTERNAL_OFFS << IF_FILTER_PREC;maxVal = (1 << bitDepth) - 1;}else{shift -= (isFirst) ? headRoom : 0;offset = (isFirst) ? -IF_INTERNAL_OFFS << shift : 0;maxVal = 0;}for (row = 0; row < height; row++){for (col = 0; col < width; col++){Int sum;//从N/2-1个整数点开始,取N个整数点乘对应的抽头系数求和。sum  = src[ col + 0 * cStride] * c[0];    sum += src[ col + 1 * cStride] * c[1];    if ( N >= 4 ){sum += src[ col + 2 * cStride] * c[2];sum += src[ col + 3 * cStride] * c[3];}if ( N >= 6 ){sum += src[ col + 4 * cStride] * c[4];sum += src[ col + 5 * cStride] * c[5];}if ( N == 8 ){sum += src[ col + 6 * cStride] * c[6];sum += src[ col + 7 * cStride] * c[7];}Pel val = ( sum + offset ) >> shift;  if ( isLast ){val = ( val < 0 ) ? 0 : val;    //保证不小于0val = ( val > maxVal ) ? maxVal : val;   //保证不越界}dst[col] = val;   //存储插值后的MV}src += srcStride;   dst += dstStride;}
}

再来看上一层的函数,函数代码如下。是一个分类处理,分为普通操作、第一次操作和最后一次操作,分别传入不同参数调用底层filter函数进行插值。这里直接通过N来区分是亮度插值和色度插值。

/*** \brief Filter a block of samples (horizontal)** \tparam N          Number of taps 抽头数:Y-8,UV-4* \param  bitDepth   Bit depth of samples* \param  src        Pointer to source samples* \param  srcStride  Stride of source samples* \param  dst        Pointer to destination samples* \param  dstStride  Stride of destination samples* \param  width      Width of block* \param  height     Height of block* \param  isLast     Flag indicating whether it is the last filtering operation* \param  coeff      Pointer to filter taps 抽头系数*/
template<Int N>     //插值
Void TComInterpolationFilter::filterHor(Int bitDepth, Pel *src, Int srcStride, Pel *dst, Int dstStride, Int width, Int height, Bool isLast, TFilterCoeff const *coeff)
{if ( isLast ){filter<N, false, true, true>(bitDepth, src, srcStride, dst, dstStride, width, height, coeff);}else{filter<N, false, true, false>(bitDepth, src, srcStride, dst, dstStride, width, height, coeff);}
}template<Int N>
Void TComInterpolationFilter::filterVer(Int bitDepth, Pel *src, Int srcStride, Pel *dst, Int dstStride, Int width, Int height, Bool isFirst, Bool isLast, TFilterCoeff const *coeff)
{if ( isFirst && isLast ){filter<N, true, true, true>(bitDepth, src, srcStride, dst, dstStride, width, height, coeff);}else if ( isFirst && !isLast ){filter<N, true, true, false>(bitDepth, src, srcStride, dst, dstStride, width, height, coeff);}else if ( !isFirst && isLast ){filter<N, true, false, true>(bitDepth, src, srcStride, dst, dstStride, width, height, coeff);}else{filter<N, true, false, false>(bitDepth, src, srcStride, dst, dstStride, width, height, coeff);}
}

最后来看最上层的filterHor和filterVer,代码如下,也是一个分类处理,调用下层函数处理。分了三种情况:frac = 0、亮度和色度。frac = 0,即整数,不需要插值。

/*** \brief Filter a block of Luma/Chroma samples (horizontal)    ** \param  compID     Chroma component ID* \param  src        Pointer to source samples* \param  srcStride  Stride of source samples* \param  dst        Pointer to destination samples* \param  dstStride  Stride of destination samples* \param  width      Width of block* \param  height     Height of block* \param  frac       Fractional sample offset  分数偏移* \param  isLast     Flag indicating whether it is the last filtering operation* \param  fmt        Chroma format* \param  bitDepth   Bit depth*/
Void TComInterpolationFilter::filterHor(const ComponentID compID, Pel *src, Int srcStride, Pel *dst, Int dstStride, Int width, Int height, Int frac, Bool isLast, const ChromaFormat fmt, const Int bitDepth )
{if ( frac == 0 )   //整数直接复制{filterCopy(bitDepth, src, srcStride, dst, dstStride, width, height, true, isLast );}else if (isLuma(compID))   //Y分量小数插值{assert(frac >= 0 && frac < LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS);filterHor<NTAPS_LUMA>(bitDepth, src, srcStride, dst, dstStride, width, height, isLast, m_lumaFilter[frac]);}else{const UInt csx = getComponentScaleX(compID, fmt);assert(frac >=0 && csx<2 && (frac<<(1-csx)) < CHROMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS);filterHor<NTAPS_CHROMA>(bitDepth, src, srcStride, dst, dstStride, width, height, isLast, m_chromaFilter[frac<<(1-csx)]);}
}Void TComInterpolationFilter::filterVer(const ComponentID compID, Pel *src, Int srcStride, Pel *dst, Int dstStride, Int width, Int height, Int frac, Bool isFirst, Bool isLast, const ChromaFormat fmt, const Int bitDepth )
{if ( frac == 0 ){filterCopy(bitDepth, src, srcStride, dst, dstStride, width, height, isFirst, isLast );}else if (isLuma(compID)){assert(frac >= 0 && frac < LUMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS);filterVer<NTAPS_LUMA>(bitDepth, src, srcStride, dst, dstStride, width, height, isFirst, isLast, m_lumaFilter[frac]);}else{const UInt csy = getComponentScaleY(compID, fmt);assert(frac >=0 && csy<2 && (frac<<(1-csy)) < CHROMA_INTERPOLATION_FILTER_SUB_SAMPLE_POSITIONS);filterVer<NTAPS_CHROMA>(bitDepth, src, srcStride, dst, dstStride, width, height, isFirst, isLast, m_chromaFilter[frac<<(1-csy)]);}
}

HEVC代码学习6:filterHor和filterVer函数相关推荐

  1. HEVC代码学习42:estIntraPredLumaQT函数

    在之前的 HEVC代码学习37:帧内预测代码整体学习 中已经提到,estIntraPredLumaQT是亮度帧内预测的入口函数,下面将对该函数进行详细学习. estIntraPredLumaQT中完成 ...

  2. HEVC代码学习39:decodeCtu和xDecodeCU函数

    在之前 HEVC代码学习38:decompressSlice函数 学习中提到,解码slice会遍历所有CTU,调用decodeCtu和decompressCtu解码每一个CTU.下面就来学习一下dec ...

  3. HEVC代码学习——帧间预测:预测MV获取(xEstimateMvPredAMVP、fillMVPCand)

    HEVC帧间预测在AMVP模式下是依靠xEstimateMvPredAMVP函数获取预测MV(MVP)的. 这部分内容的学习还可以参考这两篇博客: HEVC代码学习15:AMVP相关函数 HM编码器代 ...

  4. HEVC代码学习:帧间预测——MVP过程中MV的获取、传递及存储

    作为一个视频编码小白,最近开始着手啃HEVC帧间预测的代码,想用博客记录一下自己的学习过程,也想与大家分享.交流一下. HEVC代码的学习主要是参考两位大神岳麓吹雪.NB_vol_1的博客以及HM参考 ...

  5. H.266代码学习:decodeCtu和xDecodeCU函数

    之前 HEVC代码学习39:decodeCtu和xDecodeCU函数 中对HM中的CTU解码函数进行了学习,这里来学习一下JEM中的. 首先需要强调的是,这里只是用来解码flag.系数等,没有进行预 ...

  6. H.266代码学习:decompressCtu和xDecompressCU函数

    今天来学习一下JEM的decompressCtu和xDecompressCU函数.之前在 H.266代码学习:decodeCtu和xDecodeCU函数 学习了的学习中提到,decodeCtu和xDe ...

  7. HEVC代码学习15:AMVP相关函数

    在HEVC中,使用了AMVP技术,利用空域和时域上的运动向量的相关性,为当前PU建立候选预测MV(MVP)列表.编码器从中选出最优的预测MV,并对MV进行差分编码:解码端会构造相同的列表,仅需要运动向 ...

  8. H.266/VVC-VTM代码学习27-VTM中编码器主函数逻辑

    H.266/VVC专栏传送 上一篇:H.266/VVC-VTM代码学习26-VTM中RDcost的计算与λ的设定(二) 下一篇:持续创作中- 目录 H.266/VVC专栏传送 前言 一.简介 二.代码 ...

  9. HEVC代码学习13:predInterSearch函数

    在上一章的xCheckRDCostInter学习中,我们知道了,进行帧间搜索的入口实际是predInterSearch,今天我们就来对他进行学习. 推荐看大神博客 http://blog.csdn.n ...

最新文章

  1. linux信号以及core
  2. 爬取某瓣电影中你好,李焕英电影的短评并生成词云
  3. 立方体引起的引力异常计算&画图
  4. 数据装载器连接其他oracle数据库_07
  5. mybatis 映射成多个list_SSM:Mybatis架构与原理
  6. mysql单实例和多实例,MySQL单实例、多实例安装_MySQL
  7. 如何按距离排序 php,php做附近的人,根据距离由近到远进行排序
  8. tensorflow中如何进行可视化和减轻过拟合(转)
  9. 扩大人类对车辆的控制 新种双轨制自驾车出现
  10. 不精确微分/不完整微分(Inexact differential/Imperfect differential)
  11. 最佳实践:银尔达YED-S724 网红4G DTU 接入 ThingsKit 物联网平台
  12. WINVNC(二)omni_thread
  13. 三菱FX5U系列程序 伺服轴自动运行计数回原点程序,工位单步运行程序,轴JOG,回原点,绝对定位,相对定位,力矩控制
  14. RandomAccessFile类的readLine方法对文本文件中文字符读取显示乱码的问题及解决方法的深入分析
  15. 南澳.西冲-东冲穿越之旅
  16. Js 方法函数记录笔记
  17. [leetcode] 229. Majority Element II
  18. 2022年IT服务行业研究报告
  19. 黑群晖二合一已损毁_搬运 如何在黑群晖中重置损毁的储存池/储存空间
  20. 互联网金融系列-支付清算体系介绍-上

热门文章

  1. CentOS命令之一
  2. 韩顺平Oracle笔记
  3. ListView 单条item刷新
  4. mysql卸载什么文件夹_MySQL卸载
  5. 【C++】C++静态库和动态库的区别
  6. Html利用函数输入学生的性别,JavaWeb表单及时验证功能在输入后立即验证(含用户类型,性别,爱好...的验证)...
  7. html大文件传输思路
  8. Python flask入门
  9. 所生成项目的处理器架构“MSIL”与引用“ ”的处理器架构“AMD64”不匹配。
  10. xilinx ku115上pciex1 的眼图