基于HEP(histograms of equivalent patterns【1】)框架下的特征具有良好的纹理分类效果,LBP(local binary patterns【2】)属于HEP框架下最常用的特征,具有对亮度、旋转等良好的不变特性。在基于分块的视频烟雾检测中,常使用其作为纹理分类的特征。然而,分块的图像具有局部性。这篇文章主要提出使用图像金字塔的方法让提取的烟雾块特征具有一定的全局属性。它将待检测烟雾块构成3级的金字塔,再对金字塔每一级提取不同模式的LBP特征,构成一个直方图序列作为特征向量,最后采用神经网络分类。

1.LBP 与 LBPV

本文用到的LBP共三种模式:统一模式,旋转不变模式,统一的旋转不变模式。每一级金字塔采用一种模式。LBP详见博客【3】

LBPV与LBP不同的是,LBP统计直方图的时候是每一个LBP(i,j)使第k个分量加1,而LBPV是加一个VAR(方差吧),VAR计算公式:

上面gp为领域内像素值,然后再归一化。

2.金字塔

共三级金字塔,I0,I1,I2,I0为输入图像,I0通过高斯低通滤波(Gaussian low pass filter, LPF),然后下采样得到I1(采样大小2),同样由I1得到I2,如图:

最后从下到上对每一级分别提取统一模式LBP,旋转不变LBP,统一旋转不变LBP,按照如下的顺序组合成向量:

对于24x24的图像块,I2的大小仅为6x6,这样会获得稀疏的特征向量,不利于分类。因此,I1和I2通过相邻像素增大搜索窗口。如图:

3.实现

本人采用文章中的数据集作为训练与测试,剪切成96x96的灰度图,这样大小的图像下采样两次刚好24x24。提取每一张图像特征代码:

feature_t get_pyramid_feature(cv::Mat & img)
{
assert((img.dims == 2) && (img.rows == 96) && (img.cols == 96));
feature_t result, lbp0, lbp1, lbp2, lbpv0, lbpv1, lbpv2;
result.resize(210);
cv::Mat I0, I1, I2, L0, S0, L1; I0 = img(cv::Rect(36, 36, 24, 24)).clone();
cv::GaussianBlur(img, L0, cv::Size(3,3), 0);
cv::resize(L0, S0, cv::Size(48, 48));
I1 = S0(cv::Rect(12, 12, 24, 24)).clone();
cv::GaussianBlur(S0, L1, cv::Size(3,3), 0);
cv::resize(L1, I2, cv::Size(24, 24)); lbp0 = get_u_lbp_gray(I0);
lbp1 = get_ri_lbp_gray(I1);
lbp2 = get_riu_lbp_gray(I2);
lbpv0 = get_u_lbpv_gray(I0);
lbpv1 = get_ri_lbpv_gray(I1);
lbpv2 = get_riu_lbpv_gray(I2); std::copy(lbp0.begin(), lbp0.end(), result.begin());
std::copy(lbpv0.begin(), lbpv0.end(), result.begin()+59);
std::copy(lbp1.begin(), lbp1.end(), result.begin()+118);
std::copy(lbpv1.begin(), lbpv1.end(), result.begin()+154);
std::copy(lbp2.begin(), lbp2.end(), result.begin()+190);
std::copy(lbpv2.begin(), lbpv2.end(), result.begin()+200);
return result;
}

提取特征过后,采用神经网络训练(采样tiny_cnn这个轻量级的神经网络库):

void mlp_train(std::vector<feature_t> & train_x, std::vector<int> & train_y, std::vector<feature_t> & test_x, std::vector<int> & test_y, const char * weights_file, int iter_num = 20)
{
const int num_input = train_x[0].size();
const int num_hidden_units = 30;
int num_units[] = { num_input, num_hidden_units, 2 };
auto nn = make_mlp<mse, gradient_descent_levenberg_marquardt, tan_h>(num_units, num_units + 3); //train mlp
nn.optimizer().alpha = 0.005;
boost::progress_display disp(train_x.size());
boost::timer t;
// create callback
auto on_enumerate_epoch = [&](){
std::cout << t.elapsed() << "s elapsed." << std::endl;
tiny_cnn::result res = nn.test(test_x, test_y);
std::cout << nn.optimizer().alpha << "," << res.num_success << "/" << res.num_total << std::endl;
nn.optimizer().alpha *= 0.85; // decay learning rate
nn.optimizer().alpha = std::max(0.00001, nn.optimizer().alpha);
disp.restart(train_x.size());
t.restart();
};
auto on_enumerate_data = [&](){
++disp;
}; nn.train(train_x, train_y, 1, iter_num, on_enumerate_data, on_enumerate_epoch);
nn.test(test_x, test_y).print_detail(std::cout);
nn.save_weights(weights_file);
}

最后得到测试的结果96%以上。

然后用于视频的处理中,只用处理单幅图像的情况。对于一副图像,首先得分块,将每一个分块的区域保存到向量中。分块的时候就考虑到金字塔每一级,因此3级的每一个向量对应的位置是对应的一个块。对于边缘块的处理忽略图像外的像素。分块代码如下:

std::vector<std::vector<cv::Rect>> make_image_blocks(const cv::Mat &in, const int iWD)
{
std::vector<std::vector<cv::Rect>> result;
std::vector<cv::Rect> level0, level1, level2;
int rows = in.rows;
int cols = in.cols;
int rows_level1 = rows / 2;
int cols_level1 = cols / 2;
int rows_level2 = rows / 4;
int cols_level2 = cols / 4;
int left, top, right, bottom;
for(int i = 0; i <= rows - iWD; i += iWD)
{
for(int j = 0; j <= cols - iWD; j += iWD)
{
level0.push_back(cv::Rect(j, i, iWD, iWD));
//level1
left = std::max(j/2-6,0);
top = std::max(i/2-6,0);
right = std::min(j/2+18, cols_level1);
bottom = std::min(i/2+18, rows_level1);
level1.push_back(cv::Rect(left, top, right-left, bottom-top));
//level2
left = std::max(j/4-9,0);
top = std::max(i/4-9,0);
right = std::min(j/4+15, cols_level2);
bottom = std::min(i/4+15, rows_level2);
level2.push_back(cv::Rect(left, top, right-left, bottom-top));
}
}
result.push_back(level0);
result.push_back(level1);
result.push_back(level2);
return result;
}

然后就是对每一张图像的处理了,首先创建3级金字塔,然后按照上面得到的分块索引轻松得到每一块的特征,然后predict就完事了。

template<typename NN>
std::vector<int> single_image_smoke_detect(const cv::Mat & img, const std::vector<std::vector<cv::Rect>> & locate_list, const std::vector<int> smoke_block, NN & nn)
{
std::vector<int> result;
cv::Mat I1, I2, L0, L1;
cv::GaussianBlur(img, L0, cv::Size(3,3), 0);
cv::resize(L0, I1, cv::Size(img.cols/2, img.rows/2));
cv::GaussianBlur(I1, L1, cv::Size(3,3), 0);
cv::resize(L1, I2, cv::Size(I1.cols/2, I1.rows/2)); for(auto i : smoke_block)
{
cv::Mat block = img(locate_list[0][i]);
auto lbp0 = get_u_lbp_gray(block);
auto lbpv0 = get_u_lbpv_gray(block);
block = I1(locate_list[1][i]);
auto lbp1 = get_ri_lbp_gray(block);
auto lbpv1 = get_ri_lbpv_gray(block);
block = I2(locate_list[2][i]);
auto lbp2 = get_riu_lbp_gray(block);
auto lbpv2 = get_riu_lbpv_gray(block); feature_t feat;
feat.resize(210,0);
std::copy(lbp0.begin(), lbp0.end(), feat.begin());
std::copy(lbpv0.begin(), lbpv0.end(), feat.begin()+59);
std::copy(lbp1.begin(), lbp1.end(), feat.begin()+118);
std::copy(lbpv1.begin(), lbpv1.end(), feat.begin()+154);
std::copy(lbp2.begin(), lbp2.end(), feat.begin()+190);
std::copy(lbpv2.begin(), lbpv2.end(), feat.begin()+200); vec_t y;
nn.predict(feat, &y);
const int predicted = max_index(y);
if(predicted == 1)
result.push_back(i);
}
return result;
}

在这之前加入运动检测:

smoke_blocks = single_image_smoke_detect(gray_img, locate_list, smoke_blocks, nn);

然后将检测得到的烟雾块显示出来:

auto merged_blocks = merge_blocks(smoke_blocks, gray_img.size(), 24);
for(auto rect : merged_blocks)
{
cv::rectangle(img, rect, cv::Scalar(0,0,255));
} cv::imshow("smoke", img);

效果图:

1.实验结果分析

试了一下仅使用LBP或LBPV的情况下,单独使用LBPV的分类效果最差,多次测试平均94%多一点,而LBP和LBP+LBPV情况差不多,平均都在96%以上,因此,不加LBPV更好一点。

2.总结

加入金字塔对每一级提取特征的方法的确比单独采用分块提取特征的方法稍好一点,但是对于烟雾的检测,单帧的检测并不能满足于实际。这个可以作为一个novel方法用在一个框架中。

6.引用

【1】 Texture description through histograms of equivalent patterns

【2】  Multiresolution gray-scale and rotation invariant texture classification with local binary patterns

【3】  http://blog.csdn.net/zouxy09/article/details/7929531

@waring

烟雾检测笔记1--《Video-based smoke detection with histogram sequence of LBP and LBPV pyramids》解析、实现的更多相关文章

  1. paper 27 :图像/视觉显著性检测技术发展情况梳理(Saliency Detection、Visual Attention)

    1. 早期C. Koch与S. Ullman的研究工作. 他们提出了非常有影响力的生物启发模型. C. Koch and S. Ullman . Shifts in selective visual ...

  2. 个性探测综述阅读笔记——Recent trends in deep learning based personality detection

    目录 abstract 1. introduction 1.1 个性衡量方法 1.2 应用前景 1.3 伦理道德 2. Related works 3. Baseline methods 3.1 文本 ...

  3. 论文阅读笔记五十二:CornerNet-Lite: Efficient Keypoint Based Object Detection(CVPR2019)

    论文原址:https://arxiv.org/pdf/1904.08900.pdf github:https://github.com/princeton-vl/CornerNet-Lite 摘要 基 ...

  4. Tensorflow Object_Detection 目标检测 笔记

    Tensorflow models Code:https://github.com/tensorflow/models 编写时间:2017.7 记录在使用Object_Detection 中遇到的问题 ...

  5. 论文笔记: Deep Learning based Recommender System: A Survey and New Perspectives

    (聊两句,突然记起来以前一个学长说的看论文要能够把论文的亮点挖掘出来,合理的进行概括23333) 传统的推荐系统方法获取的user-item关系并不能获取其中非线性以及非平凡的信息,获取非线性以及非平 ...

  6. 谣言检测(ClaHi-GAT)《Rumor Detection on Twitter with Claim-Guided Hierarchical Graph Attention Networks》

    论文信息 论文标题:Rumor Detection on Twitter with Claim-Guided Hierarchical Graph Attention Networks论文作者:Erx ...

  7. 论文笔记--PCN:Real-Time Rotation-Invariant Face Detection with Progressive Calibration Networks

    关键词:rotation-invariant face detection, rotation-in-plane, coarse-to-fine 核心概括:该篇文章为中科院计算所智能信息处理重点实验室 ...

  8. 谣言检测(GACL)《Rumor Detection on Social Media with Graph Adversarial Contrastive Learning》

    论文信息 论文标题:Rumor Detection on Social Media with Graph AdversarialContrastive Learning论文作者:Tiening Sun ...

  9. 谣言检测(RDEA)《Rumor Detection on Social Media with Event Augmentations》

    论文信息 论文标题:Rumor Detection on Social Media with Event Augmentations论文作者:Zhenyu He, Ce Li, Fan Zhou, Y ...

随机推荐

  1. android开发学习 几个有用的学习资料~

    <Android高级应用开发-基础篇> 针对Android基础入门课程,包含了Android四大件基础.UI,Launcher等等.这个课程学习之后也是进一步深入的基础. <Andr ...

  2. Linux一个简单的读写文件

    (1)linux中的文件描述符fd的合法范围是或者一个正正数,不可能是一个负数. (2)open返回的fd程序必须记录好,以后向这个文件的所有操作都要靠这个fd去对应这个文件,最后关闭文件时也需要fd ...

  3. c语言typedef运用与函数指针

    #include <stdio.h> #include <stdlib.h> #define PINT int * typedef short* PSHORT; //typed ...

  4. jQuery获取select option

    jQuery的一些方法理出一些常用的方法: //获取第一个option的值 $('#test option:first').val(); //最后一个option的值 $('#test option: ...

  5. Oracle 中按条件过滤重复记录

    在数据处理中,经常会遇到类似这样的情况:数据库中存在多条记录,其中某些字段值相同,其他字段值不同.实际的业务需要针对这样的情况,只保留一条数据,其他数据删除.如何做到呢?在sql中有top关键字相对容 ...

  6. C++内存分配的五种方法

    在C++中,内存分成5个区,他们分别是堆.栈.自由存储区.全局/静态存储区和常量存储区. 栈,就是那些由编译器在需要的时候分配,在不需要的时候自动清楚的变量的存储区.里面的变量通常是局部变量.函数参数 ...

  7. Linux是如何启动的

    今天早上在上操作系统课的时候,老师有提到计算机从按下开关键到最后由操作系统全然接管的整个过程. 只是讲课毕竟是十分抽象的,由于之前自己也看过这方面的内容,可是老是记不住,所以今天晚上就花了点时间,把& ...

  8. 【精品】Android游戏类源码大集合

    Android自定义效果——随机抽奖 一个模拟抽奖的效果,用户设定若干个选项,添加之后,就可以通过程序,来帮助随机选择其中一项出来.这个类似超市里面那种指针转盘抽奖,run之后是一个动态效果图,初始快 ...

  9. 文本输入框的两种div+css的写法

    1.扁平化的设计风格.--淘宝   直接使用input.对其设置height.padding.使鼠标居中又不会占满输入框.       2.背景是图片的设计.--百度   试用span将input包裹 ...

  10. 一句SQL实现MYSQL的递归查询

    众所周知,目前的mysql版本中并不支持直接的递归查询,但是通过递归到迭代转化的思路,还是可以在一句SQL内实现树的递归查询的.这个得益于Mysql允许在SQL语句内使用@变量.以下是示例代码. 创建 ...