系列文章
import cv2
import numpy as np
import matplotlib.pyplot as plt
%matplotlib inline
学习整理 Python下opencv使用笔记(五)(图像的平滑与滤波)
对于2D图像可以进行低通或者高通滤波操作,低通滤波(LPF)有利于去噪,模糊图像,高通滤波(HPF)有利于找到图像边界。
cv2.filter2D
cv2.blur
cv2.GaussianBlur
cv2.medianBlur
cv2.bilateralFilter
,是高斯模糊的一个高级版本。模糊化不仅可以溶解噪声,而且还会平滑边缘。而双边滤波器能在去除噪声的同时保持边缘锐化。这是由于它不仅使用高斯分布值,还同时考虑了距离和像素值的差异。因此,需要指定 sigmaSpace 和 sigmaColor 这两个参数。img = cv2.imread('C://Users/13663//Desktop/1.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Plot the image with different kernel sizes
kernels = [3, 5, 11, 17]
fig, axs = plt.subplots(nrows = 1, ncols = 4, figsize = (20, 20))
for ind, s in enumerate(kernels):
img_blurred = cv2.blur(img, ksize = (s, s))
ax = axs[ind]
ax.set_title('kernel {i}'.format(i=s),
fontsize =24, color = 'white')
ax.imshow(img_blurred)
ax.axis('off')
plt.show()
img = cv2.imread('C://Users/13663//Desktop/1.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Plot the image with different kernel sizes
kernels = [3, 5, 11, 17]
fig, axs = plt.subplots(nrows = 2, ncols = 2, figsize = (15, 9))
for ind, s in enumerate(kernels):
img_blurred = cv2.blur(img, ksize = (s, s))
ax = axs[ind//2][ind%2]
ax.set_title('kernel {i}'.format(i=s),
fontsize =24, color = 'white')
ax.imshow(img_blurred)
ax.axis('off')
plt.show()
img = cv2.imread('C://Users/13663//Desktop/1.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
# Blur the image
img_0 = cv2.blur(img, ksize = (7, 7))
img_1 = cv2.GaussianBlur(img, ksize = (7, 7), sigmaX = 0)
img_2 = cv2.medianBlur(img, 7)
img_3 = cv2.bilateralFilter(img, 7, sigmaSpace = 75, sigmaColor =75)
# Plot the images
images = [img_0, img_1, img_2, img_3]
names = ['Mean Blur','Gaussian Blur','Median Blur','Bilateral Filter']
fig, axs = plt.subplots(nrows = 2, ncols = 2, figsize = (15, 9))
num = 0
for ind, p in enumerate(images):
ax = axs[ind//2][ind%2]
ax.set_title('{x}'.format(x=names[num]),
fontsize =20, color = 'white')
ax.imshow(p)
ax.axis('off')
num+=1
plt.show()
图像的阈值化就是利用图像像素点分布规律,设定阈值进行像素点分割,进而得到图像的二值图像。我们需要设置阈值和最大值,然后据此相应地进行像素值转换。核心函数如下:
ret, dst = cv2.threshold(src, thresh, maxval, type)
cv2.THRESH_BINARY
cv2.THRESH_BINARY_INV
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INV
cv2.THRESH_TRUNC
也可以通过 BIF 查看,eg:help(cv2.threshold)
,返回的 ret
经测试发现同 thresh
img = cv2.imread('C://Users/13663//Desktop/5.png')
#img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Thresholding
_, thresh_0 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
_, thresh_1 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
_, thresh_2 = cv2.threshold(img, 127, _, cv2.THRESH_TRUNC)
_, thresh_3 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
_, thresh_4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
names = ['original image','binary T','zero T','trunc T','binary inv T','zero inv T']
# Plot the images
images = [img, thresh_0, thresh_1, thresh_2, thresh_3, thresh_4]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (13, 6))
for ind, p in enumerate(images):
ax = axs[ind//3, ind%3]
ax.set_title(names[ind],fontsize=14, color='white')
ax.imshow(p,cmap='gray')
ax.axis("off")
plt.show()
看了一些关于 cv2.THRESH_TRUNC
的介绍,上面的公式也显示,如果大于 Threshold,像素值就会被赋值为 Threshold,可实验发现,大于 Threshold 会赋值为 255,不晓得是不是因为 opencv 版本的问题,或者是 coding 的问题!如果有水平较高的读者有幸看到了这篇博客,可以尝试回答一下这个问题!
下面看看比较轻松的例子
img = cv2.imread('C://Users/13663//Desktop/3.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Thresholding
_, thresh_0 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY)
_, thresh_1 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO)
_, thresh_2 = cv2.threshold(img, 127, 255, cv2.THRESH_TRUNC)
_, thresh_3 = cv2.threshold(img, 127, 255, cv2.THRESH_BINARY_INV)
_, thresh_4 = cv2.threshold(img, 127, 255, cv2.THRESH_TOZERO_INV)
names = ['original image','binary T','zero T','trunc T','binary inv T','zero inv T']
# Plot the images
images = [img, thresh_0, thresh_1, thresh_2, thresh_3, thresh_4]
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (13, 6))
for ind, p in enumerate(images):
ax = axs[ind//3, ind%3]
ax.set_title(names[ind],fontsize=14, color='white')
ax.imshow(p,cmap='gray')
ax.axis("off")
plt.show()
只取一个阈值并将其应用于图像的所有部分并不能满足我们的全部需求。如果我们有一张在多个不同区域亮度差异较多的图片这种情况,将一个值应用于整个图像一般不利于我们的图像处理任务。其对应更好的方法是对图像的每个部分使用不同的阈值。对应这种情况还有另外一种阈值化技术称为自适应阈值化(Adaptive thresholding)。通过对图像邻域内阈值的计算,可以得到不同光照条件下的较好结果。
核心函数 dst = cv2.adaptiveThreshold(src, maxval, thresh_type, type, Block Size, C)
cv2.ADAPTIVE_THRESH_MEAN_C
领域内均值cv2.ADAPTIVE_THRESH_GAUSSIAN_C
领域内像素点加权和,权重为一个高斯窗口cv2.THRESH_BINARY
cv2.THRESH_BINARY_INV
cv2.THRESH_TRUNC
cv2.THRESH_TOZERO
cv2.THRESH_TOZERO_INV
for example
# Convert the image to grayscale
img = cv2.imread('C://Users/13663//Desktop/3.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Adaptive Thresholding
_, thresh_binary = cv2.threshold(img, thresh = 127, maxval = 255, type = cv2.THRESH_BINARY)
adap_mean_2 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 7, 2)
adap_mean_2_inv = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY_INV, 7, 2)
adap_mean_8 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_MEAN_C,
cv2.THRESH_BINARY, 7, 8)
adap_gaussian_8 = cv2.adaptiveThreshold(img, 255,
cv2.ADAPTIVE_THRESH_GAUSSIAN_C,
cv2.THRESH_BINARY, 7, 8)
# Plot the images
images = [img, thresh_binary, adap_mean_2, adap_mean_2_inv,
adap_mean_8, adap_gaussian_8]
names = ['img', 'thresh_binary', 'adap_mean_2', 'adap_mean_2_inv',
'adap_mean_8', 'adap_gaussian_8']
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 8))
for ind, p in enumerate(images):
ax = axs[ind%2, ind//2]
ax.set_title(names[ind],fontsize=18,color='white')
ax.imshow(p, cmap = 'gray')
ax.axis('off')
plt.show()
以 sobel
和 laplace
为例
# Convert the image to grayscale
img = cv2.imread('C://Users/13663//Desktop/1.jpg')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply gradient filtering
sobel_x = cv2.Sobel(img, cv2.CV_64F, dx = 1, dy = 0, ksize = 5)
sobel_y = cv2.Sobel(img, cv2.CV_64F, dx = 0, dy = 1, ksize = 5)
blended = cv2.addWeighted(src1=sobel_x, alpha=0.5, src2=sobel_y,
beta=0.5, gamma=0)
laplacian = cv2.Laplacian(img, cv2.CV_64F)
# Plot the images
images = [sobel_x, sobel_y, blended, laplacian]
names = ['sobel_x', 'sobel_y', 'blended', 'laplacian']
plt.figure(figsize = (14, 10))
for i in range(4):
plt.subplot(2, 2, i+1)
plt.imshow(images[i], cmap = 'gray')
plt.title(names[i],fontsize=20,color='white')
plt.axis('off')
plt.show()
可以对比下 PIL 方法的效果(【python】PIL(下))
涉及到腐蚀(erosion)、膨胀(dilation)、开(open)、闭(close)运算
img = cv2.imread('C://Users/13663//Desktop/6.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Create erosion kernels
kernel_0 = np.ones((3, 3), np.uint8)
kernel_1 = cv2.getStructuringElement(cv2.MORPH_RECT, (3, 3))
kernel_2 = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (3, 3))
kernel_3 = cv2.getStructuringElement(cv2.MORPH_CROSS, (3, 3))
kernels = [kernel_0, kernel_1, kernel_2, kernel_3]
names = ['ones','rect','ellipse','cross']
# Plot the images
plt.figure(figsize = (10, 9))
for i in range(4):
img_copy = img.copy()
img_copy = cv2.erode(img_copy, kernels[i], iterations = 1)
plt.subplot(2, 2, i+1)
plt.title(names[i],fontsize=18,color='white')
plt.imshow(img_copy,cmap='gray')
plt.axis('off')
plt.show()
cv2.getStructuringElement
有三种
cv2.MORPH_RECT
cv2.MORPH_ELLIPSE
cv2.MORPH_CROSS
迭代一次后的效果,腐蚀,可以理解为把像素腐蚀掉了(变黑了——变0了),one 和 rect 效果一样
迭代两次的结果,可以感受三者之间的不同了
迭代三次的结果,总感觉椭圆和交叉形效果差不多,可能是示例的原因!
参考文章中的例子如下,要典型一些,哈哈哈!
img = cv2.imread('C://Users/13663//Desktop/6.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply dilation
kernel = np.ones((3, 3), np.uint8)
img_dilate = cv2.dilate(img, kernel, iterations = 1)
plt.figure(figsize = (20, 10))
plt.subplot(1, 2, 1); plt.imshow(img, cmap="gray")
plt.axis('off')
plt.subplot(1, 2, 2); plt.imshow(img_dilate, cmap="gray")
plt.axis('off')
plt.show()
cv2.MORPH_OPEN
:先腐蚀,后膨胀cv2.MORPH_CLOSE
:先膨胀,后腐蚀cv2.MORPH_GRADIENT
:计算膨胀结果图与腐蚀结果图之差cv2.MORPH_TOPHAT
:顶帽,开运算结果图与原始图像之差cv2.MORPH_BLACKHAT
:黑帽,闭运算结果图与原始图像之差img = cv2.imread('C://Users/13663//Desktop/4.png')
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
# Apply the operations
kernel = np.ones((3, 3), np.uint8)
img_open = cv2.morphologyEx(img, op=cv2.MORPH_OPEN, kernel=kernel)
img_close = cv2.morphologyEx(img, op=cv2.MORPH_CLOSE, kernel=kernel)
img_grad = cv2.morphologyEx(img, op=cv2.MORPH_GRADIENT, kernel=kernel)
img_tophat = cv2.morphologyEx(img, op=cv2.MORPH_TOPHAT, kernel=kernel)
img_blackhat = cv2.morphologyEx(img, op=cv2.MORPH_BLACKHAT, kernel=kernel)
# Plot the images
images = [img, img_open, img_close, img_grad,
img_tophat, img_blackhat]
names = ['img', 'img_open', 'img_close', 'img_grad',
'img_tophat', 'img_blackhat']
fig, axs = plt.subplots(nrows = 2, ncols = 3, figsize = (15, 10))
for ind, p in enumerate(images):
ax = axs[ind//3, ind%3]
ax.set_title(names[ind],fontsize=18,color='white')
ax.imshow(p, cmap = 'gray')
ax.axis('off')
plt.show()