购买
下载掌阅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,不加改变地载入原图。

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

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

     JPEG文件 - *.jpeg, *.jpg, *.jpe
     JPEG 2000文件- *.jp2
     PNG文件 - *.png
     便携文件格式- *.pbm, *.pgm, *.ppm
     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() #释放窗口

我们对上面的六种路径进行了测试,其中任何一种都是支持的,都可以成功读取并显示图片,其中较常用的是相对路径,也就是第五种。放在和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 得到读取的图片的高度和宽度

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

shape函数是numpy.core.fromnumeric中的函数,功能是读取矩阵的长度,比如shape[0]就是读取矩阵第一维度的长度。shape的输入参数可以是一个整数(表示维度),也可以是一个矩阵。使用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个变量并打印。最后,用shape[: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 >(),所以一般情况下不用写。

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

下面的示例演示如何创建BGRA图像,将其保存为PNG图像文件,以及如何设置自定义压缩参数。

【例3.4】 将BGRA图像保存为PNG图像文件

     import cv2 as cv
     import numpy as np #导入numpy模块
     
     img = cv.imread('p1.jpg')
     height, width = img.shape[:2] # a[:n]代表列表中的第一项到第n项
     cv.line(img,(0,0),(width,height),(0,0,255),2)   #在内存中的源图片上画一对角线
     cv.imwrite('testRes.jpg',img)  #把内存中的图像写入文件
     img = cv.imread('testRes.jpg')  #再次读取结果图片
     cv.imshow("img",img) #显示窗口
     cv.waitKey(0) #等待按键
     cv.destroyAllWindows() #释放窗口

在上述代码中,我们首先读取工程目录下的源图片p1.jpg,然后获取高度和宽度,接着调用OpenCV画线函数line从左上角的点(0,0)处到右下角的点(width,height)处画一条直线。颜色值用的是(0,0,255),即红色,线条的宽度的是2。画完后再把内存中的图像写入文件,最后重新读出来并显示。

运行工程,结果如图3-5所示。此时在工程目录下多了一个testRes.jpg文件。

图3-5 3gjS/5uKALkjTQLR+eHYbyRrL7Nsa8YjE3yb70ykn9ble73pnuQC/0oELYnO9F5w

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