7、形态学处理与图像梯度(腐蚀与膨胀、核运算,梯度原理)

一、形态学处理

形态学操作是根据图像形状的简单操作。一般情况下对二值化图像进行的操作。输入两个参数,一个是原始图像,第二个称为结构化元素或核,它是用来决定操作的性质的。两个基本的形态学操作是腐蚀和膨胀。他们的变体构成了开运算、闭运算和梯度等。

腐蚀
粗略的说,腐蚀可以使目标区域范围“变小”,其实质造成图像的边界收缩,可以用来消除小且无意义的目标物。
原理:用P减去结构元素B形成的小矩形,取小矩形中最小值赋到对应原点的位置即可。
dst=cv.erode(src,kernel[,dst[,anchor[,iterations[,borderType[,borderValues]]]]])

代码演示:

import cv2
import numpy as np

img = cv2.imread('j.png', 0)
cv2.imshow('j.png', img)
print(img.shape)

#您可以将内核看作是一个小矩阵,我们在图像上滑动以进行(卷积)操作,例如模糊,锐化,边缘检测或其他图像处理操作。
kernel = np.ones((5, 5), np.uint8)
erosion = cv2.erode(img, kernel, iterations=1)

cv2.imshow('erode', erosion)
cv2.moveWindow('erode', x=img.shape[1], y=0)

cv2.waitKey(0)
cv2.destroyAllWindows()

结果演示:
7、形态学处理与图像梯度(腐蚀与膨胀、核运算,梯度原理)_第1张图片

膨胀
粗略地说,膨胀会使目标区域范围“变大”,将于目标区域接触的背景点合并到该目标物中,使目标边界向外部扩张。作用就是可以用来填补目标区域中某些空洞以及消除包含在目标区域中的小颗粒噪声。
原理:用P加上B,然后取这个区域中的最大值赋值给结构元素B的原点所对应的位置。
dst=cv.dilate(src,kernel[,dst[,anchor[,iterations[,borderType[,borderValues]]]]])

代码演示:

import cv2
import numpy as np

img = cv2.imread('j.png', 0)
cv2.imshow('j.png', img)
print(img.shape)

kernel = np.ones((5, 5), np.uint8)#定义卷积核,5行5列
dilation = cv2.dilate(img, kernel, iterations=1)#将核传入dilation函数  膨胀

cv2.imshow('dilation', dilation)
cv2.moveWindow('dilation', x=img.shape[1], y=0)

cv2.waitKey(0)
cv2.destroyAllWindows()

结果演示:
7、形态学处理与图像梯度(腐蚀与膨胀、核运算,梯度原理)_第2张图片

梯度运算
原理:用图像的膨胀图像减图像腐蚀的操作,该操作课获取原始图像中前景图像的边缘。

礼帽运算
礼帽运算是用原始图像减去其开运算图像的操作。该操作能获取图像的噪声信息,或者得到比原始图像的边缘更亮的边缘信息。

黑帽运算
黑帽运算时用闭运算图像减去原始图像的操作。该操作能够获取图像内部的小孔,或者前景色中的小黑点,或者得到比原始图像的边缘更暗的边缘信息。

核函数
retval=cv.getStructuringElement(shape, ksize[, anchor])

代码演示:

import cv2
import numpy as np

img = cv2.imread('j.png', 0)
cv2.imshow('j.png', img)
print(img.shape)

#您可以将内核看作是一个小矩阵,我们在图像上滑动以进行(卷积)操作,例如模糊,锐化,边缘检测或其他图像处理操作。
kernel = np.ones((5, 5), np.uint8)

# 开运算:先腐蚀再膨胀就叫做开运算。就像上面介绍的那样, 它用来去噪声。
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

cv2.imshow('opening', opening)
cv2.moveWindow('opening', x=img.shape[1], y=0)

# 闭运算
# 先膨胀再腐蚀。它经常用来填充前景物体中的小洞或者前景物体上的小黑点。
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv2.imshow('closing', closing)
cv2.moveWindow('closing', x=img.shape[1] * 2, y=0)

# 形态学梯度 其实就是一幅图像膨胀与腐蚀的差别。
# 结果看上去就像前景物体的轮廓。
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
cv2.imshow('gradient', gradient)
cv2.moveWindow('gradient', x=img.shape[1] * 3, y=0)

# 礼帽
# 原始图像与开运算之后得到的图像的差。
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.moveWindow('tophat', x=img.shape[1] * 4, y=0)

# 黑帽 
# 进行闭运算之后得到的图像与原始图像的差
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat', blackhat)
cv2.moveWindow('blackhat', x=img.shape[1] * 5, y=0)

cv2.waitKey(0)
cv2.destroyAllWindows()

二、图像梯度

图像梯度计算的是图像变化的速度。对于图像的边缘部分,其灰度值变化较大,梯度值也较大;相反,对于图像比较平滑的部分,其灰度值变化较小,相应的梯度值也较小。一般情况下,图像梯度计算的是图像的边缘信息
严格来讲,图像梯度计算需要求导,但是图像梯度一般通过计算像素值的差来得到梯度的近似值。

梯度简单来说就是求导。

OpenCV 提供了三种不同的梯度滤波器或者说是高通滤波器:SobelScharrLaplacian

Sobel,Scharr 其实就是求一阶或二阶导数。
Scharr 是对 Sobel (使用小的卷积核求解梯度角度时 )的优化。
Laplacian 是求二阶导数。

Sobel 算子是高斯平滑与微分操作的结合体,所以它的抗噪声能力很好
你可以设定求导的方向 xorder 或 yorder 。可以设定使用的卷积核的大 小 ksize 。
如果 ksize=-1 会使用 3x3 的 Scharr 滤波器。它的的效果 比 3x3 的 Sobel 滤波器好,所以在使用 3x3 滤波器时应尽量使用 Scharr 滤波器 。

Laplacian 算子
拉普拉斯算子可以使用二阶导数的形式定义 ,可假设其离散实现类似于二阶Sobel 导数,事实上 OpenCV 在 算拉普拉斯算子时直接 用 Sobel 算 子

代码演示:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('../data/sudoku.jpg', 0)
# cv2.CV_64F 出图像的深度 数据类型 可以使用 -1, 与原图像保持一致 np.uint8
laplacian = cv2.Laplacian(img, cv2.CV_64F)
# 参数 1,0 为只在 x 方向求一 导数 最大可以求 2 导数。
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=5)
# 参数 0,1 为只在 y 方向求一 导数 最大可以求 2 导数。
sobely = cv2.Sobel(img, cv2.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()

结果演示:
7、形态学处理与图像梯度(腐蚀与膨胀、核运算,梯度原理)_第3张图片
注意::::::

当我们可以 用参数 -1 来定出图像的深度, 数据类型与原图像保持一致
但是我们在代码中使用的却是 cv2.CV_64F。 是为什么呢 ?
想象一下一个从黑到白的边界的导数是整数,而一个从白到黑的边界点导数却是负数。
如果原图像的深度是 np.int8 时 所有的负值会截断变成 0,换句话就是把边界丢失掉。
所以如果 两种边界你想检测到,最好的的办法就是将输出的数据类型 设置的更高
比如 cv2.CV_16S ,cv2.CV_64F 等。取绝对值然后再把它回到cv2.CV_8U
下面的示例演示了输出图片的深度不同造成的不同效果。
代码演示:

import cv2
import numpy as np
from matplotlib import pyplot as plt

img = cv2.imread('../data/box.jpg', 0)

# Output dtype = cv2.CV_8U
sobelx8u = cv2.Sobel(img, cv2.CV_8U, 1, 0, ksize=5)
# Output dtype = cv2.CV_64F. Then take its absolute and convert to cv2.CV_8U
sobelx64f = cv2.Sobel(img, cv2.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()

结果演示:

7、形态学处理与图像梯度(腐蚀与膨胀、核运算,梯度原理)_第4张图片

你可能感兴趣的:(opencv-python,python,java,tomcat)