OpenCV从入门到精通实战(七)——探索图像处理:自定义滤波与OpenCV卷积核
本文主要介绍如何使用Python和OpenCV库通过卷积操作来应用不同的图像滤波效果。主要分为几个步骤:图像的读取与处理、自定义卷积函数的实现、不同卷积核的应用,以及结果的展示。
卷积
在图像处理中,卷积是一种重要的操作,它通过将图像与一个小的矩阵(称为卷积核或滤波器)进行运算来影响图像的各种属性。这种操作可以用于实现模糊、锐化、边缘检测等效果。今天,我们将探讨如何在Python中使用OpenCV库来自定义卷积核,并将其应用于图像处理任务中。
图像的读取与处理
首先,我们需要读取一张图像,并将其转换成灰度图,因为在这个例子中我们将使用灰度图像来简化处理过程:
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
自定义卷积函数的实现
接下来,我们实现一个名为convolve
的函数,该函数接收一个图像和一个卷积核作为输入,并返回卷积后的结果。在这个过程中,我们通过为图像添加边界,然后对每个像素应用卷积核来完成卷积操作:
def convolve(image, kernel):
# 输入图像和核的尺寸
(iH, iW) = image.shape[:2]
(kH, kW) = kernel.shape[:2]
# 选择pad,卷积后图像大小不变
pad = (kW - 1) // 2
# 重复最后一个元素,top, bottom, left, right
image = cv2.copyMakeBorder(image, pad, pad, pad, pad,
cv2.BORDER_REPLICATE)
output = np.zeros((iH, iW), dtype="float32")
# 卷积操作
for y in np.arange(pad, iH + pad):
for x in np.arange(pad, iW + pad):
# 提取每一个卷积区域
roi = image[y - pad:y + pad + 1, x - pad:x + pad + 1]
# 内积运算
k = (roi * kernel).sum()
# 保存相应的结果
output[y - pad, x - pad] = k
# 将得到的结果放缩到[0, 255]
output = rescale_intensity(output, in_range=(0, 255))
output = (output * 255).astype("uint8")
return output
不同卷积核的应用
为了展示不同的图像处理效果,我们定义了几种不同的卷积核:
- **小模糊(Small Blur)和大模糊(Large Blur)**用于创建模糊效果。
- **锐化(Sharpen)**卷积核可以使图像看起来更清晰。
- **拉普拉斯(Laplacian)和索贝尔(Sobel)**卷积核用于边缘检测。
smallBlur = np.ones((7, 7), dtype="float") * (1.0 / (7 * 7))
largeBlur = np.ones((21, 21), dtype="float") * (1.0 / (21 * 21))
# 尝试不同的卷积核
sharpen = np.array((
[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]), dtype="int")
laplacian = np.array((
[0, 1, 0],
[1, -4, 1],
[0, 1, 0]), dtype="int")
sobelX = np.array((
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]), dtype="int")
sobelY = np.array((
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]), dtype="int")
# 尝试不同结果
kernelBank = (
("small_blur", smallBlur),
("large_blur", largeBlur),
("sharpen", sharpen),
("laplacian", laplacian),
("sobel_x", sobelX),
("sobel_y", sobelY)
)
# 更多卷积核...
结果的展示
最后,我们遍历每一个卷积核,将其应用于原始图像,并显示结果:
for (kernelName, kernel) in kernelBank:
convoleOutput = convolve(gray, kernel)
opencvOutput = cv2.filter2D(gray, -1, kernel)
# 展示结果
# 分别展示结果
cv2.imshow("original", gray)
cv2.imshow("{} - convole".format(kernelName), convoleOutput)
cv2.imshow("{} - opencv".format(kernelName), opencvOutput)
cv2.waitKey(0)
cv2.destroyAllWindows()
可以看到卷积核在图像处理中的强大作用,以及如何通过调整卷积核来实现不同的视觉效果。
完整代码
# 导入工具包
from skimage.exposure import rescale_intensity
import numpy as np
import argparse
import cv2
def convolve(image, kernel):
# 输入图像和核的尺寸
(iH, iW) = image.shape[:2]
(kH, kW) = kernel.shape[:2]
# 选择pad,卷积后图像大小不变
pad = (kW - 1) // 2
# 重复最后一个元素,top, bottom, left, right
image = cv2.copyMakeBorder(image, pad, pad, pad, pad,
cv2.BORDER_REPLICATE)
output = np.zeros((iH, iW), dtype="float32")
# 卷积操作
for y in np.arange(pad, iH + pad):
for x in np.arange(pad, iW + pad):
# 提取每一个卷积区域
roi = image[y - pad:y + pad + 1, x - pad:x + pad + 1]
# 内积运算
k = (roi * kernel).sum()
# 保存相应的结果
output[y - pad, x - pad] = k
# 将得到的结果放缩到[0, 255]
output = rescale_intensity(output, in_range=(0, 255))
output = (output * 255).astype("uint8")
return output
# 指定输入图像
ap = argparse.ArgumentParser()
ap.add_argument("-i", "--image", default="lanpangzi.jpg",
help="path to the input image")
args = vars(ap.parse_args())
# 分别构建两个卷积核
smallBlur = np.ones((7, 7), dtype="float") * (1.0 / (7 * 7))
largeBlur = np.ones((21, 21), dtype="float") * (1.0 / (21 * 21))
# 尝试不同的卷积核
sharpen = np.array((
[0, -1, 0],
[-1, 5, -1],
[0, -1, 0]), dtype="int")
laplacian = np.array((
[0, 1, 0],
[1, -4, 1],
[0, 1, 0]), dtype="int")
sobelX = np.array((
[-1, 0, 1],
[-2, 0, 2],
[-1, 0, 1]), dtype="int")
sobelY = np.array((
[-1, -2, -1],
[0, 0, 0],
[1, 2, 1]), dtype="int")
# 尝试不同结果
kernelBank = (
("small_blur", smallBlur),
("large_blur", largeBlur),
("sharpen", sharpen),
("laplacian", laplacian),
("sobel_x", sobelX),
("sobel_y", sobelY)
)
# 简单起见,用灰度图来玩
image = cv2.imread(args["image"])
gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
# 遍历每一个核
for (kernelName, kernel) in kernelBank:
print("[INFO] applying {} kernel".format(kernelName))
convoleOutput = convolve(gray, kernel)
# -1 表示深度一致
opencvOutput = cv2.filter2D(gray, -1, kernel)
# 分别展示结果
cv2.imshow("original", gray)
cv2.imshow("{} - convole".format(kernelName), convoleOutput)
cv2.imshow("{} - opencv".format(kernelName), opencvOutput)
cv2.waitKey(0)
cv2.destroyAllWindows()