视频抖动检测方法

       这阵子在做视频质量评估系统,其中如何检测视频抖动这一块做了挺长时间的,正好在整理文档的过程中整理到博客上来,错误部分欢迎指正和交流。

视频抖动检测基本原理

       视频发生抖动时的最显著特征就是帧与帧之间会发生整体的位移,检测出位移之后再通过进一步的逻辑判定视频是否产生抖动,因此基本上视频的抖动都是围绕着如何检测出这个位移进行的。
       常用的位移估计方法有光流法、块匹配法、特征点匹配法及灰度投影法。我在实验中尝试了光流法、特征点匹配法及灰度投影法,实际操作体验如下:

       光流法网上有非常多资料,实际操作中采用了Fast角点检测和LK稀疏光流。实际使用中因为光流依赖于特征点的检测好坏,如果当前环境没有办法找到比较多的角点,那估算出的位移就会非常不准确,如果想要获得比较好的效果则计算量会较大。并且光流对于实际环境中移动的物体非常容易产生错误的估计,鲁棒性较差。

       特征点匹配法同光流法,比较依赖于特征点的寻找。而想要找到比较准确的特征点通常会有非常大的计算量,实际运行速度其实比较慢。

        灰度投影法是使用比较多,计算量也相对较小,实际效果也较好的方法,因此本文着重对灰度投影法做介绍。(以上只是个人实际体验,不代表光流和特征点方法不好,有比较好的解决方案欢迎分享)

灰度投影法

       灰度投影法其实是一种对图像分布特征进行简化提取的一种操作,以二维图像的像素行和列为单位,将图像特征转化为沿行、列坐标的曲线,从而更容易对图像分布特征进行计算。

       具体投影方法可表示为:

       Gk(j)为第 k帧图像第 j 列的灰度值;

       Gk(i,j)是第 k帧图像上 (i, j )位置处的像素灰度值

       Gk(i)为第 k帧图像第i 行的灰度值;

       对于一幅M*N的图像,其行方向的灰度投影RProj:

同理,列方向灰度投影CProj:

       其中MeanR和MeanC分别为行像素和均值、列像素和均值,说白了,行灰度投影就是将每行的像素替换为了改行所有像素和与像素均值的差值,列灰度投影同理。

       有了投影具体计算图像位移的方法有多种。如果我们将得到的灰度投影沿行和列的方向展开,可以得到如下的曲线:

       注意,因为投影是该行或列与均值的差值,因此该差值可能为负也可能为正。这样对于两帧图像将会有两个如上的曲线,通过计算曲线的相关性函数,相关性函数取得最小值时的行、列方向差值,即为图片沿行(或列)方向的位移。

       具体为何相关性可以计算位移请参考:

https://baike.baidu.com/item/%E4%BA%92%E7%9B%B8%E5%85%B3%E5%87%BD%E6%95%B0/9851299?fr=aladdin

       原理看起来是比较复杂,通过公式理解会更容易一些,先给出计算公式:


       对于w和v 在合适的取值范围内,能够使R(w)和R(v)的值达到最小的值的取值分别为Wmin和Vmin,则当前帧相对于参考帧图像在水平和垂直方向的位移矢量为:

        公式看起来比较复杂难理解,解释一下

       我们一直计算当前帧j+w-1行与前一帧m+j行间的差值的平方,而其中的自变量为w,即在w的取值范围内,可以计算着两行间的间隔为w+m-1,即其实我们遍历计算了整个M行图像中所有行间隔为w+m-1的行灰度投影差值的平方;m为设定的固定值,即行的间隔随着w的变化而变化,当某一时刻Wmin使得R(w)最小,说明此时的行与行之间的投影比较相似,那此时行与行间的间隔即为图像位移的值。

       具体为什么最终的位移距离是dx=m+1-Wmin:

       当w在1到2m+1间取值的时候,我们计算的行与行之间的距离范围在m~2m间取值,则实际 计算的位移dx的取值范围则为(m+1-1)~(m+1-2m-1)即-m~m之间取值。

       如果感觉上述描述的还不够明确,自己将w,m ,j自变量带入实际的数值模拟一遍就明白了。

以上灰度投影的基本原理就介绍完了。

提高位移计算精度

        因为实际视频中环境通常比较复杂,如果仅通过计算灰度投影可能得到的位移误差较大,甚至上述的相关性函数并不收敛,没有办法取得最小值,在取值范围内没有办法得到位移值。因此有如下方法可以提高计算精度:

进行滤波

       滤波的好处是能消除图像边缘的影响,因为图像移动量大时 ,边缘信息在每一幅图像上是唯一的 ,也就是每一幅图像的边缘信息各不相同 ,因此导致投影波形在边缘处差异较大 ,在互相关计算时对互相关的峰值产生不利的影响。而滤波器如何选择要根据实际视频场景和效果来选取(我在实际实验中没有采用滤波器)。

直方图均衡

       视频进行直方图均衡化后可以拉伸图像的对比度,能够使得相关性函数更收敛,并提高精度,实际测试中效果如下图:


       蓝色曲线为没有加入直方图均衡的100帧图像间的位移曲线,红色曲线为加入直方图均衡后的位移曲线可以发现进行对比度拉伸后,可以去除一些过大的位移偏差且不会对原位移结果产生过大的影响。

图像分块

       如果计算整体的图像位移,当图像中移动物体过多的时候,很容易因为物体的移动而产生误判,因此可以采用将图像分块的方式:

        当运动物体过多的时候,每块的运动独立计算,通过一定的逻辑规则判定可以消除运动物体的影响。

实际效果


可以看出对于一般的抖动具有比较好的检测效果,具有一定的鲁棒性。

部分源码

       计算灰度投影

//计算行列灰度投影
void Stablization::calcRgray()
{

int row = gray.rows;
int col = gray.cols;

int kRow = (row - 2 * sRow) / 2;
int kCol = (col - 2 * sCol) / 2;

int sum = 0;
int k = 0;

int meanRow = 0;    //行灰度均值
uchar*p;
/*m和n用于4个块的循环*/
for (int m = 0; m < sizeRow; m++)
{
    for (int n = 0; n < sizeCol; n++)
    {
        vector<int>grayRow;    //行灰度值
        int sumRow = 0;
        for (int i = sRow + m * kRow; i < sRow + (m + 1) * kRow; i++)
        {
            p = gray.ptr<uchar>(i);
            int sum_temp = 0;
            for (int j = sCol + n * kCol; j < sCol + (n + 1) * kCol; j++)
            {
                sum_temp += *(p + j);
            }

            grayRow.push_back(sum_temp);    //grayRow储存行像素和
            sumRow += sum_temp;
        }

        meanRow = sumRow / kRow;
        for (int t = 0; t < kRow; t++)
        {
            if (colpreRow[k].size()!=kRow)
                colpreRow[k].resize(kRow);
            colpreRow[k][t] = grayRow[t] - meanRow;
        }

        k++;

    }
}

}

计算位移值

void Stablization::getMotionVectorx() //idx为搜索序号范围
{

motion_x.resize(4);
int row;
row = (gray.rows - 2 * sRow) / 2;
vector<int>Rp[4];    //用于记录各位移下的行投影偏移值
Rp[0].resize(idx);
Rp[1].resize(idx);
Rp[2].resize(idx);
Rp[3].resize(idx);
//int s = 0;    //最小位移值

for (int i = 0; i < idx; i++)
{
    for (int j = 0; j < row - idx; j++)
    {
        Rp[0][i] += (colpreRow[0][i + j] - colpreRow_prev[0][idx / 2 + j])*(colpreRow[0][i + j] - colpreRow_prev[0][idx / 2 + j]) / (row - idx);
        Rp[1][i] += (colpreRow[1][i + j] - colpreRow_prev[1][idx / 2 + j])*(colpreRow[1][i + j] - colpreRow_prev[1][idx / 2 + j]) / (row - idx);
        Rp[2][i] += (colpreRow[2][i + j] - colpreRow_prev[2][idx / 2 + j])*(colpreRow[2][i + j] - colpreRow_prev[2][idx / 2 + j]) / (row - idx);
        Rp[3][i] += (colpreRow[3][i + j] - colpreRow_prev[3][idx / 2 + j])*(colpreRow[3][i + j] - colpreRow_prev[3][idx / 2 + j]) / (row - idx);
    }
}

for (int i = 0; i < 4; i++)
{
    int temp = Rp[i][0];
    for (int m = 0; m < idx - 1; m++)
    {
        if (Rp[i][m + 1] <= temp)
        {
            temp = Rp[i][m + 1];
            motion_x[i] = idx / 2 - (m + 1);
        }
    }
}

}

参考资料库

这里给一些博主在做抖动检测算法中的一些抖动视频的database(这个database真的难找):

https://www.cc.gatech.edu/cpl/projects/videostabilization

http://web.cecs.pdx.edu/~fliu/project/subspace_stabilization

http://pages.cs.wisc.edu/~lizhang/projects/lfstable/

上传了一个检测效果的视频,因为插入视频太麻烦放在网盘下了,感兴趣可以下载看下效果,左上角的数字代表抖动的level:

https://pan.baidu.com/s/1o8JXlOm

  • 10
    点赞
  • 74
    收藏
    觉得还不错? 一键收藏
  • 22
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 22
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值