OpenCV轻松入门:面向Python
上QQ阅读APP看本书,新人免费读10天
设备和账号都新为新人

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存储到当前目录下。