图像基础操作(1)

本文详细介绍了OpenCV在图像处理中的应用,包括图像阈值处理、滤波操作、形态学处理、边缘检测等核心功能。通过实例展示了不同滤波器的效果,如均值、高斯、中值滤波,以及形态学操作如腐蚀、膨胀、开闭运算。深入探讨了Canny边缘检测算法的工作原理和参数调整技巧。
摘要由CSDN通过智能技术生成

学习的视频:OpenCV计算机视觉实战(Python版)
B站链接 https://www.bilibili.com/video/av59316056?p=1
参考OpenCV-Python-Tutorial-中文版、OpenCV-Python 中文教程

图像阈值

当像素值高于阈值时,我们给这个像素 赋予一个新值(可能是白色),否则我们给它赋予另外一种颜色(也许是黑色)。 这个函数就是 cv2.threshhold()。
ret, dst = cv2.threshold(src, thresh, maxval, type)
src: 输入图,只能输入单通道图像,通常来说为灰度图
dst: 输出图
thresh: 阈值
maxval: 当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
type:二值化操作的类型,包含以下5种类型: cv2.THRESH_BINARY; cv2.THRESH_BINARY_INV; cv2.THRESH_TRUNC; cv2.THRESH_TOZERO;cv2.THRESH_TOZERO_INV
cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0
cv2.THRESH_BINARY_INV THRESH_BINARY的反转
cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
cv2.THRESH_TOZERO_INV THRESH_TOZERO的反转

ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)#ret是阈值,thresh1是输出图
ret, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
ret, thresh3 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TRUNC)#亮的部分保持不变,暗的更暗
ret, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)#暗的部分保持不变
ret, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV) 

titles = ['Original Image', 'BINARY', 'BINARY_INV', 'TRUNC', 'TOZERO', 'TOZERO_INV']
images = [img, thresh1, thresh2, thresh3, thresh4, thresh5]

for i in range(6):
    plt.subplot(2, 3, i + 1), plt.imshow(images[i], 'gray')
    plt.title(titles[i])
    plt.xticks([]), plt.yticks([])
plt.show()

在这里插入图片描述

滤波操作

#均值滤波:用卷积框覆盖区域所有像素的平均值来代替中心元素。(简单的平均卷积操作)
blur = cv2.blur(img, (3, 3))
cv2.imshow('blur', blur)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 方框滤波
# 基本和均值一样,可以选择归一化 
import cv2
img = cv2.imread('lenaNoise.png')
box = cv2.boxFilter(img,-1,(3,3), normalize=True)
#-1表示通道数与原来保持一致,基本能不会改动,当normalize=True的时候,与均值滤波一致
cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()
#容易越界,大于阈值的像素都取255
box = cv2.boxFilter(img,-1,(3,3), normalize=False)  
cv2.imshow('box', box)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 高斯滤波
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的
#离某个值越近,权重越大,越远权值越小,所占权重比重在卷积核体现(权重矩阵),这里的远近指定是位置的远近而不是数值的大小
aussian = cv2.GaussianBlur(img, (5, 5), 1)  
cv2.imshow('aussian', aussian)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 中值滤波
# 相当于用中值代替
#将窗口中的数值从小到大排列,取中间的那个值
median = cv2.medianBlur(img, 5)  # 中值滤波    指定窗口大小,这里5表示窗口大小5*5
cv2.imshow('median', median)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 展示所有的
res = np.hstack((blur,aussian,median))
#print (res)
cv2.imshow('median vs average', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

形态学-腐蚀操作

这个操作会把前景物体的边界腐蚀掉(但是前景仍然是白色)。卷积核沿着图像滑动,如果与卷积核对应的原图像的所有像素值都是 1,那么中心元素就保持原来的像素值,否则就变为零。
根据卷积核的大小靠近前景的所有像素都会被腐蚀掉(变为0),所以前景物体会变小,整幅图像的白色区域会减少。这对于去除白噪声很有用,也可以用来断开两个连在一块的物体等。

import cv2
import numpy as np
img = cv2.imread('dige.png')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((3,3),np.uint8) 
erosion = cv2.erode(img,kernel,iterations = 1)#iterations 腐蚀迭代的次数。次数越大,前景物越小
cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()
pie = cv2.imread('pie.png')
cv2.imshow('pie', pie)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((30,30),np.uint8) 
erosion_1 = cv2.erode(pie,kernel,iterations = 1)
erosion_2 = cv2.erode(pie,kernel,iterations = 2)
erosion_3 = cv2.erode(pie,kernel,iterations = 3)
res = np.hstack((erosion_1,erosion_2,erosion_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

形态学-膨胀操作

与腐蚀相反,与卷积核对应的原图像的像素值中只要有一个是 1,中心元 素的像素值就是 1。所以这个操作会增加图像中的白色区域(前景)。一般在去 噪声时先用腐蚀再用膨胀。因为腐蚀在去掉白噪声的同时,也会使前景对象变小。所以我们再对他进行膨胀。这时噪声已经被去除了,不会再回来了,但是 前景还在并会增加。膨胀也可以用来连接两个分开的物体。

img = cv2.imread('dige.png')
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((3,3),np.uint8) 
dige_erosion = cv2.erode(img,kernel,iterations = 1)

cv2.imshow('erosion', erosion)
cv2.waitKey(0)
cv2.destroyAllWindows()
kernel = np.ones((3,3),np.uint8) 
dige_dilate = cv2.dilate(dige_erosion,kernel,iterations = 1)

cv2.imshow('dilate', dige_dilate)
cv2.waitKey(0)
cv2.destroyAllWindows()
pie = cv2.imread('pie.png')

kernel = np.ones((30,30),np.uint8) 
dilate_1 = cv2.dilate(pie,kernel,iterations = 1)
dilate_2 = cv2.dilate(pie,kernel,iterations = 2)
dilate_3 = cv2.dilate(pie,kernel,iterations = 3)
res = np.hstack((dilate_1,dilate_2,dilate_3))
cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

开运算与闭运算

开:先腐蚀,再膨胀,被用来去除噪声
闭:先膨胀,再腐蚀,经常被用来填充前景物体中的小洞,或者前景物体上的小黑点。

img = cv2.imread('dige.png')

kernel = np.ones((5,5),np.uint8) 
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)

cv2.imshow('opening', opening)
cv2.waitKey(0)
cv2.destroyAllWindows()
 
img = cv2.imread('dige.png')

kernel = np.ones((5,5),np.uint8) 
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)

cv2.imshow('closing', closing)
cv2.waitKey(0)
cv2.destroyAllWindows()

礼帽与黑帽

礼帽 = 原始输入-开运算结果
黑帽 = 闭运算-原始输入

#礼帽
img = cv2.imread('dige.png')
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv2.imshow('tophat', tophat)
cv2.waitKey(0)
cv2.destroyAllWindows()
#黑帽
img = cv2.imread('dige.png')
blackhat  = cv2.morphologyEx(img,cv2.MORPH_BLACKHAT, kernel)
cv2.imshow('blackhat ', blackhat )
cv2.waitKey(0)
cv2.destroyAllWindows()

梯度运算

梯度=膨胀-腐蚀,得到前景物体的轮廓,也就是边缘信息

pie = cv2.imread('pie.png')
kernel = np.ones((7,7),np.uint8) 
dilate = cv2.dilate(pie,kernel,iterations = 5)
erosion = cv2.erode(pie,kernel,iterations = 5)

res = np.hstack((dilate,erosion))

cv2.imshow('res', res)
cv2.waitKey(0)
cv2.destroyAllWindows()

图像梯度-Sobel算子

在这里插入图片描述只有在边界位置才会出现梯度
GX卷积核:计算某点左右两边的差异(右-左),即水平方向
GY卷积核:计算某点上下之间的差异(下-上),即竖直方向

img = cv2.imread('pie.png',cv2.IMREAD_GRAYSCALE)
cv2.imshow("img",img)
cv2.waitKey()
cv2.destroyAllWindows()

dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
ddepth:图像的深度
dx和dy分别表示水平和竖直方向
ksize是Sobel算子的大小

def cv_show(img,name):
    cv2.imshow(name,img)
    cv2.waitKey()
    cv2.destroyAllWindows()
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
cv_show(sobelx,'sobelx')

可以通过参数 -1 来设定输出图像的深度(数据类型)与原图像保持一致,但是我们在代码中使用的却是 cv2.CV_64F。这是为什么呢?想象一下一个从黑到白的边界 的导数是整数,而一个从白到黑的边界点导数却是负数。如果原图像的深度是 np.int8 时,所有的负值都会被截断变成 0,换句话说就是把把边界丢失掉。 所以如果这两种边界你都想检测到,最好的的办法就是将输出的数据类型 设置的更高,比如 cv2.CV_16S,cv2.CV_64F 等。取绝对值然后再把它转回 到 cv2.CV_8U
分别计算水平方向和垂直方向边界,最后加起来

img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
#提取轮廓区域
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)
cv_show(sobelxy,'sobelxy')

直接计算两个方向的和,不建议直接计算

img = cv2.imread('lena.jpg',cv2.IMREAD_GRAYSCALE)
sobelxy=cv2.Sobel(img,cv2.CV_64F,1,1,ksize=3)
sobelxy = cv2.convertScaleAbs(sobelxy) 
cv_show(sobelxy,'sobelxy')

图像梯度-Scharr算子

在这里插入图片描述`import cv2
#不同算子的差异
img = cv2.imread(‘lena.jpg’,cv2.IMREAD_GRAYSCALE)
sobelx = cv2.Sobel(img,cv2.CV_64F,1,0,ksize=3)
sobely = cv2.Sobel(img,cv2.CV_64F,0,1,ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
sobely = cv2.convertScaleAbs(sobely)
sobelxy = cv2.addWeighted(sobelx,0.5,sobely,0.5,0)

scharrx = cv2.Scharr(img,cv2.CV_64F,1,0)#比sobel算子更加敏感,描绘更多的细节
scharry = cv2.Scharr(img,cv2.CV_64F,0,1)
scharrx = cv2.convertScaleAbs(scharrx)
scharry = cv2.convertScaleAbs(scharry)
scharrxy = cv2.addWeighted(scharrx,0.5,scharry,0.5,0)

laplacian = cv2.Laplacian(img,cv2.CV_64F)
laplacian = cv2.convertScaleAbs(laplacian)
def cv_show(img,name):
cv2.imshow(name,img)
cv2.waitKey()
cv2.destroyAllWindows()
res = np.hstack((sobelxy,scharrxy,laplacian))
cv_show(res,‘res’)`
在这里插入图片描述## Canny边缘检测

1) 使用高斯滤波器,以平滑图像,滤除噪声。

2) 计算图像中每个像素点的梯度强度和方向。

3) 应用非极大值(Non-Maximum Suppression)抑制,以消除边缘检测带来的杂散响应。

4) 应用双阈值(Double-Threshold)检测来确定真实的和潜在的边缘。

5) 通过抑制孤立的弱边缘最终完成边缘检测。

高斯滤波:
在这里插入图片描述梯度和方向(这里使用sobel算子)
在这里插入图片描述非极大值抑制
在这里插入图片描述在这里插入图片描述双阈值检测
现在要确定哪些边界才是真正的边界。这时我们需要设置两个阈值: minVal 和 maxVal。当图像的灰度梯度高于 maxVal 时被认为是真的边界, 那些低于 minVal 的边界会被抛弃。如果介于两者之间的话,就要看这个点是 否与某个被确定为真正的边界点相连,如果是就认为它也是边界点,如果不是 就抛弃。如下图:
在这里插入图片描述A 高于阈值 maxVal 所以是真正的边界点,C 虽然低于 maxVal 但高于 minVal 并且与 A 相连,所以也被认为是真正的边界点。而 B 就会被抛弃,因 为他不仅低于 maxVal 而且不与真正的边界点相连。所以选择合适的 maxVal 和 minVal 对于能否得到好的结果非常重要。 在这一步一些小的噪声点也会被除去,因为我们假设边界都是一些长的线 段。

img=cv2.imread("lena.jpg",cv2.IMREAD_GRAYSCALE)

v1=cv2.Canny(img,80,150)
v2=cv2.Canny(img,50,100)
#值越小越精确,检测到的越精确
res = np.hstack((v1,v2))
cv_show(res,'res')

这个函数的第一个参数是输入图像。第二和第三 个分别是 minVal 和 maxVal。第三个参数设置用来计算图像梯度的 Sobel 卷积核的大小,默认值为 3。最后一个参数是 L2gradient,它可以用来设定 求梯度大小的方程。如果设为 True,就会使用我们上面提到过的方程,否则 使用方程:Edge−Gradient(G) = |Gx的平方|+|Gy的平方| 代替,默认值为 False。

img=cv2.imread("car.png",cv2.IMREAD_GRAYSCALE)

v1=cv2.Canny(img,120,250)
v2=cv2.Canny(img,50,100)

res = np.hstack((v1,v2))
cv_show(res,'res')

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值