数据读取-图像
- cv2.IMREAD_COLOR:彩色图像
- cv2.IMREAD_GRAYSCALE:灰度图像
opencv对于读进来的图片的通道排列是BGR,而不是主流的RGB!谨记!
#opencv读入的矩阵是BGR,如果想转为RGB,可以这么转
img = cv2.imread('1.jpg')
img = cv2.cvtColor(img4,cv2.COLOR_BGR2RGB)
import cv2
import matplotlib.pyplot as plt
import numpy as np
%matplotlib inline
# 由于 %matplotlib inline 的存在,当输入plt.plot(x,y_1)后,不必再输入 plt.show(),图像将自动显示出来
img = cv2.imread('pic/cat.jpg')
img # 值都是在0~255之间,0是白色,255是黑色。共有3个维度,[h, w, c]
array([[[161, 166, 169],
[110, 115, 118],
[110, 115, 118],
...,
[ 93, 103, 113],
[101, 111, 121],
[106, 116, 126]],
[[162, 167, 170],
[130, 135, 138],
[114, 119, 122],
...,
[ 92, 102, 112],
[ 98, 108, 118],
[103, 113, 123]],
[[165, 170, 173],
[158, 163, 166],
[138, 143, 146],
...,
[ 95, 105, 115],
[ 98, 108, 118],
[102, 112, 122]],
...,
[[200, 209, 218],
[178, 187, 196],
[147, 156, 165],
...,
[178, 187, 200],
[151, 160, 173],
[104, 113, 126]],
[[234, 243, 252],
[197, 206, 215],
[151, 160, 169],
...,
[167, 176, 189],
[182, 193, 207],
[159, 170, 184]],
[[151, 160, 169],
[130, 139, 148],
[111, 120, 129],
...,
[133, 142, 155],
[184, 195, 209],
[184, 195, 209]]], dtype=uint8)
# 图像的显示,也可以创建多个窗口
cv2.imshow('img', img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()
def cv_show(name, img): # 定义一个函数,使得可以显示图片并且任意键关闭
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv_show('img',img)
img.shape # 查看图像的大小,3表示是RGB这3个颜色通道
(720, 720, 3)
# 以灰度图读取进来
gray_img = cv2.imread('pic/cat2.jpg', cv2.IMREAD_GRAYSCALE)
gray_img
array([[ 62, 125, 105, ..., 242, 168, 173],
[192, 128, 83, ..., 234, 214, 171],
[ 88, 124, 127, ..., 228, 211, 170],
...,
[126, 145, 118, ..., 96, 95, 122],
[133, 160, 152, ..., 94, 98, 154],
[157, 173, 170, ..., 120, 119, 135]], dtype=uint8)
cv_show('gray_img', gray_img)
gray_img.shape # 只有两个维度,只有1个颜色通道
(720, 720)
cv2.imwrite('pic/gray_cat.jpg', gray_img) # 保存图片
True
type(gray_img) # 是numpy中ndarray类型
numpy.ndarray
gray_img.size # 像素点个数=720*720*1
518400
gray_img.dtype # 元素数据类型
dtype('uint8')
数据读取-视频
- cv2.VideoCapture可以捕获摄像头有,用数字来控制不同的设备,例如0,1
- 如果是视频文件,直接指定好路径即可。
基本上1s内30帧,人眼就看不出来卡顿了。
人脸识别不是对于视频去做的,也是对于图像去做的。
read函数返回2个类型,第一个是bool类型,第二个是图像,第一个open随便改名都可以
vc = cv2.VideoCapture(r'D:\SoftWareInstallMenu\EVCapture\A_EVsavefile\Latex使用方法.mp4')
# 检查是否打开正确
if vc.isOpened():
open, frame = vc.read() # open表示是否正确打开,frame是第一帧的图像
else:
open = False
while open:
ret, frame = vc.read()
if frame is None:
break
if ret == True:
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY) # 转成一个灰度图
cv2.imshow('result', gray)
if cv2.waitKey(10) & 0xFF == 27: # 10表示切换到下一帧的时间,27表示退出键
break
vc.release()
cv2.destroyAllWindows()
截取部分图像数据
ROI:感兴趣的区域
切分:b, g, r = img.split(img)
组合:img = cv2.merge((b, g, r))
只保留一种颜色通道
img = cv2.imread('pic/cat.jpg') # 原图的shape是(720, 720, 3)
cat = img[200:400, 200:400]
cv_show('cat',cat)
颜色通道提取
b, g, r = cv2.split(img) # 把3个颜色通道单独切分出来
b
array([[161, 110, 110, ..., 93, 101, 106],
[162, 130, 114, ..., 92, 98, 103],
[165, 158, 138, ..., 95, 98, 102],
...,
[200, 178, 147, ..., 178, 151, 104],
[234, 197, 151, ..., 167, 182, 159],
[151, 130, 111, ..., 133, 184, 184]], dtype=uint8)
# 或者写成这样,效果也是一样的
b = img[:, :, 0]
b
array([[161, 110, 110, ..., 93, 101, 106],
[162, 130, 114, ..., 92, 98, 103],
[165, 158, 138, ..., 95, 98, 102],
...,
[200, 178, 147, ..., 178, 151, 104],
[234, 197, 151, ..., 167, 182, 159],
[151, 130, 111, ..., 133, 184, 184]], dtype=uint8)
b.shape # b, g, r 的shape都是一样的,跟图像大小,切分出来不会发生改变
(720, 720)
img2 = cv2.merge((b, g, r))
cv_show('img2',img2)
# 只保留B通道颜色,其余通道颜色置为0
cur_img = img.copy()
cur_img[:, :, 1] = 0
cur_img[:, :, 2] = 0
cv_show('cur_img.png', cur_img)
边界填充
import matplotlib.pyplot as plt
img = cv2.imread('pic/cat.jpg')
img = cv2.cvtColor(img,cv2.COLOR_BGR2RGB) # 因为cv2读取的图片是BGR的,所以这里转成RGB才为真实颜色
plt.imshow(img)
<matplotlib.image.AxesImage at 0x18ccee38dc8>
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cIGnoW9W-1647082756501)(output_29_1.png)]
top_size, bottom_size, left_size, right_size = (50, 50, 50, 50)
replicate = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, borderType = cv2.BORDER_REPLICATE)
reflect = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_REFLECT_101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size, cv2.BORDER_CONSTANT, value=0) #常量设为0,边界全为黑色
import matplotlib.pyplot as plt
plt.subplot(2,3,1), plt.imshow(img), plt.title('ORIGINAL')
plt.subplot(232), plt.imshow(replicate), plt.title('REPLICATE')
plt.subplot(233), plt.imshow(reflect), plt.title('REFLECT')
plt.subplot(234), plt.imshow(reflect101), plt.title('REFLECT_101')
plt.subplot(235), plt.imshow(wrap), plt.title('WRAP')
plt.subplot(236), plt.imshow(constant), plt.title('CONSTANT')
(<AxesSubplot:title={'center':'CONSTANT'}>,
<matplotlib.image.AxesImage at 0x18cd1026a88>,
Text(0.5, 1.0, 'CONSTANT'))
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UwfBgdsE-1647082756502)(output_31_1.png)]
- BORDER_REFLICATE:复制法,也就是赋值最边缘像素
- BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制,例如fedcba|abcdefgh|hgfedcb
- BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴,对称,gfedcb|abcdefgh|gfedcba
- BORDER_WRAP:外包装法cdefgh|abcdefgh|abcdefg
- BORDER_CONSTANT:常量法,常数值填充。
数值计算
- cv2中的add加法,当越界0~255时,就取最大值255
img_cat = cv2.imread('pic/cat.jpg')
img_dog = cv2.imread('pic/dog3.jpg')
img_cat[:5, :, 0] # 只打印前5行,B通道的数据进行查看
array([[161, 110, 110, ..., 93, 101, 106],
[162, 130, 114, ..., 92, 98, 103],
[165, 158, 138, ..., 95, 98, 102],
[154, 156, 144, ..., 96, 98, 101],
[119, 134, 136, ..., 94, 99, 103]], dtype=uint8)
img_cat2 = img_cat + 10 # 所有位置的元素值都+10,但是shape没有发生变化
img_cat2[:5, :, 0]
array([[171, 120, 120, ..., 103, 111, 116],
[172, 140, 124, ..., 102, 108, 113],
[175, 168, 148, ..., 105, 108, 112],
[164, 166, 154, ..., 106, 108, 111],
[129, 144, 146, ..., 104, 109, 113]], dtype=uint8)
cv_show('img_cat2',img_cat2)
# 相当于%256,numpy的加法
(img_cat + img_cat2)[:5, :, 0]
array([[ 76, 230, 230, ..., 196, 212, 222],
[ 78, 14, 238, ..., 194, 206, 216],
[ 84, 70, 30, ..., 200, 206, 214],
[ 62, 66, 42, ..., 202, 206, 212],
[248, 22, 26, ..., 198, 208, 216]], dtype=uint8)
# cv2中的add加法,当越界0~255时,就取最大值255
cv2.add(img_cat, img_cat2)[:5, : ,0]
array([[255, 230, 230, ..., 196, 212, 222],
[255, 255, 238, ..., 194, 206, 216],
[255, 255, 255, ..., 200, 206, 214],
[255, 255, 255, ..., 202, 206, 212],
[248, 255, 255, ..., 198, 208, 216]], dtype=uint8)
图像融合
- shape值必须相同,不同的话,就需要进行resize,resize中有点奇怪,高宽的位置是变换的
- cv2.AddWeighted(src1,a,src2,b,c) 表示“结果图像=图像1×系数1+图像2×系数2+亮度调节量”,c一定要写,至少写成0
img_cat + img_dog
---------------------------------------------------------------------------
ValueError Traceback (most recent call last)
~\AppData\Local\Temp/ipykernel_67260/4029550109.py in <module>
----> 1 img_cat + img_dog
ValueError: operands could not be broadcast together with shapes (720,720,3) (638,563,3)
img_cat = cv2.resize(img_cat, (563,638))
img_cat.shape
plt.imshow((img_cat + img_dog))
res = cv2.addWeighted(img_cat, 0.4, img_dog, 0.6, 0) # 最后面那个0是指偏置,再另外加的值,应该是有一种调亮的效果
plt.imshow(res)
res = cv2.resize(img_cat, (0, 0), fx=3, fy=3) # resize时,没有指定具体大小,而是指定变长变宽的倍数
plt.imshow(res)
图像阈值
ret, dst = cv2.threshold(src, thresh, maxval, type)
- src:输入图,只能输入单通道图像,通常来说为灰度图
- dst:输出图
- thresh:阈值,通常为127
- maxval:当像素值超过了阈值(或者小于阈值,根据type来决定),所赋予的值
- type:二值化操作的类型,包含以下5种类型:
- cv2.THRESH_BINARY 超过阈值部分取maxval(最大值),否则取0
- cv2.THRESH_BINARY_INV 是THRESH_BINARY的反转
- cv2.THRESH_TRUNC 大于阈值部分设为阈值,否则不变
- cv2.THRESH_TOZERO 大于阈值部分不改变,否则设为0
- cv2.THRESH_TOZERO_INV 是THRESH_TOZERO的反转
img_gray = cv2.imread('pic/cat.jpg', cv2.IMREAD_GRAYSCALE) # 以灰度图读进来
ret, thresh1 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY)
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)
cmap的知识可以参考一下这个博客:Matplotlib的imshow()函数颜色映射(cmap的取值)
常用的应该就是’gray’吧
这是官网地址:Choosing Colormaps in Matplotlib
import matplotlib.pyplot as plt
titles = ['Original Image', 'Binary', 'Binary_inv', 'Trunc', 'Tozero', 'Tozero_inv']
images = [img_gray, thresh1, thresh2, thresh3, thresh4, thresh5]
for i in range(6):
plt.subplot(2,3,i+1)
plt.imshow(images[i], cmap = 'gray') # cmap加上这个'gray'参数,是为了显示灰色图,cmap是颜色散射值
plt.title(titles[i])
plt.xticks([]) # 数组类型,用于设置X轴刻度间隔.这里设置为空,则表示不显示刻度
plt.yticks([])
plt.show()
图像平滑处理
没有找到噪声图片,就只有手动做添加照片了
- 经典的lena照片下载地址:wiki-Lenna-512×512.png
- csdn上添加椒盐噪声、高斯噪声、随机噪声的教程:Python+OpenCV批量给图片加噪声
random.randint(0,3) 表示返回0~3之间的任意整数,左闭右闭
np.arange(0,3) 表示生成[ 0 1 2 ]数组,左闭右开
range(0,3) 表示范围0~2之间的数据
添加噪声
# 导入头文件
import os
import cv2
import numpy as np
import random
# 添加椒盐噪声
def sp_noise(noise_img, proportion):
'''
添加椒盐噪声
proportion的值表示加入噪声的量,可根据需要自行调整
return: img_noise
'''
height, width = noise_img.shape[0], noise_img.shape[1]#获取高度宽度像素值
num = int(height * width * proportion) #一个准备加入多少噪声小点
for i in range(num):
w = random.randint(0, width - 1) # 任意地方添加的0黑点,255白点
h = random.randint(0, height - 1)
if random.randint(0, 1) == 0:
noise_img[h, w] = 0
else:
noise_img[h, w] = 255
return noise_img
# 添加高斯噪声
def gaussian_noise(img, mean, sigma):
'''
此函数用将产生的高斯噪声加到图片上
传入:
img : 原图
mean : 均值
sigma : 标准差
返回:
gaussian_out : 噪声处理后的图片
'''
# 将图片灰度标准化
img = img / 255
# 产生高斯 noise
noise = np.random.normal(mean, sigma, img.shape)
# 将噪声和图片叠加
gaussian_out = img + noise
# 将超过 1 的置 1,低于 0 的置 0
gaussian_out = np.clip(gaussian_out, 0, 1)
# 将图片灰度范围的恢复为 0-255
gaussian_out = np.uint8(gaussian_out*255)
# 将噪声范围搞为 0-255
# noise = np.uint8(noise*255)
return gaussian_out# 这里也会返回噪声,注意返回值
# 添加随机噪声
def random_noise(image,noise_num):
'''
添加随机噪点(实际上就是随机在图像上将像素点的灰度值变为255即白色)
param image: 需要加噪的图片
param noise_num: 添加的噪音点数目
return: img_noise
'''
# 参数image:,noise_num:
img_noise = image
# cv2.imshow("src", img)
rows, cols, chn = img_noise.shape
# 加噪声
for i in range(noise_num):
x = np.random.randint(0, rows)#随机生成指定范围的整数
y = np.random.randint(0, cols)
img_noise[x, y, :] = 255
return img_noise
# 读取并保存
def convert(input_dir, output_dir):
for filename in os.listdir(input_dir):
path = input_dir + "/" + filename # 获取文件路径
print("doing... ", path)
noise_img = cv2.imread(path)#读取图片
img_noise = gaussian_noise(noise_img, 0, 0.12) # 高斯噪声
cv2.imwrite(output_dir+'/gaussian_'+filename,img_noise )
img_noise = sp_noise(noise_img, 0.025)# 椒盐噪声
cv2.imwrite(output_dir+'/sp_'+filename,img_noise )
img_noise = random_noise(img_noise, 500)# 随机噪声
cv2.imwrite(output_dir+'/random_'+filename,img_noise )
input_dir = "pic/input" # 输入数据文件夹
output_dir = "pic/picout" # 输出数据文件夹
convert(input_dir, output_dir)
平滑处理
卷积核会与图像中相应大小的像素点依次做内积。例如3×3的卷积核,则会与图像中3×3个像素点依次做内积操作,然后再做求均值、中位数等操作。
通常情况下,卷积核的大小是一个奇数,例如3,5等。
- 均值滤波,简单的平均卷积操作
- 方框滤波,基本和均值一样,可以选择归一化,当归一化为true时,则效果与blur相同
import cv2
import numpy as np
import matplotlib.pyplot as plt
img = cv2.imread('pic/picout/sp_Lenna.png')
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB) # 将BGR图像转为RGB图像
def cv_show(name, img):
cv2.imshow(name, img)
cv2.waitKey(0)
cv2.destroyAllWindows()
cv_show('img',img)
# 均值滤波
# 简单的平均卷积操作
blur = cv2.blur(img, (3,3)) # 采用3×3的卷积核
cv_show('blur', blur)
# 就是大小为3*3且元素全为1的矩阵与图像的9个像素点做内积,相当于就是把选中的9个值加起来,再÷9,求得均值
# 方框滤波
# 基本和均值一样,可以选择归一化,当归一化为true时,则效果与blur相同;当为False时,就少了÷9的步骤,所以值就会越界(0~255)
# 越界值就直接取最大值255,所以会有很多地方为白色(255)
box = cv2.boxFilter(img, -1, (3,3), normalize=False) # -1表示颜色通道数与图像一致,通常情况下不要更改
cv_show('box', box)
# 高斯滤波(好像是有点类似于0-1正态分布)
# 高斯模糊的卷积核里的数值是满足高斯分布,相当于更重视中间的,离自己越近的影响越大,相当于给自己构造了一个权重矩阵
# (5,5)为滤波器的大小;1为滤波器的标准差,如果标准差这个参数设置为0,则程序会根据滤波器大小自动计算得到标准差。
# 高斯滤波器模板的生成最重要的参数就是高斯分布的标准差σ
# σ较大,则生成的模板的各个系数相差就不是很大,比较类似均值模板,对图像的平滑效果比较明显
gaussian = cv2.GaussianBlur(img, (3,3), 1)
cv_show('gaussian', gaussian)
# 中值滤波
# 相当于用中值替代
median = cv2.medianBlur(img, 5)
cv_show('median', median)
# 展示所有的
res = np.hstack((img, blur, gaussian, median))
res = cv2.resize(res, (0,0), fx=0.5, fy=0.5)
cv_show('all pics', res)
形态学
腐蚀操作
拿erosion.png举例,背景都是黑色,爱心是白色的,且毛刺也是白色的。
腐蚀 cv2.erode():当卷积核圈住的范围内有黑色,就将该范围内的全变为黑色,所以会缩小一圈。(有不想要的像素就全不要)
膨胀 cv2.dilate():相反,只要卷积核圈住的范围内有白色,就将该范围内全置为白色,所以会变大一圈。(有想要的像素就全要)
去掉毛刺,往里面缩小,腐蚀次数越多,图像变得越小。跟滤波很像。
kernel的设置还是挺重要的,设置得太大的化,形态会发生改变。
img = cv2.imread('pic/erosion.png') # 大爱心
kernel = np.ones((3,3), np.uint8)
erosion1 = cv2.erode(img, kernel, iterations = 1) # iterations腐蚀次数
erosion2 = cv2.erode(img, kernel, iterations = 2) # iterations腐蚀次数
erosion3 = cv2.erode(img, kernel, iterations = 3) # iterations腐蚀次数
res = np.hstack((img, erosion1, erosion2, erosion3))
res = cv2.resize(res, (0,0), fx=0.6, fy= 0.6)
cv_show('res', res)
img = cv2.imread('pic/erosion2.png') # 线条字母
kernel = np.ones((3,3), np.uint8)
erosion1 = cv2.erode(img, kernel, iterations = 1) # iterations腐蚀次数
erosion2 = cv2.erode(img, kernel, iterations = 2) # iterations腐蚀次数
erosion3 = cv2.erode(img, kernel, iterations = 3) # iterations腐蚀次数
res = np.hstack((img, erosion1, erosion2, erosion3))
res = cv2.resize(res, (0,0), fx=0.6, fy= 0.6)
cv_show('res', res)
膨胀操作
图像会变得越来越大
当图像中是一些线条数据时,为了消去不必要的毛刺,先进行腐蚀操作消去毛刺,但是数据会受到一些损害变得很细。所以还需要再进行膨胀操作。
img = cv2.imread('pic/erosion2.png') # 线条字母
kernel = np.ones((3,3), np.uint8)
# 腐蚀+膨胀
erosion1 = cv2.erode(img, kernel, iterations = 1)
dilate1 = cv2.dilate(erosion1, kernel, iterations = 1)
erosion2 = cv2.erode(img, kernel, iterations = 2)
dilate2 = cv2.dilate(erosion2, kernel, iterations = 2)
erosion3 = cv2.erode(img, kernel, iterations = 1)
dilate3 = cv2.dilate(erosion3, kernel, iterations = 3)
res = np.hstack((img, dilate1, dilate2, dilate3))
res = cv2.resize(res, (0,0), fx=0.6, fy= 0.6)
cv_show('res', res)
开运算和闭运算
其实就是将上面的腐蚀和膨胀结合在一起了
cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel) # 开运算,先腐蚀再膨胀
cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel) # 闭运算,先膨胀再腐蚀
# 开运算
img = cv2.imread('pic/erosion2.png')
kernel = np.ones((5,5), dtype = np.uint8)
opening = cv2.morphologyEx(img, cv2.MORPH_OPEN, kernel)
cv_show('opening', cv2.resize(opening, (0,0), fx=0.6, fy=0.6))
# 闭运算
img = cv2.imread('pic/erosion2.png')
kernel = np.ones((5,5), dtype = np.uint8)
closing = cv2.morphologyEx(img, cv2.MORPH_CLOSE, kernel)
cv_show('closing', cv2.resize(closing, (0,0), fx=0.6, fy=0.6))
梯度运算
梯度=膨胀-腐蚀
cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel)
# 梯度=膨胀-腐蚀
img = cv2.imread('pic/erosion.png')
kernel = np.ones((5,5), dtype = np.uint8)
dilate = cv2.dilate(img, kernel, iterations = 5) # 膨胀
erosion = cv2.erode(img, kernel, iterations = 5) # 腐蚀
gradient = cv2.morphologyEx(img, cv2.MORPH_GRADIENT, kernel) # 梯度运算
res = np.hstack((img, dilate, erosion, gradient))
cv_show('res', cv2.resize(res,(0,0),fx=0.6,fy=0.6))
礼帽与黑帽
- 礼帽 = 原始输入 - 开运算结果 ,也就是只剩下那些毛刺了。cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
- 黑帽 = 闭运算 - 原始输入,剩下毛刺的轮廓(感觉是出现毛刺的位置点)。cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
# 礼帽
img = cv2.imread('pic/erosion2.png')
kernel = np.ones((5,5), dtype=np.uint8)
tophat = cv2.morphologyEx(img, cv2.MORPH_TOPHAT, kernel)
cv_show('tophat', tophat)
# 黑帽
blackhat = cv2.morphologyEx(img, cv2.MORPH_BLACKHAT, kernel)
cv_show('blackhat', blackhat)
# 原图、礼帽、黑帽
res = np.hstack((img, tophat, blackhat))
cv_show('res', cv2.resize(res, (0,0), fx=0.6, fy=0.6))
图像梯度
Sobel算子
G x = [ − 1 0 + 1 − 2 0 + 2 − 1 0 + 1 ] ∗ A and G y = [ − 1 − 2 − 1 0 0 0 + 1 + 2 + 1 ] ∗ A \mathbf{G}_{x}=\left[\begin{array}{ccc} -1 & 0 & +1 \\ -2 & 0 & +2 \\ -1 & 0 & +1 \end{array}\right] * \mathbf{A} \quad \text { and } \quad \mathbf{G}_{y}=\left[\begin{array}{ccc} -1 & -2 & -1 \\ 0 & 0 & 0 \\ +1 & +2 & +1 \end{array}\right] * \mathbf{A} Gx=⎣⎡−1−2−1000+1+2+1⎦⎤∗A and Gy=⎣⎡−10+1−20+2−10+1⎦⎤∗A
- 感觉梯度就是边缘的意思,通常是用来进行边缘检测的
- G x G_x Gx表示计算水平方向的像素差值,右 - 左
- G y G_y Gy表示计算竖直方向的像素差值,下 - 上
- 用 cv2.CV_64F 保留负数,再用 取绝对值。不然会出现只有一半的边缘被提取出来。opencv默认范围是0~255,超出范围就设为0或255。
- 不要一次性水平、竖直都去提取边缘,这样提取的效果不好。应该先水平提取再竖直提取,再用 进行图像融合
dst = cv2.Sobel(src, ddepth, dx, dy, ksize)
- ddepth:图像的深度,通常设为-1
- dx和dy分别表示水平和竖直方向,通常设为dx=0,dy=1 或者 dx=1,dy=0,不要设置成dx=1,dy=1
- ksize是Sobel算子的大小,通常设为3
# 先用大爱心提取边缘
img = cv2.imread('pic/erosion.png')
img = cv2.resize(img, (0,0), fx=0.6, fy=0.6)
cv_show('img',img)
白到黑是正数,黑到白就是负数了,所有的负数都会被截断为0,所以要取绝对值
cv2.CV_64F表示64位浮点数,写成cv2.CV_64FC3中的C3表示3通道。好像默认为C1,cv2.CV_64F <==> cv2.CV_64FC1
[图像处理]-Opencv中数据类型CV_8U, CV_16U, CV_16S, CV_32F 以及 CV_64F是什么?
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3) # 用cv2.CV_64F保留负数
sobelx = cv2.convertScaleAbs(sobelx) # 取绝对值
cv_show('sobelx', sobelx)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobely = cv2.convertScaleAbs(sobely)
cv_show('sobely', sobely)
分别计算出了x和y,再进行图像融合,得到最终的边缘图
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
直接用Sobel同时进行水平和竖直边缘提取,不建议!!!
sobelxxyy = cv2.Sobel(img, cv2.CV_64F, 1, 1, ksize=3)
对比展示
res = np.hstack((img, sobelx, sobely, sobelxy, sobelxxyy))
cv_show('res', res)
把代码整合在一起,提取一张复杂图像的边缘
# 以灰度图读取图像
img = cv2.imread('pic/Lenna.png', cv2.IMREAD_GRAYSCALE)
img = cv2.resize(img, (0,0), fx=0.8, fy=0.8)
# 提取sobelx
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobelx = cv2.convertScaleAbs(sobelx)
# 提取sobely
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobely = cv2.convertScaleAbs(sobely)
# 融合sobelxy
sobelxy = cv2.addWeighted(sobelx, 0.5, sobely, 0.5, 0)
# 显示查看
res = np.hstack((img, sobelxy))
cv_show('res', res)
Scharr算子
G x = [ − 3 0 3 − 10 0 10 − 3 0 3 ] ∗ A and G y = [ − 3 − 10 − 3 0 0 0 − 3 − 10 − 3 ] ∗ A \mathbf{G}_{x}=\left[\begin{array}{ccc} -3 & 0 & 3 \\ -10 & 0 & 10 \\ -3 & 0 & 3 \end{array}\right] * \mathbf{A} \quad \text { and } \quad \mathbf{G}_{y}=\left[\begin{array}{ccc} -3 & -10 & -3 \\ 0 & 0 & 0 \\ -3 & -10 & -3 \end{array}\right] * \mathbf{A} Gx=⎣⎡−3−10−30003103⎦⎤∗A and Gy=⎣⎡−30−3−100−10−30−3⎦⎤∗A
- 关注得更多,线条更丰富一些
laplacian算子(拉普拉斯算子)
G = [ 0 1 0 1 − 4 1 0 1 0 ] \mathbf{G}=\left[\begin{array}{ccc} 0 & 1 & 0 \\ 1 & -4 & 1 \\ 0 & 1 & 0 \end{array}\right] G=⎣⎡0101−41010⎦⎤
- 像是:中间 - 边缘
- 没有x、y那种水平、竖直的感觉了
- 是二阶导,对边界更敏感
- 存在的问题:对一些噪音点更敏感
- 一般是与其他算法共同使用,不会单独去使用它