• 常用图像卷积核类型小结


    目录

    低通滤波器:

    均值滤波

    高斯滤波

    高通滤波器:

    锐化卷积核

    一阶微分算子

    Prewitt算子

    Sobel算子

    二阶微分算子

    Laplace算子

    对比:

    LoG算子

    基本理论

    DoG算子

    基本理论

    高斯模糊代码

    DOG代码


    卷积操作的主要目的就是对图像进行降维以及特征提取

    1.卷积核往往是行数和列数均为奇数的矩阵,这样中心较好定位;


    2.卷积核元素的总和体现出输出的亮度,若元素总和为1,卷积后的图像与原图像亮度基本一致;若元素总和为0,则卷积后的图像基本上是黑色,其中较亮的部分往往就是提取出图像的某种特征;


    3.滤波实际上就是Same模式的卷积操作,也就是说滤波后图像的大小不变,各种滤镜和照片的风格化就是使用不同的滤波器对图像进行操作。因此卷积核、滤波器本质上都是一个东西;


    4.高通滤波器(High Pass Filter, HPF)表示仅允许图像中高频部分(即图片中变化较剧烈的部分)通过,往往用于对图像进行锐化处理、增强图像中物体边缘等。如Sobel算子、Prewitt算子、锐化滤波器等;


    5.低通滤波器(Low Pass Filter, LPF)表示仅允许图像中低频部分(即图片中变化较平缓的部分)通过,往往用于对图像进行模糊/平滑处理、消除噪点等。如高斯滤波器、均值滤波器等;


    本文在此将常用的卷积核及其对应的意义总结记录一下,以便随时复习。

    低通滤波器:

    均值滤波

    特点:滤波器中元素之和为1,输出亮度与输入基本一致;
    均为低通滤波器,主要用于图像模糊/平滑处理、消除噪点;
    核越大,模糊程度越大;


    其中均值滤波器从名字就可以看出,每个元素值都一样,是卷积核元素个数的倒数,这样每个输出像素就是其周围像素的均值。一个3 × 3 的均值滤波器如下所示:

     

    高斯滤波

    特点:滤波器中元素之和为1,输出亮度与输入基本一致;
    均为低通滤波器,主要用于图像模糊/平滑处理、消除噪点;
    核越大,模糊程度越大;

    高斯滤波器虽然元素总和也为1,但每个位置的权重不一样,权重在行和列上的分布均服从高斯分布,故称高斯滤波器。高斯分布的标准差越大,则模糊程度越大。一个3 × 3 标准差为1的高斯滤波器如下所示:
    5b412561ba2622a77050a644a30334a9.png
    高斯和均值滤波器实例

    这两个滤波器主要作用均为模糊图像,或在图像预处理中消除图像中的噪点:

    高通滤波器:

    锐化卷积核

    概念:
    锐化卷积核从名字就可以看出,主要作用就是对图片进行锐化操作,也就是让图像的边缘更加锐利。图像的边缘往往就是变化较大的地方,也就是图像的高频部分,因此锐化卷积核就是一种高通滤波器。一个3 × 3的锐化卷积核如下所示:

    4b5cec22c5d347c7f49b24799e3a4bf3.png

    可见该卷积核就是计算中心位置像素与周围像素的差值,差值越大则表示该元素附近的变化越大(频率越大),输出值也就越大,因此是高频滤波器的一种。锐化卷积核元素总和如果是0,则有提取图像边缘信息的效果。

    锐化卷积核示例
    锐化卷积核作用为突出图像中物体的边缘,相当于给物体描边:

    6ecb2514c458416e18cd78d0ff401e52.png

    一阶微分算子

    概念
    图像中物体的边缘往往就是变化较为剧烈的部分(高频部分),对于一个函数来说,变化越剧烈的地方,对应的导数的绝对值也就越大。图像就是一种二元函数,f ( x , y )表示( x , y ))处像素的值,因此导数除了大小,还有方向。那么求图像在某方向上的一阶导数(或称图像的梯度),也就可以反映出图像在该处的变化程度,变化程度越快,在该方向的垂直方向可能就存在物体的边缘。

    一阶微分算子可以计算出某个方向上物体的边缘

    但往往对噪声较为敏感,且边缘检测敏感度依赖于物体的大小。

    Prewitt算子

    Prewitt算子就是对图像进行差分来近似对图像的某个部分求一阶导数。Prewitt算子适合用来识别噪声较多、灰度渐变的图像57d6dc3b55a8c675b4c6bf0c718f67b2.png

    012cba5a611c81c6a37ab668bacf2d32.png

    例如 像素点P5处 x 和 y 方向上的梯度大小 g_x 和 g_y 分别计算为

    1. # -*- coding: utf-8 -*-
    2. import cv2
    3. import numpy as np
    4. import matplotlib.pyplot as plt
    5. # 读取图像
    6. img = cv2.imread('C:/Users/123/Pictures/66.png')
    7. img_RGB = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
    8. # 灰度化处理图像
    9. grayImage = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    10. # Prewitt算子
    11. kernelx = np.array([[1, 1, 1], [0, 0, 0], [-1, -1, -1]], dtype=int)
    12. kernely = np.array([[-1, 0, 1], [-1, 0, 1], [-1, 0, 1]], dtype=int)
    13. x = cv2.filter2D(grayImage, cv2.CV_16S, kernelx)
    14. y = cv2.filter2D(grayImage, cv2.CV_16S, kernely)
    15. # 转uint8
    16. absX = cv2.convertScaleAbs(x)
    17. absY = cv2.convertScaleAbs(y)
    18. Prewitt = cv2.addWeighted(absX, 0.5, absY, 0.5, 0)
    19. # 用来正常显示中文标签
    20. plt.rcParams['font.sans-serif'] = ['SimHei']
    21. # 显示图形
    22. plt.subplot(121),plt.imshow(img_RGB),plt.title('原始图像'), plt.axis('off') #坐标轴关闭
    23. plt.subplot(122),plt.imshow(Prewitt, cmap=plt.cm.gray ),plt.title('Prewitt算子'), plt.axis('off')
    24. plt.show()

    Sobel算子

    Sobel算子则是Prewitt算子的改进版,对中间的元素适当进行了加权,Sobel算子与Prewitt算子类似。Sobel算子包含两组3×3的滤波器,分别对水平及垂直方向上的边缘敏感。

    让两个方向模板分别沿着x轴、y轴与图像做卷积,方向是从上到下和从左到右。将模板的中 心和图像上的某个像素重合,并将该像素周围的点 与模板上对应的系数相乘,如图所示,其 中Gx及Gy分别代表经横向及纵向边缘检测的图像梯度值。

      图像上每个像素点的横向及纵向梯度值通过如 下式结合,来计算该点梯度值G的大小:

     为了减少运算时间,提高运算效率,可以使用绝 对值求和近似的方法代替开平方:



    由于Sobel算 子对于象素的位置的影响做了加权,可以降低边缘 模糊程度,与Prewitt算子、Roberts算子相比效果更好

    1. from skimage import data,filters,img_as_ubyte
    2. import matplotlib.pyplot as plt
    3. import cv2
    4. # 图像读取
    5. img = data.camera()
    6. plt.imshow(img,plt.cm.gray)
    7. '''**********skimage*************'''
    8. # sobel边缘检测
    9. edges = filters.sobel(img)
    10. # 浮点型转成uint8型
    11. edges = img_as_ubyte(edges)
    12. # 显示图像
    13. plt.figure()
    14. plt.imshow(edges,plt.cm.gray)
    15. # sobel 水平方向边缘检测
    16. edgesh = filters.sobel_h(img)
    17. edgesh = img_as_ubyte(edgesh)
    18. plt.figure()
    19. plt.imshow(edgesh,plt.cm.gray)
    20. # sobel 竖直方向边缘检测
    21. edgesv = filters.sobel_v(img)
    22. edgesv = img_as_ubyte(edgesv)
    23. plt.figure()
    24. plt.imshow(edgesv,plt.cm.gray)
    25. '''**********opencv*************'''
    26. # sobel边缘检测
    27. edges = cv2.Sobel(img,cv2.CV_16S,1,1)
    28. # 浮点型转成uint8型
    29. edges = cv2.convertScaleAbs(edges)
    30. plt.figure()
    31. plt.imshow(edges,plt.cm.gray)
    32. # sobel 水平方向边缘检测
    33. edges = cv2.Sobel(img,cv2.CV_16S,1,0)
    34. edgesh = cv2.convertScaleAbs(edgesh)
    35. plt.figure()
    36. plt.imshow(edgesh,plt.cm.gray)
    37. # sobel 竖直方向边缘检测
    38. edges = cv2.Sobel(img,cv2.CV_16S,0,1)
    39. edgesv = cv2.convertScaleAbs(edgesv)
    40. plt.figure()
    41. plt.imshow(edgesv,plt.cm.gray)

    分别是原图 全方向solab算子

    竖直方向sobel检测 水平向sobel检测

    二阶微分算子

    简介
    上一小节介绍的Prewitt算子和Sobel算子都是近似对图像进行一阶导数的计算,只能提取出某个具体方向的边缘。由微积分的知识可知,一个函数的二阶导数为0时,代表此处的一阶导数取得极值,对应地也就表明原函数在此处的变化最大。比如著名的Sigmoid函数及其一阶导数、二阶导数的图像如下:

    因此往往还可以根据图像的二阶导数过零点的位置,来预测图像中变化最剧烈的地方,也许对应物体的边缘。与一阶微分算子不同,这些二阶微分算子对边缘的计算具有旋转不变性,也就是可以检测出各个方向上的边缘。

    Laplace算子

    Laplace算子可以近似计算出图像的二阶导数,具有旋转不变性,也就是可以检测出各个方向的边缘。

    对于数字图像,拉普拉斯算子可以简化为:21acd7d0c2ec7548d33bccdcff68d981.png

    也可以按卷积形式表示:
    b6bb83242ae7789f842eaa5f02b65703.png
    其中K=1,I=1时H(r,s)取下式,四方面模板:

    通过模板可以发现,当邻域内像素灰度相同时,模板的卷积运算结果为0;

    当中心像素灰度高于邻域内其他像素的平均灰度时,模板的卷积运算结果为正数;

    当中心像素的灰度低于邻域内其他像素的平均灰度时,模板的卷积的负数。

    对卷积运算的结果用适当的衰弱因子处理并加在原中心像素上,就可以实现图像的锐化处理。

    1. #coding=utf-8
    2. import cv2
    3. import numpy as np
    4. img = cv2.imread("test.jpg", 0)
    5. gray_lap = cv2.Laplacian(img,cv2.CV_16S,ksize = 3)
    6. dst = cv2.convertScaleAbs(gray_lap)
    7. cv2.imshow('laplacian',dst)
    8. cv2.waitKey(0)
    9. cv2.destroyAllWindows()

    对比:

    锐化卷积核计算的是中心像素减去周围像素的差值(中心权重为正,周边权重为负);而Laplace算子则是周围像素之和减去中心像素的差值(中心权重为负,周边权重为正)。

    LoG算子


    Laplace算子对噪声依然很敏感。因此常常先使用高斯滤波器对图像进行平滑操作,再使用Laplace算子计算二阶微分。二者结合称为LoG算子(Laplacian of Gaussian),该算子可以更加稳定地计算图像的二阶微分。

    基本理论

    高斯卷积函数定义为:

    而原始图像与高斯卷积定义为:

    因为:

    所以Laplacian of Gaussian(LOG)8579033794368a8de114e1b6e8ec8520.png可以通过先对高斯函数进行偏导操作,然后进行卷积求解。公式表示为:

    因此,我们可以LOG核函数定义为:

    9b73ad071499ec0e29d312a8ca606937.bmp

    1. import numpy as np
    2. import cv2
    3. from matplotlib import pyplot as plt
    4. #定义掩膜
    5. m1 = np.array([[0,0,-1,0,0],[0,-1,-2,-1,0],[-1,-2,16,-2,-1],[0,-1,-2,-1,0],[0,0,-1,0,0]]) #LoG算子模板
    6. img = cv2.imread("lena_1.tiff",0)
    7. #边缘扩充
    8. image = cv2.copyMakeBorder(img, 2, 2, 2, 2, borderType=cv2.BORDER_REPLICATE)
    9. # image = cv2.GaussianBlur(img,(3,3),4)
    10. rows = image.shape[0]
    11. cols = image.shape[1]
    12. temp = 0
    13. image1 = np.zeros(image.shape)
    14. for i in range(2,rows-2):
    15. for j in range(2,cols-2):
    16. temp = np.abs(
    17. (np.dot(np.array([1, 1, 1, 1, 1]), (m1 * image[i - 2:i + 3, j - 2:j + 3])))
    18. .dot(np.array([[1], [1], [1], [1], [1]])))
    19. image1[i,j] = int(temp)
    20. if image1[i, j] > 255:
    21. image1[i, j] = 255
    22. else:
    23. image1[i, j] = 0
    24. cv2.imshow("LoG",image1)
    25. cv2.waitKey(0)

    DoG算子

    LoG算子的计算量较大,因此有数学家发明了DoG(Difference of Gaussians)算子来近似LoG算子。DoG算子翻译为高斯差分算子,从名称上可以看出,就是使用两个标准差不同的高斯滤波器对图像进行滤波操作,再将滤波后的两个结果相减,最后的结果可以近似LoG算子。其中涉及到的数学理论较为复杂,在此暂不讨论。

    是灰度图像增强和角点检测(也叫特征点提取)的一种方法

    基本理论

    首先,高斯函数表示定义为:

    其次,两幅图像的高斯滤波表示为:

                    ​​​​​​​        ​​​​​​​        69c2a5e3db4ff93342a5be810c59e966.png

     

    最后,将上面滤波得到的两幅图像g1和g2相减得到:

    即:可以DOG表示为:

    7369786d0ff23371fee09ab26f963db4.png

    高斯模糊代码

    1. import cv2
    2. import numpy as np
    3. img = cv2.imread('D://fangzi.jpg')
    4. cv2.imshow('img',img)
    5. cv2.resizeWindow('img',640,480)
    6. #img_ = cv2.GaussianBlur(img,ksize=(9,9),sigmaX=0,sigmaY=0)
    7. img_ = cv2.GaussianBlur(img,(9,9),2)
    8. cv2.imshow('img_',img_)
    9. cv2.resizeWindow('img_',640,480)
    10. cv2.waitKey()

    效果:

    4fb8d4d9e587d56ec6d33d39c44acbc9.png

    DOG代码

    左边是原图和三种不同σ的高斯模糊后的图。右边是对高斯滤波后的图片(相邻状态下)依次进行两两相减可得到右边的三个高斯函数的差分图(简称DOG)。

    红色标记为当前像素点,黄色对应的像素点表示当前像素点邻接的点,共26(上图中27个黄点减去一个红点)个,如果该点(红点)是所有邻接像素点(黄点)的最大值或最小值,则红色标记对应的点为特征点。

    1. #include
    2. #include
    3. using namespace std;
    4. using namespace cv;
    5. int main()
    6. {
    7. Mat ori_img = imread("D://lena.png");
    8. Mat gray_img;
    9. cvtColor(ori_img, gray_img, CV_RGB2GRAY);
    10. imshow("gray", gray_img);
    11. gray_img.convertTo(gray_img, CV_32F);// float -像素是在0-1.0之间的任意值,这对于一些数据集的计算很有用,但是它必须通过将每个像素乘以255来转换成8位来保存或显示。
    12. Mat gauss1, gauss2;
    13. GaussianBlur(gray_img, gauss1, Size(5, 5), 0.3, 0.3);
    14. GaussianBlur(gray_img, gauss2, Size(5, 5), 0.4, 0.4);
    15. Mat DoG1, DoG2, DoG3;
    16. DoG1 = gauss1 - gauss2;
    17. imshow("DOG1", DoG1);
    18. GaussianBlur(gray_img, gauss1, Size(5, 5), 0.6, 0.6);
    19. GaussianBlur(gray_img, gauss2, Size(5, 5), 0.7, 0.7);
    20. DoG2 = gauss1 - gauss2;
    21. imshow("DOG2", DoG2);
    22. GaussianBlur(gray_img, gauss1, Size(5, 5), 0.7, 0.7);
    23. GaussianBlur(gray_img, gauss2, Size(5, 5), 0.8, 0.8);
    24. DoG3 = gauss1 - gauss2;
    25. imshow("DOG3", DoG3);
    26. for (int j = 1; j < gray_img.rows - 1; j++)
    27. {
    28. for (int i = 1; i < gray_img.cols - 1; i++)
    29. {
    30. if (DoG2.at<float>(j, i) < DoG2.at<float>(j - 1, i - 1) && DoG2.at<float>(j, i) < DoG2.at<float>(j - 1, i) &&
    31. DoG2.at<float>(j, i) < DoG2.at<float>(j - 1, i + 1) && DoG2.at<float>(j, i) < DoG2.at<float>(j, i - 1) && DoG2.at<float>(j, i) < DoG2.at<float>(j, i + 1) &&
    32. DoG2.at<float>(j, i) < DoG2.at<float>(j + 1, i - 1) && DoG2.at<float>(j, i) < DoG2.at<float>(j + 1, i) && DoG2.at<float>(j, i) < DoG2.at<float>(j + 1, i + 1)
    33. && DoG2.at<float>(j, i) < DoG1.at<float>(j, i) && DoG2.at<float>(j, i) < DoG1.at<float>(j - 1, i - 1) && DoG2.at<float>(j, i) < DoG1.at<float>(j - 1, i) &&
    34. DoG2.at<float>(j, i) < DoG1.at<float>(j - 1, i + 1) && DoG2.at<float>(j, i) < DoG1.at<float>(j, i - 1) && DoG2.at<float>(j, i) < DoG1.at<float>(j, i + 1) &&
    35. DoG2.at<float>(j, i) < DoG1.at<float>(j + 1, i - 1) && DoG2.at<float>(j, i) < DoG1.at<float>(j + 1, i) && DoG2.at<float>(j, i) < DoG1.at<float>(j + 1, i + 1)
    36. && DoG2.at<float>(j, i) < DoG3.at<float>(j, i) && DoG2.at<float>(j, i) < DoG3.at<float>(j - 1, i - 1) && DoG2.at<float>(j, i) < DoG3.at<float>(j - 1, i) &&
    37. DoG2.at<float>(j, i) < DoG3.at<float>(j - 1, i + 1) && DoG2.at<float>(j, i) < DoG3.at<float>(j, i - 1) && DoG2.at<float>(j, i) < DoG3.at<float>(j, i + 1) &&
    38. DoG2.at<float>(j, i) < DoG3.at<float>(j + 1, i - 1) && DoG2.at<float>(j, i) < DoG3.at<float>(j + 1, i) && DoG2.at<float>(j, i) < DoG3.at<float>(j + 1, i + 1))
    39. {
    40. //cout << DoG2.at(j, i);
    41. if (DoG2.at<float>(j, i) < -3)
    42. {
    43. circle(ori_img, Point(i, j), 3, CV_RGB(0, 0, 255));
    44. }
    45. }
    46. else
    47. if (DoG2.at<float>(j, i) > DoG2.at<float>(j - 1, i - 1) && DoG2.at<float>(j, i) > DoG2.at<float>(j - 1, i) &&
    48. DoG2.at<float>(j, i) > DoG2.at<float>(j - 1, i + 1) && DoG2.at<float>(j, i) > DoG2.at<float>(j, i - 1) && DoG2.at<float>(j, i) > DoG2.at<float>(j, i + 1) &&
    49. DoG2.at<float>(j, i) > DoG2.at<float>(j + 1, i - 1) && DoG2.at<float>(j, i) > DoG2.at<float>(j + 1, i) && DoG2.at<float>(j, i) > DoG2.at<float>(j + 1, i + 1)
    50. && DoG2.at<float>(j, i) > DoG1.at<float>(j, i) && DoG2.at<float>(j, i) > DoG1.at<float>(j - 1, i - 1) && DoG2.at<float>(j, i) > DoG1.at<float>(j - 1, i) &&
    51. DoG2.at<float>(j, i) > DoG1.at<float>(j - 1, i + 1) && DoG2.at<float>(j, i) > DoG1.at<float>(j, i - 1) && DoG2.at<float>(j, i) > DoG1.at<float>(j, i + 1) &&
    52. DoG2.at<float>(j, i) > DoG1.at<float>(j + 1, i - 1) && DoG2.at<float>(j, i) > DoG1.at<float>(j + 1, i) && DoG2.at<float>(j, i) > DoG1.at<float>(j + 1, i + 1)
    53. && DoG2.at<float>(j, i) > DoG3.at<float>(j, i) && DoG2.at<float>(j, i) > DoG3.at<float>(j - 1, i - 1) && DoG2.at<float>(j, i) > DoG3.at<float>(j - 1, i) &&
    54. DoG2.at<float>(j, i) > DoG3.at<float>(j - 1, i + 1) && DoG2.at<float>(j, i) > DoG3.at<float>(j, i - 1) && DoG2.at<float>(j, i) > DoG3.at<float>(j, i + 1) &&
    55. DoG2.at<float>(j, i) > DoG3.at<float>(j + 1, i - 1) && DoG2.at<float>(j, i) > DoG3.at<float>(j + 1, i) && DoG2.at<float>(j, i) > DoG3.at<float>(j + 1, i + 1))
    56. {
    57. if (DoG2.at<float>(j, i) > 3)
    58. {
    59. circle(ori_img, Point(i, j), 3, CV_RGB(255, 0, 0));
    60. }
    61. }
    62. }
    63. }
    64. imshow("result", ori_img);
    65. waitKey(0);

    效果:

    输入的灰度图:9ab99c706afb8854c0ecd1f30e4d1e7b.png

    1:fa51cbb8a6c36853d79488e1a5e26f1a.png

    2:e55cfe753caf1ef33a0d8df51b61b23b.png

    3:bc135a76127f028931175f13353186d6.png

    result:

  • 相关阅读:
    2.09_python+Django+mysql实现pdf转word项目_项目开发-创建路由
    【21天学习挑战赛】荷兰国旗问题到快速排序
    【C++】神奇字符串(力扣481)
    150. 以前编写好能够正常运行的 SAP UI5 代码,几个月后忽然不能运行了该怎么办?
    mcu日志输出的一种方法
    容器化部署gitlab、jenkins,jenkins应用示例
    java计算机毕业设计客户关系智能管理系统源码+mysql数据库+系统+lw文档+部署
    线程池的使用及ThreadPoolExecutor源码分析
    2024年成都高新区支持企业申报国家、省级、市级大数据产业发展、新一代信息技术与制造业融合发展、工业互联网推广应用等试点示范项目申报对象条件和奖补
    0 基础 Java 自学之路(2021年最新版)
  • 原文地址:https://blog.csdn.net/qq_62932195/article/details/126337971