视觉处理算法基础
本文研究 卷积算法,形态学操作,直方图,区域生长,分水岭算法
卷积操作
卷积滤波原理
- 卷积核/掩膜:一个m*n大小的加权矩阵
- 卷积滤波:先将掩膜旋转180度,再依次滑过图像上每个像素,相应滑动区域与掩膜对应元素乘积之和作为像素的新值
\[ (f*g)(x,y)=\sum_i\sum_jf(x-i,y-j)g(i,j) \]
低通滤波(去噪)
压制、弱化或消除图像中的细节、突变、边缘和噪声,就是图像平滑化。
图像平滑是对图像作低通滤波,可在空间域或频率域实现。
空域图像平滑方法主要用低通卷积滤波、中值滤波等;
频域图像平滑常用的低通滤波器有低通梯形滤波器、低通高斯滤波器、低通指数滤波器、巴特沃思低通滤波器等
均值滤波
- 优点:使用简单,计算方便
- 特点:核中区域贡献率相同
- 函数: cv2.blur(InputArray src, OutputArray dst, Size ksize, Point anchor=Point(-1,-1), int borderType=BORDER_DE)
中值滤波
- 优点:对于椒盐噪声有效
- 特点:中心点的像素值被核中的中位数的像素代替
- void medianBlur( InputArray src, OutputArray dst, int ksize ); Python:dst = cv.medianBlur( src, ksize[, dst] )
高斯滤波
- 优点:对于服从正太分布的噪声非常有效
- 特点:核中区域贡献率与距离中心成正比,权重与高斯分布相关
- void GaussianBlur( InputArray src, OutputArray dst, Size ksize, double sigmaX,double sigmaY = 0, int borderType = BORDER_DEFAULT ); Python: dst = cv.GaussianBlur( src, ksize, sigmaX[, dst[, sigmaY[, borderType]]] )
高通滤波(边缘检测)
- 图象的边缘是指图象局部区域亮度变化显著的部分,该区域的灰度剖面一般可以看作是一个阶跃,即从一个灰度值在很小的缓冲区域内急剧变化到另一个灰度相差较大的灰度值。
- 图象的边缘部分集中了图象的大部分信息,图象边缘的确定与提取对于整个图象场景的识别与理解是非常重要的,同时也是图象分割所依赖的重要特征,边缘检测主要是图象的灰度变化的度量、检测和定位。
- 边缘检测的基本原理: 边缘检测的本质是微分,当相邻两个像素点的灰度值差异越大时,也就是其斜率越陡,也就是微分值越大,进而通过这个来判断边缘,实际中常用差分,x方向和y方向。
Robert算子
Sobel算子
原理:使用两个3X3的矩阵去和原始图片作卷积
分别得到横向G(x)和纵向G(y)的梯度值
如果梯度值大于某一个阈值,则认为该点为边缘点
void Sobel( InputArray src, OutputArray dst, int ddepth, int dx, int dy, int ksize=3, double scale=1,double delta=0, int borderType=BORDER_DEFAULT );
Python:dst = cv.Sobel( src, ddepth, dx, dy[, dst[, ksize[, scale[, delta[, borderType]]]]] )
Laplace算子
void Laplacian(InputArray src, OutputArray dst, int ddepth, int ksize = 1, double scale = 1, doubledelta = 0, int borderType = BORDER_DEFAULT ) |
LoG算子
- 原理:首先对图像做高斯滤波,然后再求其拉普拉斯(Laplacian)二阶导数。
- 即图像与 Laplacian of the Gaussian function 进行滤波运算。
- 最后,通过检测滤波结果的零交叉(Zero crossings)可以获得图像或物体的边缘。
- 因而,也被业界简称为Laplacian-of-Gaussian (LoG)算子
Canny算子,核心优点:边缘可自动连通
- 对原始图像进行灰度化。Canny算法通常处理的图像为灰度图,因此如果摄像机获取的是彩色图像,那首先就得进行灰度化。对一幅彩色图进行灰度化,就是根据图像各个通道的采样值进行加权平均。
- 对图像进行高斯滤波。图像高斯滤波的实现可以用两个一维高斯核分别两次加权实现,也可以通过一个二维高斯核一次卷积实现。
- 用一阶偏导的有限差分来计算梯度的幅值方向。关于图像灰度值得梯度可使用一阶有限差分来进行近似,这样就可以得图像在x和y方向上偏导数的两个矩阵。
- 对梯度幅值进行非极大值抑制。图像梯度幅值矩阵中的元素值越大,说明图像中该点的梯度值越大,但这不不能说明该点就是边缘(这仅仅是属于图像增强的过程)。
- Canny算法中减少假边缘数量的方法是采用双阈值法。选择两个阈值,根据高阈值得到一个边缘图像,这样一个图像含有很少的假边缘,但是由于阈值较高,产生的图像边缘可能不闭合,为解决这样一个问题采用了另外一个低阈值。在高阈值图像中把边缘链接成轮廓,当到达轮廓的端点时,该算法会在断点的8邻域点中寻找满足低阈值的点,再根据此点收集新的边缘,直到整个图像边缘闭合。
- void Canny( InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false );
- Python:dst = cv.Canny( image, threshold1, threshold2[, edges[, apertureSize[, L2gradient]]] )
形态学操作
膨胀
腐蚀
开闭运算
膨胀和腐蚀不为逆运算,二者级联使用可产生新的形态学运算
开运算:先腐蚀后膨胀,可断开连接 \[ A\circ B=(A\ominus B)\oplus B \]
闭运算:先膨胀后腐蚀,可连接狭缝
\[ A\bullet B=(A\oplus B)\ominus B \]
- 先开后闭,可以有效去除噪声
dst = cv.morphologyEx(src, op, kernel[, dst[, anchor[, iterations[, borderType[, borderValue]]]]] )
/** @brief Returns a structuring element of the specified size and shape for morphological operations. |
基于阈值的图像分割
灰度直方图
- 统计各个灰度级别在图像中的出现次数或概率,并用直方图显示出来
- 具有图像平移、旋转、缩放不变性等众多优点
- 直方图在进行图像计算处理时代价较小,所以经常用于图像处理
void calcHist( const Mat* arrays, intnarrays, const int* channels, InputArray mask, OutputArrayhist, int dims, const int* histSize, const float** ranges, bool uniform=true, bool accumulate=false ); |
阈值分割
- 假设:图像中的目标区和背景区之间或者不同目标区之间,存在不同的灰度或平均灰度
- 凡是灰度值包含于z的像素都变成某一灰度值,其他的变成另一个灰度值,则该图像就以z为界被分成两个区域
- 如果=1和=0,分割后的图像为二值图像
- 确定最佳阈值,使背景和目标之间的差异最大。可使用灰度直方图来确定最佳阈值
可对图像分块,进行局部自适应阈值分割,以提升分割效果
大津算法
遍历灰度取值,确定最佳阈值,使背景和目标之间的类间方差最大 (因为二者差异最大);
- 记t为前景与背景的分割阈值,前景点数占图像比例为w0,平均灰度为u0;
- 背景点数占图像比例为w1,平均灰度为u1。则图像的总平均灰度为:u=w0u0+w1u1。
- 前景和背景图象的方差:g=w0(u0-u)(u0-u)+w1(u1-u)(u1-u)=w0w1(u0-u1)*(u0-u1),此公式为方差公式。
- 可参照概率论课本上面的g的公式也就是下面程序中的sb的表达式。当方差g最大时,可以认为此时前景和背景差异最大,此时的灰度t是最佳阈值sb = w0w1(u1-u0)*(u0-u1)
enum AdaptiveThresholdTypes { |
基于区域的图像分割
区域生长分割
从种子点开始,按照一定准则(如相邻像素灰度相似性)向周围扩散,将邻域相似像素加入区域中
- 对图像顺序扫描!找到第1个还没有归属的像素, 设该像素为(x 0 , y 0 );
- 以(x 0 , y 0 )为中心, 考虑(x 0 , y 0 )的8邻域像素(x, y),如果(x, y)满足生 长准则, 将(x, y)与 (x 0 , y 0 )合并, 同时将(x, y)压入堆栈;
- 从堆栈中取出一个像素, 把它当作(x 0 , y 0 )返回到步骤2;
- 当堆栈为空时,返回到步骤1;
- 重复步骤1 - 4直到图像中的每个点都有归属时。生长结束。
/** @brief Fills a connected component with the given color. |
分水岭算法
假设我们在盆地的最小值点,打一个洞,然后往盆地里面注水,并阻止两个盆地的水汇集,我们会在两个盆地的水汇集的时刻,在交接的边缘线上(也即分水岭线),建一个坝,来阻止两个盆地的水汇集成一片水域。这样图像就被分成2个像素集,一个是注水盆地像素集,一个是分水岭线像素集。
局部最小值点,该点对应一个盆地的最低点,当我们在盆地里滴一滴水的时候,由于重力作用,水最终会汇聚到该点。注意:可能存在一个最小值面,该平面内的都是最小值点。
盆地的其它位置点,该位置滴的水滴会汇聚到局部最小点。
盆地的边缘点,是该盆地和其它盆地交接点,在该点滴一滴水,会等概率的流向任何一个盆地。
由于噪声点或者其它干扰因素的存在,使用分水岭算法常常存在过度分割的现象,这是因为很多很小的局部极值点的存在
为了解决过度分割的问题,可以使用基于标记(mark)图像的分水岭算法,就是指定mark图像,在这个区域的洪水淹没过程中,水平面都是从定义的marker开始的,这样可以避免一些很小的噪声极值区域的分割。
/** @brief Performs a marker-based image segmentation using the watershed algorithm. |
基于边缘轮廓的分割
- 可以在边缘检测的基础上,基于闭合边缘构建分割后的结果
- 在分割前需要进行边缘检测
- OpenCV提供几种基于边缘轮廓的分割方法,包括简单的基于封闭曲线的方法,以及基于活动廓线(active contour的方法)。本文只介绍前者
- 该算法基于Suzuki, S. 1985的方法,相对古老,但OpenCV基于此提供了关于图像描述的支持,比较好用
|
void findContours( InputOutputArray image, OutputArrayOfArrays contours,OutputArray hierarchy, int mode, int method, Point offset=Point());
- image : 单通道图像矩阵,可以是灰度图,但更常用的是经过边缘检测算子处理后的二值图像;
- contours :定义为“vector<vector
> contours”,是一个轮廓列表; - hierarchy : 存在嵌套轮廓时,分别为第i个轮廓的后一个轮廓、前一个轮廓、父轮廓、内嵌轮廓的索引编号;
- mode : 定义轮廓的检索模式, 包括CV_RETR_EXTERNAL只检测最外围轮廓, CV_RETR_LIST检测所有轮廓,但不建立等级关系等;
- method : 包括CV_CHAIN_APPROX_SIMPLE 仅保存轮廓的拐点信息,把所有轮廓拐点处的点保存入contours等;
- offset : 所有的轮廓信息相对于原始图像对应点的偏移量, 缺省不设置。
/** @brief Finds contours in a binary image. |