滤波

第(二)篇中的模糊操作都是可以进行滤波的,但是全局模糊容易损失图像的边缘信息,因此在需要保留边缘的情况就需要边缘保留滤波,以下记录学习了几个常用的边缘保留滤波的方法。

高斯双边滤波

在边缘像素只取一般进行高斯滤波,减小对边缘梯度的影响。

opencv4提供的API:

void cv::bilateralFilter(InputArray src,OutputArray dst,int d,double sigmaColor,double sigmaSpace,int borderType = BORDER_DEFAULT)

参数d是滤波操作时像素领域的直径,可理解为卷积核大小,当d<=0时,自动由sigmaspace计算得到;sigmacolor是颜色空间的高斯公式中的sigma(标准差),可理解为像素深度的高斯滤波;sigmaspace空间滤波的“标准差”,数值越大核越大。

非局部均值滤波

这种方法是在求均值时加入权重,相似的像素领域权重大,不相似的权重小。比较废时。

如下图取L*L的搜索窗(避免全图搜索导致计算时间长),将要处理的两个像素的邻域(图为3*3)中的所有像素对比计算,带入权重公式中(w是权重),求得权重。再对i像素进行均值滤波。

opencv有两个API分别是处理彩色三通道的和灰度单通道的:

void cv::fastNlMeansDenoisingColored(InputArray src,OutputArray dst,float h = 3,float hColor = 3,int templateWindowSize = 7,int searchWindowSize = 21)	

这处理彩色图的,h对应权重公式的h;hcolor是颜色中计算权重的h;templatewindowsize是模板补丁,即为上图3*3的小邻域;search_window是上图的L;

void cv::cuda::fastNlMeansDenoising(InputArray src,OutputArray dst,float h,int search_window = 21,int block_size = 7)

这处理灰度图的,h对应权重公式的h一般取3;search_window是上图的L;block_size是模板补丁,即为上图3*3的小邻域。

Canny边缘提取

Canny边缘提取步骤:

1.高斯滤波去噪

2.使用sobel或scharr、robert算子计算梯度。并求其梯度幅值和角度。

幅值:

角度:

3.非最大抑制

在其梯度方向上,非最大值置0,如图中黄线为梯度方向(θ=180°),(i,j)不为最大会被置0。这样可以去掉不必要像素,进行“瘦”边。

4.滞后阈值法连接边缘

取两个阈值t2=(2~3)t1,当该像素的值大于t2保留,小于t1置零,在t1-t2间若其邻域内有大于t1的像素则保留,否则置零。

其API:

void cv::Canny(InputArray image,OutputArray edges,double threshold1,double threshold2,int apertureSize = 3,bool L2gradient = false)

参数threshold1和threshold2对应t1和t2;sobel算子的大小,默认3即可;L2gradient使用L2梯度的使能参数。

二值化

其实经过canny边缘提取后的图像为二值图像,即像素只有两个大小,255或0。

二值化分隔

二值化的图像需要有阈值,根据阈值常有5中分隔方法

蓝线是阈值。在opencv4中对应的枚举依次为:1. THRESH_BINARY      2. THRESH_BINARY_INV      3. THRESH_TRUNC     4. THRESH_TOZERO    5. THRESH_TOZERO_INV

阈值选取

全局阈值:

求取整张图的像素均值作为阈值

void mean_th(Mat src, Mat dst)
{
  Scalar m = mean(src);
  threshold(src, dst, m[0], 255, THRESH_BINARY);
}

OTSU,利用直方图计算不同阈值时的前景像素比重Wf,均值uf,方差sigmaf。背景像素同理(下标为b),再计算类内方差(下面公式)。最后选取类内方差最小时为阈值。

threshold(src, dst, 0, 255, THRESH_BINARY | THRESH_OTSU);

三角法:

图为直方图,当h最大时,h线对应的像素值为阈值。该法一般用于x光片处理等。

threshold(src, dst, 0, 255, THRESH_BINARY | THRESH_TRIANGLE);

自适应:

void cv::adaptiveThreshold(InputArray src,OutputArray dst,double maxValue,int adaptiveMethod,int thresholdType,int blockSize,double C )	

maxvalue为二值化最大值,一般255;adaptivemethod为计算阈值方法,有均值法ADAPTIVE_THRESH_MEAN_C在blocksize大小的区域中求均值作为该像素的二值化的阈值,高斯法ADAPTIVE_THRESH_GAUSSIAN_C用高斯卷积核进行加权求均值获得阈值;thresholdtype是分隔方式,只能用前2种方法。C为求得均值后要加的常数。

连通域扫描

在二值图像中通过连通域扫描给连通的区域标上一样的标记,区分图像中各个“块”。

连通域扫描时以一个模板进行扫描,并利用决策表进行标号,最终取得各个块的区别标号。

决策表

模板

有些时候为了提高速度会以像素块的形式进行扫描,因此会另有决策表。

int cv::connectedComponentsWithStats(InputArray image,OutputArray labels,OutputArray stats,OutputArray centroids,int connectivity,int ltype,int ccltype)

以上是opencv4提供的api。stats参数是连通域的统计信息。centroids为质心坐标。connectivity可选4路和8路。ltype是输出labels的类型。该函数返回的是连通域的个数。

轮廓

在二值图像中可以在0-1像素边缘处找到轮廓。

void cv::findContours(InputArray image,OutputArrayOfArrays contours,OutputArray hierarchy,int mode,int method,Point offset = Point())

该api可以获取轮廓信息在contours中再调用drawContours()便可将轮廓绘制出来。

霍夫检测

霍夫直线检测原理大致为:在xy坐标系中一条直线y=kx+q,转换到k-q坐标系时只有一个点。即x-y坐标系中的一条线可由k-q坐标系中的一个点表示。同理k-q坐标系中的一条线代表x-y坐标系中的一个点。在二值图像中前景像素转换到k-q坐标系,会有多条直线,他们多数的交点即为x-y坐标系的直线,也是大多数像素共同在的一条直线,这样图像的直线就被检测出来了。

但是使用k-q坐标系存在一个问题,当直线为垂直线时k为无穷,此时无法计算。因此采用极坐标替代k-q坐标系进行工作。

opencv对于霍夫直线检测也有提供api:

void cv::HoughLines(inputArray image,OutputArray lines,double rho,double theta,int threshold,double srn = 0,double stn = 0,double min_theta = 0,double max_theta = CV_PI)

霍夫圆检测原理相似,也是将圆上的点转换到极坐标系,然后统计交点获得原图上的圆。原图的圆边使用边缘检测得到,即梯度大的像素点。

其api为:

void cv::HoughCircles(InputArray image,OutputArray circles,int method,double dp,double minDist,double param1 = 100,double param2 = 100,int minRadius = 0,int maxRadius = 0)

膨胀腐蚀

膨胀与腐蚀用在二值图像中,膨胀是将前景像素进行膨胀,即选取一个窗口如3*3,当窗口中有像素值为255时,则改窗口所有像素都改为255,然后窗口进行逐一像素滑动。同理,腐蚀是在窗口中存在为0的像素时,窗口内所有像素修改为0.

在opencv中提供api:

膨胀

void cv::dilate(InputArray src,OutputArray dst,InputArray kernel,Point anchor = Point(-1,-1),int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar & borderValue = morphologyDefaultBorderValue())

腐蚀

void cv::erode(InputArray src,OutputArray dst,InputArray kernel,Point anchor = Point(-1,-1),int iterations = 1,int borderType = BORDER_CONSTANT,const Scalar & borderValue = morphologyDefaultBorderValue())

其中它们的“窗口”也就是kernel参数可以使用以下api进行设定:

Mat cv::getStructuringElement(int shape,Size ksize,Point anchor = Point(-1,-1))

参考链接:https://docs.opencv.org/