前言
显著性检测,顾名思义,就是提取一幅图像中的突出对象,灵感就来自于人的视觉特性,如果一张底色纯白,中间一块黑色的纸,那人眼的注意力肯定就会在黑色部分,而显著性检测就是计算图像的显著性图,突出部分显著性图的值就高。
显著性检测工作往往伴随这其他工作,比如分割,分割可以基于显著性检测的结果来进行分割,进而把突出对象分割出来。
本来是想做缺陷检测来着,但一不小心看了两天的显著性检测论文,所以出四篇文章分享四个显著性检测算法:LC/HC/AC/FT
这四个算法都特别特别简单,而且得益于网络的发达以及前辈的总结,学起来简直不能再容易了。
此外我建了一个微信视觉算法交流群,加我微信yanshilin216备注“加群”拉大家进群交流。
算法原理
论文名:
Visual Attention Detection in Video Sequences Using Spatiotemporal Cues
该算法可以用一句话概括:每个像素的显著性值是该像素距图片所有其他像素的某个距离的和。这个距离一般是像素特征值的欧式距离。
所以公式也就很明了了:
S§就是显著性值,d(p,q)就是像素p距离q的距离,I就是整幅图像。
这个显著性检测数学模型就是这么简单。
不过代码却不能直接for循环写,时间复杂度会很高的,所以可以利用直方图进行优化。
-
将像素的特征值归一化到[0,255],如果图像是单通道,这个特征值就是像素灰度值。我们计算的距离也就是特征值之间的颜色距离。然后利用直方图对图像特征值进行统计,得到fn,即特征值在图像中出现的次数。
这时显著性计算公式为:
其中d(p,n)为特征值p和n的欧氏距离。
-
为进一步优化,我们提前计算出每一个d(n1,n2)。由于任何两个像素的特征值都在[0,255]之间,可以提前计算距离矩阵D。这样就可查表得到两个像素的特征距离。这样特征值p的显著性进一步优化为:
-
记录已经计算的显著值,避免重复计算。
该算法缺点:
其求全局对比度的策略会导致稀有颜色(特征值)占优,也就是具有较高的对比度,这在很多情况下是不合理的。所以的方法检测效果不够好
算法实现
算法流程:
- 计算图像特征值的直方图
- 遍历图像计算特征值距离矩阵D
- 为每一个对应像素值分配显著值,得到显著图
- 将显著图归一化到[0,255]范围内并显示
具体代码:
void LC::calculateSaliencyMap(Mat *src, Mat * dst)
{
Mat img;
//【1】处理灰度图
if ((*src).channels() == 3)
cvtColor(*src, img, COLOR_BGR2GRAY);
else
img = *src;
//【2】定义变量并初始化为0
double f[256], s[256];
memset(f, 0, 256 * sizeof(double));
/* memset(void *s,int ch,size_t n)
memset函数是计算机中C/C++语言函数。将s所指向的某一块内存中的前n个字节的内容全部设置为ch指定的ASCII值,
第一个值为指定的内存地址,块的大小由第三个参数指定,这个函数通常为新申请的内存做初始化工作,
其返回值为指向s的指针。所在头文件<memory.h>或<string.h>*/
memset(s, 0, 256 * sizeof(double));
//【3】统计直方图
for (int r = 0; r < img.rows; r++)
{
uchar* data = img.ptr<uchar>(r);
for (int c = 0; c < img.cols; c++)
f[data[c]] += 1;//直方图f 将对应像素值的直方图加1
}
//【4】计算特征显著值并保存到S[]
double s_min = DBL_MAX, s_max = 0;
for (int i = 0; i < 256; i++)
{
for (int j = 0; j < 256; j++)
s[i] += abs(i - j) * abs(i - j) * f[j];//直方图的累积
if (s[i]>s_max) s_max = s[i];
if (s[i]<s_min) s_min = s[i];
}
//【5】显著特征值归一化到0-255
for (int i = 0; i < 256; i++)
{
s[i] = (s[i] - s_min) / (s_max - s_min)*255;
}
//【6】为每一个像素分配显著值。(i,j)是什么灰度级就赋予它相应灰度级的显著值。得到显著图。
Mat salimg(img.size(), CV_64F);
for (int r = 0; r < img.rows; r++)
{
uchar* data = img.ptr<uchar>(r);
double* sal = salimg.ptr<double>(r);
for (int c = 0; c < img.cols; c++)
sal[c] = s[data[c]];
}
salimg.convertTo(*dst, CV_8U, 1, 0);
}
算法效果
输入为灰度图,输出为显著图:
THE END
今天就到这里啦。接下来的三天会把剩下的HC/AC/FT三个算法更新完。需要代码的欢迎加微信群。