opencv-python学习教程【持续更新】

Opencv-python学习系列教程

  • 一.图片的读取
    • 代码
    • 实验结果
  • 二.视频的读取
    • 代码
    • 实验结果
  • 三.颜色通道的读取
    • 代码
    • 实验结果
  • 四.图像边界填充
    • 代码
    • 实验结果:
  • 五.图像融合
    • 代码
    • 运行效果
  • 六.图像阈值
    • 代码
    • 实验结果
    • 存疑 (未解决)
  • 七.图像滤波
    • 代码
    • 实验结果:
  • 八.图像形态学
    • 代码
    • 运行结果
  • 九.图像梯度处理
    • 代码:
    • 运行结果
  • 十.Canny边缘检测
    • 关注博主,后续内容持续更新中。。。。。。

一.图片的读取

代码

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")

实验结果

opencv-python学习教程【持续更新】_第1张图片
(600, 600, 3)

opencv-python学习教程【持续更新】_第2张图片
(300, 400)

二.视频的读取

代码

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)对应012
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 1] = 0
# 图像的显示,也可以创建多个窗口
cv2.imshow('r通道', cur_img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)

# 颜色通道单独显示
# (b,g,r)对应012
cur_img = img.copy()
cur_img[:, :, 1] = 0
cur_img[:, :, 2] = 0
# 图像的显示,也可以创建多个窗口
cv2.imshow('b通道', cur_img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)

# 颜色通道单独显示
# (b,g,r)对应012
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 2] = 0
# 图像的显示,也可以创建多个窗口
cv2.imshow('g通道', cur_img)
# 等待时间,毫秒级,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()

实验结果

opencv-python学习教程【持续更新】_第3张图片
(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()

实验结果:

opencv-python学习教程【持续更新】_第4张图片

五.图像融合

代码

重点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()

运行效果

opencv-python学习教程【持续更新】_第5张图片
由图可以看出对应像素的变化。原图像素是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()

实验结果

opencv-python学习教程【持续更新】_第6张图片
opencv-python学习教程【持续更新】_第7张图片

    plt.xticks([])  # 这两行的代码是将坐标轴的参数去掉,即不显示坐标。
    plt.yticks([])

opencv-python学习教程【持续更新】_第8张图片

存疑 (未解决)

下面这块代码需要解释下,因为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)
--------------------------------------------------------------

opencv-python学习教程【持续更新】_第9张图片

七.图像滤波

代码

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()

运行结果

opencv-python学习教程【持续更新】_第10张图片

九.图像梯度处理

主要算子:
Soble算子在这里插入图片描述
Schhar算子
Sobel算子的加强版opencv-python学习教程【持续更新】_第11张图片
Laplacian算子
经常与其他方法一起使用,而很少单独使用。opencv-python学习教程【持续更新】_第12张图片

代码:

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)


运行结果

opencv-python学习教程【持续更新】_第13张图片
opencv-python学习教程【持续更新】_第14张图片
空缺的部分是由于左减右(或者上减下)之后为负值,负值被零截断,故为黑色。
opencv-python学习教程【持续更新】_第15张图片该图与上一张图的区别在于,生成该张图片时的负值通过,cv.CV_64F保留了下来。然后通过cv2.convertScaleAbs(img)或得了负值的绝对值,故为正数,显示白色。
opencv-python学习教程【持续更新】_第16张图片直接通过对上下左右进行梯度运算的效果,不如,分别对上下和左右进行梯度运算之后的效果叠加。
opencv-python学习教程【持续更新】_第17张图片可以看出laplacian算子效果明显不如另外两个算子,故经常很少单独使用,一般是配合其他方法时搭配使用。

十.Canny边缘检测

步骤:
1.使用高斯滤波器,以平滑图像,滤除噪声。
2.计算图像中每个像素点的梯度强度和方向。
3.应用非极大值抑制(NMS),以消除边缘检测器带来的杂散响应。
4.应用双阈值(Double-Threshold)检测来确定真实和潜在的边缘。
5.通过抑制鼓励的如哟边缘最终完成边缘检测。

高斯滤波器:
opencv-python学习教程【持续更新】_第18张图片
梯度和方向
采用的Sobel算子:opencv-python学习教程【持续更新】_第19张图片
非极大值抑制:
opencv-python学习教程【持续更新】_第20张图片


关注博主,后续内容持续更新中。。。。。。

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