cv2.waitKey()
waitKey()的参数为等待键盘触发的时间,单位为毫秒,其返回值是-1(表示没有键被按下)或键盘对应的ASCII值,
如27表示按下ESC键。若参数为0,则保持窗口显示,一旦有键盘输入,则退出窗口。
OpenCV的窗口函数和waitKey()函数相互依赖。OpenCV的窗口只有在调用waitKey()函数时才会更新,waitKey()函数只有在OpenCV窗口成为活动窗口时,才能捕获输入信息。
import cv2
# 读取彩色图片
def cv_show(path, name):
# 读取图像
img = cv2.imread(path)
# 图像的显示,也可以创建多个窗口
cv2.imshow(name, img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()
# 图像的维度
print(img.shape) # (600, 600, 3)--->hwc
cv_show("cat.jpg", "heihei")
# 读取灰度图像
def cv_grayshow(path, name):
# 读取图像
img = cv2.imread(path, cv2.IMREAD_GRAYSCALE)
# 截取部分图像数据
img = img[50:350, 50:450] # 分别对应高和宽
# 图像的显示,也可以创建多个窗口
cv2.imshow(name, img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()
# 图像的维度
print(img.shape) # (300, 400)--->hw
cv_grayshow("cat.jpg", "heihei")
vc.release()
释放打开的视频文件,一定要放在循环之外。
cv2.destroyAllWindows()
用来删除窗口的,()里不指定任何参数,则删除所有窗口。
若需删除某个的窗口,()中输入某个窗口的名字;但是该句注释调之后,似乎不影响窗口的关闭。
import cv2
vc = cv2.VideoCapture("./video/错位时空.mp4")
# 如果文件正常打开,此处返回True
if vc.isOpened():
# If no frames has been grabbed the method returns false
# and the function returns empty image
isopen, frame = vc.read()
else:
isopen = False
while isopen:
# If no frames has been grabbed the method returns false
# and the function returns empty image
ret, frame = vc.read()
# 然后对于视频的每帧图像进行操作
if frame is None:
break # 有多少帧就能循环多少次,一次一帧,每帧都读取完毕后跳出循环
if ret: # 等价于if ret==True:
# 将该帧图像由BGR转化为灰度图像
gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
cv2.imshow('result', frame)
# waitKey用于控制视频每帧展示的时间;0xFF == 27对用ESC退出展示;
if cv2.waitKey(1) & 0xFF == 27:
break
vc.release() # 释放打开的视频文件,一定要放在循环之外。
cv2.destroyAllWindows()
视频结果无法上传。效果为,彩色视频以灰度图像的形式逐帧播放。通过设置循环体中waitkey()的参数,控制每帧图像的显示时间(毫秒级别),达到近似改变视频播放倍速的效果。
import cv2
path = "./cat.jpg"
# 读取图像
img = cv2.imread(path)
# 颜色通道提取
b, g, r = cv2.split(img)
print("(r.shape), (g.shape), (b.shape) ---->", r.shape, g.shape, b.shape) # h*w
img = cv2.merge((b, g, r))
print("(img.shape)---->", img.shape) # h*w*c
# 颜色通道单独显示
# (b,g,r)对应0,1,2
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 1] = 0
# 图像的显示,也可以创建多个窗口
cv2.imshow('r通道', cur_img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
# 颜色通道单独显示
# (b,g,r)对应0,1,2
cur_img = img.copy()
cur_img[:, :, 1] = 0
cur_img[:, :, 2] = 0
# 图像的显示,也可以创建多个窗口
cv2.imshow('b通道', cur_img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
# 颜色通道单独显示
# (b,g,r)对应0,1,2
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 2] = 0
# 图像的显示,也可以创建多个窗口
cv2.imshow('g通道', cur_img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()
(r.shape), (g.shape), (b.shape) ----> (600, 600) (600, 600) (600, 600)
(img.shape)----> (600, 600, 3)
注意:
cv2.imread(img)读取的图片为bgr格式,
plt.imshow(img)接收的图片是rgb格式。
-------------------------------------------------------------------------------------------
borderType=cv2.BORDER_REPLICATE:复制法,也就是复制最边缘像素
borderType=cv2.BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制,类如:
fedcba|abcdefgh|hgfedcba
borderType=cv2.BORDER_REFLECT101: 反射法,同上,不过消除了中间重复的部分,例如:fedcb|abcdefgh|gfedcba
borderType=cv2.BORDER_WRAP:外包装法,类如:cdefgh|abcdefgh|abcdefgh
borderType=cv2.BORDER_CONSTANT, value=100:常量法,需要常数值进行填充。
-------------------------------------------------------------------------------------------
import cv2
import matplotlib
import matplotlib.pyplot as plt
path = "./cat.jpg"
img = cv2.imread(path) # opencv读取的图片为bgr图片
b, g, r = cv2.split(img)
img = cv2.merge([r, g, b]) # plt.imshow(),显示图片按照rgb
top_size, bottom_size, left_size, right_size = (100, 100, 100, 100)
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,
borderType=cv2.BORDER_REFLECT)
reflect101 = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,
borderType=cv2.BORDER_REFLECT101)
wrap = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,
borderType=cv2.BORDER_WRAP)
constant = cv2.copyMakeBorder(img, top_size, bottom_size, left_size, right_size,
borderType=cv2.BORDER_CONSTANT, value=100) # valve可自行设置
# plt.subplot(231),空处2行3列6个图片的位置,从左到右,从上到下依次为123456
plt.subplot(231), 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("reflect101")
plt.subplot(235), plt.imshow(wrap), plt.title("wrap")
plt.subplot(236), plt.imshow(constant), plt.title("constant")
plt.show()
重点1:图像融合的前提是:前提两个图片的shape保持一致。融合的实质是:对应通道中的对应位置的值相加。
cat_dog = cv2.addWeighted(img0, 0.3, img1, 0.7, 0)
权值相加,img0的权是0.3,img1的权是0.7,最后一个常数是偏置项
重点2:resize的两种用法。
用法1:img1 = cv2.resize(img1, (600, 600)) # 将图像的像素改为指定大小,指定的尺寸可以大于原图,也可以小于原图。
用法2:cat_dog1 = cv2.resize(cat_dog, (0, 0), fx=2, fy=1)
fx/fy,即为长宽比,fx,fy分别为img1的长度,高度方向上像素个数的倍数;故若fx,fy若大于一,对应方向的像素个数就会变大。
import cv2
import matplotlib.pyplot as plt
path0 = "./cat.jpg"
path1 = "./dog.jpg"
# 读取图像
img0 = cv2.imread(path0)
img1 = cv2.imread(path1)
print("(img0.shape), (img1.shape) ---->", img0.shape, img1.shape) # h*w
img1 = cv2.resize(img1, (600, 600))
print("(img0.shape), (img1.shape) ---->", img0.shape, img1.shape) # h*w
cat_dog = cv2.addWeighted(img0, 0.3, img1, 0.7, 0) # 对应通道中的对应位置的值相加,前提shape一样。
b, g, r = cv2.split(cat_dog)
cat_dog = cv2.merge([r, g, b]) # plt.imshow(),显示图片按照rgb
# fx/fy,即为长宽比
# fx,fy分别为img1的长度,高度方向上像素个数的倍数
# 故若fx,fy若大于一,对应方向的像素个数就会变大
cat_dog1 = cv2.resize(cat_dog, (0, 0), fx=2, fy=1)
cat_dog2 = cv2.resize(cat_dog, (0, 0), fx=1, fy=0.5)
plt.subplot(131), plt.imshow(cat_dog), plt.title("cat_dog")
plt.subplot(132), plt.imshow(cat_dog1), plt.title("fx=2, fy=1")
plt.subplot(133), plt.imshow(cat_dog2), plt.title("fx=1, fy=0.5")
plt.show()
由图可以看出对应像素的变化。原图像素是600x600,中间图片像素是:1200x600,最右边像素是300x600。
ret, dst = cv2.threshhold(src, thresh, maxval, type)
ret:接受函数返回的阈值,即thresh
src : 输入图,只能输入单通道图像,通常来说为灰度图。
dst : 输出图
thresh: 阈值
maxval: 当像素超过了阈值(或者小于阈值,根据type来决定),所赋予的值
type :二值化操作的类型,包含以下5种类型
1.cv2.THRESH_TOZERO_INV
超过阈值部分去maxval(最大值),否则取0
2.cv2.THRESH_BINARY_INV
THRESH_BINARY的反转
3.cv2.THRESH_TRUNC
大于阈值的部分设为阈值,否则不变
4.cv2.THRESH_TOZERO
大于阈值部分不改变,否则设置为0
5.cv2.THRESH_TOZERO_INV
THRESH_TOZERO的反转
import cv2
import matplotlib.pyplot as plt
img_gray = cv2.imread("./cat.jpg")
--------------------------------------------------------------
# 下面这块代码需要解释下,因为cv2.imread读入图像是按照rgb格式,而plt.imshow(img)中按照bgr格式
传入图像参数。若直接传入rgb图像到该函数中plt.imshow(images[i], "gray"),则 "gray"参数会失效,即camp参数失效,因此不能直接传入rgb图像,故我在这里将其先进行灰度化处理。然后img的shape为由(600,600,3)变为(600,600)。然后再将其传入函数plt.imshow(images[i], "gray")中即可。但是这里存在一个疑问???
"gray",因为我已经将img转化为灰度图像了,所以我想将"gray"参数去掉。问题来了,去掉“gray”之后显示的不是原图。该问题待解决。
--------------------------------------------------------------
b, g, r = cv2.split(img_gray)
img_gray = cv2.merge((r, g, b))
img_gray = cv2.cvtColor(img_gray, cv2.COLOR_BGR2GRAY)
cv2.imshow("测试是否为灰度图", img_gray)
cv2.waitKey(0)
--------------------------------------------------------------
# 超过阈值的部分取最大值(maxval),否则取0
ret, thresh1 = cv2.threshold(img_gray, 147, 255, cv2.THRESH_BINARY)
# 超过阈值的部分取0,否则取最大值maxval
_, thresh2 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_BINARY_INV)
# 设置截断值,超过阈值的部分取阈值,小于阈值不改变。
_, thresh3 = cv2.threshold(img_gray, 50, 255, cv2.THRESH_TRUNC)
# 超过阈值的部分不变,所有、小于阈值的部分设为0
_, thresh4 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO)
# 超过阈值的部分取0,否则保持不变。
_, thresh5 = cv2.threshold(img_gray, 127, 255, cv2.THRESH_TOZERO_INV)
titles = ["Original Image", "BINARY", "BINARY_INV", "TRUNC", "TOZERO", "TOZERO_INV"]
images = [img_gray, thresh1, thresh2, thresh3, thresh4, thresh5]
fig = plt.figure(1)
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()
plt.xticks([]) # 这两行的代码是将坐标轴的参数去掉,即不显示坐标。
plt.yticks([])
下面这块代码需要解释下,因为cv2.imread读入图像是按照rgb格式,而plt.imshow(img)中按照bgr格式传入图像参数。若直接传入rgb图像到该函数中plt.imshow(images[i], “gray”),则 "gray"参数会失效,即camp参数失效,因此不能直接传入rgb图像,故我在这里将其先进行灰度化处理。然后img的shape为由(600,600,3)变为(600,600)。然后再将其传入函数plt.imshow(images[i], “gray”)中即可。但是这里存在一个疑问???
“gray”,因为我已经将img转化为灰度图像了,所以我想将"gray"参数去掉。问题来了,去掉“gray”之后显示的不是原图。该问题待解决。去掉plt.imshow(images[i], “gray”)的参数“gray“后,的图像如下图:
--------------------------------------------------------------
b, g, r = cv2.split(img_gray)
img_gray = cv2.merge((r, g, b))
img_gray = cv2.cvtColor(img_gray, cv2.COLOR_BGR2GRAY)
cv2.imshow("测试是否为灰度图", img_gray)
cv2.waitKey(0)
--------------------------------------------------------------
import cv2
img = cv2.imread("noise_salt.png")
cv2.imshow('img', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 均值滤波
blur = cv2.blur(img, (3, 3))
cv2.imshow('均值滤波', blur)
cv2.waitKey(0)
cv2.waitKey(0)
# 方框滤波,基本与均值滤波一样,唯一的区别在于normalize
# 若normalize为True,则与均值滤波i一样,否则不一样。
blur = cv2.boxFilter(img, -1, (3, 3), normalize=True)
cv2.imshow('方框滤波,normalize=True', blur)
cv2.waitKey(0)
cv2.waitKey(0)
# 方框滤波就是把卷积和中的对应图像中的数值相加,然后是否采取归一化
# 若没有归一化,容易越界,越届则保持最大值。
blur = cv2.boxFilter(img, -1, (3, 3), normalize=False)
cv2.imshow('方框滤波,normalize=False', blur)
cv2.waitKey(0)
cv2.waitKey(0)
# 高斯滤波
blur = cv2.GaussianBlur(img, (3, 3), 1)
cv2.imshow('高斯滤波', blur)
cv2.waitKey(0)
cv2.waitKey(0)
# 中值
blur = cv2.medianBlur(img, 5)
cv2.imshow('中值滤波', blur)
cv2.waitKey(0)
cv2.waitKey(0)
img = cv2.imread("cat.jpg")
cv2.imshow('未加噪音', img)
cv2.waitKey(0)
cv2.destroyAllWindows()
# 横向堆叠
res0 = np.hstack((blur, box, median))
cv2.imshow("np.hstack", res0)
cv2.waitKey(0)
# 纵向堆叠
res1 = np.vstack((blur, box, median))
cv2.imshow("np.vstack", res1)
cv2.waitKey(0)
cv2.destroyAllWindows()
import cv2
import numpy as np
ziti = cv2.imread("./picture/ziti.png")
yuan = cv2.imread("./picture/yuan.png")
kernel = np.ones((3, 3)) # kernel的尺寸越大,腐蚀的也越多。
print(kernel)
# 腐蚀操作
# zt0 = cv2.erode(ziti, kernel, iterations=2)
zt0 = cv2.morphologyEx(ziti, cv2.MORPH_ERODE, kernel, iterations=2)
cv2.imshow("腐蚀操作,迭代2次", zt0)
cv2.waitKey(0)
# 膨胀操作,在zt0的基础上进行膨胀操作
zt1 = cv2.dilate(zt0, kernel, iterations=2)
# zt1 = cv2.morphologyEx(zt0, cv2.MORPH_DILATE, kernel, iterations=2)
cv2.imshow("膨胀操作,迭代2次", zt1)
cv2.waitKey(0)
# 开运算与闭运算
# 开运算,效果等于腐蚀操作再膨胀操作
zt2 = cv2.morphologyEx(ziti, cv2.MORPH_OPEN, kernel, iterations=2)
cv2.imshow("开运算,迭代2次", zt2)
cv2.waitKey(0)
# 闭运算,效果等于膨胀操作再腐蚀操作
zt3 = cv2.morphologyEx(ziti, cv2.MORPH_CLOSE, kernel, iterations=2)
cv2.imshow("闭运算,迭代2次", zt3)
cv2.waitKey(0)
# 梯度运算
# 梯度运算等于膨胀运算减去腐蚀运算
yuan0 = cv2.morphologyEx(yuan, cv2.MORPH_ERODE, kernel, iterations=10)
yuan1 = cv2.morphologyEx(yuan, cv2.MORPH_DILATE, kernel, iterations=10)
yuan2 = cv2.morphologyEx(yuan, cv2.MORPH_GRADIENT, kernel, iterations=10)
yuan3 = np.hstack((yuan0, yuan1, yuan2))
cv2.imshow("左:腐蚀---中:膨胀---右:梯度运算(膨胀减去腐蚀)", yuan3)
cv2.waitKey(0)
cv2.destroyAllWindows()
主要算子:
Soble算子
Schhar算子
Sobel算子的加强版
Laplacian算子
经常与其他方法一起使用,而很少单独使用。
import cv2
import os
import numpy as np
path = "./picture/梯度处理"
if not os.path.exists(path):
os.makedirs(path)
def cvshow(name, src):
cv2.namedWindow(name, 0)
cv2.resizeWindow(name, 1920, 1080)
cv2.imshow(name, src)
name = os.path.join(path, name + ".jpg") # 需要保存的图片的路径
cv2.imwrite(name, src) # 保存图片
cv2.waitKey(0)
# Sobel 算子
img = cv2.imread("./picture/yuan.png")
# img = cv2.imread("./机械楼下.jpg")
cvshow("原图", img)
# 右减左,下减上
img0 = cv2.Sobel(img, ddepth=-1, dx=1, dy=0, ksize=3) # dx=1,dy=0,左右梯度计算
img1 = cv2.Sobel(img, ddepth=-1, dx=0, dy=1, ksize=3) # dx=0,dy=1,上下梯度计算
img2 = cv2.addWeighted(img0, 0.5, img1, 0.5, 0)
img3 = np.hstack((img0, img1, img2))
cvshow("【sobelx(左)sobely(中)sobelx+sobely(右)】没有做绝对值处理,之后各图均做了绝对值处理", img3)
# ddepth=cv2.CV_64F如此设置,梯度计算右减左(或着下减上),若得到负数值,则保留负值,而不是通过0进行截断
img0 = cv2.Sobel(img, ddepth=cv2.CV_64F, dx=1, dy=0, ksize=3)
img1 = cv2.Sobel(img, ddepth=cv2.CV_64F, dx=0, dy=1, ksize=3)
img0 = cv2.convertScaleAbs(img0) # 若梯度计算时存在负数的值,则将其转化为其绝对值
img1 = cv2.convertScaleAbs(img1) # 若梯度计算时存在负数的值,则将其转化为其绝对值
img21 = cv2.addWeighted(img0, 0.5, img1, 0.5, 0) # 建议如此做,而不建议直接设置dx=1,dy=1。
img3 = np.hstack((img0, img1, img21))
cvshow("sobelx(左) sobely(中) sobelx+sobely(右)", img3)
img4 = cv2.Sobel(img, ddepth=cv2.CV_64F, dx=1, dy=1, ksize=3)
img4 = cv2.convertScaleAbs(img4)
img5 = np.hstack((img4, img21))
cvshow("Sobel算子,dx=1,dy=1,直接计算上下左右梯度(左),Sobel算子,dx=1,dy=0与dx=0,dy=1分别处理之后融合(右)", img5)
# Schhar算子
img0 = cv2.Scharr(img, ddepth=cv2.CV_64F, dx=1, dy=0)
img1 = cv2.Scharr(img, ddepth=cv2.CV_64F, dx=0, dy=1)
img0 = cv2.convertScaleAbs(img0) # 若梯度计算时存在负数的值,则将其转化为其绝对值
img1 = cv2.convertScaleAbs(img1) # 若梯度计算时存在负数的值,则将其转化为其绝对值
img22 = cv2.addWeighted(img0, 0.5, img1, 0.5, 0) # 建议如此做,而不建议直接设置dx=1,dy=1。
# lapacian算子,经常与其他方法一起使用,而很少单独使用。
img23 = cv2.Laplacian(img, ddepth=cv2.CV_64F)
img23 = cv2.convertScaleAbs(img23) # 若梯度计算时存在负数的值,则将其转化为其绝对值
img5 = np.hstack((img21, img22, img23))
cvshow("sobelx+sobely(左) Schharx+Schhary(中) lapacian(右)", img5)
空缺的部分是由于左减右(或者上减下)之后为负值,负值被零截断,故为黑色。
该图与上一张图的区别在于,生成该张图片时的负值通过,cv.CV_64F保留了下来。然后通过cv2.convertScaleAbs(img)或得了负值的绝对值,故为正数,显示白色。
直接通过对上下左右进行梯度运算的效果,不如,分别对上下和左右进行梯度运算之后的效果叠加。
可以看出laplacian算子效果明显不如另外两个算子,故经常很少单独使用,一般是配合其他方法时搭配使用。
步骤:
1.使用高斯滤波器,以平滑图像,滤除噪声。
2.计算图像中每个像素点的梯度强度和方向。
3.应用非极大值抑制(NMS),以消除边缘检测器带来的杂散响应。
4.应用双阈值(Double-Threshold)检测来确定真实和潜在的边缘。
5.通过抑制鼓励的如哟边缘最终完成边缘检测。
高斯滤波器:
梯度和方向
采用的Sobel算子:
非极大值抑制: