OpenCV学习笔记(一)——图像基础知识(图像的读取、显示和保存)

1 图像的表示

数字图像在计算机中是以矩阵的形式存储的(如下图所示),矩阵中的每一个元素都描述一定的图像信息,如亮度、颜色等信息。对数字图像的处理就是通过一系列矩阵运算提取更高级的信息。在是要NumPy时,图像被读取后,将保存在一个N维数组对象ndarray中,此后便可以通过对该ndarray对象进行运算,以完成对图像的操作。
在这里插入图片描述
日常生活中所见到的图像一般为连续的模拟图像,将模拟图像转化为数字图像的过程主要分为采样、量化和数字表示3个过程

1.1 采样

采样时图像在空间上的离散化过程,即用空间上部分点的灰度值代表图像,这些点称为采样点。由于图像是一种二位分布的信息,因此对它进行采样处理的过程一般分为两步
(1)将二维信号转换成一维信号,例如:沿垂直方向按一定行间距从上到下扫描得到一维信号。
(2)将得到的一维信号按一定间隔进行采样来得到离散信号。对于运动图像(时间域上连续的图像),需要先在时间轴上采样。在对一幅图像采样时,若每行的像素为M,共有N行,则图像的大小为M×N像素。在进行采样时,采样点的间隔决定了采样后图像的质量。间隔越大,采样后图像像素越少,质量越差;反之,像素越多,质量越好。采样过程与采样间隔如下图所示。在这里插入图片描述

1.2 量化

模拟图像经过采样后,在时间和空间上离散化为像素,但采样所得到的像素值仍然是连续量。把采样后所得的各像素的灰度值从模拟量转化为离散量的过程称为图像灰度的量化。量化过程示意图如下图所示。在这里插入图片描述
若连续灰度值用 z z z来表示,满足 z i ≤ z ≤ z j z_i \le z \le z_j zizzj 的值都量化为整数 q i q_i qi q i q_i qi称为像素的灰度值, z z z q i q_i qi的差称为量化误差。一般像素值量化后用1字节来表示。如上图所示,把黑→灰→白连续变化的灰度值量化为0~255共256级灰度值,表示亮度从深到浅,对应图像中的颜色为从黑到白。

1.3 数字表示

模拟图像经过采样和量化之后就会得到一个数字矩阵,这个数字矩阵就是图像的数字表示。我们用 f ( x , y ) f(x,y) f(x,y)来表示衣服模拟图像,图像采样后得到数字图像有M行和N列,用 g g g来表示这个矩阵,坐标 ( x , y ) (x,y) (x,y)的值经过采样、量化之后变换为矩阵 g g g中相对应的离散值。
g = [ g ( 0 , 0 ) g ( 0 , 1 ) ⋯ g ( 0 , N − 1 ) g ( 1 , 0 ) g ( 1 , 1 ) ⋯ g ( 1 , N − 1 ) ⋮ ⋮ ⋮ g ( M − 1 , 0 ) g ( M − 1 , 1 ) ⋯ g ( M − 1 , N − 1 ) ] g=\left[\begin{array}{cccc} g(0,0) & g(0,1) & \cdots & g(0, N-1) \\ g(1,0) & g(1,1) & \cdots & g(1, N-1) \\ \vdots & \vdots & & \vdots \\ g(M-1,0) & g(M-1,1) & \cdots & g(M-1, N-1) \end{array}\right] g=g(0,0)g(1,0)g(M1,0)g(0,1)g(1,1)g(M1,1)g(0,N1)g(1,N1)g(M1,N1)
其中, g ( x , y ) g(x,y) g(x,y)必须为非负数,且 0 ≤ g ( i , j ) ≤ 255 0\le g(i,j)\le255 0g(i,j)255

2 图像的读取与显示

2.1 图像读取函数

#cv.imread()函数原型
img = cv.imread(filename
                [,flags]

· fliename:需要读取的图像的路径,包含图像名称和图像文件扩展名。
· flags:读取图像的形式标志,如将彩色图像按照灰度图来读取,默认时按照彩色图像格式读取。
cv.imread()函数用于读取指定的图像文件,并将读取结果返回。如果图像路径错误、破损或者格式不被支持,则无法正确读取图像,但次是并不会报错,而是返回None。因此可以通过观察print(img)返回的结果是否为None来判断图像是否读取成功。

标志 简记 作用
cv. IMREAD_UNCHANGED -1 按照图像原样读取,保留alpha通道
cv. IMREAD_GRAYSCALE 0 将图像转换成单通道灰度图像后读取
cv. IMREAD_COLOR 1 将图像转换成三通道BGR彩色图像后读取
cv. IMREAD_ANYDEPTH 2 保留原图像的16位、32位深度。若不声明该标志,则转成8位深度后读取
cv. IMREAD_ANYCOLOR 4 以任何可能的颜色格式读取图像
cv. IMREAD_LOAD_GDAL 8 使用GDAL驱动程序加载图像
cv. IMREAD_REDUCED_GRAYSCALE_2 16 将图像转成单通道灰度图像,尺寸缩小至原来的1/2。更改最后一位数字可缩小至1/4或1/8
cv. IMREAD_REDUCED_COLOR_2 17 将图像转成三通道彩色图像,尺寸缩小至原来的1/2。更改最后一位数字可缩小至1/4或1/8
cv. IMREAD_IGNORE_ORIENTATION 128 不以EXIF的方向旋转图像

注意:由于不同操作系统的编解码器不同,在某个系统中能够读取的图像文件在其他系统中可能无法读取。其中BMP文件和DIB文件在所有系统中都可读取。

不过需要说明的是,该函数能否读取文件数据与扩展名无关,而是通过文件内容确定图像的类型。例如,在将一个文件扩展名由.png修改成.exe后,cv.imread()函数仍然可以读取该图像,但是将扩展名.exe修改成.png就不能加载该文件

2.2 图像窗口函数

窗口函数并不是必需的,因为我们在使用OpenCV显示图像时如果没有主动定义图像窗口,就会自动生成一个窗口用于图像显示。但是,当我们需要使图像窗口拥有一些特殊的属性时,就需要使用图像创建窗口。

#cv.namedWindow()函数原型
None = cv.namedWindow(winname
                      [, flag])

· winname:窗口名称,用作窗口的标识符。
· flags:窗口属性设置标志。
该函数会创建一个窗口变量,用于显示图像和滑块,通过窗口名称引用该窗口。如果在创建窗口时已经存在具有相同名称的窗口,则该函数不会执行任何操作。创建一个窗口需要占用部分内存资源,因此通过该函数创建窗口后,在不需要关闭窗口时需要关闭窗口来释放资源。
该函数的第1个参数用于唯一识别窗口。第2个参数主要用于设置窗口的大小是否可调、显示的图像是否填充窗口。具体可选择的参数见下表。
(1)这些标志在功能不冲突的前提下可以同时声明多个,不同参数间用“|”分隔。
(2)在默认情况下,该函数加载的标志为 cv.WINDOW_AUTOSIZE | cv.WINDOW_KEEPRATIO | cv.WINDOW_GUI_EXPANDED。

标志 简记 作用
cv. WINDOW_NORMAL 0x00000000 显示图像后,可随意调整窗口大小
cv. WINDOW_AUTOSIZE 0x00000001 根据图像大小显示窗口,不可调整
cv. WINDOW_OPENGL 0x00001000 创建窗口时支持OpenGL
cv. WINDOW_FULLSCREEN 1 全屏显示窗口
cv. WINDOW_FREERATIO 0x00000100 调整图像尺寸以充满窗口
cv. WINDOW_KEEPRATIO 0x00000000 保持图像的比例
cv. WINDOW_GUI_EXPANDED 0x00000000 允许添加工具栏和状态栏
cv. WINDOW_GUI_NORMAL 0x00000010 不允许添加工具栏和状态栏

2.3 图像显示函数

#cv.imshow()函数原型
None = cv.imshow(winname
                 img)

·winname:要显示图像的窗口的名称,用字符串形式赋值
·img:要显示的图像
该函数会在指定的窗口中显示图像,如果在此函数之前没有创建同名的图像窗口,就会使用 cv.WINDOW_AUTOSIZE标志创建一个窗口。如果已存在同名窗口,则会缩放图像以适应窗口属性。该函数会根据图像的深度(数据类型)将其缩放,具体的缩放规则如下。

(1)如果图像是 uint8 类型 ,则会按照原样显示。

(2)如果图像是 uint16 或 int32 类型的,则会将像素值除以256,将范围由[0,255*256]映射到[0,255]。

(3)如果图像是 float32 或 floa64 类型的,则会将像素乘值以255,将其范围由[0,1]映射到[0,255]。
这里需要特殊说明的是,第2个参数类似于 cv.imread()中读取的ndarray形式的矩阵。
注意:该函数运行后会继续执行后面的程序,如果后面的程序执行完直接退出,那么可能出现图像闪一下便消失的情况,因此往往在cv.imshow()函数后会有cv.waitKey()函数,以将程序暂停一段时间,具体时间通过参数进行赋值,单位为毫秒,但参数为“0”时表示等待用户按键结束暂停该函数。

与显示窗口功能相对应,OpenCV提供了两个关闭窗口资源的函数。

#关闭指定名称的窗口
None = cv.destroyWindow(winname)
#关闭全部窗口
None = cv.destroyAllWindows()

在程序退出时会自动关闭应用程序中所有的资源和窗口,及时不主动释放窗口,也会在程序结束时释放窗口资源。因此,在一个简单的程序里,我们并不需要调用这些函数。

2.4 测试代码

import cv2 as cv  #引入OpenCV

image=cv.imread('image.jpg')   #读取图像
cv.namedWindow('test', cv.WINDOW_KEEPRATIO|cv.WINDOW_NORMAL)  #保持图像比例创建窗口,并且可以调整窗口大小
cv.imshow('test', image)  #在窗口中显示图像
cv.waitKey(0)  #无限延时等待用户按键事件
cv.destroyAllWindows()  #关闭所有窗口

代码运行结果如下图所示
在这里插入图片描述

3 图像的保存

3.1 图像保存函数

在图像处理过程中会生成新的图像(e.g. 将模糊的图像经过算法处理后变得更加清晰,将彩色图像变成灰度图像等),需要将处理之后的结果以图像的格式保存成文件。
OpenCV 提供了 cv.imwrite() 函数用于将 ndarray 数组保存为图像。

retval = cv.imwrite(filename,
	                img
	                [,params])

· filename:保存图像的路径和文件名,包含图像格式。
· img:将要保存的 Array 类型的数组。
· params:保存图片格式时的属性设置标志。
该函数用于将ndarray数组对象保存成图像文件,并将保存结果返回。默认情况下,该函数保存结果为8位的单通道图像和BGR图像,可以通过设置第3个参数来保存其他格式的图像。
· 数据类型为uint16的图像可以保存成PNG、JPEG、TIFF格式文件。
· 数据类型为float32的图像可以保存成PFM、TIFF、OpenEXR和Radiance HDR格式文件。
· 四通道(最后一个为alpha通道)的图像可以保存成 PNG 格式文件。其中,对于完全透明的像素,设置alpha为0;对于完全不透明的像素,设置alpha为255/65535。

cv.imwrite()函数的第3个参数只有在需要保存数组比较特殊时才需要填写。指定文件类型只需要改变第一个参数中的文件后缀名即可。
第3个参数的设置方法

#将img以等级为90的图像质量保存
cv.imwrite(filename,
                img,
                [int(cv.IMWRITE_JPEG_QUALITY), 90 ])
#将img以值为6的压缩级别进行保存
cv.imwrite(filename,
                img,
                [int(cv.IMWRITE_PNG_COMPRESSION), 6 ])

第3个参数常见的可选择标志如下表所示。

标志 简记 作用
cv.IMWRITE_JPEG_QUALITY 1 保存成JPEG格式的文件的图像质量,范围为0~100,默认值为95
cv.IMWRITE_JPEG_PROGRESSIZE 2 表示是否启用增强JPEG格式,1表示启用,默认值为0
cv.IMWRITE_JPEG_OPTIMIZE 3 表示是否对JPEG格式进行优化,1表示启用,默认值为0
cv.IMWRITE_JPEG_LUMA_QUALITY 5 JPEG单独的亮度质量等级,取值范围为0~100,默认值为0
cv.IMWRITE_JPEG_CHROMA_QUALITY 6 JPEG单独的色度质量等级,取值范围为0~100,默认值为0
cv.IMWRITE_PNG_COMPRESSION 16 保存成PNG文件的压缩级别,范围为0~9,值越高意味着尺寸越小,压缩时间越长,默认值为1
cv.IMWRITE_TIFF_COMPRESSION 256 保存成TIFF文件时的压缩方案

3.2 测试代码

# -*- coding:utf-8 -*-
import cv2 as cv
import sys
import numpy as np
import matplotlib.pyplot as plt


if __name__ == '__main__':
    # 读取图像并判断是否读取成功
    img = cv.imread('../images/flower.jpg')
    if img is None:
        print('Failed to read flower.jpg.')
        sys.exit()
    else:
        zeros = np.ones(img.shape[:2], dtype=img.dtype) * 100
        result = cv.merge([img, zeros])
        print('原图的通道数为:{}'.format(img.shape[2]))
        print('处理后的通道数为:{}'.format(result.shape[2]))

        # 图像展示
        plt.imshow(result)
        plt.show()

        # 图像保存
        cv.imwrite('./results/flower_alpha.png', result)

代码运行结果如下图所示。
在这里插入图片描述

Guess you like

Origin blog.csdn.net/a1016249126/article/details/120931533