现代计算机视觉应用依赖于实时视频捕捉。到目前为止,我们只讨论了作为计算机视觉领域的单一图像处理。现在,是时候在我们的计算机视觉应用中引入另一个维度,即时间。来自实时摄像机的视频可以用于许多目的,如安全、快速数字测量、交通管制和其他。因此,让我们看看如何利用我们在图像处理方面的知识并将其应用于视频处理。
1.使用OpenCV的视频捕获设置
在我们深入研究任何视频处理和一些关于如何将图像处理知识应用于视频应用的进一步解释之前,我们应该首先设置好我们需要的一切,以便进行现场录制。在这篇文章中,我们将使用一个来自互联网的视频例子,但我们将向你展示如何使用你机器上的网络摄像机。因此,没有任何进一步的广告,让我们深入到代码中。
1.1 设置
首先,我们需要了解什么是视频以及它与图像的关系。视频只是图像序列,图像变化非常快。因此,如果你想在一个视频上做一些转换,你需要对该序列中的每一个图像应用相同的转换。这些图像被称为帧。为了模拟视频,我们需要做一个无限循环,快速显示帧。使用OpenCV,它将会是这样的:
import cv2
recording = cv2.VideoCapture(0)
while True:
ret, frame = recording.read() #ret is True or False value depending on whether we successfully got an image from recording
cv2.imshow('Frame', frame)
if cv2.waitKey(1) == ord('x'):
break
cv2.destroyAllWindows()

我们可以看到,我们已经创建了一个VideoCapture类的对象记录。参数0告诉VideoCapture,我们要使用我们机器上的一个网络摄像机。如果你的机器上有多个摄像头,你可以用数字1、2、3来访问它们,以此类推。正如我们所说的,我们不打算使用我们的网络摄像机来进行录制,但我们将使用互联网上的一个视频实例。我们只需要把那个视频路径而不是一个0。
recording = cv2.VideoCapture('test.mp4')
我们正在使用一个无限循环来读取和显示我们录像中的每一帧。变量ret是一个布尔值,是真还是假,取决于我们是否成功地从录像中得到一个图像。每隔一毫秒,我们将检查 "x "键是否被按下,这将打破我们的循环。另外,当我们中断循环时,我们需要关闭所有的窗口。
2.用OpenCV建立一个文件扫描器
现在我们知道了如何将视频作为单帧来处理,让我们建立一个文件扫描器,它可以从任何给定的角度拾取文件的视频片段,对视角进行扭曲并执行图像分割,所以它最终看起来像这样:

2.1 载入视频
我们将使用这个桌子上的文件视频。我们的计划是进行边缘检测,找到能够代表文件形状的最大轮廓,然后对视角进行扭曲,最后进行阈值处理。
我们将使用以下库:
import numpy as np
import cv2
import imutils
现在让我们加载视频,看看它是什么样子的:
cap = cv2.VideoCapture('video-1657993036.mp4')
if cap.isOpened() == False:
print("Error opening the video")
while cap.isOpened():
ret, frame = cap.read()
frame = cv2.resize(frame, (812, 812))
cv2.imshow('frame', frame)
cap.release()
cv2.destroyAllWindows()

2.2 检测边缘
帧的大小是不必要的大,所以我们使用cv2.resize进行调整,并将其设置为812×812的帧。
为了找到图像的边缘,我们首先需要将其转换为灰色图像,然后我们要应用噪声(模糊),这样我们就能摆脱不必要的轮廓。完成后,我们要应用Canny边缘检测。
gray_frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
img_blurred = cv2.GaussianBlur(gray_frame, (5, 5), 1)
edge_image_blurred = cv2.Canny(img_blurred, 60, 180)
cv2.imshow("Edge image”, edge_image_blurred)
其结果如下:

2.3 图像扩张
接下来要做的是寻找轮廓,但在这之前我们应该讨论一个可能出现的问题。因为我们要处理的是由多帧组成的视频,不是每一帧都是从同一角度拍摄的,因此当涉及到照明和其他东西时,我们会遇到偏差。
这样做的结果是,它并不总是能完美地找到我们想要的文件轮廓,换句话说,可能会有遗漏的像素,这将导致我们的工作不能正确完成。
为了解决这个问题,我们将应用一种叫做图像扩张的东西。在图像处理中,上述方法在物体的边界上增加像素。扩张的反义词是侵蚀。
kernel = np.ones((5, 5))
imgDil = cv2.dilate(edge_image_blurred, kernel, iterations=2)
cv2.imshow('ImgDilated', imgDil)

2.4 轮廓检测
这方面的区别是非常明显的。
现在我们都准备好了,让我们进行轮廓检测。
key_points = cv2.findContours(imgErod, cv2.RETR_EXTERNAL, cv2.CHAIN_APPROX_SIMPLE)
contours = imutils.grab_contours(key_points)
cv2.drawContours(frame, contours, -1, (0, 255, 0), 2)

我们现在要提取最大的轮廓线,因为它将代表文件的边界。为了做到这一点,我们将浏览所有的轮廓,并挑选那些面积大于10000的轮廓。这个数字是实验性地选择的,只是为了摆脱那些明显的小轮廓,因为它们不能满足对一篇论文的描述。之后,我们要找到可以用4个点形成的类似于多边形的轮廓,这将代表一篇论文。
biggest_contour = np.array([])
for contour in contours:
area = cv2.contourArea(contour)
if area > 10000:
perimeter = cv2.arcLength(contour, True)
approximation = cv2.approxPolyDP(contour, 0.05 * perimeter, True)
if len(approximation) == 4:
biggest_contour = approximation
Biggest_contour变量将是一个数组,保存4个类似于多边形的点,这些点符合桌子上的纸张描述。
让我们画出这4个点,看看我们是否画对了:
if biggest_contour.size != 0:
cv2.drawContours(frame, biggest_contour, -1, (0, 255, 0), 15)

2.5 透视包络
我们可以看到,到目前为止,一切都在按计划进行。
现在我们有了这4个点,我们要进行透视缠绕。如果你对参数感到困惑,请查看我们关于openCv的几何变换的文章,其中我们已经深入介绍了这些东西。
if biggest_contour.size != 0:
cv2.drawContours(frame, biggest_contour, -1, (0, 255, 0), 20)
pts1 = np.float32(biggest_contour)
pts2 = np.float32([[0, 0], [0, 512], [512, 512], [512, 0]])
P = cv2.getPerspectiveTransform(pts1, pts2)
image_warped = cv2.warpPerspective(frame, P, (512, 512))
cv2.imshow('Warped image', image_warped)

2.6 树状线
剩下的事情就是对我们的图像进行阈值处理。
这里我们可以使用全局阈值,因为图像看起来有一个双峰直方图,但通常在扫描文件时,我们希望应用自适应阈值,所以我们将采用常规方法。我们应该注意,我们对灰色图像进行了扭曲,因为阈值处理只对灰度图像起作用。
if biggest_contour.size != 0:
cv2.drawContours(frame, biggest_contour, -1, (0, 255, 0), 20)
pts1 = np.float32(biggest_contour)
pts2 = np.float32([[812, 0], [0, 0], [0, 812], [812, 812]])
P = cv2.getPerspectiveTransform(pts1, pts2)
image_warped = cv2.warpPerspective(gray_frame, P, (812, 812))
binary_image = cv2.adaptiveThreshold(image_warped, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 71, 9)
cv2.imshow('frame', binary_image)

总结
通过这篇文章,我们将结束这个小而有用的系列。我们已经学到了一些图像处理的基本概念,这些概念通常只是整个计算机视觉过程中的一个步骤。我们希望你在跟随这些教程的过程中获得乐趣,并希望你坚持关注我们未来的教程和项目。