Python从0到100(六十八):Python OpenCV-图像边缘检测及图像融合

举报
是Dream呀 发表于 2024/11/11 21:14:46 2024/11/11
【摘要】 Python从0到100(六十八):Python OpenCV-图像边缘检测及图像融合

在这里插入图片描述

前言: 零基础学Python:Python从0到100最新最全教程 想做这件事情很久了,这次我更新了自己所写过的所有博客,汇集成了Python从0到100,共一百节课,帮助大家一个月时间里从零基础到学习Python基础语法、Python爬虫、Web开发、 计算机视觉、机器学习、神经网络以及人工智能相关知识,成为学习学习和学业的先行者!
欢迎大家订阅专栏:零基础学Python:Python从0到100最新最全教程!

1. 侵蚀

侵蚀的基本思想就像土壤侵蚀一样,它侵蚀前景物体的边界(尽量使前景保持白色)。它是做什么的呢?内核滑动通过图像(在2D卷积中)。原始图像中的一个像素(无论是1还是0)只有当内核下的所有像素都是1时才被认为是1,否则它就会被侵蚀(变成0)。

结果是,根据内核的大小,边界附近的所有像素都会被丢弃。因此,前景物体的厚度或大小减小,或只是图像中的白色区域减小。它有助于去除小的白色噪声(正如我们在颜色空间章节中看到的),分离两个连接的对象等。

腐蚀主要就是调用cv2.erode(img,kernel,iterations),这个函数的参数是:
第一个参数:img指需要腐蚀的图

第二个参数:kernel指腐蚀操作的内核,默认是一个简单的3X3矩阵,我们也可以利用getStructuringElement()函数指明它的形状

第三个参数:iterations指的是腐蚀次数,省略是默认为1

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

代码:

import cv2 as cv
import numpy as np
img = cv.imread('yuanxing.png',0)
kernel = np.ones((5,5),np.uint8)
erosion = cv.erode(img,kernel,iterations=2)
cv.imshow('xuxux',erosion)
cv.waitKey(0)

效果图:
在这里插入图片描述

2. 扩张

它与侵蚀正好相反。如果内核下的至少一个像素为“ 1”,则像素元素为“ 1”。因此,它会增加图像中的白色区域或增加前景对象的大小。通常,在消除噪音的情况下,腐蚀后会膨胀。因为腐蚀会消除白噪声,但也会缩小物体。因此,我们对其进行了扩展。由于噪音消失了,它们不会回来,但是我们的目标区域增加了。在连接对象的损坏部分时也很有用。
代码:

import cv2 as cv
import numpy as np
img = cv.imread('yuanxing.png',1)
kernel = np.ones((5,5),np.uint8)
# erosion=cv.erode(img,kernel,iterations = 1)
erosion=cv.dilate(img,kernel,iterations=1)
cv.imshow('erosion',erosion)

cv.waitKey(0)

效果图:
在这里插入图片描述

3.形态学梯度

这是图像扩张和侵蚀之间的区别。结果将看起来像对象的轮廓:

代码:

import cv2 as cv
import numpy as np
img = cv.imread('yuanxing.png',1)
kernel = np.ones((5,5),np.uint8)

gradient = cv.morphologyEx(img, cv.MORPH_GRADIENT, kernel)
cv.imshow('erosion',gradient)

cv.waitKey(0)

效果图:
在这里插入图片描述

二、图像梯度

前言:在本章中,我们将学习: - 查找图像梯度,边缘等 - 我们将看到以下函数:cv.Sobel(),cv.Scharr(),cv.Laplacian()等
OpenCV提供三种类型的梯度滤波器或高通滤波器,即Sobel,Scharr和Laplacian。我们将看到他们每一种。

1. Sobel 和 Scharr 算子

Sobel算子是高斯平滑加微分运算的联合运算,因此它更抗噪声。逆可以指定要采用的导数方向,垂直或水平(分别通过参数yorder和xorder)。逆还可以通过参数ksize指定内核的大小。如果ksize = -1,则使用3x3 Scharr滤波器,比3x3 Sobel滤波器具有更好的结果。

2. Laplacian 算子

在这里插入图片描述

# -*-coding:utf-8 -*-
# @Author:到点了,心疼徐哥哥
# 奥利给干!!!

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('qipan.jpg',0)
# 其中,0表示将图片以灰度读出来。
laplacian = cv.Laplacian(img,cv.CV_64F)
# 图像边缘处理sobel细节
# 利用Sobel方法可以进行sobel边缘检测
# img表示源图像,即进行边缘检测的图像
# cv2.CV_64F表示64位浮点数即64float。
# 这里不使用numpy.float64,因为可能会发生溢出现象。用cv的数据则会自动
# 第三和第四个参数分别是对X和Y方向的导数(即dx,dy),对于图像来说就是差分,这里1表示对X求偏导(差分),0表示不对Y求导(差分)。其中,X还可以求2次导。
# 注意:对X求导就是检测X方向上是否有边缘。
# 第五个参数ksize是指核的大小。

# 这里说明一下,这个参数的前四个参数都没有给谁赋值,而ksize则是被赋值的对象
# 实际上,这时可省略的参数,而前四个是不可省的参数。注意其中的不同点
sobelx = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
sobely = cv.Sobel(img,cv.CV_64F,0,1,ksize=5)
plt.subplot(2,2,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,2),plt.imshow(laplacian,cmap = 'gray')
plt.title('Laplacian'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,3),plt.imshow(sobelx,cmap = 'gray')
plt.title('Sobel X'), plt.xticks([]), plt.yticks([])
plt.subplot(2,2,4),plt.imshow(sobely,cmap = 'gray')
plt.title('Sobel Y'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述
在我们的最后一个示例中,输出数据类型为cv.CV_8U或np.uint8。但这有一个小问题。黑色到白色的过渡被视为正斜率(具有正值),而白色到黑色的过渡被视为负斜率(具有负值)。因此,当您将数据转换为np.uint8时,所有负斜率均​​设为零。简而言之,您会错过这一边缘信息。

如果要检测两个边缘,更好的选择是将输出数据类型保留为更高的形式,例如cv.CV_16S,cv.CV_64F等,取其绝对值,然后转换回cv.CV_8U。

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('box.png',0)
# Output dtype = cv.CV_8U
sobelx8u = cv.Sobel(img,cv.CV_8U,1,0,ksize=5)
# Output dtype = cv.CV_64F. Then take its absolute and convert to cv.CV_8U
sobelx64f = cv.Sobel(img,cv.CV_64F,1,0,ksize=5)
abs_sobel64f = np.absolute(sobelx64f)
sobel_8u = np.uint8(abs_sobel64f)
plt.subplot(1,3,1),plt.imshow(img,cmap = 'gray')
plt.title('Original'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,2),plt.imshow(sobelx8u,cmap = 'gray')
plt.title('Sobel CV_8U'), plt.xticks([]), plt.yticks([])
plt.subplot(1,3,3),plt.imshow(sobel_8u,cmap = 'gray')
plt.title('Sobel abs(CV_64F)'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

三、Canny边缘检测

目标
在本章中,我们将学习 - Canny边缘检测的概念 - OpenCV函数: cv.Canny()

理论: Canny Edge Detection是一种流行的边缘检测算法。它由John F. Canny发明
这是一个多阶段算法,我们将经历每个阶段。
降噪:
由于边缘检测容易受到图像中噪声的影响,因此第一步是
使用5x5高斯滤波器消除图像中的噪声。我们已经在前面的章节中看到了这一点。

1.查找图像的强度梯度

然后使用Sobel核在水平和垂直方向上对平滑的图像进行滤波,以在水平方向(Gx)和垂直方向(Gy)上获得一阶导数。从这两张图片中,我们可以找到每个像素的边缘渐变和方向,如下所示:
在这里插入图片描述

1.1非极大值抑制

在获得梯度大小和方向后,将对图像进行全面扫描,以去除可能不构成边缘的所有不需要的像素。为此,在每个像素处,检查像素是否是其在梯度方向上附近的局部最大值。查看下面的图片:

点A在边缘(垂直方向)上。渐变方向垂直于边缘。点B和C在梯度方向上。因此,将A点与B点和C点进行检查,看是否形成局部最大值。如果是这样,则考虑将其用于下一阶段,否则将其抑制(置为零)。 简而言之,你得到的结果是带有“细边”的二进制图像。

1.2磁滞阈值

该阶段确定哪些边缘全部是真正的边缘,哪些不是。为此,我们需要两个阈值minVal和maxVal。强度梯度大于maxVal的任何边缘必定是边缘,而小于minVal的那些边缘必定是非边缘,因此将其丢弃。介于这两个阈值之间的对象根据其连通性被分类为边缘或非边缘。如果将它们连接到“边缘”像素,则将它们视为边缘的一部分。

2.OpenCV中的Canny Edge检测

OpenCV将以上所有内容放在单个函数 cv.Canny() 中。我们将看到如何使用它。第一个参数是我们的输入图像。第二个和第三个参数分别是我们的minVal和maxVal。第三个参数是perture_size。它是用于查找图像渐变的Sobel内核的大小。默认情况下为3。最后一个参数是L2gradient,它指定用于查找梯度幅度的方程式。如果为True,则使用上面提到的更精确的公式,否则使用以下函数:默认情况下,它为False。

2.1不加高斯滤波

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('haojin.jpg',0)
# img1 = cv.GaussianBlur(img, (1,1),0)
edges = cv.Canny(img,50,100)
plt.subplot(121),plt.imshow(img,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

2.2加高斯滤波

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('haojin.jpg',0)
img1 = cv.GaussianBlur(img, (5,5),0)
edges = cv.Canny(img1,50,100)
plt.subplot(121),plt.imshow(img1,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

2.3调整高斯滤波

import numpy as np
import cv2 as cv
from matplotlib import pyplot as plt
img = cv.imread('haojin.jpg',0)
img1 = cv.GaussianBlur(img, (1,1),0)
edges = cv.Canny(img1,50,100)
plt.subplot(121),plt.imshow(img1,cmap = 'gray')
plt.title('Original Image'), plt.xticks([]), plt.yticks([])
plt.subplot(122),plt.imshow(edges,cmap = 'gray')
plt.title('Edge Image'), plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

四、图像金字塔

目标
在本章中, - 我们将学习图像金字塔 - 我们将使用图像金字塔创建一个新的水果“Orapple” - 我们将看到以下功能:cv.pyrUp(),cv.pyrDown()

1 图像金字塔简介

以多个分辨率来表示图像的一种有效且概念简单的结构是图像金字塔。图像金字塔最初用于机器视觉和图像压缩,一个图像金字是一系列以金字塔形状排列的、分辨率逐步降低的图像集合。

如图下图所示,它包括了四层图像,将这一层一层的图像比喻成金字塔。图像金字塔可以通过梯次向下采样获得,直到达到某个终止条件才停止采样,在向下采样中,层级越高,则图像越小,分辨率越低

在这里插入图片描述

生成图像金字塔主要包括两种方式:向下取样 和 向上取样

如下图所示:

向下取样:将图像从G0转换为G1、G2、G3,图像分辨率不断降低的过程;
向上取样:将图像从G3转换为G2、G1、G0,图像分辨率不断增大的过程。在这里插入图片描述

2 向下取样——pyrDown()

2.1 基础理论

在图像向下取样中,一般分两步:

  • (1)对图像Gi进行高斯卷积核(高斯滤波);
  • (2)删除所有的偶数行和列。

在这里插入图片描述
其中,高斯核卷积运算(高斯滤波) 就是对整幅图像进行加权平均的过程,每一个像素点的值,都由其本身和邻域内的其他像素值(权重不同)经过加权平均后得到。常见的 33 和 55 高斯核如下:
在这里插入图片描述

注:由于每次向下取样会删除偶数行和列,所以它会不停地丢失图像的信息。

2.2 代码示例

在OpenCV中,向下取样使用的函数为pyrDown(),其函数用法如下所示:

dst = pyrDown(src[, dst[, dstsize[, borderType]]])

其中,参数:

src 表示输入图像;

dst 表示输出图像,和输入图像具有一样的尺寸和类型;

dstsize 表示输出图像的大小,默认值为Size();

borderType 表示像素外推方法,详见cv::bordertypes 。

取一次样:

# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取原始图像
img = cv2.imread('juzi.png')

# 图像向下取样
r = cv2.pyrDown(img)

# 显示图像
cv2.imshow('original', img)
cv2.imshow('PyrDown', r)

cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
多次向下采样:

# -*-coding:utf-8 -*-
# @Author:到点了,心疼徐哥哥
# 奥利给干!!!
# -*- coding: utf-8 -*-
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

#  读取原始图像
img=cv.imread('pinguo.png')

#  图像向下取样
r1=cv.pyrDown(img)
r2=cv.pyrDown(r1)
r3=cv.pyrDown(r2)

#  显示图像
cv.imshow('original',img)
cv.imshow('pyrDown1',r1)
cv.imshow('pyrDown2',r2)
cv.imshow('pyrDown3',r3)
cv.waitKey()
cv.destroyWindow()

在这里插入图片描述

3 向上取样——pyrUp()

3.1 基础理论

在图像向上取样是由小图像不断放图像的过程。它将图像在每个方向上扩大为原图像的2倍,新增的行和列均用0来填充,并使用与“向下取样”相同的卷积核乘以4,再与放大后的图像进行卷积运算,以获得“新增像素”的新值。
在这里插入图片描述

注:向上取样和向下取样无法互逆的。

3.2 代码示例

和上文中的向下取样其时并无大异。
在OpenCV中,向上取样使用 pyrUp() 函数,其函数用法如下所示:

dst = pyrUp(src[, dst[, dstsize[, borderType]]])

其中,参数:

src 表示输入图像;

dst 表示输出图像,和输入图像具有一样的尺寸和类型;

dstsize 表示输出图像的大小,默认值为Size();

borderType 表示像素外推方法,详见cv::bordertypes 。

取一次样:

# -*- coding: utf-8 -*-
import cv2
import numpy as np
import matplotlib.pyplot as plt

# 读取原始图像
img = cv2.imread('juzi.png')

# 图像向下取样
r = cv2.pyrUp(img)

# 显示图像
cv2.imshow('original', img)
cv2.imshow('PyrDown', r)

cv2.waitKey()
cv2.destroyAllWindows()

在这里插入图片描述
多次取样:

# -*-coding:utf-8 -*-
# @Author:到点了,心疼徐哥哥
# 奥利给干!!!
# -*- coding: utf-8 -*-
import cv2 as cv
import numpy as np
import matplotlib.pyplot as plt

#  读取原始图像
img=cv.imread('pinguo.png')

#  图像向下取样
r1=cv.pyrUp(img)
r2=cv.pyrUp(r1)
r3=cv.pyrUp(r2)

#  显示图像
cv.imshow('original',img)
cv.imshow('pyrDown1',r1)
cv.imshow('pyrDown2',r2)
cv.imshow('pyrDown3',r3)
cv.waitKey()
cv.destroyWindow()

4.图像融合

4.1理论代码:

# -*-coding:utf-8 -*-
# @Author:到点了,心疼徐哥哥
# 奥利给干!!!
# -*- coding: utf-8 -*-
from cv2 import cv2
import numpy as np

A = cv2.imread('pinguo.png')
# 注意:为了使后面可以逐渐减半,这里的尺寸必须为2的次幂
A = cv2.resize(A, (256, 256), interpolation=cv2.INTER_CUBIC)
B = cv2.imread('juzi.png')
B = cv2.resize(B, (256, 256), interpolation=cv2.INTER_CUBIC)

# 生成高斯金字塔
G = A.copy()
gpA = [G]
for i in range(5):
    G = cv2.pyrDown(G)
    gpA.append(G)

G = B.copy()
gpB = [G]
for i in range(5):
    G = cv2.pyrDown(G)
    gpB.append(G)

# 产生Laplacian金字塔
lpA = [gpA[5]]
for i in range(5, 0, -1):
    GE = cv2.pyrUp(gpA[i])
    L = cv2.subtract(gpA[i - 1], GE)
    lpA.append(L)

lpB = [gpB[5]]
for i in range(5, 0, -1):
    GE = cv2.pyrUp(gpB[i])
    L = cv2.subtract(gpB[i - 1], GE)
    lpB.append(L)

# 合并
LS = []
for la, lb in zip(lpA, lpB):
    rows, cols, dpt = la.shape
    ls = np.hstack((la[:, 0:cols // 2], lb[:, cols // 2:]))
    LS.append(ls)

# 重新构建图像
ls_ = LS[0]
for i in range(1, 6):
    ls_ = cv2.pyrUp(ls_)
    ls_ = cv2.add(ls_, LS[i])

# 连接
real = np.hstack((A[:, :cols // 2], B[:, cols // 2:]))

cv2.imshow("LS", ls_)
cv2.imshow("Real", real)

cv2.waitKey()
cv2.destroyAllWindows()

4.2融合效果:

在这里插入图片描述

【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。