OpenCV4 实现背景分离的详细步骤(背景减法模型)

目录
  • 定义:
  • 背景建模包括两个主要步骤:
  • OpenCV中三个背景分离的重要函数
    • BackgroundSubtractorMOG()
    • BackgroundSubtractorMOG2
    • BackgroundSubtractorGMG
    • BackgroundSubtractorKNN
    • C++实现:
    • python实现:
  • 利用图像减法函数实现(python版本):

定义:

背景分离,又称背景减法模型。

背景分离(BS)是一种通过使用静态相机来生成前景掩码(即包含属于场景中的移动对象像素的二进制图像)的常用技术。

顾名思义,BS计算前景掩码,在当前帧与背景模型之间执行减法运算,其中包含场景的静态部分,或者更一般而言,考虑到所观察场景的特征,可以将其视为背景的所有内容。

背景建模包括两个主要步骤:

1. 背景初始化;

2. 背景更新。

初步,计算背景的初始模型,而在第二步中,更新模型以适应场景中可能的变化。

OpenCV中三个背景分离的重要函数

BackgroundSubtractorMOG()

这是一个以混合高斯模型为基础的前景/背景分割算法。

它使用 K(K=3 或 5)个高斯分布混合对背景像素进行建模。使用这些颜色(在整个视频中)存在时间的长短作为混合的权重。背景的颜色一般持续的时间最长,而且更加静止。

在 x,y平面上一个像素就是一个像素,没有分布,但是背景建模是基于时间序列的,因此每一个像素点所在的位置在整个时间序列中就会有很多值,从而构成一个分布

使用函数时先用函数:CV2.createBackgroundSubtractorMOG() 创建一个背景对象。这个函数有些可选参数,比如要进行建模场景的时间长度,高斯混合成分的数量,阈值等。将他们全部设置为默认值。然后在整个视频中我们是需要使用backgroundsubtractor.apply() 就可得到前景的掩模了,移动的物体会被标记为白色,背景会被标记为黑色的,前景的掩模就是白色的了。

不过目前这个方法已经被弃用了,OpenCV中也没有了相关函数的API。

BackgroundSubtractorMOG2

这个也是以高斯混合模型为基础的背景/前景分割算法。这个算法的一个特点是它为每 一个像素选择一个合适数目的高斯分布。(上一个方法中我们使用是 K 高斯分布),这样就会对由于亮度等发生变化引起的场景变化产生更好的适应。

和前面一样我们需要创建一个背景对象。但在这里我们我们可以选择是否检测阴影。如果 detectShadows = True(默认值),它就会检测并将影子标记出来,但是这样做会降低处理速度。影子会被标记为灰色。

BackgroundSubtractorMOG2算法的两个改进点:

  1. -阴影检测
  2. -速度快了一倍

BackgroundSubtractorGMG

此算法结合了静态背景图像估计和每个像素的贝叶斯分割。它使用前面很少的图像(默认为前 120 帧)进行背景建模。使用了概率前景估计算法(使用贝叶斯估计鉴定前景)。这是一种自适应的估计,新观察到的 对象比旧的对象具有更高的权重,从而对光照变化产生适应。一些形态学操作 如开运算闭运算等被用来除去不需要的噪音,在前几帧图像中你会得到一个黑色窗口,对结果进行形态学开运算对与去除噪声很有帮助。

不过同样的,这个方法目前也是不再用了。

BackgroundSubtractorKNN

KNN作为大名鼎鼎的机器学习算法,其用在背景分离应用中也是得心应手,但是在此不对KNN作过多的解释。

C++实现:

#include "opencv2/imgcodecs.hpp"
#include "opencv2/imgproc.hpp"
#include "opencv2/videoio.hpp"
#include <opencv2/highgui.hpp>
#include <opencv2/video.hpp>
#include <iostream>
#include <sstream>
using namespace cv;
using namespace std;

const int HISTORY_NUM = 7;// 14;// 历史信息帧数
const int nKNN = 3;// KNN聚类后判断为背景的阈值
const float defaultDist2Threshold = 20.0f;// 灰度聚类阈值

struct PixelHistory
{
	unsigned char* gray;// 历史灰度值
	unsigned char* IsBG;// 对应灰度值的前景/背景判断,1代表判断为背景,0代表判断为前景
};

int main()
{
	PixelHistory* framePixelHistory = NULL;// 记录一帧图像中每个像素点的历史信息
	cv::Mat frame, FGMask, FGMask_KNN;
	int keyboard = 0;
	int rows, cols;
	rows = cols = 0;
	bool InitFlag = false;
	int frameCnt = 0;
	int gray = 0;
	VideoCapture capture("768X576.avi");
	Ptr<BackgroundSubtractorKNN> pBackgroundKnn =
		createBackgroundSubtractorKNN();
	pBackgroundKnn->setHistory(200);
	pBackgroundKnn->setDist2Threshold(600);
	pBackgroundKnn->setShadowThreshold(0.5);

	while ((char)keyboard != 'q' && (char)keyboard != 27)
	{
		// 读取当前帧
		if (!capture.read(frame))
			exit(EXIT_FAILURE);

		cvtColor(frame, frame, COLOR_BGR2GRAY);
		if (!InitFlag)
		{
			// 初始化一些变量
			rows = frame.rows;
			cols = frame.cols;
			FGMask.create(rows, cols, CV_8UC1);// 输出图像初始化

			// framePixelHistory分配空间
			framePixelHistory = (PixelHistory*)malloc(rows * cols * sizeof(PixelHistory));
			for (int i = 0; i < rows * cols; i++)
			{
				framePixelHistory[i].gray = (unsigned char*)malloc(HISTORY_NUM * sizeof(unsigned char));
				framePixelHistory[i].IsBG = (unsigned char*)malloc(HISTORY_NUM * sizeof(unsigned char));
				memset(framePixelHistory[i].gray, 0, HISTORY_NUM * sizeof(unsigned char));
				memset(framePixelHistory[i].IsBG, 0, HISTORY_NUM * sizeof(unsigned char));
			}

			InitFlag = true;
		}
		if (InitFlag)
		{
			FGMask.setTo(Scalar(255));
			for (int i = 0; i < rows; i++)
			{
				for (int j = 0; j < cols; j++)
				{
					gray = frame.at<unsigned char>(i, j);
					int fit = 0;
					int fit_bg = 0;
					// 比较确定前景/背景
					for (int n = 0; n < HISTORY_NUM; n++)
					{
						if (fabs(gray - framePixelHistory[i * cols + j].gray[n]) < defaultDist2Threshold)// 灰度差别是否位于设定阈值内
						{
							fit++;
							if (framePixelHistory[i * cols + j].IsBG[n])// 历史信息对应点之前被判断为背景
							{
								fit_bg++;
							}
						}
					}
					if (fit_bg >= nKNN)// 当前点判断为背景
					{
						FGMask.at<unsigned char>(i, j) = 0;
					}
					// 更新历史值
					int index = frameCnt % HISTORY_NUM;
					framePixelHistory[i * cols + j].gray[index] = gray;
					framePixelHistory[i * cols + j].IsBG[index] = fit >= nKNN ? 1 : 0;// 当前点作为背景点存入历史信息

				}
			}
		}

		pBackgroundKnn->apply(frame, FGMask_KNN);
		imshow("Frame", frame);
		imshow("FGMask", FGMask);
		imshow("FGMask_KNN", FGMask_KNN);

		keyboard = waitKey(30);
		frameCnt++;
	}
	capture.release();

	return 0;

}

python实现:

import cv2

cap=cv2.VideoCapture('./768x576.avi')

fgbg = cv2.createBackgroundSubtractorKNN()

while (1):
    ret, frame = cap.read()

    fgmask = fgbg.apply(frame)

    cv2.imshow('frame', fgmask)
    k = cv2.waitKey(100) & 0xff
    if k == 27:
        break

cap.release()
cv2.destroyAllWindows()

利用图像减法函数实现(python版本):

import cv2
import time
"""
背景减法
"""
cap = cv2.VideoCapture("./768x576.avi")

_, first_frame = cap.read()
first_gray = cv2.cvtColor(first_frame, cv2.COLOR_BGR2GRAY)
first_gray = cv2.GaussianBlur(first_gray, (5, 5), 0)
cv2.imshow("First frame", first_frame)
cv2.imwrite('first_frame.jpg', first_frame)

count_frame = 0
start = time.time()

while True:
    count_frame += 1
    _, frame = cap.read()
    gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    gray_frame = cv2.GaussianBlur(gray_frame, (5, 5), 0)

    difference = cv2.absdiff(first_gray, gray_frame)
    _, difference = cv2.threshold(difference, 25, 255, cv2.THRESH_BINARY)

    cv2.imshow("Frame", frame)
    cv2.imshow("difference", difference)

    key = cv2.waitKey(30)
    if key == 27:
        break

    end = time.time()
    print("time %.2f s" % (end-start))
    print(count_frame)

cap.release()
cv2.destroyAllWindows()

到此这篇关于OpenCV4 实现背景分离、背景减法模型的文章就介绍到这了,更多相关OpenCV4 实现背景分离内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • c++ 基于opencv 识别、定位二维码

    前言 因工作需要,需要定位图片中的二维码:我遂查阅了相关资料,也学习了opencv开源库.通过一番努力,终于很好的实现了二维码定位.本文将讲解如何使用opencv定位二维码. 定位二维码不仅仅是为了识别二维码:还可以通过二维码对图像进行水平纠正以及相邻区域定位.定位二维码,不仅需要图像处理相关知识,还需要分析二维码的特性,本文先从二维码的特性讲起. 1 二维码特性 二维码在设计之初就考虑到了识别问题,所以二维码有一些特征是非常明显的. 二维码有三个"回""字形图案,这一点非常

  • 使用c++实现OpenCV绘制圆端矩形

    功能函数 // 绘制圆端矩形(药丸状,pill) void DrawPill(cv::Mat mask, const cv::RotatedRect &rotatedrect, const cv::Scalar &color, int thickness, int lineType) { cv::Mat canvas = cv::Mat::zeros(mask.size(), CV_8UC1); // 确定短边,短边绘制圆形 cv::RotatedRect rect = rotatedre

  • 使用c++实现OpenCV绘制图形旋转矩形

    目录 功能函数 // 绘制旋转矩形 void DrawRotatedRect(cv::Mat mask,const cv::RotatedRect &rotatedrect,const cv::Scalar &color,int thickness, int lineType) { // 提取旋转矩形的四个角点 cv::Point2f ps[4]; rotatedrect.points(ps); // 构建轮廓线 std::vector<std::vector<cv::Poin

  • 使用c++实现OpenCV图像横向&纵向拼接

    功能函数 // 图像拼接 cv::Mat ImageSplicing(vector<cv::Mat> images,int type) { if (type != 0 && type != 1) type = 0; int num = images.size(); int newrow = 0; int newcol = 0; cv::Mat result; // 横向拼接 if (type == 0) { int minrow = 10000; for (int i = 0;

  • C++ opencv霍夫圆检测使用案例详解

    本程序是一个最简单的霍夫圆检测函数的使用案例,刚刚学会的用法,发一下,可以参考,参数啥的可根据图片调节. #pragma once #include<quickopencv.h> #include<vector> #include <stdio.h> #include <iostream> #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgpr

  • C++中实现OpenCV图像分割与分水岭算法

    分水岭算法是一种图像区域分割法,在分割的过程中,它会把跟临近像素间的相似性作为重要的参考依据,从而将在空间位置上相近并且灰度值相近的像素点互相连接起来构成一个封闭的轮廓,封闭性是分水岭算法的一个重要特征. API介绍 void watershed( InputArray image, InputOutputArray markers ); 参数说明: image: 必须是一个8bit 3通道彩色图像矩阵序列 markers: 在执行分水岭函数watershed之前,必须对第二个参数markers

  • C++基于OpenCV实现手势识别的源码

    先给大家上效果图: 源码在下面 使用 RGB 值分割手部区域,即手部的 GB 值将与背景不同 或者使用边缘检测 或者 背景减法. 我这里使用了背景减法模型.OpenCV为我们提供了不同的背景减法模型,codebook   它的作用是对某些帧进行一段时间的精确校准.其中对于它获取的所有图像:它计算每个像素的平均值和偏差,并相应地指定框. 在前景中它就像一个黑白图像,只有手是白色的 用 Convex Hull 来找到指尖.Convex hull 基本上是包围手部区域的凸集. 包围手的红线是凸包.基本

  • OpenCV4 实现背景分离的详细步骤(背景减法模型)

    目录 定义: 背景建模包括两个主要步骤: OpenCV中三个背景分离的重要函数 BackgroundSubtractorMOG() BackgroundSubtractorMOG2 BackgroundSubtractorGMG BackgroundSubtractorKNN C++实现: python实现: 利用图像减法函数实现(python版本): 定义: 背景分离,又称背景减法模型. 背景分离(BS)是一种通过使用静态相机来生成前景掩码(即包含属于场景中的移动对象像素的二进制图像)的常用技

  • IntelliJ IDEA 2020如何设置背景图片的方法步骤

    使用idea总看小黑框很难受?眼睛疼?看不清?试试按照你的风格来定制一哈! 1.首先我们选择idea的最左上角的file 2.选择settings 3.找到Appearance&Behavior 4.点击Appearance 5.选择Background Image 之后会就可以开始设置背景图片啦O(∩_∩)O 具体的设置如图所示 图片路径最好不要更改,因为更改后idea就找不到背景图的位置了,需要我们重新设置 设置好了你们都懂点ok就好啦 到此这篇关于IntelliJ IDEA 2020如何设

  • OpenCV实现背景分离(证件照背景替换)

    目录 实现原理 功能函数代码 C++测试代码 完整改进代码 本文主要介绍了OpenCV实现背景分离(证件照背景替换),具有一定的参考价值,感兴趣的可以了解一下 实现原理 图像背景分离是常见的图像处理方法之一,属于图像分割范畴.如何较优地提取背景区域,难点在于两个: 背景和前景的分割.针对该难点,通过人机交互等方法获取背景色作为参考值,结合差值均方根设定合理阈值,实现前景的提取,PS上称为蒙版:提取过程中,可能会遇到前景像素丢失的情况,对此可通过开闭运算或者提取外部轮廓线的方式,将前景内部填充完毕

  • SpringBoot+Prometheus+Grafana实现应用监控和报警的详细步骤

    背景 SpringBoot的应用监控方案比较多,SpringBoot+Prometheus+Grafana是目前比较常用的方案之一.它们三者之间的关系大概如下图: 开发SpringBoot应用 首先,创建一个SpringBoot项目,pom文件如下: <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</art

  • Pycharm及python安装详细步骤及PyCharm配置整理(推荐)

    首先我们来安装python 1.首先进入网站下载:点击打开链接(或自己输入网址: https://www.python.org/downloads/),进入之后如下图,选择图中红色圈中区域进行下载. 2.下载完成后如下图所示 3.双击exe文件进行安装,如下图,并按照圈中区域进行设置,切记要勾选打钩的框,然后再点击Customize installation进入到下一步: 4.对于上图中,可以通过Browse进行自定义安装路径,也可以直接点击Install进行安装,点击install后便可以完成

  • Java实现画图的详细步骤(完整代码)

    一.导入依赖 <dependency> <groupId>net.coobird</groupId> <artifactId>thumbnailator</artifactId> <version>0.4.13</version> </dependency> 二.工具类 package com.geidco.dcp.util; import net.coobird.thumbnailator.Thumbnail

  • Docker部署Microsoft Sql Server详细步骤

    目录 1 背景 2 创建容器 3 修改SA密码 4 链接mssql 5 容器外链接mssql 总结 1 背景 自 SQL Server 2019 CU3 起,支持 Ubuntu 18.04. 自 SQL Server 2019 CU10 起,支持 Ubuntu 20.04. Docker 引擎 1.8+ 至少 2 GB 的磁盘空间 至少 2 GB 的 RAM 博主机器: Welcome to Ubuntu 20.04.3 LTS (GNU/Linux 5.11.0-37-generic x86_

  • 国产化之银河麒麟安装.netcore3.1的详细步骤(手动安装)

    目录 背景 环境 下载 安装 x64.Arm处理器 MIPS.龙芯处理器 其它依赖 测试 背景 某个项目需要实现基础软件全部国产化,其中操作系统指定银河麒麟,银河麒麟就是一个Linux发行版,数据库使用达梦V8,这个数据库很多概念和Oracle相似,CPU平台的范围:龙芯.飞腾.鲲鹏等. 龙芯团队自己支持了.netcore3.1,飞腾之类的是ARM架构,官方已经支持..netcore3.1仅在 x64 体系结构上支持包管理器安装,对于 ARM 等其他体系结构,必须通过其他一些方式安装 .NET.

  • uniapp引用echarts的详细步骤(附柱状图实例)

    相信很多小伙伴对于echarts这个东西应该不会陌生,我在网上看到很多文章,那么他到底是怎么用的呢,却是五花八门,我现在就来总结一下我的方法. 如果使用npm全局安装,太麻烦,这里推荐使用官网(ECharts 在线构建)定制下载,这样会方便我们使用. 选择柱状图,折线图,饼图:这三样是平常较常用到的: 坐标系选择直角坐标系: 组件可以全选,也可以选择自己所需要的,在这里个人建议除了工具栏不选,其他都选上:下载后的文件为echarts.min.js,建议把他放在static内. 好了,来到下一步,

  • win10系统安装Nginx的详细步骤

    目录 第一步:下载Nginx 第二步:将下载好的Nginx压缩包解压到自己想要的位置 第三步:在当前目录下(D:\Nginx),打开命令窗口,执行 start nginx.exe 或者直接执行 nginx.exe 即可启动Nginx 第四步:在浏览器上验证Nginx是否启动成功(在浏览器上输入:localhost:80) Nginx是一款自由的.开源的.高性能的HTTP服务器和反向代理服务器,同时也提供了IMAP/POP3/SMTP服务. Nginx可以进行反向代理.负载均衡.HTTP服务器(动

随机推荐