1.2.2 显示图像

OpenCV提供了多个与显示有关的函数,下面对几个常用的函数进行简单的介绍。

1.namedWindow函数

函数cv2.namedWindow()用来创建指定名称的窗口,其语法格式为

其中,winname是要创建的窗口的名称。

例如,下列语句会创建一个名为lesson的窗口:

2.imshow函数

函数cv2.imshow()用来显示图像,其语法格式为

其中:

● winname是窗口名称。

● mat是要显示的图像。

【例1.2】在一个窗口内显示读取的图像。

根据题目要求,编写代码如下:

在本程序中,首先通过cv2.imread()函数读取图像lena.bmp,接下来通过cv2.namedWindow()函数创建一个名为lesson的窗口,最后通过cv2.imshow()函数在窗口lesson内显示图像lena.bmp。

运行上述程序,得到的运行结果如图1-5所示。

图1-5 【例1.2】程序的运行结果

在实际使用中,可以先通过函数cv2.namedWindow()来创建一个窗口,再让函数cv2.imshow()引用该窗口来显示图像。也可以不创建窗口,直接使用函数cv2.imshow()引用一个并不存在的窗口,并在其中显示指定图像,这样函数cv2.imshow()实际上会完成如下两步操作。

第1步:函数cv2.imshow()创建一个指定名称的新窗口。

第2步:函数cv2.imshow()将图像显示在刚创建的窗口内。

例如,在下面的语句中,函数cv2.imshow()完成了创建demo窗口和显示image图像的操作,该语句的功能与例1.2相同。

在显示图像时,初学者最经常遇到的一个错误是“error:(-215:Assertion failed)size.width>0&& size.height>0 in function 'cv::imshow'”,说明当前要显示的图像是空的(None),这通常是由于在读取文件时没有找到图像文件造成的。一般来说,没有找到要读取的图像文件,可能是因为文件名错误;如果确认要读取的图像的完整文件名(路径名和文件名)没有错,那么往往是工作路径配置错误造成的。

例如,当前程序所在路径为e:\lesson,要读取当前路径下存在的图像lena.bmp。那么:

● 如果设置的当前工作路径是e:\lesson,程序就会读取当前工作路径下的文件lena.bmp,该文件的完整文件名就是e:\lesson\lena.bmp。正是我们想要读取的指定文件。

● 如果设置的当前工作路径不是e:\lesson,例如,当前的工作路径是e:\python,程序仍然会读取当前工作路径下的文件lena.bmp(而不是读取当前程序所在路径下的lena.bmp),该文件的完整文件名就是e:\python\lena.bmp。显然,这样是无法读取到指定图像的。

为了避免上述错误,可以在读取图像前判断图像文件是否存在,并在显示图像前判断图像是否存在。

3.waitKey函数

函数cv2.waitKey()用来等待按键,当用户按下键盘按键后,该语句会被执行,并获取返回值。其语法格式为

其中:

● retval表示返回值。如果在指定的等待时间(参数delay指定)内,有按键被按下,则返回该按键的ASCII码。在等待时间(参数delay指定)结束后,如果没有按键被按下,则返回-1。

● 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')”语句来完成。

根据题目要求及以上分析,编写代码如下:

运行上述程序,按下键盘上的A键或者B键,会在一个新的窗口内显示图像lena.bmp,它们的不同之处在于:

● 如果按下的是键盘上的A键,则新出现的窗口名称为PressA,如图1-6(a)所示。

● 如果按下的是键盘上的B键,则新出现的窗口名称为PressB,如图1-6(b)所示。

图1-6 【例1.3】程序的运行结果

从另外一个角度理解,该函数还能够让程序实现暂停功能。当程序运行到该语句时,会按照参数delay的设定等待特定时长。根据该值的不同,可能有不同的情况:

● 如果参数delay的值为0,则程序会一直等待。直到有按下键盘按键的事件发生时,才返回按键的ASCII码,执行后续程序。

● 如果参数delay的值为一个正数,则在这段时间内,程序等待按下键盘按键。当有按下键盘按键的事件发生时,返回该按键的ASCII码,继续执行后续程序语句;如果在delay参数所指定的时间内一直没有这样的事件发生,则在超过等待时间后,返回值-1,继续执行后续的程序语句。

【例1.4】在一个窗口内显示图像,用函数cv2.waitKey()实现程序暂停,在按下键盘的按键后让程序继续运行。

根据题目要求,编写代码如下:

运行上述程序,首先会在一个名为demo的窗口内显示lena.bmp图像。在未按下键盘上的按键时,程序处于暂停状态,没有新的状态出现;当按下键盘上的任意一个按键后,会在控制台打印“触发了按键”。

在本例中,函数cv2.waitKey()没有参数,也就是说它使用的是默认参数0,表示无限等待。因此,存在两种情况:

● 当未按下键盘上的按键时,函数cv2.waitKey()一直处于等待状态,等待着接收键盘事件(单击键盘),所以程序会一直处于暂停状态。

● 当按下键盘上的任意一个按键时,函数cv2.waitKey()的返回值为按键ASCII码。此时,key的值一定不是-1,条件“key!=-1”成立,程序输出“触发了按键”。

4.destroyWindow函数

函数cv2.destroyWindow()用来释放(销毁)指定窗口,其语法格式为

其中,winname是窗口的名称。

在实际使用中,该函数通常与函数cv2.waitKey()组合实现窗口的释放。

【例1.5】编写一个程序,演示如何使用函数cv2.destroyWindow()释放窗口。

根据题目要求,编写代码如下:

运行上述程序,首先会在一个名为demo的窗口内显示lena.bmp图像。在程序运行的过程中,当未按下键盘上的按键时,程序没有新的状态出现;当按下键盘上的任意一个按键后,窗口demo会被释放。

5.destroyAllWindows函数

函数cv2.destroyAllWindows()用来释放(销毁)所有窗口,其语法格式为

【例1.6】编写一个程序,演示如何使用函数cv2.destroyAllWindows()释放所有窗口。

根据题目要求,编写代码如下:

运行上述程序,会分别出现名称为demo1和demo2的窗口,在两个窗口中显示的都是lena.bmp图像。在未按下键盘上的按键时,程序没有新的状态出现;当按下键盘上的任意一个按键后,两个窗口都会被释放。