OpenCV_Python-木子_bilibili

OpenCV Tutorials


计算机眼中的图像

图像的存储形式

import cv2

img = cv2.imread('img/test.jpg')
print(img)

可见在计算机中是以矩阵的形式存储图像的每一个像素点。

不过在 OpenCV 中是以 BGR 的顺序存储的,参见 官方文档

imread 还有第 2 个参数:

  • cv2.IMREAD_COLOR:彩色图像,就是一般的 BGR 形式

  • cv2.IMREAD_GRAYSCALE:灰度图像

    • 把白色与黑色之间按对数关系分为若干等级,称为 灰度
    • 灰度图与黑白图不同,黑白图只有黑与白两种颜色

使用 img.shape 可以查看图像的 (h, w, c) ,即 (高,宽,通道数),3 表示 RGB 形式。

img1 = cv2.imread('img/test.jpg')
img2 = cv2.imread('img/test.jpg', cv2.IMREAD_COLOR)
img3 = cv2.imread('img/test.jpg', cv2.IMREAD_GRAYSCALE)
print(img1.shape)
print(img2.shape)
print(img3.shape)

输出:

查看图像

进行一些图像操作之后,想查看一下图片目前是什么样子的,可以通过 cv2.imshow() 函数实现。

cv2.imshow('window name', img)
# 等待时间,以毫秒为单位,0表示任意键终止
cv2.waitKey(0)
cv2.destroyAllWindows()

这 3 行代码很常用,可以封装成一个方法简化调用:

def cv_show(window_name, temp):
    cv2.imshow(window_name, temp)
    cv2.waitKey(0)
    cv2.destroyAllWindows()
cv_show('window1', img2)
cv_show('window2', img3)

保存图像:

cv2.imwrite('文件名.png', img)

视频的读取与处理

其实视频就是很多张连续的图像。

vc = cv2.VideoCapture('media/test.mp4')

if vc.isOpened():
    flag = vc.read()
else:
    flag = False

while flag:
    ret, frame = vc.read()
    if frame is None:
        break
    if ret:
        gray = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
        cv2.imshow('result', gray)
        if cv2.waitKey(100) & 0xFF == 27:
            break
vc.release()
cv2.destroyAllWindows()

ROI 区域

Rgion Of Interest,感兴趣区域。
即在图像中以方框等方式勾勒出需要处理的区域。

img = cv2.imread('test.jpg')
sect = img[200:500, 300:500]
cv_show('sect', sect)

颜色通道提取

split 函数可以将多通道图像分割为单通道图像。
容易知道 B、G、R 分别对应的下标为:

B G R
0 1 2

所以就可以分别绘制出只包含其中一个通道的图像。

b, g, r = cv2.split(img)

# 只保留R
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 1] = 0
cv_show('R', cur_img)

# 只保留G
cur_img = img.copy()
cur_img[:, :, 0] = 0
cur_img[:, :, 2] = 0
cv_show('G', cur_img)

# 只保留B
cur_img = img.copy()
cur_img[:, :, 1] = 0
cur_img[:, :, 2] = 0
cv_show('B', cur_img)

边界填充

  • BORDER_REPLICATE:复制法,也就是复制最边缘像素
  • BORDER_REFLECT:反射法,对感兴趣的图像中的像素在两边进行复制
  • BORDER_REFLECT_101:反射法,也就是以最边缘像素为轴对称
  • BORDER_WRAP:外包装法
  • BORDER_CONSTANT:常量法,常数值填充
    import matplotlib.pyplot as plt
    img = cv2.imread('media/yuki.jpg')
    
    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, 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)
    
    plt.subplot(231), plt.imshow(img, 'gray'), plt.title('ORIGINAL')
    plt.subplot(232), plt.imshow(replicate, 'gray'), plt.title('REPLICATE')
    plt.subplot(233), plt.imshow(reflect, 'gray'), plt.title('REFLECT')
    plt.subplot(234), plt.imshow(reflect101, 'gray'), plt.title('REFLECT_101')
    plt.subplot(235), plt.imshow(wrap, 'gray'), plt.title('WRAP')
    plt.subplot(236), plt.imshow(constant, 'gray'), plt.title('CONSTANT')
    
    plt.show()

不知道为啥这里的结果有些泛蓝,原图是这样的:


加法运算

直接加上一个常数相当于每一个像素点都加。

sky = cv2.imread('media/sky.jpg')
sky1 = sky + 10
print(sky[0, 0, :], "\n")
print(sky1[0, 0, :])

两张图像宽高相同时,也可以进行相加。如果宽高不相同,可以用 cv2.resize() 方法来调整大小,如

img_fix = cv2.resize(img, (400, 300))

加出的结果如果越界(0~255),会对 256 取模。

OpenCV 也提供了 cv2.add 用来进行加法运算,不过这个方法在越界时会取最大值 255。


图像融合

图像相加和图像融合是不同的,图像融合有专门的函数,其公式为:

$result = αx_1 + βx_2 + b$

yuki = cv2.imread('media/yuki.jpg')
sky = cv2.imread('media/sky.jpg')
result1 = yuki + sky
result2 = cv2.addWeighted(yuki, 0.4, sky, 0.6, 0)
cv_show("result1", result1)
cv_show("result2", result2)

这里的 0.4 相当于 α0.6 相当于 β0 相当于 b