OpenCV之Python学习笔记(1)(2): 图像的载入、显示和保存 图像元素的访问、通道分离与合并

OpenCV之Python学习笔记

一直都在用Python+OpenCV做一些算法的原型。本来想留下发布一些文章的,可是整理一下就有点无奈了,都是写零散不成系统的小片段。现在看到一本国外的新书《OpenCV Computer Vision with Python》,于是就看一遍,顺便把自己掌握的东西整合一下,写成学习笔记了。更需要的朋友参考。

阅读须知:

        本文不是纯粹的译文,只是比较贴近原文的笔记;
        请设法购买到出版社出版的书,支持正版。

       从书名就能看出来本书是介绍在Python中使用OpenCV,全书分为5章,两个附录:

  • 第一章OpenCV设置,介绍如何在Windows、Mac和Ubuntu上设置Pyhton、OpenCV和相关库的环境。还讨论了OpenCV社区、OpenCV文档以及官方的示例代码。
  • 第二章处理文件、摄像头和GUI,讨论OpenCV的I/O功能,接着使用面向对象的设计编写一个主应用程序,用于显示摄像头实时场景、处理键盘输入、将摄像头写入视频文件和静态图像文件。
  • 第三章图像过滤,介绍使用OpenCV、NumPy和SciPy来编写图像过滤器。过滤器可用于线性颜色操作、曲线颜色操作、模糊化、锐化和寻找边缘。本章修改第一章的主程序,将过滤器应用到实时摄像头场景中。
  • 第四章使用Haar Cascades追踪人脸,本章将编写一个层次化的人脸追踪器,使用OpenCV定位图像中的脸部、眼睛、鼻子和嘴巴。同时还编写了用于复制和改变图像中某块区域的大小。同样,本章也将修改之前的主应用程序,让其可以用于找到并处理摄像头场景中的人脸。
  • 第五章检测前景/背景区域和深度。通过本章将了解有关OpenCV(在OpenNI和SensorKinect的支持下)从深度摄像头中获得的数据类型的信息。接着编写一些函数,使用这些数据对前景区域施加一些限制效果。最后将这些函数整合到主程序中,使得在处理人脸之前先进行细化操作。
  • 附录A,与Pygame整合。修改主程序,用Pygame替换OpenCV来处理特定的I/O事件。(Pygame提供了更多样的事件处理函数。)
  • 附录B,为自定义目标生成Haar Cascades,允许我们检测一系列的OpenCV工具,来对任何类型的目标或模式构建跟踪器,而不仅仅是人脸。

本书第一章是介绍在不同操作系统上对OpenCV、Python及相关库的配置,这里就不介绍了。下一篇文章将直接从第二章开始介绍。






OpenCV Python教程(1、图像的载入、显示和保存)


本文是OpenCV  2 Computer Vision Application Programming Cookbook读书笔记的第一篇。在笔记中将以Python语言改写每章的代码。

PythonOpenCV的配置这里就不介绍了。

注意,现在OpenCV for Python就是通过NumPy进行绑定的。所以在使用时必须掌握一些NumPy的相关知识!

图像就是一个矩阵,在OpenCV for Python中,图像就是NumPy中的数组!

如果读取图像首先要导入OpenCV包,方法为:

[python]  view plain  copy
  1. import cv2  

读取并显示图像

在Python中不需要声明变量,所以也就不需要C++中的cv::Mat xxxxx了。只需这样:

[python]  view plain  copy
  1. img = cv2.imread("D:\cat.jpg")  

OpenCV目前支持读取bmp、jpg、png、tiff等常用格式。更详细的请参考OpenCV的参考文档。

接着创建一个窗口

[python]  view plain  copy
  1. cv2.namedWindow("Image")  

然后在窗口中显示图像

[python]  view plain  copy
  1. cv2.imshow("Image", img)  

最后还要添上一句:

[python]  view plain  copy
  1. cv2.waitKey (0)  

如果不添最后一句,在IDLE中执行窗口直接无响应。在命令行中执行的话,则是一闪而过。

完整的程序为:

[python]  view plain  copy
  1. import cv2   
  2.   
  3. img = cv2.imread("D:\\cat.jpg")   
  4. cv2.namedWindow("Image")   
  5. cv2.imshow("Image", img)   
  6. cv2.waitKey (0)  
  7. cv2.destroyAllWindows()  
最后释放窗口是个好习惯!

创建/复制图像


新的OpenCV的接口中没有CreateImage接口。即没有cv2.CreateImage这样的函数。如果要创建图像,需要使用numpy的函数(现在使用OpenCV-Python绑定,numpy是必装的)。如下:

[python]  view plain  copy
  1. emptyImage = np.zeros(img.shape, np.uint8)  
在新的OpenCV-Python绑定中,图像使用NumPy数组的属性来表示图像的尺寸和通道信息。如果输出img.shape,将得到(500, 375, 3),这里是以OpenCV自带的cat.jpg为示例。最后的3表示这是一个RGB图像。

也可以复制原有的图像来获得一副新图像。

[python]  view plain  copy
  1. emptyImage2 = img.copy();  
如果不怕麻烦,还可以用cvtColor获得原图像的副本。
[python]  view plain  copy
  1. emptyImage3=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  
  2. #emptyImage3[...]=0  
后面的emptyImage3[...]=0是将其转成空白的黑色图像。

保存图像 

保存图像很简单,直接用cv2.imwrite即可。

cv2.imwrite("D:\\cat2.jpg", img)

第一个参数是保存的路径及文件名,第二个是图像矩阵。其中,imwrite()有个可选的第三个参数,如下:

cv2.imwrite("D:\\cat2.jpg", img,[int(cv2.IMWRITE_JPEG_QUALITY), 5])

第三个参数针对特定的格式: 对于JPEG,其表示的是图像的质量,用0-100的整数表示,默认为95。 注意,cv2.IMWRITE_JPEG_QUALITY类型为Long,必须转换成int。下面是以不同质量存储的两幅图:

对于PNG,第三个参数表示的是压缩级别。cv2.IMWRITE_PNG_COMPRESSION,从0到9,压缩级别越高,图像尺寸越小。默认级别为3:

[python]  view plain  copy
  1. cv2.imwrite("./cat.png", img, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])   
  2. cv2.imwrite("./cat2.png", img, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])  
保存的图像尺寸如下:

还有一种支持的图像,一般不常用。

完整的代码为:

[python]  view plain  copy
  1. import cv2  
  2. import numpy as np  
  3.   
  4. img = cv2.imread("./cat.jpg")  
  5. emptyImage = np.zeros(img.shape, np.uint8)  
  6.   
  7. emptyImage2 = img.copy()  
  8.   
  9. emptyImage3=cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)  
  10. #emptyImage3[...]=0  
  11.   
  12. cv2.imshow("EmptyImage", emptyImage)  
  13. cv2.imshow("Image", img)  
  14. cv2.imshow("EmptyImage2", emptyImage2)  
  15. cv2.imshow("EmptyImage3", emptyImage3)  
  16. cv2.imwrite("./cat2.jpg", img, [int(cv2.IMWRITE_JPEG_QUALITY), 5])  
  17. cv2.imwrite("./cat3.jpg", img, [int(cv2.IMWRITE_JPEG_QUALITY), 100])  
  18. cv2.imwrite("./cat.png", img, [int(cv2.IMWRITE_PNG_COMPRESSION), 0])  
  19. cv2.imwrite("./cat2.png", img, [int(cv2.IMWRITE_PNG_COMPRESSION), 9])  
  20. cv2.waitKey (0)  
  21. cv2.destroyAllWindows()  

参考资料:
《OpenCV References Manuel》
《OpenCV  2 Computer Vision Application Programming Cookbook》
《OpenCV Computer Vision with Python》




OpenCV Python教程(2、图像元素的访问、通道分离与合并)


OpenCV Python教程之图像元素的访问、通道分离与合并

转载请详细注明原作者及出处,谢谢!

访问像素

像素的访问和访问numpy中ndarray的方法完全一样,灰度图为:

[python]  view plain  copy
  1. img[j,i] = 255  
其中j,i分别表示图像的行和列。对于BGR图像,为:
[python]  view plain  copy
  1. img[j,i,0]= 255  
  2. img[j,i,1]= 255  
  3. img[j,i,2]= 255  
第三个数表示通道。

下面通过对图像添加人工的椒盐现象来进一步说明OpenCV Python中需要注意的一些问题。完整代码如下:

[python]  view plain  copy
  1. import cv2  
  2. import numpy as np  
  3.   
  4. def salt(img, n):  
  5.     for k in range(n):  
  6.         i = int(np.random.random() * img.shape[1]);  
  7.         j = int(np.random.random() * img.shape[0]);  
  8.         if img.ndim == 2:   
  9.             img[j,i] = 255  
  10.         elif img.ndim == 3:   
  11.             img[j,i,0]= 255  
  12.             img[j,i,1]= 255  
  13.             img[j,i,2]= 255  
  14.     return img  
  15.   
  16. if __name__ == '__main__':  
  17.     img = cv2.imread("图像路径")  
  18.     saltImage = salt(img, 500)  
  19.     cv2.imshow("Salt", saltImage)  
  20.     cv2.waitKey(0)  
  21.     cv2.destroyAllWindows()  
处理后能得到类似下面这样带有模拟椒盐现象的图片:



上面的代码需要注意几点:

1、与C++不同,在Python中灰度图的img.ndim = 2,而C++中灰度图图像的通道数img.channel() =1

2、为什么使用np.random.random()?
这里使用了numpy的随机数,Python自身也有一个随机数生成函数。这里只是一种习惯,np.random模块中拥有更多的方法,而Python自带的random只是一个轻量级的模块。不过需要注意的是np.random.seed()不是线程安全的,而Python自带的random.seed()是线程安全的。如果使用随机数时需要用到多线程,建议使用Python自带的random()和random.seed(),或者构建一个本地的np.random.Random类的实例。

分离、合并通道

由于OpenCV Python和NumPy结合的很紧,所以即可以使用OpenCV自带的split函数,也可以直接操作numpy数组来分离通道。直接法为:

[python]  view plain  copy
  1. import cv2  
  2. import numpy as np  
  3.   
  4. img = cv2.imread("D:/cat.jpg")  
  5. b, g, r = cv2.split(img)  
  6. cv2.imshow("Blue", r)  
  7. cv2.imshow("Red", g)  
  8. cv2.imshow("Green", b)  
  9. cv2.waitKey(0)  
  10. cv2.destroyAllWindows()  
其中split返回RGB三个通道,如果只想返回其中一个通道,可以这样:
[python]  view plain  copy
  1. b = cv2.split(img)[0]  
  2. g = cv2.split(img)[1]  
  3. r = cv2.split(img)[2]  
最后的索引指出所需要的通道。

也可以直接操作NumPy数组来达到这一目的:

[python]  view plain  copy
  1. import cv2  
  2. import numpy as np  
  3.   
  4. img = cv2.imread("D:/cat.jpg")  
  5.   
  6. b = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)  
  7. g = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)  
  8. r = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)  
  9.   
  10. b[:,:] = img[:,:,0]  
  11. g[:,:] = img[:,:,1]  
  12. r[:,:] = img[:,:,2]  
  13.   
  14. cv2.imshow("Blue", r)  
  15. cv2.imshow("Red", g)  
  16. cv2.imshow("Green", b)  
  17. cv2.waitKey(0)  
  18. cv2.destroyAllWindows()  
注意先要开辟一个相同大小的图片出来。这是由于numpy中数组的复制有些需要注意的地方,具体事例如下:
[python]  view plain  copy
  1. >>> c= np.zeros(img.shape, dtype=img.dtype)  
  2. >>> c[:,:,:] = img[:,:,:]  
  3. >>> d[:,:,:] = img[:,:,:]  
  4. >>> c is a  
  5. False  
  6. >>> d is a  
  7. False  
  8. >>> c.base is a  
  9. False  
  10. >>> d.base is a #注意这里!!!  
  11. True  
这里,d只是a的镜像,具体请参考《 NumPy简明教程(二,数组3 )》中的“复制和镜像”一节。

通道合并

同样,通道合并也有两种方法。第一种是OpenCV自带的merge函数,如下:

[python]  view plain  copy
  1. merged = cv2.merge([b,g,r]) #前面分离出来的三个通道  
接着是NumPy的方法:
[python]  view plain  copy
  1. mergedByNp = np.dstack([b,g,r])   
注意:这里只是演示,实际使用时请用OpenCV自带的merge函数! 用NumPy组合的结果不能在OpenCV中其他函数使用,因为其组合方式与OpenCV自带的不一样,如下:
[python]  view plain  copy
  1. merged = cv2.merge([b,g,r])  
  2. print "Merge by OpenCV"   
  3. print merged.strides  
  4.   
  5. mergedByNp = np.dstack([b,g,r])   
  6. print "Merge by NumPy "   
  7. print mergedByNp.strides  
结果为:
[python]  view plain  copy
  1. Merge by OpenCV  
  2. (112531)  
  3. Merge by NumPy  
  4. (1500187500)  
NumPy数组的strides属性表示的是在每个维数上以字节计算的步长。这怎么理解呢,看下面这个简单点的例子:
[python]  view plain  copy
  1. >>> a = np.arange(6)  
  2. >>> a  
  3. array([012345])  
  4. >>> a.strides  
  5. (4,)  
a数组中每个元素都是NumPy中的整数类型,占4个字节,所以第一维中相邻元素之间的步长为4(个字节)。

同样,2维数组如下:

[python]  view plain  copy
  1. >>> b = np.arange(12).reshape(3,4)  
  2. >>> b  
  3. array([[ 0,  1,  2,  3],  
  4.        [ 4,  5,  6,  7],  
  5.        [ 8,  91011]])  
  6. >>> b.strides  
  7. (164)  
从里面开始看,里面是一个4个元素的一维整数数组,所以步长应该为4。外面是一个含有3个元素,每个元素的长度是4×4=16。所以步长为16。

下面来看下3维数组:

[python]  view plain  copy
  1. >>> c = np.arange(27).reshape(3,3,3)  
其结果为:
[python]  view plain  copy
  1. array([[[ 0,  1,  2],  
  2.         [ 3,  4,  5],  
  3.         [ 6,  7,  8]],  
  4.   
  5.        [[ 91011],  
  6.         [121314],  
  7.         [151617]],  
  8.   
  9.        [[181920],  
  10.         [212223],  
  11.         [242526]]])  
根据前面了解的,推断下这个数组的步长。从里面开始算,应该为(3×4×3,3×4,4)。验证一下:
[python]  view plain  copy
  1. >>> c.strides  
  2. (36124)  
完整的代码为:
[python]  view plain  copy
  1. import cv2  
  2. import numpy as np  
  3.   
  4. img = cv2.imread("D:/cat.jpg")  
  5.   
  6. b = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)  
  7. g = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)  
  8. r = np.zeros((img.shape[0],img.shape[1]), dtype=img.dtype)  
  9.   
  10. b[:,:] = img[:,:,0]  
  11. g[:,:] = img[:,:,1]  
  12. r[:,:] = img[:,:,2]  
  13.   
  14. merged = cv2.merge([b,g,r])  
  15. print "Merge by OpenCV"   
  16. print merged.strides  
  17. print merged  
  18.   
  19. mergedByNp = np.dstack([b,g,r])   
  20. print "Merge by NumPy "   
  21. print mergedByNp.strides  
  22. print mergedByNp  
  23.   
  24. cv2.imshow("Merged", merged)  
  25. cv2.imshow("MergedByNp", merged)  
  26. cv2.imshow("Blue", b)  
  27. cv2.imshow("Red", r)  
  28. cv2.imshow("Green", g)  
  29. cv2.waitKey(0)  
  30. cv2.destroyAllWindows()  


from: http://blog.csdn.net/sunny2038/article/category/904451

  • 1
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值