简介
码书法,是对每个像素建立一个记录背景范围的码书,一个码书上包含多个表示背景范围的盒子,类似于颜色空间YUV(背景颜色变化多发生在亮度轴V上)上漂浮着的气团。如果新像素和先前建立的盒子足够近,则扩展盒子。否则,新建新的盒子。为了能在存在移动的前景时进行背景学习,需要更低频率地(为了更好地学习背景和避免清理掉背景盒子)清理码书中的被判定为前景的盒子。最后进行背景差分,不在码书中全部盒子范围内的为前景,否则,为背景。
由于不可避免的存在各种干扰,需要使用形态学操作和连通分量法进行处理,最后用多边形逼近或者凸包逼近绘出处理后的前景结果。
结构
- 码书类继承自std::vector,加上记录当前帧数的成员变量t。
- 码数单元(盒子),包括盒子上下边界(max,min)、学习阈上下边界(learnHigh,learnLow)、最后一次被击中帧数(t_last_update)、盒子创建时帧数(t_create)(自己加的成员变量,改进stale计算方式)、用于判断长时间未被击中(stale)。
codeBookh.h
#pragma once
#include <iostream>
#include <opencv.hpp>
using namespace std;
//#define TRANSPOSE
#define CHANNELS 3
#define DP_EPSILON_DENOMINATOR 100.0
#define CVCLOSE_KSIZE 5 //闭操作核大小
#define CVOPEN_ITR 1//腐蚀膨胀次数
string videoname = "test2.mp4";
unsigned int bound[3] = {
10,10,10 };//像素边框,用于调整学习阈
int minMod[3] = {
5,5,5 };//误差范围,用于检测前景
int maxMod[3] = {
5,5,5 };
int fpr = 300;//每轮学习帧数
int fpsScale = 2;//每隔几帧取一帧计算
int rsb = 8;//图像缩小倍数
bool p1h0 = true;
int deleNum = 0;//调用清理盒子次数
float minAreaScale = 0.005;//最小面积比例
/*
码书单元(盒子)
*/
class CodeElement {
public:
uchar learnHigh[CHANNELS];//学习阈值上限
uchar learnLow[CHANNELS];//学习阈值下限
uchar max[CHANNELS];//盒子上边界
uchar min[CHANNELS];//盒子下边界
int t_last_update;//最后一次被命中的时间戳
int stale;//反映历史最长没命中此盒子的时间段,time(此时)-t_last_update>stale便更新,越大表示此盒子越不是背景(干扰或移动前景)
int t_create;//创建时间戳
CodeElement() {
for (int i = 0; i < CHANNELS; i++)
learnHigh[i] = learnLow[i] = max[i] = min[i] = 0;
t_last_update = t_create = 0;
}
CodeElement& operator=(const CodeElement& ce) {
for (int i = 0; i < CHANNELS; i++) {
learnHigh[i] = ce.learnHigh[i];
learnLow[i] = ce.learnLow[i];
max[i] = ce.max[i];
min[i] = ce.min[i];
}
t_last_update = ce.t_last_update;
t_create = ce.t_create;
stale = ce.stale;
return *this;
}
CodeElement(const CodeElement& ce) {
*this = ce; }
};
/*
码书,每个像素上对应一个码书
*/
class CodeBook :public std::vector<CodeElement> {
//vector太低效,可用MAX_CODE来限制最大码书大小,静态分配数组CodeElement[MAX_CODES]
public:
int t;//累加的点的个数
CodeBook() {
t = 0; }
CodeBook(int n) :std::vector<CodeElement>(n) {
t = 0; }//构建有n个码书单元的码书,更新出直接用book[i]
};
int updateCodebook(const cv::Vec3b& p, CodeBook& c, unsigned* cbBounds, int numChannels);
int clearStaleEntries(CodeBook& c);
uchar backgroundDiff(const cv::Vec3b& p, CodeBook& c, int numChannels, int* minMod, int* maxMod);
void findConnectedComponents(cv::Mat& mask, std::vector<cv::Rect>& bbs, std::vector<cv::Point>& centers, float minArea, bool poly1_hull0 = 1);
背景学习
由于在背景学习时,stale只是t-t_last_update并不能很好的表示此盒子在一轮视频流中未被击中的情况。这样会导致在盒子清理时,视频流偏后面出现的移动前景不能被清理掉。所以,此处修改为t - t_last_update + t_create。
每个像素自带一个自定义大小的边框,用于在创建新盒子时设置学习阈,或者是在扩展旧盒子时调整学习阈。
#include "pch.h"
#include