实验三:图像分割

实验目的

  1. 了解图像分割的基本原理,并利用图像分割算法进行图像分割处理;
  2. 掌握数学形态学的基本运算。

实验内容

  1. 利用类间方差阈值算法实现图像的分割处理;
  2. 利用形态学处理进行处理结果修正。

实验环境

  • macOS 14.1 Sonoma(Apple M1 PRO)
  • PyCharm/Python3.10
  • OpenCV-Python

实验原理及过程

  1. 对输入图像进行平滑处理,以减小噪声对分割处理的影响,比较中值滤波范围取不同值时对图像滤波的效果

    • 中值滤波是基于排序统计理论的一种能有效抑制噪声的非线性信号处理技术,中值滤波的基本原理是把数字图像或数字序列中一点的值用该点的一个邻域中各点值的中值代替,让周围的像素值接近真实值,从而消除孤立的噪声点。方法是用某种结构的二维滑动模板,将板内像素按照像素值的大小进行排序, 生成单调上升(或下降)的为二维数据序列。

    • 二维中值滤波输出为:

      g(x,y)=Mid{f(xk,yl)k,lW}g(x,y)=Mid\{f(x-k,y-l)|k,l\in W\}

    • 代码实现:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      def median_filter(image, kernel_size):
      """
      中值滤波
      :param image: 原图像
      :param kernel_size: 窗口大小
      :return:
      """
      median_filtered = cv2.medianBlur(image, kernel_size)
      return median_filtered
    • 生成并输出图像:

      1
      2
      3
      4
      5
      6
      7
      8
      fig = figure(figsize=(8, 2))
      filtered_images = list(map(median_filter, [gray_image] * 4, [3, 5, 7, 9]))
      for i in range(4):
      imgWrite(filtered_images[i], f"filtered_image{i + 1}.png")
      subplot2grid((1, 4), loc[i])
      imshow(filtered_images[i], cmap='gray')
      tight_layout()
      savefig("./实验三:图像分割/filtered_images.png", dpi=300)
      • 窗口大小依次为3,5,7,9

        filtered_images

  2. 利用类间方差阈值算法对滤波处理后图像进行分割处理,获取分割图像;

    • 类间方差阈值算法是按图像的灰度特性,将图像分成背景和目标两部分。背景和目标间的类间方差越大,说明构成图像的两部分差别越大,当部分目标错分为背景或部分背景错分为目标都会导致两部分差别变小。因此,使类间方差最大的分割意味着错分概率最小。

    • 代码实现:

      1
      2
      3
      4
      5
      6
      7
      8
      def otsu_threshold(image):
      """
      使用Otsu's二值化算法
      :param image: 原图
      :return: 阈值, 分割后图像
      """
      threshold, thresholded = cv2.threshold(image, 0, 255, cv2.THRESH_BINARY + cv2.THRESH_OTSU)
      return threshold, thresholded
    • 生成并输出阈值和图像:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      otsu_s = list(map(otsu_threshold, filtered_images))
      thresholds = [s[0] for s in otsu_s]
      segmented_images = [s[1] for s in otsu_s]
      fig = figure(figsize=(8, 2))
      for i in range(4):
      imgWrite(segmented_images[i], f"segmented_image{i + 1}.png")
      subplot2grid((1, 4), loc[i])
      imshow(segmented_images[i], cmap='gray')
      tight_layout()
      savefig("./实验三:图像分割/segmented_images.png", dpi=300)
      print(f"optimal threshold:{thresholds}")
      • 对应中值滤波窗口不同大小的最佳阈值为:
        image-20231207125327189
      • 对应中值滤波窗口不同大小的分割后图像为:

        segmented_images

  3. 利用数学形态学中的腐蚀和膨胀运算处理,剔除分割处理结果中的一些细小的残余误分割点,在进行腐蚀和膨胀运算时可采用半径为 rr的圆形结构元素,注意比较选取不同 rr值时的处理结果(rr 分别取 3、5、7)。

    • 腐蚀和膨胀运算处理:形态学图像处理是在图像中移动一个结构元素,然后将结构元素与下面的二值图像进行交、并等集合运算;先腐蚀后膨胀的过程称为开运算。它具有消除细小物体,在纤细处分离物体和平滑较大物体边界的作用。先膨胀后腐蚀的过程称为闭运算。它具有填充物体内细小空洞,连接邻近物体和平滑边界的作用

    • 代码实现:

      1
      2
      3
      4
      5
      6
      7
      8
      9
      10
      11
      12
      13
      14
      def morphological_operations(image, radius):
      """
      形态学操作:腐蚀、膨胀
      :param image: 原图像
      :param radius: 半经
      :return:
      """
      # 定义圆形结构元素
      kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (2 * radius + 1, 2 * radius + 1))
      # 进行腐蚀操作
      eroded = cv2.erode(image, kernel)
      # 进行膨胀操作
      dilated = cv2.dilate(eroded, kernel)
      return dilated
    • 使用中值滤波窗口大小为3的分割后图像进行腐蚀与膨胀运算:

      1
      2
      3
      4
      5
      6
      7
      processed_images = list(map(morphological_operations, [segmented_images[0]] * 3, [3, 5, 7]))
      for i in range(3):
      imgWrite(processed_images[i], f"processed_images{i + 1}.png")
      subplot2grid((1, 3), loc[i])
      imshow(processed_images[i], cmap='gray')
      tight_layout()
      savefig("./实验三:图像分割/processed_images.png", dpi=300)
      • 半径分别为3、5、7时的处理结果:

        processed_images

  • 使用中值滤波窗口大小为3,处理半径为3的系列组图如下:

    images_radius3