最近想对OpenCV进行系统学习,看到网上这份教程写得不错,于是跟着来学习实践一下。 【youcans@qq.com, youcans 的 OpenCV 例程, https://youcans.blog.csdn.net/article/details/125112487】
图像的读写与显示
读取图像
retval = cv.imread(filename[, flags])
参数说明:
- filename:读取图像的文件路径和文件名
- flags:读取图片的方式,可选项
- cv2.IMREAD_COLOR(1):始终将图像转换为 3 通道BGR彩色图像,默认方式
- cv2.IMREAD_GRAYSCALE(0):始终将图像转换为单通道灰度图像
- cv2.IMREAD_UNCHANGED(-1):按原样返回加载的图像(使用Alpha通道)
- cv2.IMREAD_ANYDEPTH(2):在输入具有相应深度时返回16位/ 32位图像,否则将其转换为8位
- cv2.IMREAD_ANYCOLOR(4):以任何可能的颜色格式读取图像
- 返回值 retval:读取的 OpenCV 图像,nparray 多维数组
保存图像
retval = cv2.imwrite(filename, img [, paras])
参数说明:
- filename:要保存的文件的路径和名称,包括文件扩展名
- img:要保存的 OpenCV 图像,nparray 多维数组
- paras:不同编码格式的参数,可选项
- cv2.CV_IMWRITE_JPEG_QUALITY:设置 .jpeg/.jpg 格式的图片质量,取值为 0-100(默认值 95),数值越大则图片质量越高;
- cv2.CV_IMWRITE_WEBP_QUALITY:设置 .webp 格式的图片质量,取值为 0-100;
- cv2.CV_IMWRITE_PNG_COMPRESSION:设置 .png 格式图片的压缩比,取值为 0-9(默认值 3),数值越大则压缩比越大。
- retval:返回值,保存成功返回 True,否则返回 False。
显示图像
None = imshow(winname, img)
参数说明:
- winname:字符串,显示窗口的名称。
- img:所显示的 OpenCV 图像,nparray 多维数组。
扩展使用:
可以通过cv2.namedWindow
和cv2.resizeWindow
来指定窗口显示尺寸。
下面看个示例:
代码语言:javascript复制"""
读取、保存、显示图像
"""
import cv2
import numpy as np
import urllib.request as request
# 读取本地图像
imgFile = "img/img.jpg" # 读取文件的路径
img1 = cv2.imread(imgFile, flags=1) # flags=1 读取彩色图像(BGR)
img2 = cv2.imread(imgFile, flags=0) # flags=0 读取为灰度图像
# 读取网络图像
response = request.urlopen("https://img-blog.csdnimg.cn/8992e07a6539448b94b06936c21b4a67.jpeg")
imgUrl = cv2.imdecode(np.array(bytearray(response.read()), dtype=np.uint8), -1)
# 显示图像
cv2.namedWindow("img1", cv2.WINDOW_NORMAL)
cv2.resizeWindow("img1", 600, 600)
cv2.imshow("img1", img1) # img1为窗口标题
cv2.namedWindow("img2", cv2.WINDOW_NORMAL)
cv2.resizeWindow("img2", 600, 600)
cv2.imshow("img2", img2)
cv2.imshow("img3", imgUrl)
key = cv2.waitKey(0) # 等待按键命令, 1000ms 后自动关闭
# 保存图像
saveFile = "img/imgSave.png" # 保存文件的路径
cv2.imwrite(saveFile, img2) # 保存图像文件
# cv2.imwrite(saveFile, img3, [int(cv2.IMWRITE_PNG_COMPRESSION), 8]) # 保存图像文件, 设置压缩比为 8
运行效果:
用matplotlib显示图像(BGRtoRGB)
值得注意的是OpenCV加载的彩色图片使用的是BGR格式,而 matplotlib 使用 RGB 格式,因此使用matplotlib显示图像时,先要对颜色空间进行转换。
同时需注意matplotlib显示灰度图像时,需要设置cmap
参数。
下面是示例程序:
代码语言:javascript复制"""
用 matplotlib 显示图像
"""
import cv2
import matplotlib.pyplot as plt
imgFile = "img/img.jpg" # 读取文件的路径
img1 = cv2.imread(imgFile, flags=1) # flags=1 读取彩色图像(BGR)
imgRGB = cv2.cvtColor(img1, cv2.COLOR_BGR2RGB) # 图片格式转换:BGR(OpenCV) -> RGB(PyQt5)
img2 = cv2.cvtColor(img1, cv2.COLOR_BGR2GRAY) # 图片格式转换:BGR(OpenCV) -> Gray
plt.rcParams['font.sans-serif'] = ['FangSong'] # 支持中文标签
plt.subplot(221), plt.title("1. RGB 格式(mpl)"), plt.axis('off')
plt.imshow(imgRGB) # matplotlib 显示彩色图像(RGB格式)
plt.subplot(222), plt.title("2. BGR 格式(OpenCV)"), plt.axis('off')
plt.imshow(img1) # matplotlib 显示彩色图像(BGR格式)
plt.subplot(223), plt.title("3. 设置 Gray 参数"), plt.axis('off')
plt.imshow(img2, cmap='gray') # matplotlib 显示灰度图像,设置 Gray 参数
plt.subplot(224), plt.title("4. 未设置 Gray 参数"), plt.axis('off')
plt.imshow(img2) # matplotlib 显示灰度图像,未设置 Gray 参数
plt.savefig("result.png")
plt.show()
运行效果:
图像的属性
图像的属性主要有以下四种:
- img.ndim:查看图像的维数,彩色图像的维数为 3,灰度图像的维数为 2。
- img.shape:查看图像的形状,即图像栅格的行数(高度)、列数(宽度)、通道数。
- img.size:查看图像数组元素总数,灰度图像的数组元素总数为像素数量,彩色图像的数组元素总数为像素数量与通道数的乘积。
- img1.dtype:图像编码类型
示例程序:
代码语言:javascript复制"""
查看图像属性
"""
import cv2
imgFile = "img/img.jpg" # 读取文件的路径
img1 = cv2.imread(imgFile, flags=1) # flags=1 读取彩色图像(BGR)
img2 = cv2.imread(imgFile, flags=0) # flags=0 读取为灰度图像
# 维数(ndim), 形状(shape), 元素总数(size), 元素类型(dtype)
print("Ndim of img1(BGR): {}, img2(Gray): {}".format(img1.ndim, img2.ndim)) # number of rows, columns and channels
print("Shape of img1(BGR): {}, img2(Gray): {}".format(img1.shape, img2.shape)) # number of rows, columns and channels
print("Size of img1(BGR): {}, img2(Gray): {}".format(img1.size, img2.size)) # size = rows * columns * channels
print("Dtype of img1(BGR): {}, img2(Gray): {}".format(img1.dtype, img2.dtype)) # uint8
输出:
代码语言:javascript复制Ndim of img1(BGR): 3, img2(Gray): 2
Shape of img1(BGR): (954, 980, 3), img2(Gray): (954, 980)
Size of img1(BGR): 2804760, img2(Gray): 934920
Dtype of img1(BGR): uint8, img2(Gray): uint8
图像裁剪
图像的本质是一个array,因此可以直接使用数组切片的方式对图像进行裁剪。
另外,OpenCV还提供了一个cv2.selectROI
函数,可以通过鼠标选择感兴趣的矩形区域(ROI)。
示例程序:
代码语言:javascript复制"""
裁剪图像
"""
import cv2
img1 = cv2.imread("img/img.jpg") # flags=1 读取彩色图像(BGR)
# 方式一:自定义切片位置参数
# xmin, ymin, w, h = 180, 190, 200, 200 # 矩形裁剪区域 (ymin:ymin h, xmin:xmin w)
# imgROI = img1[ymin:ymin h, xmin:xmin w].copy() # 切片获得裁剪后保留的图像区域
# 方式二:鼠标框选获得位置参数
roi = cv2.selectROI(img1, showCrosshair=True, fromCenter=False)
xmin, ymin, w, h = roi # 矩形裁剪区域 (ymin:ymin h, xmin:xmin w) 的位置参数
imgROI = img1[ymin:ymin h, xmin:xmin w].copy() # 切片获得裁剪后保留的图像区域
cv2.imshow("DemoRIO", imgROI)
cv2.waitKey(0)
鼠标框选之后,按回车
显示裁剪之后的图片。
图像拼接
因为图像的本质是array,因此可以使用np.hstack
和np.vstack
来实现图像的水平拼接和垂直拼接。
示例程序:
代码语言:javascript复制"""
拼接图像
"""
import cv2
import numpy as np
img1 = cv2.imread("img/img.jpg") # 读取彩色图像(BGR)
img2 = cv2.imread("img/img.jpg") # 读取彩色图像(BGR)
img1 = cv2.resize(img1, (400, 400))
img2 = cv2.resize(img2, (300, 400))
img3 = cv2.resize(img2, (400, 300))
imgStackH = np.hstack((img1, img2)) # 高度相同图像可以横向水平拼接
imgStackV = np.vstack((img1, img3)) # 宽度相同图像可以纵向垂直拼接
cv2.imshow("DemoStackH", imgStackH) # 在窗口显示图像 imgStackH
cv2.imshow("DemoStackV", imgStackV) # 在窗口显示图像 imgStackV
key = cv2.waitKey(0) # 等待按键命令
效果:
图像通道拆分
图像通道拆分有两种思路:第一种思路是调用cv2.split
进行拆分,需要注意的是拆分出来的通道并不能直接显示,必须扩展为三通道才能显示出来。
"""
图像通道的拆分
"""
import cv2
import numpy as np
img1 = cv2.imread("img/img.jpg", flags=1) # flags=1 读取彩色图像(BGR)
# 思路一:使用cv2.split进行拆分
bImg, gImg, rImg = cv2.split(img1) # 拆分为 BGR 独立通道
cv2.imshow("bImg", bImg) # 直接显示蓝色分量 bImg 显示为灰度图像
# 将单通道扩展为三通道
imgZeros = np.zeros_like(img1) # 创建与 img1 相同形状的黑色图像
imgZeros[:, :, 0] = bImg # 在黑色图像模板添加蓝色分量 bImg
cv2.imshow("channel B", imgZeros) # 扩展为 BGR 通道
cv2.waitKey(0)
第二种思路还是利用图片的本质,利用切片的方式将各通道剥离开来。
示例程序:
代码语言:javascript复制"""
图像通道的拆分
"""
import cv2
import numpy as np
import matplotlib.pyplot as plt
img1 = cv2.imread("img/img.jpg", flags=1) # flags=1 读取彩色图像(BGR)
# # 思路一:使用cv2.split进行拆分
# bImg, gImg, rImg = cv2.split(img1) # 拆分为 BGR 独立通道
# cv2.imshow("bImg", bImg) # 直接显示蓝色分量 bImg 显示为灰度图像
# # 将单通道扩展为三通道
# imgZeros = np.zeros_like(img1) # 创建与 img1 相同形状的黑色图像
# imgZeros[:, :, 0] = bImg # 在黑色图像模板添加蓝色分量 bImg
# cv2.imshow("channel B", imgZeros) # 扩展为 BGR 通道
# cv2.waitKey(0)
# 思路二:切片分离
# 获取 B 通道
bImg = img1.copy() # 获取 BGR
bImg[:, :, 1] = 0 # G=0
bImg[:, :, 2] = 0 # R=0
# 获取 G 通道
gImg = img1.copy() # 获取 BGR
gImg[:, :, 0] = 0 # B=0
gImg[:, :, 2] = 0 # R=0
# 获取 R 通道
rImg = img1.copy() # 获取 BGR
rImg[:, :, 0] = 0 # B=0
rImg[:, :, 1] = 0 # G=0
# 消除 B 通道
grImg = img1.copy() # 获取 BGR
grImg[:, :, 0] = 0 # B=0
plt.subplot(221), plt.title("1. B channel"), plt.axis('off')
bImg = cv2.cvtColor(bImg, cv2.COLOR_BGR2RGB) # 图片格式转换:BGR(OpenCV) -> RGB(PyQt5)
plt.imshow(bImg) # matplotlib 显示 channel B
plt.subplot(222), plt.title("2. G channel"), plt.axis('off')
gImg = cv2.cvtColor(gImg, cv2.COLOR_BGR2RGB)
plt.imshow(gImg) # matplotlib 显示 channel G
plt.subplot(223), plt.title("3. R channel"), plt.axis('off')
rImg = cv2.cvtColor(rImg, cv2.COLOR_BGR2RGB)
plt.imshow(rImg) # matplotlib 显示 channel R
plt.subplot(224), plt.title("4. GR channel"), plt.axis('off')
grImg = cv2.cvtColor(grImg, cv2.COLOR_BGR2RGB)
plt.imshow(grImg) # matplotlib 显示 channel GR
plt.savefig("result2.png")
plt.show()
两种方式效果一样,不过使用OpenCV提供的接口似乎更加费时。
图像通道合并
图像通道合并同样有两种方式,第一种方式是使用OpenCV的接口cv2.merge
,第二种方式是使用np.stack
进行拼接。
示例程序:
代码语言:javascript复制"""
图像通道合并
"""
import cv2
import numpy as np
img1 = cv2.imread("img/img.jpg", flags=1) # flags=1 读取彩色图像(BGR)
bImg, gImg, rImg = cv2.split(img1) # 拆分为 BGR 独立通道
# cv2.merge 实现图像通道的合并
imgMerge = cv2.merge([bImg, gImg, rImg])
cv2.imshow("cv2Merge", imgMerge)
# Numpy 拼接实现图像通道的合并
imgStack = np.stack((bImg, gImg, rImg), axis=2)
cv2.imshow("npStack", imgStack)
cv2.waitKey(0)
cv2.destroyAllWindows() # 释放所有窗口
这两种方式效果等价,先把原图三个通道拆开来再合并,合并之后和原图一样。