OpenCV 4.x API 详解与C++实例-运动分析和对象跟踪

第十节 运动分析和对象跟踪

OpenCV的imgproc模块提供了运动分析和对象跟踪的基础函数,可以根据这些函数对视频进行前景-背景分离,从而达到运动分析和对象跟踪的目的。

1、cv::accumulate


将多幅图像累加。

void cv::accumulate(InputArray src,InputOutputArray dst,InputArraymask = noArray())

该函数将src或其某些元素添加到dst中:

dst ( x , y ) ← dst ( x , y ) + src ( x , y ) if mask ( x , y ) ≠ 0 \texttt{dst} (x,y) \leftarrow \texttt{dst} (x,y) + \texttt{src} (x,y) \quad \text{if} \quad \texttt{mask} (x,y) \ne 0 dst(x,y)dst(x,y)+src(x,y)ifmask(x,y)=0

该函数支持多通道图像,对每个通道都是独立处理的。

函数cv :: accumulate可以用于例如收集静态相机所查看的场景背景的统计信息,以及用于进一步的前景背景分割。

参数如下:

参数名称 参数描述
src 输入图像,支持的类型为 CV_8UC(n), CV_16UC(n), CV_32FC(n) or CV_64FC(n),
dst 通道数与输入图像相同的累加器图像, 类型为 CV_32F or CV_64F.
mask 可选的操作掩膜。
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
    // 打开摄像头
    cv::VideoCapture cap(0);
    if(!cap.isOpened()){
        cerr << "cannot open camera.\n";
        return EXIT_FAILURE;
    }

    cv::Mat frame;
    int averageCounts = 30;
    int currentCount = 0;
    cv::Mat averageFrame;
    cv::Mat gray;
    cv::Mat frameDelta;
    cv::Mat foreGround,backGround;
    int key = -1;
    while(true){
        cap >> frame;
        if(frame.empty()){
            cerr << "cannot grab frame from camera.\n";
            break;
        }

        cv::cvtColor(frame,gray,cv::COLOR_BGR2GRAY);

        if(currentCount < averageCounts){
            if(averageFrame.empty()){
                averageFrame.create(frame.rows,frame.cols,CV_32FC1);
            }
            // 计算平均图像
            cv::accumulate(gray,averageFrame);
            currentCount++;
        }else{
            // 背景分离
            cv::convertScaleAbs(averageFrame,averageFrame);
            cv::absdiff(gray,averageFrame,frameDelta);

            // 阈值分割
            cv::threshold(frameDelta,foreGround,25,255,cv::THRESH_BINARY_INV);

            cv::imshow("camera",frame);
            cv::imshow("deltaFrame",frameDelta);
            cv::imshow("foreGround",foreGround);

            key = cv::waitKey(10);
            if(key == 27){
                break;
            }
        }
    }
    cv::destroyAllWindows();
    return 0;
}

在这里插入图片描述

2、cv::accumulateProduct


将两个输入图像的每个元素乘积添加到累加器图像。

void cv::accumulateProduct(InputArray src1,InputArray src2,InputOutputArray dst,InputArray mask = noArray())

该函数将两个图像或其所选区域的乘积添加到累加器dst中:

dst ( x , y ) ← dst ( x , y ) + src1 ( x , y ) ⋅ src2 ( x , y ) if mask ( x , y ) ≠ 0 \texttt{dst} (x,y) \leftarrow \texttt{dst} (x,y) + \texttt{src1} (x,y) \cdot \texttt{src2} (x,y) \quad \text{if} \quad \texttt{mask} (x,y) \ne 0 dst(x,y)dst(x,y)+src1(x,y)src2(x,y)ifmask(x,y)=0

该函数支持多通道图像并且每个通道都是独立处理的。参数如下:

参数名称 参数名称描述
src1 第一个输入图像,1或3通道,8位或32位浮点。
src2 与src1具有相同类型和相同大小的第二个输入图像。
dst 与32位或64位浮点数具有与输入图像相同通道数的累加器图像。
mask 可选的操作掩膜。

3、cv::accumulateWeighted


void cv::accumulateWeighted(InputArray src,InputOutputArray dst,double alpha,InputArray mask = noArray())

该函数计算输入图像src和累加器dst的加权和,以使dst成为帧序列的运行平均值:

dst ( x , y ) ← ( 1 − alpha ) ⋅ dst ( x , y ) + alpha ⋅ src ( x , y ) if mask ( x , y ) ≠ 0 \texttt{dst} (x,y) \leftarrow (1- \texttt{alpha} ) \cdot \texttt{dst} (x,y) + \texttt{alpha} \cdot \texttt{src} (x,y) \quad \text{if} \quad \texttt{mask} (x,y) \ne 0 dst(x,y)(1alpha)dst(x,y)+alphasrc(x,y)ifmask(x,y)=0

也就是说,alpha调节更新速度(累加器“忘记”较早图像的速度)。 该功能支持多通道图像。 每个通道都是独立处理的。

参数名称 参数描述
src 输入图像为1或3通道,8位或32位浮点。
dst 通道数与输入图像相同的累加器图像,32位或64位浮点。
alpha 输入图像的权重。
mask 可选的掩膜操作。
#include <iostream>
#include <opencv2/opencv.hpp>

using namespace std;

int main()
{
    // 打开摄像头
    cv::VideoCapture cap("videos/vtest.avi",cv::CAP_ANY);
    if(!cap.isOpened()){
        cerr << "cannot open camera.\n";
        return EXIT_FAILURE;
    }

    cv::Mat background;
    cap >> background;
    if(background.empty()){
        cerr << "cannot grab frame.\n";
        return EXIT_FAILURE;
    }
    cv::cvtColor(background,background,cv::COLOR_BGR2GRAY);
    cv::GaussianBlur(background,background,cv::Size(0,0),1.0);

    cv::Mat fback(background.size(),CV_32FC1);
    vector<vector<cv::Point> > contours;
    vector<cv::Vec4i> hierarchy;

    cv::Mat frame,gray,diff;

    int counts = 0;

    while(cap.isOpened()){
        cap >> frame;
        if(frame.empty()){
            cerr << "cannot grab frame\n";
            break;
        }
        // 转换成灰度图像
        cv::cvtColor(frame,gray,cv::COLOR_BGR2GRAY);
        // 高斯模糊
        cv::GaussianBlur(gray,gray,cv::Size(0,0),1.0);

        if(counts < 30){
            // 对图像进行累加,读取前30帧图像,作为背景
            cv::accumulateWeighted(gray,fback,0.8);
            counts ++;
        }else{
             cv::accumulateWeighted(gray,fback,0.2);
        }

        // 分离前景和背景
        cv::absdiff(gray,background,diff);

        // 阈值化处理
        cv::threshold(diff,diff,120,255,cv::THRESH_BINARY);

        cv::imshow("src",frame);
        cv::imshow("diff",diff);

        if(cv::waitKey(10) == 27){
            break;
        }

    }

    return 0;
}

在这里插入图片描述

4、cv::phaseCorrelate


用于检测两个图像之间发生的平移。

Point2d cv::phaseCorrelate(InputArray src1,InputArray src2,InputArray window = noArray(),double * response = 0)

该函数运算利用傅立叶位移定理来检测频域中的平移。 它可以用于快速图像配准以及运动估计。 有关更多信息,请参见http://en.wikipedia.org/wiki/Phase_correlation

计算两个提供的源阵列的交叉功率谱。 如果需要,可以使用getOptimalDFTSize填充数组。

该函数执行以下方程式:

  • 首先,它将Hanning窗口(请参见http://en.wikipedia.org/wiki/Hann_function)应用于每个图像,以消除可能的边缘效果。 缓存此窗口,直到阵列大小更改以加快处理时间为止。

  • 接下来,它计算每个源数组的前向DFT: G a = F { s r c 1 } ,    G b = F { s r c 2 } \mathbf{G}_a = \mathcal{F}\{src_1\}, \; \mathbf{G}_b = \mathcal{F}\{src_2\} Ga=F{ src1},Gb=F{ src2},其中 F \mathcal{F} F是前向DFT。

  • 然后,它计算每个频域阵列的交叉功率谱: R = G a G b ∗ ∣ G a G b ∗ ∣ R = \frac{ \mathbf{G}_a \mathbf{G}_b^*}{|\mathbf{G}_a \mathbf{G}_b^*|} R=GaGbGaGb

  • 接下来,通过逆DFT将互相关转换回时域: r = F − 1 { R } r = \mathcal{F}^{-1}\{R\} r=F1{ R}

  • 最后,它计算峰值位置,并在峰值周围计算5x5加权质心,以实现亚像素精度

    ( Δ x , Δ y ) = weightedCentroid { arg ⁡ max ⁡ ( x , y ) { r } } (\Delta x, \Delta y) = \texttt{weightedCentroid} \{\arg \max_{(x, y)}\{r\}\} (Δx,Δy)=weightedCentroid{ argmax(x,y){ r}}

  • 如果不为零,则将响应参数计算为峰位置周围5x5重心内r的元素之和。 归一化为最大值1(意味着有一个峰),当有多个峰时会更小。

参数如下:

参数名称 参数描述
src1 源浮点数组(CV_32FC1或CV_64FC1)
src2 源浮点数组(CV_32FC1或CV_64FC1)
window 具有开窗系数的浮点数组,可减少边缘效应(可选)。
response 峰值周围5x5质心内的信号功率,介于0和1之间(可选)。
#include <stdio.h>
#include <iostream>
#include "opencv2/core.hpp"
#include "opencv2/videoio.hpp"
#include "opencv2/highgui.hpp"
#include "opencv2/imgproc.hpp"

using namespace cv;


int main(int argc, char* argv[])
{

  auto src_im = cv::imread(images/balloon.png", 0);
  auto dst_im = cv::imread("images/balloon-2.png", 0);

  cv::Mat src_im_32f, dst_im_32f, hann;

  // src_im.convertTo(src_im_32f, CV_32F);
  // dst_im.convertTo(dst_im_32f, CV_32F);

  cv::Laplacian(src_im, src_im_32f, CV_32F);
  cv::Laplacian(dst_im, dst_im_32f, CV_32F);

  cv::Point2d shift = cv::phaseCorrelate(src_im_32f, dst_im_32f, hann);

  std::cout << shift.x << ", " << shift.y << std::endl;

  return 0;
}

5、cv::createHanningWindow


该函数计算二维的汉宁(Hanning)窗系数。

void cv::createHanningWindow(OutputArray dst,Size winSize,int type)

有关更多信息,请参见(http://en.wikipedia.org/wiki/Hann_function)和(http://en.wikipedia.org/wiki/Window_function)。

参数如下:

参数名称 参数描述
dst 将Hann系数放入其中的目标数组
winSize 窗口大小规格(宽度和高度都必须> 1)
type 创建的数组类型
// create hanning window of size 100x100 and type CV_32F
Mat hann;
createHanningWindow(hann, Size(100, 100), CV_32F);

猜你喜欢

转载自blog.csdn.net/wujuxKkoolerter/article/details/113068318
今日推荐