积分图时一种允许子区域快速求和的数据结构,这种求和在很多方面都很有用,值得一提的是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. 三款免费强大的SSH工具食用指南

    食用清单 XShell FinalShell Electerm 食用方案 XShell 先说说老牌ssh工具XShell吧,用过很多年,说实话没啥别的毛病挺好用的.不过,还是有些地方有待加强,比如文件 ...

  2. DDD领域驱动设计总结和C#代码示例

    DDD(领域驱动设计)是一种软件设计方法,它强调以业务领域为核心来驱动软件的设计和开发. DDD 的设计初衷是为了解决复杂业务领域的设计和开发问题,它提供了一套丰富的概念和模式,帮助开发者更好地理解和 ...

  3. HL7简介

    HL7是特定于医疗保健的标准组织,其主要重点是创建一组定义的国际消息传递标准,以支持应用程序和设备之间的互操作性和通信.这些消息标准可以分为三个主要的 HL7标准版本,HL7版本2(v2).版本3(v ...

  4. 力扣54(java)-螺旋矩阵(中等)

    题目: 给你一个 m 行 n 列的矩阵 matrix ,请按照 顺时针螺旋顺序 ,返回矩阵中的所有元素. 示例 1: 提示: m == matrix.lengthn == matrix[i].leng ...

  5. 力扣61(java&python)-旋转链表(中等)

    题目: 给你一个链表的头节点 head ,旋转链表,将链表每个节点向右移动 k 个位置. 示例1: 输入:head = [1,2,3,4,5], k = 2 输出:[4,5,1,2,3] 示例2: 输 ...

  6. 力扣50(java)-Pow(x,n)(中等)

    题目: 实现 pow(x, n) ,即计算 x 的整数 n 次幂函数(即xn ). 示例 1: 输入:x = 2.00000, n = 10输出:1024.00000示例 2: 输入:x = 2.10 ...

  7. 转载 | 如何把 thinkphp5 的项目迁移到阿里云函数计算来应对流量洪峰?

    简介: 函数计算评测局的优秀征文! 如何把thinkphp5的项目迁移到阿里云函数计算来应对流量洪峰? 1. 为什么要迁移到阿里云函数? 我的项目是一个节日礼品领取项目,过节的时候会有短时间的流量洪峰 ...

  8. 工商银行分布式服务 C10K 场景解决方案

    简介: Dubbo 是一款轻量级的开源 Java 服务框架,是众多企业在建设分布式服务架构时的首选.中国工商银行自 2014 年开始探索分布式架构转型工作,基于开源 Dubbo 自主研发了分布式服务平 ...

  9. 一文总结Java\JDK 17发布的新特性

    ​简介: JDK 17已经于2021年3月16日如期发布.本文介绍JDK 17新特性.JDK 17于2021年9月14日正式发布(General-Availability Release).JDK 1 ...

  10. [GPT] 哪些职业面临 AI 威胁?

      随着人工智能技术的不断发展和应用,一些重复性.机械化或标准化程度高的职业可能会面临被自动化取代的威胁.例如: 工厂生产线上的装配工人,因为许多工厂已经开始使用自动化机器人完成装配任务: 行政助理, ...