OpenCV Python 教程
在这个OpenCV Python 的教程中, 我们将使用Python中的OpenCV库来介绍计算机视觉的各个方面。OpenCV 长期以来一直是软件开发的重要部分。对开发人员来说学习OpenCV是提高编程能力并帮助他们发展软件开发职业生涯的好方法。
什么是计算机视觉?
为了简化这个问题的答案, 让我们来试想一个场景。
假设你和你的朋友去度假,然后你上传了很多照片到Facebook上。但是现在在每张照片中找到你朋友的脸并标记它们要花费很多时间。实际上,Facebook已经足够智能,它可以帮你标记人物。
那么,你认为自动的特征标记是如何工作的呢? 简单来说,它通过计算机视觉来实现。
计算机视觉是一个跨学科领域,它解决如何使计算机从数字图像或视频中获得高层次的理解的问题。
这里的想法是将人类视觉系统可以完成的任务自动化。因此,计算机应该能够识别诸如人脸或者灯柱甚至雕像之类的物体。
计算机如何读取图像?
思考以下图片:
我们可以认出它是纽约天际线的图片。 但是计算机可以自己发现这一切吗?答案是不!
计算机将任何图片都读取为一组0到255之间的值。
对于任何一张彩色图片,有三个主通道——红色(R),绿色(G)和蓝色(B)。它的工作原理非常简单。
对每个原色创建一个矩阵,然后,组合这些矩阵以提供R, G和B各个颜色的像素值。
每一个矩阵的元素提供与像素的亮度强度有关的数据。
思考下图:
如图所示,图像的大小被计算为B x A x 3。
注意:对于黑白图片,只有一个单一通道。
现在让我们来看看OpenCV究竟是什么。
什么是OpenCV?
OpenCV是一个旨在解决计算机视觉问题的Python库。OpenCV最初由Intel在1999年开发,但是后来由Willow Garage资助。它支持很多编程语言,如C++,Python,Java等等。它也支持多种平台,包括Windows,Linux和MacOS。
OpenCV Python只是一个与Python一起使用的原始C++库的包装类。通过使用它,所有OpenCV数组结构都能被转化为NumPy数组或从NumPy数组转化而来。这样就可以轻松地将其与其他使用NumPy的库集成。例如,SciPy和Matplotlib等库。
OpenCV的基础操作?
Opencv能完成以下从加载图像到调整大小等基本操作:
- 使用OpenCV加载图片
- 查看图片形状/分辨率
- 显示图片
- 调整图像大小
1. 使用OpenCV加载图片
Import cv2 # colored Image Img = cv2.imread ("Penguins.jpg",1) # Black and White (gray scale) Img_1 = cv2.imread ("Penguins.jpg",0)
如以上代码所示,第一个要求是导入OpenCV模块。
之后,我们可以用imread模块读取图片。参数中的1代表这是一个彩色图片。如果这个参数的值是0,就意味着这个将被导入的图片是黑白图片。这里的图片名称是“Penguins”。很简单吧?
2. 查看图片形状/分辨率
我们可以使用shape子函数来输出图片的形状。看看以下代码:
Import cv2 # Black and White (gray scale) Img = cv2.imread ("Penguins.jpg",0) Print(img.shape)
对于图片的形状,我们指的是NumPy数组的形状。执行代码之后你将会看到这个矩阵由768行和1024列组成。
3. 显示图片
使用OpenCV显示图片非常简单和直接。思考以下图片:
import cv2 # Black and White (gray scale) Img = cv2.imread ("Penguins.jpg",0) cv2.imshow("Penguins", img) cv2.waitKey(0) # cv2.waitKey(2000) cv2.destroyAllWindows()
正如你所见,我们首先使用imread导入图片。我们需要一个输出窗口来显示这个图片,对吧?
然后,我们等待用户事件。waitKey使窗口保持静态直到用户按下一个键。传入的参数是以毫秒为单位的时间。
最后,我们根据waitForKey的参数使用destroyAllWindows关闭窗口。
4. 调整图像大小
类似地,调整图像大小非常简单。 这里有另一个代码段:
import cv2 # Black and White (gray scale) img = cv2.imread ("Penguins.jpg",0) resized_image = cv2.resize(img, (650,500)) cv2.imshow("Penguins", resized_image) cv2.waitKey(0) cv2.destroyAllWindows()
这里,resize函数用于将图像大小调整为所需的形状。这里的参数是新调整大小后的图像的形状。
与之前的代码相比,剩下的代码非常简单,对吗?
我相信你们对企鹅很好奇,这是我们想要输出的图片!
这是另一个向resize函数传递参数的方法。看看下面的表示方法:
Resized_image = cv2.resize(img, int(img.shape[1]/2), int(img.shape[0]/2)))
这里,我们得到的新图像大小是原始图像的一半。
使用OpenCV进行人脸检测
这看起来很复杂,但实际上很容易。 让我带你了解整个过程,然后你也会有同样的感受。
第一步:想一想我们的先决条件。我们首先需要一个图像。然后,我们需要创建一个级联分类器,它最后会给我们提供面部特征。
第二步:这一步要使用到OpenCV读取图像和特征文件。所以这个时候,原始数据点是NumPy数组的形式。
我们要做的就是搜索面部 NumPy n维数组的行和列的值。这是具有面部矩形坐标的数组。
第三步:最后一步是使用矩形面框显示图像。
看看下面的图片,这里我以图片的形式总结了上述的三个步骤以便于阅读:
非常直接明了,对吧?
首先,如之前所述,我们创建CascadeClassifier对象来提取面部特征。包含面部特征的XML文件路径是此处的参数。
下一步是读取一个包含面部的图片,并且使用COLOR_BGR2GREY将其转化为黑白图片。接下来,我们搜索图像的坐标。这是使用detectMultiScale来实现的。
你问什么坐标?它是面部矩形的坐标。scaleFactor被用来减小5%的形状值,直到找到面部。因此,总的来说,值越小,准确度越高。
最后,这张脸被显示到窗口。
- 给识别的人脸添加矩形面框
这个逻辑很简单——就像使用for循环语句一样简单。看看下面的图片:
我们通过传递参数(比如图片对象,轮廓框的RGB值和矩形的宽度),使用cv2.rectangle来定义方法以创建一个矩形。
让我们来看看面部检测的完整代码:
import cv2
# Create a CascadeClassifier Object
face_cascade = cv2.CascadeClassifier("haarcascade_frontalface_default.xml")
# Reading the image as it is
img = cv2.imread("photo.jpg")
# Reading the image as gray scale image
gray_img = cv2.cvtColor(img,cv2.COLOR_BGR2GRAY)
# Search the co-ordintes of the image
faces = face_cascade.detectMultiScale(gray_img, scaleFactor = 1.05, minNeighbors=5)
for x,y,w,h in faces:
img = cv2.rectangle(img, (x,y), (x+w,y+h),(0,255,0),3)
resized = cv2.resize(img, (int(img.shape[1]/7),int(img.shape[0]/7)))
cv2.imshow("Gray", resized)
cv2.waitKey(0)
cv2.destroyAllWindows()
使用OpenCV捕获视频
使用OpenCV捕获视频同样非常简单。以下的流程能够给你一个更好的理解。看看这个:
图片被逐个读取,因此由于帧的快速处理而产生视频,这使得独立的图片动起来。
看一看下面这张图:
首先我们像平常一样导入OpenCV库。接下来,我们有一个叫做VideoCapture的方法,用来创建VideoCapture对象。这个方法用于触发用户计算机上的摄像头。这个函数的参数表示程序应该使用内置摄像头还是附加摄像头。“0”表示在这个例子中使用内置摄像头。
最后,release方法用于在几毫秒内释放摄像头。
当你继续并尝试执行上述代码时,你会注意到摄像头指示灯会在一瞬间开启并稍后关闭。为什么会这样?
这是因为没有持续时间来保持相机功能。
看看以上代码,我们有新的一行time.sleep(3)。这使脚本停止3秒。请注意,传递的参数是以秒为单位的时间。因此,当代码执行时,网络摄像头将开启3秒钟。
- 添加窗口
添加一个窗口来显示视频的输出非常简单,与用于图像的相同方法差不多。 但是,还是有一点变化。 请看以下代码:
我很确定除了一两行外,你能很大程度地理解以上的代码。
在这里,我们定义了一个NumPy数组,用于表示视频捕获的第一个图像。它被保存在frame数组中。
我们还有check。这是一个布尔数据类型。如果Python能够访问和读取VideoCapture对象则返回True。
看看下面的输出:
如你所见,我们得到的输出为True,并打印了frame数组的一部分。
但是我们需要读取视频的第一个帧/图才能开始,是吧?
要做到这一点,首先我们需要创建一个frame对象,它将读取VideoCapture对象的图像。
如上所示,imshow方法用于捕获视频的第一帧。
在此期间,我们已经尝试了捕获视频的第一图像/帧。
那么我们如何在OpenCV中捕获视频而不是第一张图像呢?
- 直接捕获视频
为了捕获视频,我们将使用while循环。while的条件是这样的:除非“check”值为True,否则Python将显示帧。
这是代码段的图片:
我们利用cvtColor函数将每个帧转换为灰度图像,如前所述。
waitKey(1)将确保在每毫秒的间隙后生成一个新帧。
重要的是要注意while循环是完全有效的,以帮助完全迭代帧并在最终显示视频。
这里还有一个用户事件触发器。一旦用户按下“q”键,程序窗口就会关闭。
OpenCV很容易掌握,对吧? 我个人喜欢它的良好的可读性以及初学者开始使用OpenCV时极快的上手速度。
使用案例:使用OpenCV的运动检测器
1. 问题描述
你正在接触一家研究人类行为的公司。你的任务是为他们提供可以检测前方运动的网络摄像头。它应该返回一个图,并且这个图应该包含人/物体在摄像头前面的时间。
那么现在我们已经定义了我们的问题陈述,我们需要构建一个解决方案逻辑以结构化的方式来解决问题。
看看下面的图表:
最开始,我们将图像保存在特定的frame当中。
下一步是将图像转化为高斯模糊图像。这样做是为了保证我们能计算模糊图像和真实图像之间的明显的差异。
此时,图像仍然不是对象。我们定义一个阈值来去除瑕疵,比如图像中的阴影和其他噪声。
对象的边框稍后定义。我们在对象周围添加一个矩形框,正如我们在本教程前面所讨论的那样。
最后,我们计算对象出现在画面和退出画面的时间。
很简单吧?
这是代码段:
这里也遵循同样的原则。我们首先导入包并创建VideoCapture对象,以确保我们使用网络摄像头捕获视频。
While循环遍历视频的各个帧。我们将彩色帧转换为灰度图像,然后将此灰度图像转化为高斯模糊图。
我们需要存储视频的第一个图像/帧,对吧?出于这个目的,我们使用if语句。
现在,让我们深入了解一下代码:
我们使用absdiff函数计算第一个帧和其他所有帧之间的差异。
threshold函数提供阈值,它将差异值小于30的像素转换为黑。如果像素的差异值大于30,则转换为白色。THRESH_BINARY就适用于此目的。
然后,我们使用findContours函数给图像定义轮廓区域。我们也在这个阶段加入边界。
就像之前解释过的,contourArea函数可消除阴影和噪声。为了简化,它将只保留白色部分,正如我们定义的,白色部分面积大于1000像素。
然后,在我们的工作帧中在对象的周围创建一个矩形框。
接下来是这个简单的代码:
如前所述,帧每毫秒改变一次,并且当用户输入“q”时,跳出循环并关闭窗口。
在我们的用例中还有一件事就是我们要计算对象在摄像头前的时间。
2. 计算时间
我们用DataFrame存储对象出现在帧中被检测到的时间和运动的时间。
接下来是我们之前解释过的VideoCapture函数。但是在这里,我们有一个标志位,称之为status。我们在记录开始时设置status为0,因为对象在最初是不可见的。
如上图所示,当检测到对象时,我们将status标志更改为1。很简单吧?
我们将创建一个status的列表存储每一个扫描到的帧的状态,然后如果某处发生改变则使用datetime在列表中记录日期和时间。
如以上的解释图所示,我们将时间值存储在 DataFrame中。我们以把DataFrame写入CSV文件中结束,如图所示。
3. 绘制运动检测图
我们实例的最后一步是显示结果。我们将要显示的是表示两轴上的运动的图形。看看以下代码:
首先,我们从motion_detector.py文件中导入DataFrame。
下一步是将时间转换为可读的并且可以解析的字符串形式。
最后,使用Bokeh plots在浏览器上绘制时间值的DataFrame。
输出:
结论:
我希望这个OpenCV Python的教程能帮助你学习所有由Python开始使用OpenCV所需的基础。
当你尝试开发需要图像识别和类似原理的软件时就会非常方便了。现在你还能够在Python OpenCV的帮助下轻松地使用这些概念来开发应用程序。