【OpenCV】帧差法、级联分类器、透视变换

发布于:2025-05-22 ⋅ 阅读:(20) ⋅ 点赞:(0)

一、帧差法(移动目标识别):

好处:开销小,不怎么消耗CPU的算力,对硬件要求不高,但只适合固定摄像头

1、优点

  1. 计算效率高,硬件要求

  2. 响应速度快,实时性强

    直接利用连续帧的像素差异检测运动,延迟极低,可实时捕捉动态目标的运动轨迹。
  3. 无需背景建模,适应动态变化

    不依赖静态背景模型(如高斯混合模型 GMM),因此对光照突变、背景微小变化(如树叶晃动)不敏感,鲁棒性优于部分背景差分法。
  4. 内存占用少

    仅需存储前一帧或前几帧图像,内存开销小,适合资源受限的系统。

2、缺点

  1. 仅适用于固定摄像头场景

  2. 无法检测静止目标

    若目标在相邻帧间无位移(如短暂静止的行人),差分结果中目标区域像素差异为零,会被误认为背景,导致漏检。
  3. 对噪声敏感,检测精度低

    微小噪声(如传感器噪声、光线波动)会导致差分图像出现大量 “伪运动区域”,需依赖形态学操作(如腐蚀、膨胀)降噪,但可能模糊目标边界或丢失细节
  4. 目标 “空洞化” 与 “断裂” 问题

    当目标内部像素变化较小时(如纯色物体),差分后可能出现 “空洞”;若目标运动速度快,相邻帧重叠区域少,可能导致检测结果不连续(如车辆被分割为多个碎片)。
  5. 无法提供目标完整信息

    仅能检测运动区域的位置和轮廓,无法获取目标类别(如人、车)、尺寸、历史轨迹关联等高级信息,需结合其他算法(如目标跟踪、深度学习分类)补充。
步骤 目的 核心操作 / 算法
1. 读取图像 获取视频流中的连续帧(如第 t 帧和第 t+1 帧) VideoCapture + read()
2. 灰度转换 减少计算复杂度,突出亮度变化 cvtColor()
3. 帧差分计算 计算相邻帧像素差异,突出运动区域 absdiff()
4. 二值化处理 将差分结果转换为二值图像(前景为运动区域,背景为静止区域) threshold()
5. 降噪 去除噪声,连接断裂的运动区域 腐蚀(erode())+ 膨胀(dilate()
6. 多边拟合 提取运动目标轮廓,过滤无效噪声 findContours() + 轮廓面积过滤
7. 结果输出 在原图标记目标区域并显示 / 保存 rectangle() + imshow()

3.开、闭运算

开运算:先腐蚀在膨胀,用来消除图像周边小白点

闭运算:先膨胀在腐蚀,用来消除物体内部的小黑点

二、opencv级联分类器:

正样本数据采集:需要检测的物体图片

负样本数据采集:非检测物的图片(是正样本的3倍)

1.优点

  • 可检测特定目标,泛化能力较强(需充分训练)。
  • 计算效率高(级联结构快速排除非目标区域)。
  • 无需视频时序信息,适用于单图像检测。

2.缺点

  • 需要大量标注数据进行训练,且训练过程耗时。
  • 仅能检测预定义目标,无法检测未知类型目标。
  • 对目标尺度、姿态变化敏感(需多尺度检测或重新训练)。

3.步骤:

  • 1.灰度处理

Mat gray;
cvtColor(frame,gray,CV_BGR2GRAY);

  • 2.压缩到原来一半

Mat small(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1);
resize(gray,small,small.size(),0,0,INTER_LINEAR);

  • 3.直方图均衡化

equalizeHist(small,small);

  • 4.用级联分类识别车辆

 vector<Rect> cars;
cascade.detectMultiScale(small,cars,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30));

  • 5.绘制矩形

vector<Rect>::const_iterator iter;

for(iter=cars.begin();iter!=cars.end();iter++){
    rectangle(frame,
         cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),
         cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),
         Scalar(0,255,0),
         2,3
        );

}

#include <iostream>
#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

void detec_car(Mat& frame,CascadeClassifier& cascade,double scale)
{
    //1.灰度处理
    Mat gray;
    cvtColor(frame,gray,CV_BGR2GRAY);
    //2.压缩到原来一半
    Mat small(cvRound(frame.rows/scale),cvRound(frame.cols/scale),CV_8UC1);
    resize(gray,small,small.size(),0,0,INTER_LINEAR);

    //3.直方图均衡化
    equalizeHist(small,small);
    //4.用级联分类识别车辆
    vector<Rect> cars;
    cascade.detectMultiScale(small,cars,1.1,2,0|CV_HAAR_SCALE_IMAGE,Size(30,30));
    //5.绘制矩形
    vector<Rect>::const_iterator iter;
    for(iter=cars.begin();iter!=cars.end();iter++){
        rectangle(frame,
                  cvPoint(cvRound(iter->x*scale),cvRound(iter->y*scale)),
                  cvPoint(cvRound((iter->x+iter->width)*scale),cvRound((iter->y+iter->height)*scale)),
                  Scalar(0,255,0),
                  2,3
                  );
    }
    imshow("res",frame);
}

int main()
{
    Mat frame;
    //加载级联分类器
    CascadeClassifier cascade;
    cascade.load("xml/cars.xml");
    VideoCapture video("video/carMove.mp4");
    while(video.read(frame)){
        imshow("video",frame);
        detec_car(frame,cascade,2.0);
        waitKey(40);
    }
    return 0;
}

三、透视变换

将倾斜的图片物品扯平变正

(1)findHomography函数

Mat findHomography( InputArray srcPoints, InputArray dstPoints,
                                 int method = 0, double ransacReprojThreshold = 3,
                                 OutputArray mask=noArray(), const int maxIters = 2000,
                                 const double confidence = 0.995);

//输入点击坐标顺序,接收顺序(都是存在容器当中)

通过原图四个坐标和转化后四个坐标计算映射矩阵

Mat homo=findHomography(data.points,obj,CV_FM_RANSAC);

(2)warpPerspective函数

void warpPerspective( InputArray src, OutputArray dst,
                                   InputArray M, Size dsize,

                                   int flags = INTER_LINEAR,
                                   int borderMode = BORDER_CONSTANT,
                                   const Scalar& borderValue = Scalar());


 warpPerspective(img,result,homo,result.size());

#include <iostream>

#include <opencv2/opencv.hpp>

using namespace cv;
using namespace std;

typedef struct{
    Mat img;//需操作的图像
    vector<Point2f> points;//保存每次鼠标点击的位置信息
}DATA;

void mouseHandle(int event,int x,int y,int flag,void* arg){
    DATA* pd=(DATA*)arg;
    if(event==EVENT_LBUTTONDOWN){//鼠标左键按下

        //图片,中心坐标,半径,圆颜色,边缘粗细,线条类型
        circle(pd->img,Point(x,y),3,Scalar(0,255,0),3,CV_AA);
        if(pd->points.size()<4){
            pd->points.push_back(Point2f(x,y));
        }
        imshow("book",pd->img);
    }

}
int main()
{
    //行列(高宽)
    Mat result=Mat::zeros(400,800,CV_8UC1);
    Mat img=imread("image/1.jpg");
    imshow("book",img);
    DATA data;
    data.img=img;
    setMouseCallback("book",mouseHandle,&data);
    waitKey(0);

    //设置转换后的坐标
    //这个顺序要与点击顺序一致,左上为开始,顺时针
    vector<Point2f> obj;
    obj.push_back(Point2f(0,0));
    obj.push_back(Point2f(800,0));
    obj.push_back(Point2f(800,400));
    obj.push_back(Point2f(0,400));

    //通过原图四个坐标和转化后四个坐标计算映射矩阵
    Mat homo=findHomography(data.points,obj,CV_FM_RANSAC);
    imshow("homo",homo);

    //四个参数类型
    //InputArray src,   OutputArray dst,  InputArray M,   Size dsize
    warpPerspective(img,result,homo,result.size());

    imshow("result",result);
    waitKey(0);
    return 0;
}