【OpenCV】Chapter1.图像的基本操作

2022-09-21 15:07:24 浏览数 (1)

最近想对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.namedWindowcv2.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.hstacknp.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进行拆分,需要注意的是拆分出来的通道并不能直接显示,必须扩展为三通道才能显示出来。

代码语言:javascript复制
"""
图像通道的拆分
"""
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()  # 释放所有窗口

这两种方式效果等价,先把原图三个通道拆开来再合并,合并之后和原图一样。

0 人点赞