5 阈值分割
5.1 基础理论
- 二进制阈值化:大于等于阈值的为设置的最大值(假设为255),小于阈值的为0;
- 反二进制阈值化:大于等于阈值的为设置的0,小于阈值的为最大值(假设为255);
- 截断阈值化:大于等于阈值的为设置为该阈值,小于阈值的为0;
- 反阈值化为0:大于等于阈值的为0,小于阈值的不变;
- 阈值化为0:大于等于阈值的不变,小于阈值的为0;
- retval,dst = cv2.threshold(src,thresh,maxval,type) #thresh ==retval阈值
5.2 threhold函数
# 1. 二进制阈值化cv2.THRESH_BINARY 白的更白,黑的更黑
import cv2
img = cv2.imread('')
r,img1 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# 2. 反二进制阈值化cv2.THRESH_BINARY_INV 白的变黑,黑的变白
r,img2 = cv2.threshold(img,127,255,cv2.THRESH_BINARY)
# 3. 截断阈值化cv2.THRESH_TRUNK 把图像中亮的变暗
r,img3 = cv2.threshold(img,127,255,cv2.THRESH__TRUNK)
# 4. 反阈值化为0 cv2.THRESH_TOZERO_INV 把图像中亮的变非常暗
r,img4 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO_INV)
# 5. 阈值化为0 cv2.THRESH_TOZERO 把图像中亮的不变,暗得更暗
r,img5 = cv2.threshold(img,127,255,cv2.THRESH_TOZERO)
cv2.imshow('img',img)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.imshow('img3',img3)
cv2.imshow('img4',img4)
cv2.imshow('img5',img5)
cv2.waitKey(0)
cv2.destroyAllWindows()
5.3 直方图阈值
- 算法描述:根据图像灰度直方图,人工寻找阈值。
- 算法特点:适用双峰图像
- plt.hist(img.ravel(), 256, [0,256])
- cv.threshold(img, th, 255, cv.THRESH_BINARY)
'''
'''
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./2.png')
plt.hist(img.ravel(), 256, [0, 256])
plt.show()
plt.hist(img.flatten(), bins=np.arange(-0.5, 256, 1), color='green')
plt.show()
_, img_bin = cv2.threshold(img, 125, 255, cv2.THRESH_BINARY)
plt.imshow(img_bin)
5.4 三角法阈值
- 算法思想:几何法,适用单峰图像
- th, img_bin = cv.threshold(img, th, 255, cv.THRESH_TRIANGLE)
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./2.png')
plt.hist(img.ravel(), 256, [0,256], color='blue')
plt.show()
th, img_bin = cv2.threshold(img, -1, 255, cv2.THRESH_TRIANGLE)
plt.imshow(img_bin)
5.5 迭代法阈值及实现
- 选取初始分割阈值,通常选图像灰度平均值T。
- 根据T将图像像素分为背景和前景,分别求出平均灰度T0和T1。
- 计算新的阈值T_new= (T0+T1)/2
- 迭代循环,直到T_new==T
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./2.png')
T = img.mean()
while True:
t1 = img[img > T].mean()
t2 = img[img <= T].mean()
t = (t1 + t2) / 2
if abs(t - T) < 1:
break
T = t
T = int(T)
print(f'Best threshold = {T}')
_, img_bin = cv.threshold(img, T, 255, cv.THRESH_BINARY)
plt.imshow(img_bin)
5.6 大津法(OTSU)阈值及实现
- 给定阈值T,根据阈值吧图像分为前景和背景,前背景占总像素的比例p0、p1,平均灰度为m0,m1。上述变量满足以下数学关系
- th, img_bin = cv.threshold(img, th, 255, cv.THRESH_OTSU)
p_0+ p_1= 1
\bar{m} = p_0*m_0+ p_1*m_1
\sigma^2 = p_0*(m_0-\bar{m} )^2+ p_1*(m_1-\bar{m} )^2
\sigma^2 = p_0*p_1(m_0-m_1)^2
所有的灰度,找出使得方差最大的阈值T。
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./2.png')
th, img_bin = cv2.threshold(img, 0, 255, cv2.THRESH_OTSU)
plt.imshow(img_bin)
python实现大津法(OTSU)阈值
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./2.png')
T = 1
Sigma = 0
for t in range(0, 255):
bg = img[img <= t]
obj = img[img > t]
p0 = bg.size / img.size
p1 = obj.size / img.size
m0 = 0 if bg.size == 0 else bg.mean()
m1 = 0 if obj.size == 0 else obj.mean()
sigma = p0 * p1 * (m0 - m1)**2
if sigma > Sigma:
T = t
Sigma = sigma
print(T)
_, img_bin = cv2.threshold(img, T, 255, cv2.THRESH_BINARY)
plt.imshow(img_bin)
5.7 自适应阈值及实现
自适应阈值的本质时局部二值化
- 对某个像素值s,取其周围n*n区域,求区域均值或高斯加权值T。
- 对8位图像,如果S>T,则该像素点二值化为255,否则为0。
- 优化:S>T-C or S>(1-a)T,C,a为超参数。
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('./2.png')
plt.hist(img.ravel(), bins=256)
plt.show()
_, img_thres = cv2.threshold(img, 100, 255, cv2.THRESH_BINARY)
show(img_thres)
img_adapt = cv2.adaptiveThreshold(img, 255, cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 21, 12)
show(img_adapt)
plt.imshow(img_adapt)
python实现自适应阈值分割
# 1 S>T-C
import cv2
import numpy as np
import matplotlib.pyplot as plt
winSize = 21
img_blur = cv.blur(img, (winSize, winSize))
img_bin = (np.float32(img_blur) - np.float32(img) < 8).astype(np.uint8) * 255
plt.imshow(img)
# 2 S>(1-a)T
import cv2
import numpy as np
import matplotlib.pyplot as plt
winSize = 21
ratio = 0.15
img_blur = cv.blur(img, (winSize, winSize))
thresh = (1 - ratio) * img_blur
img[img < thresh] = 0
img[img >= thresh] = 255
plt.imshow(img)
6 图像平滑处理
# 卷积
cv2.filter2D(img,-1,kernel)
6.1 均值滤波
- 任意一点的像素值,都是周围NN个像素值的均值。k=1/(NN)* np.ones([n,n])
- dst = cv2.blur(img,k.shape)
- 效用:原图有杂质,可以平滑掉。
import cv2
img1 = cv2.imread('')
img2 = cv2.blur(img1,(5,5))
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.2 方框滤波
- dst = cv2.boxFilter(img,目标图像深度,k.shape,normalize属性)
- 目标图像深度=-1,表示和原始图像深度一致。
- normalize = True,k=1/(NxN)* np.ones([n,n])
- normalize = True,k= np.ones([n,n])
import cv2
img1 = cv2.imread('')
img2 = cv2.cv2.boxFilter(img1,-1,(5,5),normalize = 1)
img3 = cv2.cv2.boxFilter(img1,-1,(5,5),normalize = 0) # 白色
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.imshow('img3',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.3 高斯滤波
- 让临近的像素有更大的权值。
- dst = cv2.GaussianBlur(img,k.shape,sigmax) # x 方向的方差,控制权重
- sigmax=0时,sigmax=0.3*((ksize-1)*0.5-1)+0.8,sigmax是方差。
import cv2
img1 = cv2.imread('')
img2 = cv2.GaussianBlur(img1,(3,3),0.5)
img3 = cv2.GaussianBlur(img1,(3,3),6)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.imshow('img3',img3)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.4 中值滤波
- 让临近元素按照大小排列,去中间位置的元素的值作为种植滤波的像素值。
- dst = cv2.medianBlur(img,k.shape) # k 必须是大于1的奇数。
import cv2
img1 = cv2.imread('')
img2 = cv2.medianBlur(img1,3)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
6.5 双边滤波
- 考虑空间因素和数值差异
cv.bilateralFilter(img, -1, sigmaColor=50, sigmaSpace=3)
import cv2
img1 = cv2.imread('')
img2 = cv2.bilateralFilter(img, -1, sigmaColor=50, sigmaSpace=3)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
Python实现双边滤波
import numpy as np
import matplotlib.pyplot as plt
import cv2
def get_C(sigmad, n): # 空间差异
C = np.zeros((n,n))
# 0, 1, 2
x = np.array([n//2, n//2])
for i in range(n):
for j in range(n):
ksi = np.array([i, j])
C[i,j] = np.exp(-0.5 * (np.linalg.norm(ksi - x) / sigmad)**2)
C /= C.sum() # 归一化
return C
def get_S(f, sigmar, n): # 数值差异
f = np.float64(f)
S = np.exp(-0.5 * ((f - f[n//2, n//2]) / sigmar)**2)
S /= S.sum()
return S
img = cv2.imread('', 0)
sigmar = 50
sigmad = 3
n = 11
h, w = img.shape
img2 = np.zeros_like(img)
C = get_C(sigmad, n)
for i in range(h-n):
for j in range(w-n):
f = img[i:i+n, j:j+n]
S = get_S(f, sigmar, n) # 数值权重
K = C * S # 空间权重*数值权重
K /= K.sum()
img2[i,j] = (f * K).sum()
plt.imshow(img)
plt.imshow(img2)
6.6 双边滤波
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('')
kernel = np.ones((5,5), dtype=np.float32)
kernel /= kernel.sum()
img_filter = cv2.filter2D(img, -1, kernel)
plt.imshow(np.hstack([img, img_filter]))
7 图像形态学
7.1 图像腐蚀
- 针对二值图像,如果核里的点都是1,核的值为1;核里面有一个0,核中心值为0.
- 效果:图像变小,周围的杂质磨平,理解为提纯。
- dst = cv2.erode(img,kernel,iterations)
import cv2
import numpy as np
kernel = np.ones((5,5),np.uint8)
img1 = cv2.imread('')
img2 = cv2.erode(img1,kernel,6)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
7.2 图像膨胀
- 针对二值图像,如果核里的点都是0,核的值为0;核里面有一个1,核中心值为
- 效果:把腐蚀操作后的图片膨胀,可以保持去噪后的图片。
- dst = cv2.dilate(img,kernel,iterations)
import cv2
import numpy as np
img1 = cv2.imread('')
kernel = np.ones((5,5),np.uint8)
img2 = cv2.dilate(img1,kernel,6)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
7.3 开运算
- 先腐蚀后膨胀,去掉外部毛刺
- img2 = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
import cv2
import numpy as np
img1 = cv2.imread('')
kernel = np.ones((5,5),np.uint8)
img2 = cv2.morphologyEx(img,cv2.MORPH_OPEN,kernel)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
7.4 闭运算
- 先膨胀后腐蚀,去除内部小黑点
- img2 = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
import cv2
import numpy as np
img1 = cv2.imread('')
kernel = np.ones((5,5),np.uint8)
img2 = cv2.morphologyEx(img,cv2.MORPH_CLOSE,kernel)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
7.5 梯度运算
- 梯度 = 膨胀-腐蚀,得到轮廓
- img2 = cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel)
import cv2
import numpy as np
img1 = cv2.imread('')
kernel = np.ones((5,5),np.uint8)
img2 = cv2.morphologyEx(img,cv2.MORPH_GRADIENT,kernel)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
7.6 礼帽操作
- 礼帽 = 原始图像-开运算 ,得到外部毛刺
- img2 = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
import cv2
import numpy as np
img1 = cv2.imread('')
kernel = np.ones((5,5),np.uint8)
img2 = cv2.morphologyEx(img,cv2.MORPH_TOPHAT,kernel)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()
7.7 黑帽图像处理
- 黑帽图像 = 闭运算 - 原始图像 ,得到内部毛刺
- img2 = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
import cv2
import numpy as np
img1 = cv2.imread('')
kernel = np.ones((5,5),np.uint8)
img2 = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT,kernel)
cv2.imshow('img1',img1)
cv2.imshow('img2',img2)
cv2.waitKey(0)
cv2.destroyAllWindows()