我在大学学习图像的时候,主要有两个科目,一个是计算机图形学和数字图像处理,数字图像处理就是很直观的把图像当做一堆数据、矩阵、行列阵等。对这些数据,像是对图像的矩阵进行卷积、加减、乘除等操作就是对图像进行各种的处理,如滤波,对比度增强,减少噪声等各种操作。而本篇文章是对图像获得的数据里的关于图像亮度的数值进行判断而已,而sobel算子就是根据什么算法和标准对一张图像的清晰度进行一个总和判断。
而计算机图形学里我常常学到的是对一张图形的点、线等如何快速处理一个图像,像是PS等画一条直线、裁剪图像等操作,还有涉及一些光照模型,像是phong光照模型、lamber、Blinn-Phong光照模型,这些知识点我在学习技术美术时候得到了进一步的了解,如果有好奇技术美术的,比如公司面试、岗位相关资讯等我有了解一点,如果好奇欢迎底下评论,后续我会考虑做一篇关于技术美术的,但是这个坑真的很深,如果你想做的好其实是很难的、
这里还要对想要使用sobel算子判断清晰度的人说一句,首先你得了解一下你对图像判断清晰度的质量要求,如果你要求很高的话,我觉得sobel算子可能不太适合你,可以去看刀刃法等更加准确的方法来帮助判断图像清晰度,否则这篇文章和网上其实挺多的文章使用sobel算法只能说大概能判断出来,但是其实还是不够精确。而且会收到光照、噪声等影响,如果想在sobel算子上提高精度,这里建议你可以先对图像先做处理,然后再对图像清晰度进行判断。算法本质就是一种输入到一种输出。所以对图像的处理就是一种把输入通过数学计算转换成新的输出。
在研究调焦的过程中,发现最主要有两个步骤:
1.判断图像清晰度
2.搜索策略
3.图像窗口的选择
4.视场、亮度归一化
边缘检测是图像最基本的特征,图像的轮廓,细节基本都存在于图像的边缘部分,在图像分析中借助它能大大减少所要处理的信息,又保留了图像中物体的形状信息。因此边缘检测在图像处理、模式识别、机器视觉等领域中有很重要的作用。在边缘检测中最常用的就是sobel算子,在图像空间利用水平和垂直两个3x3方向模板与图像中每个像素点进行领域卷积,两方向sobel算子边缘检测提取了水平和垂直两个方向的边缘梯度信息,这种边缘检测算法对图像边缘的方向特征考虑较少,常丢失部分边缘细节,尤其是在动态环境下,实际图像的梯度方向是未知的。
在查阅各类文献中,了解到了许多判断清晰度的评价函数,比较流行的是边缘灰度梯度的评价函数,基于频率特征的评价函数和基于信息、统计特征的评价函数。基于频率特征的评价函数计 算量大,在快速自动调焦系统中的应用较少;基于信息学、统计学的评价函数在自动调焦的过程中对于 外界环境的稳定性要求较高,灵敏度低,所以基本不能正确地反映焦点的位置。
所有物体进入计算机里都是转化成数值来进行表示,因此一张图就是以数值进入计算机的存储中的。
往往一张图像包含坐标点,你如果想简单的了解,可以选择电脑里一般都有的最基础的工具,画图软件,打开画图软件,你可以对图像进行各种数值查看,一个是图像的大小,和相应每个点的坐标、颜色RGB、亮度等值,同时你也可以对图像用画笔工作等进行操作。
显示器里的像素往往包含颜色信息、位置信息,颜色信息是,显示器里红绿蓝的灯条组成。256哥级别的亮度。视觉混合效应就会把不同级别的红绿蓝混在一起看成了其他颜色。
这里的图像清晰度主要就是对图像的边缘进行判断。
图像的低频段和中频段包含图像大部分能量,高频成分则决定了图像细节的丰富程度和图像轮廓的锐度。所以根据这个特性,就有了很多种计算图像高频分量和检测图像边缘信息的方法。图像聚焦的表现是在空域上表现为图像的细节和轮廓是否清晰,在频域上表现为图像的高频分量是否丰富。其中评价图像清晰度函数上最流行的是边缘灰度梯度的评价函数,基于频率特征的评价函数和基于信息,统计特征的评价函数。
Sobel算子判断图像清晰度本质是梯度运算。
索贝尔算子(Sobeloperator)主要用于获得数字图像的一阶梯度,常见的应用和物理意义是边缘检测。
在技术上,它是一个离散的一阶差分算子,用来计算图像亮度函数的一阶梯度之近似值,用来运算图像亮度函数的灰度值近似值。在图像的任何一点使用此算子,将会产生该点对应的梯度矢量或是其法矢量。
***什么情况下产生梯度:***灰度图里在全黑、全白区域里,数值转换是一样的,但是在有黑有白的边缘区域是有梯度变化。纯白是255,纯黑是0,则梯度运算可以是两者相减。sobel算子主要目的是图像边缘检测。
***Sobel算子:***Sobel算子是一阶导数的边缘检测算子,在算法实现过程中,通过3×3模板作为核与图像中的每个像素点做卷积和运算,然后选取合适的阈值以提取边缘。
如果出现一个边缘,那么图像的灰度就会有一定的变化,假设由黑渐变为白代表一个边界,那么对其灰度分析,在边缘的灰度函数就是一个一次函数y=kx,对其求一阶导数就是其斜率k,就是说边缘的一阶导数是一个常数,而由于非边缘的一阶导数为零,这样通过求一阶导数就能初步判断图像的边缘了。Sobel1就是用一个3×3的窗口来对图像进行近似求导。
对X方向求导为例,某一点的导数为第三列的元素之和减去第一列元素之和,这样就求得了某一点的近似导数。其实也很好理解为什么它就近似代表导数,导数就代表一个变化率,从第一列变为第三列,灰度值相减,当然就是一个变化率了。Y方向导数与X方向导数求法相似,只不过是用第三行元素之和减去第一行元素之和。X方向和Y方向导数有了,那么梯度也就出来了。
由于只采用了2个方向的模板,只能检测水平和垂直方向的边缘,这种算法对于纹理较为复杂的图像,其边缘检测效果就不是很理想。
Sobel算子,Sobel原始模型为标准3x3模板,但可以扩展成5x5到任意奇数x奇数的大小,而模板系数的确定可以根据帕斯卡三角来计算。
Sobeli实现可以利用sobel的卷积核对图像的像素矩阵进行遍历乘上卷积核,最后遍历完,可以以图像卷积核的核心区域来观察。也可以对图像用0来填充边缘,这样就可以把所有像素点考虑进去。
Sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常用的边缘检测方法。
但是传统sobel算子没有考虑到细节上的方向,所以在为了能够更精确的判断合焦,我才有了改进的sobel边缘算子,就是对八方向的sobel算子进行加权计算。
根据人类视觉系统的视觉多通道特性,视觉系统对水平以及垂直方向的刺激最敏感,而往对焦方向的敏感性逐渐减弱,将八方向sobel边缘成分在计算梯度时进行方向权重分配,调整各方向边缘成分在图像梯度计算中的比重,从而在动态环境下能够更精确的计算图像清晰度评价值。
这里先附上一部分,是已经经过测试过,是可以体现出不同清晰度下图像的不同的判断数值。
这里是用C语言底层代码进行实现的。
首先第一步转换成灰度图的代码我先暂时不附上了,因为我拿到的图原本就是灰度图,数值是从0-255开始。
第一步先拿到图像,把图像存储到一个矩阵数组里
//利用多级指针+循环实现矩阵存储方式
int **result = (int **)malloc(nH * sizeof(int*));
for (i = 0; i < nW/n; ++i)
{
result[i] =(int *)MALLOC(sizeof(int)*(nH/n));
}
第二步声明不同的sobel算子,然后利用sobel算子与存储下来的图像矩阵进行卷积
//这里是与灰度图像进行卷积的八个方向的sobel算子
int s0[5][5] = {
{ 0, 0, 0, 0, 0 },
{ -1, -2, -4, -2, -1 },
{ 0, 0, 0, 0, 0 },
{ 1, 2, 4, 2, 1 },
{ 0, 0, 0, 0, 0 },
};
int s22[5][5] = {
{ 0, 0, 0, 0, 0 },
{ 0, -2, -4, -2, 0 },
{ -1, -4, 0, 4, 1 },
{ 0, 2, 4, 2, 0 },
{ 0, 0, 0, 0, 0 },
};
int s45[5][5] = {
{ 0, 0, 0, 0, 0 },
{ 0, -2, -4, 0, 0 },
{ 0, -4, 0, 4, 0 },
{ -1, 0, 4, 2, 0 },
{ 0, 1, 0, 0, 0 },
};
int s67[5][5] = {
{ 0, 0, -1, 0, 0 },
{ 0, -2, -4, 2, 0 },
{ 0, -4, 0, 4, 0 },
{ 0, -2, 4, 2, 0 },
{ 0, 0, 1, 0, 0 },
};
int s90[5][5] = {
{ 0, -1, 0, 1, 0 },
{ 0, -2, 0, 2, 0 },
{ 0, -4, 0, 4, 0 },
{ 0, -2, 0, 2, 0 },
{ 0, -1, 0, 1, 0 },
};
int s112[5][5] = {
{ 0, 0, 1, 0, 0 },
{ 0, -2, 4, 2, 0 },
{ 0, -4, 0, 4, 0 },
{ 0, -2, -4, 2, 0 },
{ 0, 0, -2, 0, 0 },
};
int s135[5][5] = {
{ 0, 1, 0, 0, 0 },
{ -1, 0, 4, 2, 0 },
{ 0, -4, 0, 4, 0},
{ 0, -2, -4, 0, 0 },
{ 0, 0, 0, -1 0 },
};
int s157[5][5] = {
{ 0, 0, 0, 0, 0 },
{ 0, 2, 4, 2, 0 },
{ -1,-4, 0, 4, 1 },
{ 0, -2, -4, -2, 0 },
{ 0, 0, 0, 0, 0 },
};
卷积的实现
int **Conv2(int **pImgBuf,int **w, int rows, int cols, int Bm, int Bn)
{
//Bm、Bn为sobel算子的矩阵大小
int n1 = rows + Bm - 1;
int n2 =cols + Bn - 1;
int **result = (int **)MALLOC(n1 * sizeof(int*));
for (int i = 0; i < n1; i++) {
result[i] = (int *)MALLOC(n2 * sizeof(int));
for (int j = 0; j < n2; j++) {
int sum = 0;
for (int m = 0; m < Bm; m++) {
for (int n = 0; n < Bn; n++) {
int rm = i - m;
int rn = j - n;
/*补0处理*/
if (rm >= 0 && rm < rows && rn >= 0 && rn < cols)
sum += pImgBuf[rm][rn] * w[m][n];
}
}
/*取绝对值,超过255则取255,小于0则取0*/
sum = abs(sum);
result[i][j] = sum;
if(sum < 0)
{
result[i][j] = 0;
}
else if (sum > 255)
{
result[i][j] = 255;
}
}
}
return result;
}
//最后通过多次对调用Conv2函数得到的与不同sobel算子卷积后的结果,把每个像素的八个方向与sobel卷积得到的数值相加,这里直接取绝对值没有开根号,是为了减少计算量,但是按照论文的结果应该是要开根号
for (int i = 0; i < w; i++) {
s1 = sobel1[i];
s2 = sobel2[i];
s3 = sobel3[i];
s4 = sobel4[i];
s5 = sobel5[i];
s6 = sobel6[i];
s7 = sobel7[i];
s8 = sobel8[i];
for (int j = 0; j < h; j++) {
num = abs(s1[j]) + abs(s2[j]) + abs(s3[j]) + abs(s4[j])+ abs(s5[j])+ abs(s6[j])+ abs(s7[j])+ abs(s8[j]);
if (num > 255)
{
num = 255;
}
else if(num < 0)
{
num = 0;
}
result += num;
}
}
sobel算子输入的图像需要是灰度图,所以第一步是对图片进行处理,让它转变为灰度图。
边缘是值在图像上灰度变化最显著的地方,边缘检测算子则利用图像边缘灰度的突变来检测边缘。sobel算子包含两组3x3的滤波器,分别对水平以及垂直方向上的边缘敏感。
后期有空持续更新~
估计会分为卷积的原理,代码如何实现卷积,然后根据什么标准来判断哪张图像更清晰。
最后会与搜索算法进行结合
PS:虽然有了图像清晰度的算法,但是图像的选取
一种改进的自动调焦爬山搜索算法-牟宏鑫1,吴庆畅1,张 翠1,吴 诚2
显微镜的快速自动对焦算法-苗立刚** , 轩 波 , 彭思龙
下面是我强烈推荐一个论文,我看了下,发现总结的特别多,细节特别多,虽然很早是2004年的论文,但是总的来说如果看完了我觉得收货挺多的,尤其是对我这种在很多层面上是小白的人。是浙大的一个博士论文。
数字自动对焦技术的理论及实现方法研究-李奇
sobel算子