基于Python的OpenCV图像处理1

Apr 23,2017   7933 words   29 min


OpenCV Python的英文教程可以看这里

1.准备工作

(1)安装配置Python

[2018-5-12更新] 建议直接安装Python的Anaconda发行版,已经集成了常用的科学计算库,很方便,可以参考这篇博客。 当然如果想自己安装Python,体验一下,按照下面的流程也是可以的。

[更新结束]

这一步针对电脑上没有Python的全新安装,如果已有可以直接跳过。
首先到Python官网下载安装包,点击下载。考虑到兼容性下载2.7.x版本。 下载完成后进行安装。安装界面如下: 这里我们指定安装路径为Python2.7.13,也可以不改,但注意不要出现中文。 然后要记住勾选上Add path…,这样安装程序会自动添加路径到环境变量了,不需要手动添加。 接着便是等待安装,不是很慢。 很快就安装好了。 在控制台里输入python会弹出版本信息,说明安装完成,没有错误。

(2)安装PIP

pip是Python下管理包的方便的工具之一,建议安装。如果电脑上已经有了,可以跳过此步。
我们打开Python安装目录下的Scripts文件夹,发现有一个”easy_install.exe”。 在控制台中切换到这个目录下,然后输入”easy_install.exe pip”,如下图所示。 等待即可完成安装。这时我们在命令行里输入pip list即可出现已安装的包。如下所示。 这一步有可能出现问题说pip不是有效的命令,这是因为没有添加路径造成的。只需要在 系统变量Path里添加上Python的安装路径和Scripts路径,即可正常打开了。

(3)安装Numpy

如果你电脑上之前有了,那么可以跳过,如果是新的,需要安装。因为OpenCV依赖于这个库。 得益于上一步已经安装好的pip,这里我们只需要在控制台中输入”pip install numpy”即可实现自动安装。 并且如果缺少依赖还会自动安装依赖,十分方便。这类似于Ubuntu下面的apt-get和aptitude,都是自动包管理工具。 安装完成后如下图。 我们可以使用”pip list”来查看已安装的包。检测是否安装成功也可以在控制台输入”python”进入python环境,然后 输入”import numpy”,如果没有报错,说明是正确的,否则会提示错误。如下图。 此外由于后续还会用到Matplotlib,因此与上面一样的步骤进行安装,输入”pip install matplotlib”即可。安装时 你会发现他已经自动下载了其它的一些包,把依赖给解决了,不然的话需要手动一个个安装。如下图,除了Matplotlib还提示 安装好了其它必要的包。 利用pip安装包的步骤就是这样,如果以后需要安装其它的包也是同样的方法。下图是我电脑上目前安装的包。

(4)安装PyCharm

如果你电脑上已经有PyCharm可以跳过,或者用你喜欢的IDE都可以。
在配置完Python环境后,需要安装一个能用来编程的IDE,这里推荐PyCharm。因为它有比较强大的代码提示功能,界面也比较友好。 可以直接去官网下载community版本,然后一路安装即可。

(5)安装OpenCV

[2019-6-3更新] 安装Python版OpenCV最简单的办法是用pip命令,直接pip install opencv-python即可,默认安装最新版。 如果需要Contrib版,直接pip install opencv-contrib-python即可,默认最新版。 关于Contrib版的详细说明,见这篇博客。 关于OpenCV安装无需再看下面的内容,当然如果愿意,按照下面的内容安装应该也是没问题的。

[更新结束]

在Python中使用OpenCV之前,首先需要下载OpenCV库。首先可以到OpenCV官网选择对应的版本下载。
需要注意的是OpenCV目前最高支持Python到2.7,因此最好使用对应的版本,不然后续配置很麻烦。 下载完成后双击打开按照提示安装(其实就是解压)。
安装完成后,打开安装目录如下所示。 我们依次进入..\opencv\build\python\2.7\,这里有x86和x64两个文件夹。如果你安装的Python是64位,则用x64。 如果你系统是64位,但安装的是32位Python,那么依然选择x86文件夹。要以Python的位数为准,否则可能会报如下错误, 说DLL装载失败,不是有效的Win32应用程序。这就是因为Python与OpenCV位数不统一造成的。 由于OpenCV的Python版是依赖于NumPy的,还有可能出现的错误是提示NumPy库版本过低。 解决办法是去NumPy官网升级一下库即可。 可以在Windows命令行中输入Python查看版本。 我的Python是32位的,所以选择x86。打开会发现有个cv2.pyd文件。 我们只需要将这个文件拷贝到Python的安装目录下的..\Lib\site-packages\下即可,如图所示。 至此,安装OpenCV库的工作就完成了。 利用PyCharm新建一个Python文件,然后import cv2即可调用相关函数了。可以看到,有非常丰富的代码提示, 比单纯用文本编辑器好很多。 但PyCharm编辑器在我的老电脑上似乎并不能很好的识别。 会报错提示找不到这个cv2模块,而且也不会有语法提示。但在我的平板上 全新安装Python环境是可以识别的,而且有代码提示,一切正常。
我们输入以下代码利用OpenCV打开一幅图片并显示,进行简单测试:

import cv2

img = cv2.imread("E:\\test.jpg")
cv2.imshow("test", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

运行结果如下,可以看到虽然编辑器无法识别cv2这个模块,但是可以正常运行, 最后出现了Process finished with exit code 0 至此,说明我们正确安装了OpenCV,并成功调用。 它不影响编译和运行,是可以正常运行出结果的,只是IDE编辑器识别不了。说明不是安装的问题,是编译器的问题。 后来我想了一下,应该是IDE解释器设置的问题。在设置完解释器之后,编译器会进行已安装库的扫描和编目。由于在我的 老电脑上一开始PyCharm扫描的时候并没有OpenCV的库,后来我手动将库放进去,但是没有让PyCharm重新扫描更新,所以 出现了虽然能编译,但是没代码提示的情况。所以如果遇到这种情况,有两种解决办法。一种是在PyCharm中重新设置解释器(删除再新建), 然后系统应该会重新扫描已安装的包,如下所示。注意Console和Project都要重新设置。 重新添加解释器后会更新相关库,如下: 第二种办法是重新安装Python,配置环境。这是迫不得已的办法,但也是最有效的办法。一般第一种办法应该就已经可以解决问题了。
至此OpenCV安装便结束了,完成了我们的准备工作,下面就可以进入正题了。

2.图像读取、显示和输出

无论做任何图像处理,这三步是基本的操作。读取一个图片,对其进行各种操作,最后输出处理过的成果。 那么下面分别来看实现的代码。

(1)读取

OpenCV读取图像的函数很简单,即imread()。其参数为需要读入的图片的路径。如:

img = cv2.imread("E:\\test.jpg")
print type(img)
print img.dtype
print img.shape

表示读取test.jpg并且赋值给img变量。在这里打印出img的相关信息如下:
可以看到,OpenCV将读取到的图像文件转换成了”ndarray”对象,这也正印证了之前说的OpenCV是依赖于NumPy库的。 然后在”ndarray”中,每个元素的类型是“uint8”,一个无符号的8字节的整型变量,对应8bit量化,范围为0 - 255。 然后img的大小为(334, 500, 3),也即长为500,宽为334,高为3。这里的“高”其实可以理解为一幅图像的不同通道。 由于图像是由R、G、B三个通道组成的,因此对应通道数为3。这里还需要注意的一点是,shape中第一个元素保存的是 图像的宽度,第二个元素是长度,在遍历的时候不要弄反了。
此外,该函数还有一个参数,表示是否以灰度形式读取。0表示以灰度方式读取RGB图像,1表示不改变原图。默认是1。

(2)显示

利用OpenCV显示图像非常简单,只需要调用imshow()函数即可。第一个参数是窗口的名称,第二个参数是演示的数据。

cv2.imshow("test", img)

执行上述代码便可以新建一个窗口并显示图片。但如果执行便会发现问题,窗口一闪而过。这和运行C++的黑框框程序一样, 执行完就自动关闭了。在C++中,解决这个问题很简单。只需要添加引用#include <stdlib.h>,然后在需要停顿的地方加上 一句system("pause");则可以了。那么在Python中如何做呢?也非常简单,只需要加上一句cv2.waitKey(0)即可。 这个函数表示等待用户输入,其中参数表示等待时间,0表示永久等待。再次运行,会发现已经不再是“一闪而过”了。 如果还想更进一步,可以再添上一句cv2.destroyAllWindows()表示完成之后销毁窗体。完整代码如下:

cv2.imshow("test", img)
cv2.waitKey(0)
cv2.destroyAllWindows()

(3)输出

在我们通过各种算法对图像进行了处理之后,总希望将处理结果保存成文件,以便于和他人交流分享。在OpenCV中,保存文件也是 十分简单的操作,只需要imwrite()函数便可实现。第一个参数是保存路径,包括你想要保存的扩展名,第二个参数是需要保存的数据。

cv2.imwrite("E:\\output.jpg", img)

执行上述代码后,我们便会在E盘找到一个新的图片文件。对某些格式有额外参数,这些参数都以IMWRITE_开头。
JPEG格式的文件可以指定画质参数,参数名为cv2.IMWRITE_JPEG_QUALITY,范围为0 - 100,默认为95。
图像参数以[参数名,参数值,参数名,参数值,…]的形式传递给imwrite()的第三个参数。 对于PNG来说,第三个参数表示压缩级别。参数名为cv2.IMWRITE_PNG_COMPRESSION,范围为0到9。0表示不压缩,9表示压缩最大。 压缩级别越高,图像尺寸越小。默认级别为3。
如下是示例代码:

cv2.imwrite("E:\\output_30.jpg", img, [cv2.IMWRITE_JPEG_QUALITY, 30])
cv2.imwrite("E:\\output_8.png", img, [cv2.IMWRITE_PNG_COMPRESSION, 8])

3.图像读取进阶

在第二部分我们介绍了图像IO以及显示相关内容。这一部分我们介绍图像读取的进一步操作。如RGB通道的分离与合并等。 由于OpenCV读取的图像本质上来说是个ndarray数组,因此我们完全可以按照操作数组的一套方法来对其进行操作。当然 OpenCV也自带了很多函数用于相关处理。

(1)RGB通道分离

要实现这个目标,有两种办法。一种基于NumPy数组,一种基于OpenCV。首先介绍第一种。

基于数组

由于在上文提到,img是一个高度为3的数组,分别对应R、G、B三通道,因此我们只需要提取某一层的数组即可。 具体而言,我们可以直接这样去写:

r = img[:, :, 2]
g = img[:, :, 1]
b = img[:, :, 0]
print r.shape
print g.shape
print b.shape

运行的结果如下: 可以看到,正确完成了我们的目标。 不过要注意的是,OpenCV读取顺序并不是RGB,而是BGR。所以在提取的时候不要弄错索引。

基于OpenCV

在OpenCV中,有内置的函数专门用于分离RGB,即split()函数。函数接受一个参数即为原始图像数据, 返回三个与原始图像类型相同的分量,分别对应B、G、R。

b, g, r = cv2.split(img)

运行此代码,便可以得到各个分量的值。若只想接收某一个通道,只需在后面加上索引即可,如cv2.split(img)[0]

需要注意的是,cv2.split()是个比较耗时的操作。只有真正需要的时候才使用它,能用Numpy索引就尽量使用。

(2)RGB通道合并

这里直接介绍OpenCV的方法。调用merge()函数即可。参数为分离出来的三个通道。

img2 = cv2.merge([b, g, r])

(3)单像素处理

在我们将图片读取之后,其实我们就可以通过数组索引的方式来获取某个点上的像素灰度值/RGB值了。

print img[9, 9]
print img[9, 9, 2]

上述代码分别获取了第10行、第10列的BGR三个颜色分量和单独的R颜色分量,输出如下所示。 需要注意的是数组下标是从0开始的。所以索引9对应的是第10个元素。
B=107,G=57,R=37,以及单独获取的R分量。我们可以将其在Photoshop中取色,以检验是否正确。 可以看到和我们用代码获取到的RGB颜色值是一样的。

(4)遍历图像

我们可以逐一对图像的像素进行遍历,从而进行相关操作。这与遍历二维数组类似,注意数组不要越界。 例如将img的B通道复制给img2,代码如下:

img2 = np.zeros(img.shape)
for i in range(img.shape[0]):
    for j in range(img.shape[1]):
        img2[i, j, 0] = img[i, j, 0]
cv2.imshow("copy", img2)
cv2.waitKey(0)

4.实例练习

在掌握上面的知识后,我们可以对上面的知识进行综合,动手实现一个给图片添加椒盐噪声的实例。代码如下:

import numpy as np
import cv2


def addPepperAndSalt(img, n):
    img2 = img
    for i in range(n):
        x = int(np.random.random() * img.shape[0])
        y = int(np.random.random() * img.shape[1])
        img2[x, y, 0] = 255
        img2[x, y, 1] = 255
        img2[x, y, 2] = 255
    return img2


img = cv2.imread("E:\\Desktop\\img2.png")
img2 = addPepperAndSalt(img, 500)
cv2.imshow("salt and pepper", img2)
cv2.waitKey(0)
cv2.destroyAllWindows()

在这里我们定义了一个函数addPepperAndSalt(img, n)用于添加椒盐噪声,第一个参数是图像数据,第二个参数是 添加椒盐噪声的数量,如这里设置成500。由于彩色图像有3个分量,因此需要把RGB全部设置成255。 我们分别将噪声值设置成500、5000、50000,原图与运行的效果分别如下所示: 原图 n=500 n=5000 n=50000 可以看到效果还是很明显的。

5.添加椒盐噪声进阶版

在完成了基本的功能后,我们还可以不断扩展功能。如可以添加黑白椒盐噪声、彩色椒盐噪声等等。代码如下所示:

def peppersalt(img, n, m):
    """
    Add peppersalt to image
    :param img: the image you want to add noise
    :param n: the total number of noise (0 <= n <= width*height)
    :param m: different mode
    m=1:add only white noise in whole image
    m=2:add only black noise in whole image
    m=3:add black and white noise in whole image
    m=4:add gray scale noise range from 0 to 255
    m=5:add color noise in whole image,RGB is combined randomly with every channel ranges from 0 to 255
    :return: the processed image
    """
    img2 = img
    if m == 1:
        for i in range(n):
            x = int(np.random.random() * img.shape[0])
            y = int(np.random.random() * img.shape[1])
            img2[x, y, 0] = 255
            img2[x, y, 1] = 255
            img2[x, y, 2] = 255
    elif m == 2:
        for i in range(n):
            x = int(np.random.random() * img.shape[0])
            y = int(np.random.random() * img.shape[1])
            img2[x, y, 0] = 0
            img2[x, y, 1] = 0
            img2[x, y, 2] = 0
    elif m == 3:
        for i in range(n):
            x = int(np.random.random() * img.shape[0])
            y = int(np.random.random() * img.shape[1])
            flag = np.random.random() * 255
            if flag > 128:
                img2[x, y, 0] = 255
                img2[x, y, 1] = 255
                img2[x, y, 2] = 255
            else:
                img2[x, y, 0] = 0
                img2[x, y, 1] = 0
                img2[x, y, 2] = 0
    elif m == 4:
        for i in range(n):
            x = int(np.random.random() * img.shape[0])
            y = int(np.random.random() * img.shape[1])
            flag = int(np.random.random() * 255)
            img2[x, y, 0] = flag
            img2[x, y, 1] = flag
            img2[x, y, 2] = flag
    elif m == 5:
        for i in range(n):
            x = int(np.random.random() * img.shape[0])
            y = int(np.random.random() * img.shape[1])
            f1 = int(np.random.random() * 255)
            f2 = int(np.random.random() * 255)
            f3 = int(np.random.random() * 255)
            img2[x, y, 0] = f1
            img2[x, y, 1] = f2
            img2[x, y, 2] = f3
    return img2

不同模式下运行结果如下所示:
原图: 添加白色高斯噪声(m=1): 添加黑色高斯噪声(m=2): 添加黑白高斯噪声(m=3): 添加不同灰度高斯噪声(m=4): 添加彩色高斯噪声(m=5):

4.Just for fun

在添加椒盐噪声的实例中,如果我们将x方向上的距离限定在原始图像大小的35%,由于图像的上半部分是美丽的夜空, 添加的椒盐噪声会如同星星一样,十分漂亮。同时可适当减少噪声数。下图中n=200,范围为上35%,效果如图所示:

本文作者原创,未经许可不得转载,谢谢配合

返回顶部