积分图时一种允许子区域快速求和的数据结构,这种求和在很多方面都很有用,值得一提的是haar小波的计算,它用于人脸识别和类似的算法。Opencv支持积分图的三种变体,分别是总和、平方求和以及倾斜求和。每种情况的结果图像在图像的每个方向上都加1之后,与原始图像的大小相同。

通过积分图,你可以对图像的任意直立或“倾斜”的矩形区域求和、平均以及标准差。即使目标区域大小不确定,也可以高效进行快速模糊、近似梯度、求均值以及标准差,即使对可变大小的窗口,也可以执行快速块相关。

如果要计算中间区域的像素和,我们可以这样计算398-9-10+1=380。使用四个量就可以对任意矩形区域计算并且将计算复杂度控制为O(1)。

cv::integral()标准求和积分

void integral( InputArray src, OutputArray sum, int sdepth = -1 );

第一个参数和第二个参数是输入图像和输出图像,如果输入图像的大小时W×H,输出图像的大小就是(W+1)×(H+1)。第三个参数sdepth指明求和图像(结果图像)需要的深度。sdepth可以是CV:S32, CV::F32或CV::F64.

cv::integral()平方求和积分

void integral( InputArray src, OutputArray sum, OutputArray sqsum, int sdepth = -1, int sqdepth = -1 );

sqsum参数告诉函数除了标准求和还要计算平方和。与之前一样,sdepth指明目标图像的深度。sdepth可以是CV:S32, CV::F32或CV::F64.

cv::integral()倾斜求和积分

void integral( InputArray src, OutputArray sum, OutputArray sqsum, OutputArray tilted, int sdepth = -1, int sqdepth = -1 );

附加参数titled作为一个附加的项由函数计算而来,其他参数全部相同。

积分图应用(1)——均值滤波

通过利用积分图实现任意窗口大小的均值滤波,代码实现如下:

#include <opencv2/opencv.hpp>
using namespace std;
using namespace cv;
int getBlockSum(Mat& sum, int row1,int col1,int row2, int col2) {
int br = sum.at<int>(col2 + 1, row2 + 1);
int tl = sum.at<int>(col1, row1);
int tr = sum.at<int>(col2 + 1, row1);
int bl = sum.at<int>(col1, row2 + 1);
return br + tl - tr - bl;
}
void blur_demo(Mat &src, Mat& dst, int ksize) {
int h = src.rows;
int w = src.cols;
int radius = ksize / 2;
Mat sum;
integral(src, sum, CV_32S);
int row1, col1, row2, col2;
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
row1 = i - radius < 0 ? 0 : i - radius;
col1 = j - radius < 0 ? 0 : j - radius;
row2 = i + radius < w ? i + radius : w - 1;
col2 = j + radius < h ? j + radius : h - 1;
dst.at<uchar>(j, i) = (uchar)(getBlockSum(sum, row1, col1, row2, col2)/ ksize/ ksize);
}
}
} int main() {
Mat src = imread("D:/Backup/桌面/1.bmp", 0);
clock_t start, end;
start = clock();
int ksize = 5;
cout << "ksize = " << ksize << endl;
// 均值滤波
Mat dst1;
blur(src, dst1,Size(ksize, ksize),Point(-1,-1), BORDER_CONSTANT);
end = clock();
cout << "blur花费"<< end - start << endl;
// 积分图
start = clock();
Mat dst2 = Mat::zeros(src.size(), CV_8UC1);
blur_demo(src, dst2, ksize);
end = clock();
cout << "积分图花费" << end - start << endl;
return 0;
}

积分图应用(2)——动态阈值分割dyn_threshold

原理: 动态阈值分割是指在图像分割的过程中,不用人为的去设置阈值,而是根据图像中存在的特征,进行分割。一般是将原图像与处理后的图像作差,然后去计算差值图像中的亮色区域或者暗色区域。其本质相当于对图像灰度直方图的平滑,进而求取图像中的波谷或者波峰。

pixel = (pixel > (mean - c)) ? object : background

其中默认情况下参数C取值为0。object表示前景像素,background表示背景像素。

特点: 动态阈值分割具有抗干扰性强,稳定性强的特点,对光照变化不敏感。

  • 使用均值滤波+二值化实现动态阈值
int ksize = 7;
Mat mean, sub, dst1;
blur(src, mean, Size(ksize, ksize));
subtract(mean, src, sub);
threshold(sub, dst1, 10, 255, THRESH_BINARY_INV);
  • 使用积分图实现动态阈值
/*实现步骤
1. 彩色图像转灰度图像
2. 获取灰度图像的像素数据,预计算积分图
3. 根据输入的参数窗口半径大小从积分图中获取像素总和,求得平均值
4. 循环每个像素,根据局部均值实现中心像素的二值化赋值
5. 输出二值图像*/
void dyn_threshold(Mat &src, Mat& dst, int ksize, int c) {
int h = src.rows;
int w = src.cols;
int radius = ksize / 2;
Mat sum;
integral(src, sum, CV_32S);
int row1, col1, row2, col2;
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
row1 = i - radius < 0 ? 0 : i - radius;
col1 = j - radius < 0 ? 0 : j - radius;
row2 = i + radius < w ? i + radius : w - 1;
col2 = j + radius < h ? j + radius : h - 1;
dst.at<uchar>(j, i) = src.at<uchar>(j, i) - (uchar)(getBlockSum(sum, row1, col1, row2, col2) / ksize / ksize) + c>0 ? 255 : 0;
}
}
}

结果:运行时间差不多

积分图应用(3)——var_threshold

var_threshold的分割,非常适用于具有较为复杂背景的标识内容进行分割,它能够对获取指定蒙板大小且具有相同灰度的目标进行分割。

算子的函数签名如下:

var_threshold(Image : Region : MaskWidth, MaskHeight, StdDevScale, AbsThreshold, LightDark : )

其中,MaskWidth、 MaskHeight 定义的过滤器蒙板大小决定了要分割的对象的最大尺寸,但是,如果选择的蒙版太大,则可能会合并非常接近的对象。

StdDevScale 局部标准差用作图像中噪声的度量,它可以通过StdDevScale进行缩放,以反映所需的灵敏度,值越高,表示仅选择与其周围环境截然不同的像素。

AbsThreshold则为目标区域的绝对灰度值,在图像的均匀区域中,标准偏差较低;因此,单个灰度值的影响很大,为了降低在均匀区域中的灵敏度,可以调整AbsThreshold。因此,可以忽略同质环境中的微小灰度值变化。

请注意,对于 StdDevScale 的负值,AbsThreshold 也应选择负值。

lightDark: dark 暗 light 光亮 equal 等于 not_equal 不等于

令g(x,y)为点(x,y)处的灰度值,m(x,y) 为均值,d(x,y) 为方差(实际是标准差),则点(x,y)处的阈值v(x,y)定义为:

当LightDark = ‘light’:时,满足下述条件的点被选中到输出区域

当LightDark = ‘dark’:时,满足下述条件的点被选中到输出区域

当LightDark = ‘equal’:时,满足下述条件的点被选中到输出区域

当LightDark = ‘not_equal’:时,满足下述条件的点被选中到输出区域

代码实现:

void var_threshold(Mat& src, Mat& dst, int StdDevScale, int AbsThreshold, int ksize) {
int h = src.rows;
int w = src.cols;
int radius = ksize / 2;
Mat mean, sub, sum, qsum;
blur(src, mean, Size(ksize, ksize));
subtract(mean, src, sub);
integral(sub, sum, qsum, CV_32S, CV_32S);
int row1, col1, row2, col2;
for (int i = 0; i < w; i++) {
for (int j = 0; j < h; j++) {
row1 = i - radius < 0 ? 0 : i - radius;
col1 = j - radius < 0 ? 0 : j - radius;
row2 = i + radius < w ? i + radius : w - 1;
col2 = j + radius < h ? j + radius : h - 1;
int varthreshold = max(StdDevScale * (int)sqrt(getBlockSum(qsum, row1, col1, row2, col2)) / ksize, AbsThreshold);
dst.at<uchar>(j, i) = src.at<uchar>(j, i) - mean.at<uchar>(j, i) - varthreshold>0?255:0;
}
}
}

参考文献:

1.图像处理之积分图应用四(基于局部均值的图像二值化算法)

2. OpenCV中积分图介绍与应用

Opencv笔记(13)积分图的更多相关文章

  1. 颜色空间转换 cvtColor()[OpenCV 笔记13]

    void cvtColor(InputArray src, OutputArray dst, ) src: 输入图像 dst: 输出图像 code: 颜色空间转换标识符 OpenCV2的CV_前缀宏命 ...

  2. OpenCv关于灰度积分图的SSE代码学习和改进。

    最近一直沉迷于SSE方面的优化,实在找不到想学习的参考资料了,就拿个笔记本放在腿上翻翻OpenCv的源代码,无意中看到了OpenCv中关于积分图的代码,仔细研习了一番,觉得OpenCv对SSE的灵活运 ...

  3. SSE图像算法优化系列六:OpenCv关于灰度积分图的SSE代码学习和改进。

    最近一直沉迷于SSE方面的优化,实在找不到想学习的参考资料了,就拿个笔记本放在腿上翻翻OpenCv的源代码,无意中看到了OpenCv中关于积分图的代码,仔细研习了一番,觉得OpenCv对SSE的灵活运 ...

  4. Opencv中integral计算积分图

    Paul Viola和Michael Jones在2001年首次将积分图应用在图像特征提取上,在他们的论文"Rapid Object Detection using a Boosted Ca ...

  5. OpenCV笔记大集锦(转载)

    整理了我所了解的有关OpenCV的学习笔记.原理分析.使用例程等相关的博文.排序不分先后,随机整理的.如果有好的资源,也欢迎介绍和分享. 1:OpenCV学习笔记 作者:CSDN数量:55篇博文网址: ...

  6. opencv笔记3:trackbar简单使用

    time:2015年 10月 03日 星期六 13:54:17 CST # opencv笔记3:trackbar简单使用 当需要测试某变量的一系列取值取值会产生什么结果时,适合用trackbar.看起 ...

  7. 机器学习实战 - 读书笔记(13) - 利用PCA来简化数据

    前言 最近在看Peter Harrington写的"机器学习实战",这是我的学习心得,这次是第13章 - 利用PCA来简化数据. 这里介绍,机器学习中的降维技术,可简化样品数据. ...

  8. opencv笔记6:角点检测

    time:2015年10月09日 星期五 23时11分58秒 # opencv笔记6:角点检测 update:从角点检测,学习图像的特征,这是后续图像跟踪.图像匹配的基础. 角点检测是什么鬼?前面一篇 ...

  9. opencv笔记5:频域和空域的一点理解

    time:2015年10月06日 星期二 12时14分51秒 # opencv笔记5:频域和空域的一点理解 空间域和频率域 傅立叶变换是f(t)乘以正弦项的展开,正弦项的频率由u(其实是miu)的值决 ...

  10. opencv笔记4:模板运算和常见滤波操作

    time:2015年10月04日 星期日 00时00分27秒 # opencv笔记4:模板运算和常见滤波操作 这一篇主要是学习模板运算,了解各种模板运算的运算过程和分类,理论方面主要参考<图像工 ...

随机推荐

  1. redis 简单整理——redis 准备篇[一]

    前言 简单整理一下redis. 正文 为什么使用redis? 速度快 1.1 内存执行 1.2 c语言编写,速度相对快一些 1.3 单线程,比较符合这种存储模式 2 丰富的数据结构 3 丰富的功能机制 ...

  2. docker 应用篇————nginx 例子[六]

    前言 简单整理一下nginx 例子. 正文 拉取nginx 镜像. docker pull nginx 那么会拉取nginx:latest 这个. 如果需要其他的,可以去官网查询一下. 2.docke ...

  3. c# mvc action 跳转方式

    前言 c# 的mvc 有很多的action跳转方式,在此总结一下具体的跳转方法. 这里不用去记,而是知道能怎么做,为什么能这样做,重在思考. 正文 一.RedirectToAction("I ...

  4. Pytorch-tensor维度的扩展,挤压,扩张

    数据本身不发生改变,数据的访问方式发生了改变 1.维度的扩展 函数:unsqueeze() # a是一个4维的 a = torch.randn(4, 3, 28, 28) print('a.shape ...

  5. Unity性能优化——托管堆/GC

    了解托管堆 许多 Unity 开发者面临的另一个常见问题是托管堆的意外扩展.在 Unity 中,托管堆的扩展比收缩容易得多.此外,Unity 的垃圾收集策略往往会使内存碎片化,因此可能阻止大型堆的收缩 ...

  6. 手动给docusaurus添加一个搜索

    新版博客用docusaurus重构已经有些日子了,根据docusaurus的文档上也申请了Algolia,想一劳永逸的解决博客的搜索问题.但是流水有意,落花无情. algolia总是不给我回复,我只能 ...

  7. 牛客网-SQL专项训练22

    ①在SQL中如何删除触发器:DROP TRIGGER 解析: 辨别:DROP TRIGGER 和 DELETE TRIGGER 删除触发器操作必须使用DROP语句进行,DELETE是删除行记录. ②请 ...

  8. 手把手教你PolarDB-X中的表设计——用户表

    简介: 本系列旨在描述一个具体的业务场景,给出建表的例子,帮助大家更好的使用PolarDB-X.本期的主题是:用户表. 本系列旨在描述一个具体的业务场景,给出建表的例子,帮助大家更好的使用PolarD ...

  9. 一个好的网站logo设计长这样

    ​简介:一个好的网站logo,不仅让用户一眼知道网站品牌传递的信息,还能提高网站专业度和丰富度,增加SEO搜索排名.今天分享下如何设计一款实用的网站logo.阿里云智能logo设计,在线免费体验log ...

  10. DE10-Lite锁相环使用教程

    DE10-Lite锁相环使用教程 目标:本文讲述如何在Quartus里设置和例化一个锁相环. 引言 锁相环(PLL)是一种闭环频率控制电路,用于比较压控振荡器的输入信号和输出信号之间的相位差. 负反馈 ...