1.2 图像处理基本操作
在图像处理过程中,读取图像、显示图像、保存图像是最基本的操作。本节将简单介绍这几项基本操作。
1.2.1 读取图像
OpenCV提供了函数cv2.imread()来读取图像,该函数支持各种静态图像格式。该函数的语法格式为:
retval = cv2.imread( filename[, flags] )
式中:
● retval是返回值,其值是读取到的图像。如果未读取到图像,则返回“None”。
● filename表示要读取的图像的完整文件名。
● flags是读取标记。该标记用来控制读取文件的类型,具体如表1-1所示。表1-1中的第一列参数与第三列数值是等价的。例如cv2.IMREAD_UNCHANGED=-1,在设置参数时,既可以使用第一列的参数值,也可以采用第三列的数值。
表1-1 flags标记值
函数cv2.imread()能够读取多种不同类型的图像,具体如表1-2所示。
表1-2 cv2.imread()函数支持的图像格式
例如,想要读取当前目录下文件名为lena.bmp的图像,并保持按照原有格式读入,则使用的语句为:
lena=cv2.imread("lena.bmp", -1)
需要注意,上述程序要想正确运行,首先需要导入cv2模块,大多数常用的OpenCV函数都在cv2模块内。与cv2模块所对应的cv模块代表传统版本的模块。这里的cv2模块并不代表该模块是专门针对OpenCV 2版本的,而是指该模块引入了一个改善的API接口。在cv2模块内部采用了面向对象的编程方式,而在cv模块内更多采用的是面向过程的编程方式。
本书中所使用的模块函数都是cv2模块函数,为了方便理解,在函数名前面加了“cv2.”。但是如果函数名出现在标题中,那么希望突出的是该函数本身,所以未加“cv2.”。
【例1.1】使用cv2.imread()函数读取一幅图像。
根据题目要求,编写代码如下:
import cv2 lena=cv2.imread("lenacolor.png") print(lena)
上述程序首先会读取当前目录下的图像lena.bmp,然后使用print语句打印读取的图像数据。运行上述程序后,会输出图像的部分像素值,如图1-2所示。
图1-2 图像部分像素值
1.2.2 显示图像
OpenCV提供了多个与显示有关的函数,下面对常用的几个进行简单介绍。
1.namedWindow函数
函数cv2.namedWindow()用来创建指定名称的窗口,其语法格式为:
None = cv2.namedWindow( winname )
式中,winname是要创建的窗口的名称。
例如,下列语句会创建一个名为lesson的窗口:
cv2.namedWindow(“lesson”)
2.imshow函数
函数cv2.imshow()用来显示图像,其语法格式为:
None = cv2.imshow( winname, mat )
式中:
● winname是窗口名称。
● mat是要显示的图像。
【例1.2】
在一个窗口内显示读取的图像。
根据题目要求,编写代码如下:
import cv2 lena=cv2.imread("lena.bmp") cv2.namedWindow("lesson") cv2.imshow("lesson", lena )
在本程序中,首先通过cv2.imread()函数读取图像lena.bmp,接下来通过cv2.namedWindow()函数创建一个名为lesson的窗口,最后通过cv2.imshow()函数在窗口lesson内显示图像lena.bmp。
运行上述程序,得到的运行结果如图1-3所示。
图1-3 【例1.2】程序的运行结果
在实际使用中,可以先通过函数cv2.namedWindow()来创建一个窗口,再让函数cv2.imshow()引用该窗口来显示图像。也可以不创建窗口,直接使用函数cv2.imshow()引用一个并不存在的窗口,并在其中显示指定图像,这样函数cv2.imshow()实际上会完成如下两步操作。
第1步:函数cv2.imshow()创建一个指定名称的新窗口。
第2步:函数cv2.imshow()将图像显示在刚创建的窗口内。
例如,在下面的语句中,函数cv2.imshow()完成了创建demo窗口和显示image图像的操作,该语句的功能与例1.2相同。
import cv2 lena=cv2.imread("lena.bmp") cv2.imshow("demo", lena )
在显示图像时,初学者最经常遇到的一个错误是“error: (-215:Assertion failed) size.width>0&& size.height>0 in function 'cv::imshow'”,说明当前要显示的图像是空的(None),这通常是由于在读取文件时没有找到图像文件造成的。一般来说,没有找到要读取的图像文件,可能是因为文件名错误;如果确认要读取的图像的完整文件名(路径名和文件名)没有错,那么往往是工作路径配置错误造成的。
例如,当前程序所在路径为e:\lesson,要读取当前路径下存在的图像image\lena.bmp。那么:
● 如果设置的当前工作路径是e:\lesson,程序就会读取当前工作路径下的文件image\lena.bmp,该文件的完整文件名就是e:\lesson\ image\lena.bmp。正是我们想要读取的指定文件。
● 如果设置的当前工作路径不是e:\lesson,例如,当前的工作路径是e:\python,程序仍然会读取当前工作路径下的文件image\lena.bmp(而不是读取当前程序所在路径下的image\lena.bmp),该文件的完整文件名就是e:\python\ image\lena.bmp。显然,这样是无法读取到指定图像的。
为了避免上述错误,可以在读取图像前判断图像文件是否存在,并在显示图像前判断图像是否存在。
3.waitKey函数
函数cv2.waitKey()用来等待按键,当用户按下键盘后,该语句会被执行,并获取返回值。其语法格式为:
retval = cv2.waitKey( [delay] )
式中:
● retval表示返回值。如果没有按键被按下,则返回-1;如果有按键被按下,则返回该按键的ASCII码。
● delay表示等待键盘触发的时间,单位是ms。当该值是负数或者零时,表示无限等待。该值默认为0。
在实际使用中,可以通过函数cv2.waitKey()获取按下的按键,并针对不同的键做出不同的反应,从而实现交互功能。例如,如果按下A键,则关闭窗口;如果按下B键,则生成一个窗口副本。
下面通过一个示例演示如何通过函数cv2.waitKey()实现交互功能。
【例1.3】在一个窗口内显示图像,并针对按下的不同按键做出不同的反应。
函数cv2.waitKey()能够获取按键的ASCII码。例如,如果该函数的返回值为97,表示按下了键盘上的字母a键。通过将返回值与ASCII码值进行比较,就可以确定是否按下了某个特定的键。例如,通过语句“返回值==97”就可以判断是否按下了字母a键。
Python提供了函数ord(),用来获取字符的ASCII码值。因此,在判断是否按下了某个特定的按键时,可以先使用ord()函数获取该特定字符的ASCII码值,再将该值与cv2.waitKey()函数的返回值进行比较,从而确定是否按下了某个特定的键。这样,在程序设计中就不需要ASCII值的直接参与了,从而避免了使用ASCII进行比较可能带来的不便。例如,要判断是否按下了字母A键,可以直接使用“返回值==ord('A')”语句来完成。
根据题目要求及以上分析,编写代码如下:
import cv2 lena=cv2.imread("lena.bmp") cv2.imshow("demo", lena ) key=cv2.waitKey() if key==ord('A'): cv2.imshow("PressA", lena) elif key==ord('B'): cv2.imshow("PressB", lena)
运行上述程序,按下键盘上的A键或者B键,会在一个新的窗口内显示图像lena.bmp,它们的不同之处在于:
● 如果按下的是键盘上的A键,则新出现的窗口名称为PressA,如图1-4的左图所示。
● 如果按下的是键盘上的B键,则新出现的窗口名称为PressB,如图1-4的右图所示。
图1-4 【例1.3】程序的运行结果
从另外一个角度理解,该函数还能够让程序实现暂停功能。当程序运行到该语句时,会按照参数delay的设定等待特定时长。根据该值的不同,可能有不同的情况:
● 如果参数delay的值为0,则程序会一直等待。直到有按下键盘按键的事件发生时,才会执行后续程序。
● 如果参数delay的值为一个正数,则在这段时间内,程序等待按下键盘按键。当有按下键盘按键的事件发生时,就继续执行后续程序语句;如果在delay参数所指定的时间内一直没有这样的事件发生,则超过等待时间后,继续执行后续的程序语句。
【例1.4】在一个窗口内显示图像,用函数cv2.waitKey()实现程序暂停,在按下键盘的按键后让程序继续运行。
根据题目要求,编写代码如下:
import cv2 lena=cv2.imread("lena.bmp") cv2.imshow("demo", lena ) key=cv2.waitKey() if key! =-1: print("触发了按键")
运行上述程序,首先会在一个名为demo的窗口内显示lena.bmp图像。在未按下键盘上的按键时,程序没有新的状态出现;当按下键盘上的任意一个按键后,会在控制台打印“触发了按键”。
在本例中,由于cv2.waitKey()函数的参数值是默认值0,所以在未按下键盘上的按键时,程序会一直处于暂停状态。当按下键盘上的任意一个按键时,程序中key=cv2.waitKey()下方的语句便得以执行,程序输出“触发了按键”。
4.destroyWindow函数
函数cv2.destroyWindow()用来释放(销毁)指定窗口,其语法格式为:
None = cv2.destroyWindow( winname )
其中,winname是窗口的名称。
在实际使用中,该函数通常与函数cv2.waitKey()组合实现窗口的释放。
【例1.5】编写一个程序,演示如何使用函数cv2.destroyWindow()释放窗口。
根据题目要求,编写代码如下:
import cv2 lena=cv2.imread("lena.bmp") cv2.imshow("demo", lena ) cv2.waitKey() cv2.destroyWindow("demo")
运行上述程序,首先会在一个名为demo的窗口内显示lena.bmp图像。在程序运行的过程中,当未按下键盘上的按键时,程序没有新的状态出现;当按下键盘上的任意一个按键后,窗口demo会被释放。
5.destroyAllWindows函数
函数cv2.destroyAllWindows()用来释放(销毁)所有窗口,其语法格式为:
None = cv2.destroyAllWindows( )
【例1.6】编写一个程序,演示如何使用函数cv2.destroyAllWindows()释放所有窗口。
根据题目要求,编写代码如下:
import cv2 lena=cv2.imread("lena.bmp") cv2.imshow("demo1", lena ) cv2.imshow("demo2", lena ) cv2.waitKey() cv2.destroyAllWindows()
运行上述程序,会分别出现名称为demo1和demo2的窗口,在两个窗口中显示的都是lena.bmp图像。在未按下键盘上的按键时,程序没有新的状态出现;当按下键盘上的任意一个按键后,两个窗口都会被释放。
1.2.3 保存图像
OpenCV提供了函数cv2.imwrite(),用来保存图像,该函数的语法格式为:
retval = cv2.imwrite( filename, img[, params] )
式中:
● retval是返回值。如果保存成功,则返回逻辑值真(True);如果保存不成功,则返回逻辑值假(False)。
● filename是要保存的目标文件的完整路径名,包含文件扩展名。
● img是被保存图像的名称。
● params是保存类型参数,是可选的。
【例1.7】编写一个程序,将读取的图像保存到当前目录下。
根据题目要求,编写代码如下:
import cv2 lena=cv2.imread("lena.bmp") r=cv2.imwrite("result.bmp", lena)
上述程序会先读取当前目录下的图像lena.bmp,生成它的一个副本图像,然后将该图像以名称result.bmp存储到当前目录下。