• OpenCV从入门到精通实战(七)——探索图像处理:自定义滤波与OpenCV卷积核


    本文主要介绍如何使用Python和OpenCV库通过卷积操作来应用不同的图像滤波效果。主要分为几个步骤:图像的读取与处理、自定义卷积函数的实现、不同卷积核的应用,以及结果的展示。

    卷积

    在图像处理中,卷积是一种重要的操作,它通过将图像与一个小的矩阵(称为卷积核或滤波器)进行运算来影响图像的各种属性。这种操作可以用于实现模糊、锐化、边缘检测等效果。今天,我们将探讨如何在Python中使用OpenCV库来自定义卷积核,并将其应用于图像处理任务中。

    图像的读取与处理

    首先,我们需要读取一张图像,并将其转换成灰度图,因为在这个例子中我们将使用灰度图像来简化处理过程:

    image = cv2.imread(args["image"])
    gray = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)
    
    • 1
    • 2

    自定义卷积函数的实现

    接下来,我们实现一个名为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
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    不同卷积核的应用

    为了展示不同的图像处理效果,我们定义了几种不同的卷积核:

    • **小模糊(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)
    )
    
    # 更多卷积核...
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35

    结果的展示

    最后,我们遍历每一个卷积核,将其应用于原始图像,并显示结果:

    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述在这里插入图片描述
    在这里插入图片描述

    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述

    可以看到卷积核在图像处理中的强大作用,以及如何通过调整卷积核来实现不同的视觉效果。

    完整代码

    # 导入工具包
    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()
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
    • 68
    • 69
    • 70
    • 71
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94
    • 95
    • 96
  • 相关阅读:
    SystemVerilog验证导论
    java代理模式
    WebRTC系列-SDP之setLocalDescription(1)
    docker容器搭建discuz论坛
    IDEA创建Springboot多模块项目
    昇思25天学习打卡营第五天|应用实践/计算机视觉/FCN图像语义分割
    kerberos:介绍
    小啊呜产品读书笔记001:《邱岳的产品手记-03》第04+05讲 如何当好AI时代的产品经理 & 06讲 产品案例分析·TheGuardian的文本之美
    查询优化与并发控制[姊妹篇.第六弹]
    【从头构筑C#知识体系】1.2 结构体
  • 原文地址:https://blog.csdn.net/weixin_42917352/article/details/137896282