基于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. Erlang ODBC 处理中文

    erlang处理utf8字符集相对比较简单,因为它是用integer的list来保存所有的string的,所以处理什么字符集都没关系. 话虽这么说,但我在使用erlang的ODBC处理中文时,着实费了 ...

  2. hdu 5685 Problem A

    Problem Description 度熊手上有一本字典存储了大量的单词,有一次,他把所有单词组成了一个很长很长的字符串.现在麻烦来了,他忘记了原来的字符串都是什么,神奇的是他竟然记得原来那些字符串 ...

  3. 辛星解读mysql的用户管理

    可能做开发的多半不太关注这方面,可是要说到做运维.那就不能不关注了.由于我们都知道,root的权限太大了.不是随便能用的.我们平时最好用一些比較低的权限的用户.这样会让我们的安全性大大提高,也能防止我 ...

  4. .NET基础拾遗(3)字符串、集合和流3

    三.流和序列化 3.1 流概念及.NET中常见流 无论什么信息,文字,声音,图像,只要进入了计算机就都被转化为数字,以数字方式运算.存储.由于计算机中使用二进制运算,因此数字只有两个:0 与 1,就是 ...

  5. 关闭程序 提示 C#

    private void Form1_FormClosing(object sender, FormClosingEventArgs e) { DialogResult dr = MessageBox ...

  6. MySQL的一些语法总结

    初学MySQL,今天遇到了一个问题,然后汇总了一下MySQL的一些语法 1. date和datetime类型是不同的 date只记录日期(包括年月日),datetime记录日期和时间(包括年月日时分秒 ...

  7. js+图片实现图片flash效果

    var pic_width=685; //图片宽度 var pic_height=225; // 图片高度 var button_pos=4; //按扭位置 1左 2右 3上 4下 var stop_ ...

  8. 货币小写转大写.htm

    <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/ ...

  9. Android-图标

    首先需要申明一点,系统图标并不存在于项目资源中,而是存在于设备中. 这就带来一个大问题,界面风格的控制权交到了不同的设备手中.这是我们不愿意看到的. 如何解决这个问题?有两种方法: 1.创建自己的图标 ...

  10. 如何在Visual studio中修改所使用C#语言的版本

    有时候,我们需要在Visual studio里修改当前使用的C#语言的版本,具体修改方法如下:在solution explorer中右键工程->选择属性->切换到Build页->点击 ...