购买
下载掌阅APP,畅读海量书库
立即打开
畅读海量书库
扫码下载掌阅APP

3.2 图像输入/输出模块imgcodecs

要处理图像,第一步就是把图像文件从磁盘上读取到内存,处理完毕后再保存到内存。因此我们先来看一下图像文件读取与保存模块imgproc。imgproc提供了一系列全局函数,用于读取或保存图像文件。

3.2.1 读取图像文件

函数imread用于读取图像文件或加载图像文件,其声明如下:

cv.imread(filename[, flags]) -> retval

其中,参数filename表示要读取的图像文件名;flags表示读取模式,取值如下:

● cv.IMREAD_ANYDEPTH:其值是2,取这个标志的话,若载入的图像深度为16位或者32位,则返回对应深度的图像,否则转换为8位图像再返回。

● cv.IMREAD_COLOR:其值是1,取这个标志的话,图像转为彩色图像(BGR,3通道)。

● cv.IMREAD_GRAYSCALE:其值是0,取这个标志的话,始终将图像转换成灰度图,即返回灰度图像,1通道。

● cv. IMREAD_UNCHANGED,其值是−1,表示载入原图。

如果从指定文件加载图像成功,就返回一个存储着图片像素数据的矩阵。如果无法读取图像(缺少文件、权限不正确、格式不受支持或无效),那么函数将返回空。

在Windows操作系统下,OpenCV的imread函数支持如下类型的图像载入:

● JPEG文件:*.jpeg、*.jpg、*.jpe。

● JPEG 2000文件:*.jp2。

● PNG文件:*.png。

● 便携文件格式:*.pbm、*.pgm、*.pp。

● Sun rasters光栅文件:*.sr、*.ras。

● TIFF文件:*.tiff、*.tif。

● Windows位图:*.bmp、*.dib。

如果文件读取失败,就返回None。可以通过返回值是否为None来判断是否读取正确,比如:

img = cv.imread("p1.jpg"); #读取第一幅图片
if img is None:
    sys.exit("Could not read the p1.jpg.") #如果读取为空,则退出程序

img实际上是一个NumPy的array数组,包含了每个像素点的数据(如果是彩色模式,就是BGR值,如果是灰度模式,则是灰度值)。我们可以通过下标访问每一个像素点的数据,对每一个像素点进行更改操作。

值得注意的是,函数imread根据内容而不是文件扩展名来确定图像的类型,比如把某个BMP图像文件的后缀名改为JPG,imread依然能探测到这个文件是BMP图像文件。

另外,imread的第一个参数一般是图像文件的绝对路径或相对路径。对于绝对路径,imread除了不支持单右斜线(\)形式,其他斜线形式都支持,比如双右斜线形式(\\)、双左斜线形式(//)、单左斜线形式(/)等。通常相对路径更加方便点,只要把图像文件放在工程目录下即可。注意,imread的文件路径不支持中文路径,如果要支持中文路径,可以用函数imdecode。该函数从指定的内存缓存中读取一幅图像,其声明如下:

cv.imdecode(buf, flags) -> retval

其中,buf是存放图像数据的内存缓存,通常用字节数组或向量的形式表示;flags的含义同imread函数的flags参数。如果内存缓冲区太短或包含无效数据,就返回空矩阵图像。实际上,imread和imdecode内部都是通过ImageDecoder类来进行图像解码的。

例如,下列代码从文件中读取数据到内存:

import numpy as np #导入numpy模块
img = cv.imdecode(np.fromfile(imgpath,dtype=np.uint8),-1)

其中,fromfile函数是支持中文路径的,它通过读取文件在内存中构造数组数据,这样imdecode就能在内存中获得数组数据了,继而进行解码。

【例3.1】 多种路径读取图像文件

import cv2 as cv
import numpy as np #导入numpy模块

#imgpath = "d:\\我的图片\\p1.jpg";
#imgpath = "d://test//p1.jpg";
#imgpath = "d:/test/p1.jpg"
#imgpath = "d:/test//test2\\test3//test4//p1.jpg";#-- 4 --以上三种混合法
imgpath = "p1.jpg"; #-- 5 --相对路径法,放在和test.py同一路径
img = cv.imdecode(np.fromfile(imgpath,dtype=np.uint8),-1)
cv.imshow("img",img) #显示窗口
cv.waitKey(0) #等待按键
cv.destroyAllWindows() #释放窗口

我们对上面的6种路径进行了测试,任何一种都是支持的,都可以成功读取并显示图片。其中较常用的是相对路径,也就是第5种。图片放在和3.1.py文件同一路径下,既可以在PyCharm中直接运行来打开图片,也可以到命令行窗口下执行“pthon 3.1.py”命令来打开图片。

运行工程,结果如图3-3所示。

在用imread或imdecode加载图像文件时,可以指定模式来获得不同的效果。

【例3.2】 用不同模式打开图像文件

import cv2 as cv
import numpy as np #导入numpy模块
imgpath = "p1.jpg";
img = cv.imread(imgpath,cv.IMREAD_ANYDEPTH)
cv.imshow("img",img) #显示窗口
cv.waitKey(0) #等待按键

img = cv.imread(imgpath,cv.IMREAD_COLOR)
cv.imshow("img",img) #显示窗口
cv.waitKey(0) #等待按键继续下一幅

img = cv.imread(imgpath,cv.IMREAD_GRAYSCALE)
cv.imshow("img",img) #显示窗口
cv.waitKey(0) #等待按键

cv.destroyAllWindows() #释放窗口

我们用3种模式来加载图片。对于cv.IMREAD_ANYDEPTH,由于p1.jpg的位深度是24(不是16或32),因此会转换为8位图像。cv.IMREAD_COLOR会将图像以彩色方式加载,由于我们的图像本来就是彩色图像,因此没有变化。cv.IMREAD_GRAYSCALE会将图像转换成灰度图,所以加载后图像是黑白的。

保存工程并运行,第一幅图像运行结果如图3-4所示。然后按空格键,可以继续显示下一幅图像。

图3-3

图3-4

3.2.2 得到读取的图片的高度和宽度

3.2.1节提到imread如果读取图像文件成功,就返回该图像的矩阵。既然是矩阵,就有宽度和高度两个属性,宽度相当于矩阵的列数,高度相当于矩阵的行数。在Python中,可以利用NumPy库提供的shape函数来得到矩阵的行数和列数。

shape函数是numpy.core.fromnumeric中的函数,其输入参数可以是一个整数(表示维度),也可以是一个矩阵。使用shape[0]读取矩阵第一维度的长度,也就是行数;使用shape[1]计算列数;使用shape[2]存放图像的通道数。该函数功能强大,这里我们只需要知道如何获得图片的高度和宽度即可。介绍该函数主要是为了让读者在读取图像文件到内存后,能对内存中存放图像的矩阵有一个感性的认识。

【例3.3】 得到读取图像后的高度和宽度

import cv2 as cv
import numpy as np #导入numpy模块
img = cv.imread('test.jpg')
print(np.shape(img))

height=np.shape(img)[0]
width=np.shape(img)[1]
channles=np.shape(img)[2]
print("h=",height,"w=",width,"channles=",channles)

height, width,channles = np.shape(img)[:3] # a[:n]代表列表中的第一项到第n项
print("h=",height,"w=",width,"channles=",channles)

在上述代码中,首先读取了当前工程目录下的图像文件test.jpg,返回值为一个矩阵数组,然后通过shape打印了图像的高度和宽度。注意:在Python中,a[:n]代表列表中的第1项到第n项,这里的shape[:3]表示shape[0](高度)、shape[1](宽度)和shape[2](通道数)。最后又赋值给了3个变量并打印。

运行工程,结果如下:

(183, 335, 3)
h= 183 w= 335 channles= 3
h= 183 w= 335 channles= 3

3.2.3 imwrite保存图片

函数imwrite可以用来输出图像到文件,其声明如下:

imwrite(filename, img[, params]) -> retval

其中参数filename表示需要写入的文件名,必须加上后缀,比如123.png,注意要保存图片为哪种格式,就带什么后缀;img表示Mat类型的图像数据,就是要保存到文件中的原图像数据;params表示为特定格式保存的参数编码,它有一个默认值std::vector< int >(),所以一般情况下不用写。

通常,使用imwrite函数能保存8位单通道或三通道(具有BGR通道顺序)图像。16位无符号(CV_16U)图像可以保存为PNG、JPEG 2000和TIFF格式。32位浮点(CV_32F)图像可以保存为PFM、TIFF、OpenEXR和Radiance HDR格式。三通道(CV_32FC3)TIFF图像将使用LogLuv高动态范围编码(每像素4字节)保存。另外,使用此函数可以保存带有alpha通道的PNG图像。为此,创建8位(或16位)四通道图像BGRA,其中alpha通道最后到达。完全透明的像素应该将alpha通道设置为0,完全不透明的像素应该将alpha设置为255/65535。如果格式、深度或通道顺序不同,就在保存之前使用Mat::convertTo和cv::cvtColor进行转换,或者使用通用文件存储I/O函数将图像保存为XML或YAML格式。

下面示例将演示如何创建BGRA图像并将其保存到PNG文件中,还将演示如何设置自定义压缩参数。

【例3.4】 创建BGRA图像并将其保存到PNG文件

 import cv2
    import numpy as np
    img=cv2.imread("cat.jpeg")
    img_bgra=cv2.cvtColor(img,cv2.COLOR_BGR2BGRA)
    print(img_bgra.shape)
    print(img_bgra)
    b, g, r ,a= cv2.split(img_bgra)
    print(a)
    print(a.shape)
    a[int(a.shape[0]/2):,int(a.shape[1]/2):]=0
    alpha = np.ones(b.shape, dtype=b.dtype)*0  #creating a dummy alpha channel
image.
    print(alpha)
    alpha = alpha.astype(np.uint8)
    img_bgra = cv2.merge((b, g, r, a))
    print(img_bgra.shape)
    cv2.imshow("BGRA", img_bgra)
    cv2.imwrite("BGRA.png",img_bgra, [cv2.IMWRITE_PNG_COMPRESSION, 9])
    cv2.waitKey(0)

在上述代码中,首先读取工程目录下的原图片cat.jpg,本例输入一幅24位BGR真彩色图像,输出32位BGRA带透明通道的彩色图像。在输出前,对alpha透明通道做了一些处理。源24位真彩色图像的左下角置为透明。在运行中查看BGRA四通道图片时,是无法查看到透明通道的,需要把图片保存下来才能看到。

目前支持alpha透明通道的图片封装格式为:

(1)PNG:支持透明效果。PNG可以为原图像定义256个透明层次,使得彩色图像的边缘能与任何背景平滑地融合,从而彻底地消除锯齿边缘。这种功能是GIF和JPEG没有的。

(2)GIF:GIF的原义是“图像互换格式”,GIF文件的数据是一种基于LZW算法的连续色调的无损压缩格式。

(3)TIFF:TIFF是一种灵活的位图格式,主要用来存储包括照片和艺术图在内的图像。它最初由Aldus公司与微软公司一起为PostScript打印开发。TIFF与JPEG和PNG一起成为流行的高位彩色图像格式。

(4)PSD:这是Photoshop图像处理软件的专用文件格式,文件扩展名是.psd,可以支持图层、通道、蒙板和不同色彩模式的各种图像特征,是一种非压缩的原始文件保存格式。扫描仪不能直接生成该种格式的文件。PSD文件有时容量很大,但可以保留所有原始信息,在图像处理中对于尚未制作完成的图像,选用PSD格式保存是最佳的选择。

运行工程,结果如图3-5所示。此时在工程目录下多了一个BGRA.png文件。这里不保存下来查看的话,在程序运行中展示时看不出差异。保存图片时,要保存为PNG格式,因为PNG或TIFF格式的文件即为BGRA四通道色彩空间的图像文件形式。另外,保存图片时,imwrite的第三个参数是[cv2.IMWRITE_PNG_COMPRESSION, 9],它实现了图片压缩功能,其中9代表图片保存时的压缩程度,有0~9这个范围的10个等级,数字越大表示压缩程度越高。

图3-5 dZ7yVI/WHepHYnYtG6nbRa6b0bDrzGIWf8AVTaBbfi1k0hfnHugJa82KNuoiSo0V

点击中间区域
呼出菜单
上一章
目录
下一章
×