Python opencv image processing basic summary (7) image segmentation based on watershed algorithm

1. Principle

1. Principle of Watershed Algorithm

  • Any gray-scale image can be regarded as a topological plane, areas with high gray values ​​can be regarded as mountains, and areas with low gray values ​​can be regarded as valleys. We pour water of different colors into each valley. As the water level rises, the waters of different valleys will meet and converge. To prevent the waters of different valleys from converging, we need to build dams where the water converges. I kept irrigating and building dams until all the peaks were submerged. The dam we built is the image segmentation, which is the principle behind the watershed algorithm.
  • OpenCV uses a mask-based watershed algorithm. In this algorithm, we have to set those valley points that will converge, and those that will not. This is an interactive image segmentation, all we have to do is to label the objects we already know. If a certain area is definitely the foreground or object, mark it with a certain color (or gray value) label. If a certain area is definitely not an object but a background, use another color label to mark it. The remaining areas that cannot be determined as foreground or background are marked with 0, which is our label. Then implement the watershed algorithm. Every time we fill water, our label will be updated. When two labels of different colors meet, we build a dam until all the peaks are submerged, and finally we get the value of the boundary object (the dam) -1.

2. Distance transformation

  • The basic meaning of distance transformation is to calculate the distance from a non-zero pixel in an image to the nearest zero pixel, that is, the shortest distance to the zero pixel
  • The most common distance transformation algorithm is realized by continuous erosion operation. The stopping condition of the erosion operation is that all foreground pixels are completely
  • corrosion. In this way, according to the order of corrosion, we get the distance from each foreground pixel to the foreground center skeleton pixel
  • According to the distance value of each pixel, it is set to different gray value. This completes the distance transformation of the binary image

3. The usage of opencv related functions

cv2.distanceTransform(src, distanceType, maskSize, dst=None, dstType=None)
  • src: input binary image
  • distanceType: How to calculate distance
  • maskSize: mask size
cv2.connectedComponents(image, labels=None, connectivity=None, ltype=None)
  • image: input 8-bit single-channel image
  • labels: output label map
  • connectivity: connectivity, the default is 8, you can also take 4
  • Itype: output tag type, the default is CV_32S, it can also be CV_16U
cv2.watershed(image, markers)
  • image: input image
  • markers: markers

2. Distance-based watershed segmentation process

Insert picture description here

  • If the input image is noisy, denoise first
  • Convert to grayscale image
  • Binary processing, morphological operation
  • Distance transformation
  • Find seeds and generate markers
  • Implement the watershed algorithm and output the segmented image

Three, python code implementation

import cv2 as cv
import numpy as np


def watershed_algorithm(image):
    # 边缘保留滤波EPF  去噪
    blur = cv.pyrMeanShiftFiltering(image,sp=10,sr=100)
    # 转成灰度图像
    gray = cv.cvtColor(blur, cv.COLOR_BGR2GRAY)
    # 得到二值图像   自适应阈值
    ret, binary = cv.threshold(gray, 0, 255, cv.THRESH_BINARY | cv.THRESH_OTSU)
    cv.imshow('binary image', binary)

    # 形态学操作   获取结构元素  开操作
    kernel = cv.getStructuringElement(cv.MORPH_RECT, (3, 3))
    opening = cv.morphologyEx(binary, cv.MORPH_OPEN, kernel=kernel, iterations=2)
    # 确定区域
    sure_bg = cv.dilate(opening, kernel, iterations=3)
    # cv.imshow('mor-opt', sure_bg)

    # 距离变换
    dist = cv.distanceTransform(opening, cv.DIST_L2, 3)
    dist_out = cv.normalize(dist, 0, 1.0, cv.NORM_MINMAX)
    # cv.imshow('distance-', dist_out * 50)
    ret, surface = cv.threshold(dist_out, dist_out.max() * 0.6, 255, cv.THRESH_BINARY)
    # cv.imshow('surface-markers', surface)

    surface_fg = np.uint8(surface)    # 转成8位整型
    unkonown = cv.subtract(sure_bg, surface_fg)        # 找到位置区域
    # Marker labelling
    ret, markers = cv.connectedComponents(surface_fg)  # 连通区域
    print(ret)

    # 分水岭变换
    # Add one to all labels so that sure background is not 0, but 1
    markers = markers + 1
    # Now, mark the region of unknown with zero
    markers[unkonown == 255] = 0
    # 实施分水岭算法了。标签图像将会被修改,边界区域的标记将变为 -1
    markers = cv.watershed(image, markers=markers)
    image[markers == -1] = [0, 0, 255]      # 被标记的区域   设为红色
    cv.imshow('result', image)


src = cv.imread(r'./test/042.png')
src = cv.resize(src, None, fx=0.5, fy=0.5)
cv.imshow('input image', src)
watershed_algorithm(src)
cv.waitKey(0)
cv.destroyAllWindows()

The operation effect is as follows:

Insert picture description here
Insert picture description here

Insert picture description here

Insert picture description here

Guess you like

Origin blog.csdn.net/fyfugoyfa/article/details/108150378