参考:
1、http://docs.opencv.org/3.3.0/ 官方文档api
2、http://docs.opencv.org/3.3.0/d6/d00/tutorial_py_root.html 官方英文教程
3、https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
4、https://github.com/makelove/OpenCV-Python-Tutorial# 进阶教程
5、https://docs.opencv.org/3.3.0/index.html 官方英文教程
6、https://github.com/abidrahmank/OpenCV2-Python-Tutorials
7、https://www.learnopencv.com/
8、http://answers.opencv.org/questions/ OpenCV论坛
9、https://github.com/opencv/opencv 官方github
10、https://github.com/abidrahmank/OpenCV2-Python-Tutorials
注:安装的版本 opencv_python-3.3.0-cp36-cp36m-win_amd64.whl
参考:https://opencv-python-tutroals.readthedocs.io/en/latest/py_tutorials/py_tutorials.html
用分水岭算法进行图像分割
目标
- In this chapter,
-
- We will learn to use marker-based image segmentation using watershed algorithm
- We will see: cv2.watershed()
代码
下面我们将看到一个关于如何使用距离变换和分水岭来分割相互接触的对象的例子。考虑下面的硬币图像,硬币相互接触。 即使你的阈值,它会互相接触。
我们从硬币的近似估计开始。 为此,我们可以使用Otsu’s二值化。
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('coins.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) cv2.imshow('ret',thresh) cv2.waitKey(0) cv2.destroyAllWindows()
现在我们需要去除图像中的任何小的白色噪音。 为此,我们可以使用形态开放。 要删除物体中的任何小孔,我们可以使用形态关闭。 所以,现在我们知道,对象中心附近的区域是前景,远离对象的区域是背景。 只有我们不确定的地区是硬币的边界区域。
所以我们需要提取我们确定他们是硬币的区域。 侵蚀去除边界像素。 所以无论如何,我们可以肯定是硬币。 如果对象没有互相接触,那将会奏效。 但是由于他们互相接触,另一个好的选择是找到距离变换并应用适当的阈值。 接下来我们需要找到我们确定他们不是硬币的区域。 为此,我们扩大了结果。 扩张将对象边界增加到背景。 这样,我们可以确保结果背景中的任何区域都是真正的背景,因为边界区被删除。 见下图。
剩下的区域是我们没有任何想法的区域,无论是硬币还是背景。 流域算法应该找到它。 这些区域通常位于前景和背景相遇的硬币的边界(甚至两个不同的硬币相遇)。 我们称之为边界。 可以从sure_bg区域中减去sure_fg区域获得。
# noise removal kernel = np.ones((3,3),np.uint8) opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2) # 形态开运算 # sure background area sure_bg = cv2.dilate(opening,kernel,iterations=3) # Finding sure foreground area dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5) ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0) # Finding unknown region sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg,sure_fg)
看到结果。 在阈值图像中,我们得到一些硬币区域,我们确定硬币,现在它们已经分离。 (在某些情况下,您可能只对前景分割感兴趣,而不是分离相互接触的对象,在这种情况下,您不需要使用距离变换,只是侵蚀就足够了。侵蚀只是提取前景区域的另一种方法, 所有。)
现在我们知道哪些是硬币区域,哪些是背景和全部。 所以我们创建标记(它是一个与原始图像大小相同的数组,但是使用int32数据类型),并标记它内部的区域。 我们知道的区域(无论是前景还是背景)都被标记为任何正整数,但是不同的整数,我们不知道的区域只能保持为零。 为此,我们使用cv2.connectedComponents()。 它用0标记图像的背景,然后其他对象用从1开始的整数标记。
但是我们知道如果背景被标记为0,则分水岭将其视为未知区域。 所以我们想用不同的整数来标记它。 相反,我们将用未知的区域标记未知区域。
# Marker labelling ret, markers = cv2.connectedComponents(sure_fg) # 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[unknown==255] = 0
现在我们的标记准备好了 现在是最后一步,应用流域的时候了。 然后标记图像将被修改。 边界区域将被标记为-1。
markers = cv2.watershed(img,markers) img[markers == -1] = [255,0,0]
Additional Resources
- CMM page on Watershed Tranformation
import numpy as np import cv2 from matplotlib import pyplot as plt img = cv2.imread('coins.jpg') gray = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY) ret, thresh = cv2.threshold(gray,0,255,cv2.THRESH_BINARY_INV+cv2.THRESH_OTSU) # noise removal kernel = np.ones((3,3),np.uint8) opening = cv2.morphologyEx(thresh,cv2.MORPH_OPEN,kernel, iterations = 2) # 形态开运算 # sure background area sure_bg = cv2.dilate(opening,kernel,iterations=3) # Finding sure foreground area dist_transform = cv2.distanceTransform(opening,cv2.DIST_L2,5) ret, sure_fg = cv2.threshold(dist_transform,0.7*dist_transform.max(),255,0) # Finding unknown region sure_fg = np.uint8(sure_fg) unknown = cv2.subtract(sure_bg,sure_fg) # Marker labelling ret, markers = cv2.connectedComponents(sure_fg) # 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[unknown==255] = 0 markers = cv2.watershed(img,markers) img[markers == -1] = [255,0,0] cv2.imshow('img',img) cv2.waitKey(0) cv2.destroyAllWindows()