树莓派计算机视觉编程:11~13

十一、计算机视觉的实际应用

在上一章中,我们研究了计算机视觉中的各种高级概念,例如形态运算和轮廓。

本章是我们在前面各章中学习和展示的所有计算机视觉概念的最终总结。 在本章中,我们将使用我们较早学习的计算机视觉操作来实现一些实际项目。 我们还将学习一些新概念,例如背景减法和光流计算,然后在小型应用中进行演示。 本章包含许多动手的编程示例,以及有关代码和新功能的详细说明。

在本章中,我们将学习和演示以下主题的代码:

  • 实现最大 RGB 过滤器
  • 实现背景减法
  • 计算光流
  • 检测并跟踪运动
  • 检测图像中的条形码
  • 实现色度键效果

完成本章后,您将能够实现所学到的概念,以使用 Raspberry Pi(RPi)和一些相机传感器来创建现实应用,例如安全系统和运动检测系统。

技术要求

可以在 GitHub 上找到本章的代码文件。

观看以下视频,以查看这个页面上的“正在执行的代码”。

实现最大 RGB 过滤器

我们知道,过滤器允许并根据某些标准阻止信号或数据。 让我们根据像素颜色的强度值手动编写用于实现特殊过滤器的代码。 这称为最大 RGB 过滤器。 在 Max RGB 过滤器中,我们比较每个像素的彩色图像的所有颜色通道的强度。

然后,我们将通道的强度保持为最大强度,并将所有其他通道的强度降低为零。 对于图像中的每个像素都会发生这种情况。 假设对于一个像素,强度为(30, 200, 120)。 然后,在应用最大 RGB 过滤器后,它将为(0, 200, 0)。 让我们看一个将使用 NumPy 和 OpenCV 函数实现此功能的程序:

代码语言:javascript
复制
import cv2
import numpy as np
def maxRGB(img):
    b = img[:, :, 0]
    g = img[:, :, 1]
    r = img[:, :, 2]
    M = np.maximum(np.maximum(b, g), r)
    b[b < M] = 0
    g[g < M] = 0
    r[r < M] = 0
    return(cv2.merge((b, g, r)))
cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    cv2.imshow('Max RGB Filter', maxRGB(frame))
    if cv2.waitKey(1) == 27:
        break
cv2.destroyAllWindows()
cap.release()

运行前面的程序并查看输出。 看到过滤后的实时供稿很有趣。 输出如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aMrzZUHy-1681873301183)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_01.jpg)]

图 11.1 –最大 RGB 过滤器的输出

在下一节中,我们将学习并演示背景减法的概念。

实现背景减法

静态摄像机用于的许多应用中,例如安全性和监视。 我们可以通过应用称为背景减法的过程来分离背景和运动对象。 通常返回二进制图像,背景图像(场景的静态部分)以黑色像素为单位,而运动部分(变化或动态)以白色像素为单位。 OpenCV 可以通过两种算法来实现。 第一个是createBackgroundSubtractorKNN()。 这将创建 K 最近邻KNN)背景减法器对象。 然后,我们可以使用对象调用apply()函数以获得前景遮罩。 我们可以直接实时显示前景遮罩。

以下是如何使用它的演示:

代码语言:javascript
复制
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorKNN()
while(True):
    ret, frame = cap.read()
    fgmask = fgbg.apply(frame)
    cv2.imshow('frame', fgmask)
    if cv2.waitKey(30) == 27:
        break
cap.release()
cv2.destroyAllWindows()

输出为二进制视频流,如以下屏幕截图所示。 我挥舞着我的手,该手以白色像素突出显示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SmKfCDkW-1681873301184)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_02.jpg)]

图 10.2 –使用 KNN 进行背景减法

请注意,如果您不动手一段时间,OpenCV 会将其视为背景的一部分,并将其慢慢溶解在输出中。

另一个类似的函数是cv2.createBackgroundSubtractorMOG2()。 这还会使用apply()函数生成前景遮罩。 以下是使用它的示例程序:

代码语言:javascript
复制
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
fgbg = cv2.createBackgroundSubtractorMOG2()
while(True):
    ret, frame = cap.read()
    fgmask = fgbg.apply(frame)
    cv2.imshow('frame', fgmask)
    if cv2.waitKey(30) == 27:
        break
cap.release()
cv2.destroyAllWindows()

运行前面的程序并查看输出。 在这两个程序中,我们都将创建fgbg对象,并使用apply()函数来计算前景遮罩,即fgmask。 然后,我们只是使用imshow()函数实时显示前景遮罩。 可以在前面的屏幕快照中看到此代码的预期输出。 运行程序并自己查看输出。

计算光流

光流(也称为,称为光流)是视频(实时或录制)中对象运动时出现的模式。 注意前一句中的外观一词。 这意味着,如果观察者(在我们的示例中为摄像机)处于运动中,则场景中的对象即使在静止时也被视为正在移动。 这称为相对运动。 简而言之,光流突出显示了视频中的相对运动。 OpenCV 具有许多可以计算光流的功能的实现。 cv2.calcOpticalFlowFarneback()函数使用密集方法计算光流。 这意味着它将计算所有点的流量。 此函数实现 Gunner Farneback 算法。

注意:

您可以通过以下 URL 阅读有关 Gunner Farneback 参数的更多信息:

http://www.diva-portal.org/smash/get/diva2:273847/FULLTEXT01.pdfTwo-Frame

让我们看看如何使用以下代码使用 OpenCV 和 Python 3 计算光流:

代码语言:javascript
复制
import cv2
import numpy as np
cap = cv2.VideoCapture(0)
ret, frame1 = cap.read()
prvs = cv2.cvtColor(frame1,
                    cv2.COLOR_BGR2GRAY)
hsv = np.zeros_like(frame1)
hsv[..., 1] = 255
while(cap):
    ret, frame2 = cap.read()
    next = cv2.cvtColor(frame2,
                        cv2.COLOR_BGR2GRAY)
    flow = cv2.calcOpticalFlowFarneback(prvs,
                                       next,
                                       None, 0.5,
                                       3, 15,
                                       3, 5,
                                       1.2, 0)
    mag, ang = cv2.cartToPolar(flow[..., 0],
                               flow[..., 1])
    hsv[..., 0] = ang * 180/np.pi/2
    hsv[..., 2] = cv2.normalize(mag, None, 0,
                                255, cv2.NORM_MINMAX)
    rgb = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)
    cv2.imshow('Optical Flow', rgb)
    if cv2.waitKey(1) == 27:
        break
    prvs = next
cap.release()
cv2.destroyAllWindows()

在前面的程序中, cv2.calcOpticalFlowFarneback()返回 XY(笛卡尔)系统中的流坐标。 然后,使用cv2.cartToPolar()函数将其转换为极性。 然后,色相显示运动的角度,而值显示最终 HSV 帧中运动的强度,该强度将转换为 BGR 并显示为输出。 输出看起来就像前面的屏幕快照所示。 唯一的区别是光流将由各种颜色表示。

光流的概念在以下领域具有应用:

  • 目标检测与跟踪
  • 运动检测和跟踪
  • 机器人导航

检测并跟踪运动

让我们构建一个用于使用 RPi,OpenCV 和 Python 实时检测和跟踪运动的系统。 我们将使用非常简单的技术来检测运动。 基本上,我们将计算视频源(视频文件或 USB 网络摄像头的实时源)的连续帧之间的差异。 然后,我们将在希望检测连续帧之间差异的像素区域周围绘制轮廓:

我们将从导入 OpenCV 和 NumPy 开始。 另外,初始化对应于 USB 网络摄像头的对象:

代码语言:javascript
复制
import cv2
代码语言:javascript
复制
import numpy as np
代码语言:javascript
复制
cap = cv2.VideoCapture(0)

我们将对视频中的帧应用扩散操作。 为此,我们需要一个核。 我们将在视频循环之前定义它。 让我们定义如下:

代码语言:javascript
复制
k = np.ones((3, 3), np.uint8)

以下代码捕获并在单独变量中存储连续帧:

代码语言:javascript
复制
t0 = cap.read()[1]
代码语言:javascript
复制
t1 = cap.read()[1]

现在,让我们为while循环编写该块。 在此块中,我们计算之前捕获的帧之间的绝对差。 我们将为此使用cv2.absdiff()函数。 然后,我们将计算出的绝对差转换为灰度以进行进一步处理:

代码语言:javascript
复制
while(True):
代码语言:javascript
复制
d=cv2.absdiff(t1, t0)
代码语言:javascript
复制
grey = cv2.cvtColor(d, cv2.COLOR_BGR2GRAY)

以下是前面代码的输出。 它显示了连续捕获的帧之间的绝对差的灰度:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IFbiNnHs-1681873301184)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_03.jpg)]

图 11.3 –连续帧之间的绝对差

我们在上一步中计算的输出有一些噪声。 因此,我们必须首先使用高斯模糊技术对其进行模糊处理以消除噪声:

代码语言:javascript
复制
blur = cv2.GaussianBlur(grey, (3, 3), 0)

我们使用二进制阈值化技术将上一步的模糊输出转换为二进制图像,并通过以下代码进行进一步处理:

代码语言:javascript
复制
ret, th = cv2.threshold(blur, 15, 255, cv2.THRESH_BINARY)

现在,让我们将膨胀形态学操作应用于该二进制图像。 这使检测阈值图像中的边界变得容易:

代码语言:javascript
复制
dilated = cv2.dilate(th, k, iterations=2)

以下是膨胀操作的输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TMLBgIxd-1681873301185)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_04.jpg)]

图 11.4 –扩展输出

让我们继续在膨胀图像中查找轮廓:

代码语言:javascript
复制
contours, hierarchy = cv2.findContour(dilated,
代码语言:javascript
复制
cv2.RETR_TREE,
代码语言:javascript
复制
cv2.CHAIN_APPROX_SIMPLE)
代码语言:javascript
复制
t2=t0
代码语言:javascript
复制
cv2.drawContours(t2, contours, -1, (0, 255, 0), 2)
代码语言:javascript
复制
cv2.imshow('Output', t2)

现在,让我们将最新的帧复制到保存较旧帧的变量,然后使用网络摄像头捕获下一帧:

代码语言:javascript
复制
t0=t1
代码语言:javascript
复制
t1=cap.read()[1]

当按下键盘上的Esc键时,我们结束while循环:

代码语言:javascript
复制
if cv2.waitKey(5) == 27 :
代码语言:javascript
复制
break

循环结束后,我们将执行通常的清理任务,例如释放相机捕获对象并破坏显示窗口:

代码语言:javascript
复制
cap.release()
代码语言:javascript
复制
cv2.destroyAllWindows()

以下是执行程序的输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lIWVxnbc-1681873301185)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_05.jpg)]

图 11.5 –检测到并突出显示的移动

请记住,此代码在计算上是昂贵的。 不要期望 RPi 的较老型号和非超频型号具有很高的帧率。 作为练习,绘制不同颜色的轮廓。 我们还可以借助cv2.moments()函数来计算质心,并用小圆圈表示它们。 这将使输出更有趣。

检测图像中的条形码

条形码是一种信息,可以直观地表示信息,对于特定用途的机器而言,易于理解。 条码格式很多。 常用格式具有不同厚度的平行垂直线,并且它们之间的间距不同。

在本节中,我们将演示如何从静止图像中检测简单的平行线格式的条形码。 我们将使用以下汽水罐图像:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fU4VinsU-1681873301185)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_06.jpg)]

图 11.6 –原始源图像

让我们使用以下代码读取汽水罐的原始图像:

代码语言:javascript
复制
import numpy as np
代码语言:javascript
复制
import cv2
代码语言:javascript
复制
image=cv2.imread('/home/pi/book/dataset/barcode.jpeg', 1)
代码语言:javascript
复制
input = cv2.cvtColor(image, cv2.COLOR_BGR2GRAY)

条形码的水平图像具有低和高的垂直梯度。 因此,候选图像必须具有适合该标准的区域。 我们将使用cv2.Sobel()函数来计算水平和垂直导数,然后计算差值以找出符合条件的区域。 让我们看看如何做到这一点:

代码语言:javascript
复制
hor_der = cv2.Sobel(input, ddepth=-1, dx=1, dy=0, ksize = 5)
代码语言:javascript
复制
ver_der = cv2.Sobel(input, ddepth=-1, dx=0, dy=1, ksize=5)
代码语言:javascript
复制
diff = cv2.subtract(hor_der, ver_der)

OpenCV 提供cv2.convertScaleAbs()函数。 它将任何数字数组转换为 8 位无符号整数的数组。 让我们使用它,如下所示:

代码语言:javascript
复制
diff = cv2.convertScaleAbs(diff)

输出如下:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8wzzJHmr-1681873301185)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_07.jpg)]

图 11.7 –水平和垂直 Sobel 导数之间的差异

前面的输出显示了具有非常水平和非常低的垂直梯度的区域。 让我们应用高斯模糊来消除前面输出的噪声。 使用以下代码执行此操作:

代码语言:javascript
复制
blur = cv2.GaussianBlur(diff, (3, 3), 0)

以下是上述代码的输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uHxqu9E8-1681873301186)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_08.jpg)]

图 11.8 –应用高斯模糊之后

现在,让我们通过对其应用阈值将其转换为二进制图像。 以下是执行此操作的代码:

代码语言:javascript
复制
ret, th = cv2.threshold(blur, 225, 255, cv2.THRESH_BINARY)

以下是输出二进制图像:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CEUHsFjV-1681873301186)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_09.jpg)]

图 11.9 –二进制输出

如上图所示,它是二进制图像,突出显示了条形码和其他高垂直梯度区域。 我们可以扩大图像以进行进一步处理。 它填补了垂直线之间的空隙:

代码语言:javascript
复制
dilated = cv2.dilate(th, None, iterations = 10)

前面代码的输出包含许多与原始图像中的条形码和其他区域相对应的矩形框。 我们对包含条形码的区域感兴趣,而不对其他区域感兴趣:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hBAAzKE3-1681873301186)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_10.jpg)]

图 11.10 –扩展的二进制输出

形态腐蚀操作将消除与条形码不对应的其他大部分区域:

代码语言:javascript
复制
eroded = cv2.erode(dilated, None, iterations = 15)

以下是上述代码的输出:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5Me5B6c4-1681873301186)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_011.jpg)]

图 11.11 –腐蚀图像

让我们在此计算出的二进制图像中找到所有轮廓的列表。 使用以下代码来执行此操作:

代码语言:javascript
复制
(contours, hierarchy) = cv2.findContours(eroded, cv2.RETR_TREE, cv2.CHAIN_APPROX_SIMPLE)

二值图像中最大的轮廓是与条形码区域相对应的轮廓。 以下代码在图像中找到最大轮廓:

代码语言:javascript
复制
areas = [cv2.contourArea(temp) for temp in contours]
代码语言:javascript
复制
max_index = np.argmax(areas)
代码语言:javascript
复制
largest_contour = contours[max_index]

让我们使用 OpenCV cv2.boundingRect()函数检索最大轮廓的边界矩形的坐标,然后在图像中绘制该矩形:

代码语言:javascript
复制
```py
x, y, width, height = cv2.boundingRect(largest_contour)
```
cv2.rectangle(image, (x, y), (x+width, y+height),(0,255,0), 2)
cv2.imshow(&#39;Detected Barcode&#39;,image)
cv2.waitKey(0)
cv2.destroyAllWindows()

前面的代码在与图像中最大轮廓(条形码的区域)相对应的区域上绘制边界矩形,如以下输出所示:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cukzbIYg-1681873301186)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_11_012.jpg)]

图 11.12 –检测到的条形码

如先前的屏幕截图所示,条形码的大致区域以蓝色矩形勾勒出轮廓。 相同的代码可能不适用于许多图像,但适用于大多数图像。 我们可能需要调整代码以检测其他图像中带有条形码的区域。 您可能需要为特定输入更改以下代码行:

代码语言:javascript
复制
blur = cv2.GaussianBlur(diff, (3, 3), 0)
dilated = cv2.dilate(th, None, iterations = 10)
eroded = cv2.erode(dilated, None, iterations = 15)

基于此程序,我们可以创建许多实际应用。 第一个应用是条形码区域检测器,用于检测来自 USB 网络摄像头的实时视频。 我们可以创建的另一个应用是检测条形码的通用程序。 为了调整传递给函数的参数,我们可以使用跟踪栏。

在下一节中,我们将学习如何使用 RPi 和 USB 网络摄像头通过 OpenCV 和 Python 3 应用胶片风格的色度键控。

实现色度键效果

色度键控也称为色度键合成。 由于我们在创建绿色或蓝色效果时会使用绿色或蓝色背景,因此俗称它为绿色或蓝色屏幕效果。 它是后期制作技术,也可以用于静止图像和实时视频。 在色度键效果中,我们将一个对象或一个人放在前景中并捕获图像或镜头。 背景通常是绿色或蓝色的织物或墙壁。 然后,我们将捕获的图像或素材中的绿色或蓝色替换为另一个视频或图像。 这使观看者感到前景中的人物或物体与他们拍摄的摄影棚不在同一个位置。 此效果是新闻广播中电影制作和实时天气预报中最常用的效果之一:

首先,导入所有需要的库并启动视频捕获对象:

代码语言:javascript
复制
import numpy as np
代码语言:javascript
复制
import cv2
代码语言:javascript
复制
cap = cv2.VideoCapture(0)

为了获得更好的帧频或每秒帧FPS)速率,我们将 USB 网络摄像头的分辨率设置为640x480像素。 这样会产生更好的帧速率,并且绿屏效果看起来很自然:

代码语言:javascript
复制
cap.set(3, 640)
代码语言:javascript
复制
cap.set(4, 480)

import mahotas
photo = mahotas.demos.load(&#39;luispedro&#39;)
plt.imshow(photo)
plt.axis(&#39;off&#39;)
plt.show()</code></pre></div></div><p>在前面的代码中, <code>mahotas.demos.load()</code>用于将内置图像加载到 NumPy 数组。 <code>luispedro</code>是该库作者的图像。 与 OpenCV 不同,Mahotas 以 RGB 格式读取和存储彩色图像。 我们还可以在灰度模式下加载和显示图像,如下所示:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">photo = mahotas.demos.load(&#39;luispedro&#39;, as_grey=True)
plt.imshow(photo, cmap=&#39;gray&#39;)</code></pre></div></div><p>我们可以加载其他库图像,如下所示:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">photo = mahotas.demos.load(&#39;nuclear&#39;)
photo = mahotas.demos.load(&#39;lena&#39;)
photo = mahotas.demos.load(&#39;DepartmentStore&#39;)</code></pre></div></div><p>我们还可以读取存储在磁盘上的图像,如下所示:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">photo= mahotas.imread(&#39;/home/pi/book/dataset/4.1.01.tiff&#39;)</code></pre></div></div><p>此函数的工作方式与<code>cv2.imread()</code> OpenCV 函数相同。</p><h3 id="1k1m9" name="%E5%AF%B9%E5%9B%BE%E5%83%8F%E5%BA%94%E7%94%A8%E9%98%88%E5%80%BC">对图像应用阈值</h3><p>我们已经知道阈值化的基础知识。 我们可以通过使用<code>mahotas</code>中提供的函数对灰度图像进行阈值处理。 让我们演示大津的二值化:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">import matplotlib.pyplot as plt
import numpy as np
import mahotas
photo = mahotas.demos.load(&#39;luispedro&#39;, as_grey=True)
photo = photo.astype(np.uint8)
T_otsu = mahotas.otsu(photo)
plt.imshow(photo &gt; T_otsu, cmap=&#39;gray&#39;)
plt.axis(&#39;off&#39;)
plt.show()</code></pre></div></div><p><code>mahotas.otsu()</code>函数接受灰度图像作为参数,并返回阈值。<code>photo &gt; T_otsu</code>代码返回阈值图像。 以下是输出:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BmvVbXSb-1681873301188)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_001.jpg)]</p><p>图 12.1 –大津的二值化</p><p>我们也可以使用 Riddler-Calvard 方法执行阈值化,如下所示:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">T_rc = mahotas.rc(photo)
plt.imshow(photo &gt; T_rc, cmap=&#39;gray&#39;)</code></pre></div></div><p><code>mahotas.rc()</code>函数接受灰度图像作为参数,并返回阈值。 <code>photo &gt; T_rc</code>代码返回阈值图像。 运行此命令并检查输出。 它将使用 Riddler-Calvard 方法向我们显示阈值图像。</p><h3 id="bg8cf" name="%E8%B7%9D%E7%A6%BB%E5%8F%98%E6%8D%A2">距离变换</h3><p>距离变换是一种形态学操作。 最好使用二进制(0 和 1)图像进行可视化。 它将二进制图像转换为灰度图像,以使点的灰度强度可视化其距图像边界的距离。 <code>mahotas.distance()</code>函数接收图像并计算距离变换。 让我们看一个例子:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">import matplotlib.pyplot as plt
import numpy as np
import mahotas
f = np.ones((256, 256), bool)
f[64:191, 64:191] = False
plt.subplot(121)
plt.imshow(f, cmap=&#39;gray&#39;)
plt.title(&#39;Original Image&#39;)
dmap = mahotas.distance(f)
plt.subplot(122)
plt.imshow(dmap, cmap=&#39;gray&#39;)
plt.title(&#39;Distance Transform&#39;)
plt.show()</code></pre></div></div><p>这将创建一个正方形的自定义图像,在白色背景上填充有黑色。 然后,它计算距离变换并将其可视化。 这将产生以下输出:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Lx2qOOP3-1681873301188)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_002.jpg)]</p><p>图 12.2 –距离变换演示</p><h3 id="f3orl" name="%E8%89%B2%E5%BD%A9%E7%A9%BA%E9%97%B4">色彩空间</h3><p>我们可以将 RGB 图像转换为棕褐色,如下所示:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">import matplotlib.pyplot as plt
import mahotas
photo = mahotas.demos.load(&#39;luispedro&#39;)
photo = mahotas.colors.rgb2sepia(photo)
plt.imshow(photo)
plt.axis(&#39;off&#39;)
plt.show()</code></pre></div></div><p>前面的代码从库中读取灰度图像,并使用<code>rgb2sepia()</code>函数的调用将其转换为具有棕褐色色彩空间的图像。 它接受图像作为参数并返回转换后的图像。 以下是,是先前程序的输出:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L34p3ZMA-1681873301188)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_003.jpg)]</p><p>图 12.3 –棕褐色图像</p><p>在下一节中,我们将学习如何结合 Mahotas 和 OpenCV 的代码。</p><h2 id="3h5kb" name="%E7%BB%93%E5%90%88-Mahotas-%E5%92%8C-OpenCV">结合 Mahotas 和 OpenCV</h2><p>像 OpenCV 一样,Mahotas 使用 NumPy 数组存储并处理图像。 我们还可以将 OpenCV 和 Mahotas 结合起来。 让我们来看一个这样的示例,如下所示:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">import cv2
import numpy as np
import mahotas as mh
cap = cv2.VideoCapture(0)
while True:
    ret, frame = cap.read()
    frame = cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)
    T_otsu = mh.otsu(frame)
    output = frame &gt; T_otsu
    output = output.astype(np.uint8) * 255
    cv2.imshow(&#39;Output&#39;, output)
    if cv2.waitKey(1) == 27:
        break
cv2.destroyAllWindows()
cap.release()</code></pre></div></div><p>在前面的程序中,我们将实时帧转换为灰度版本。 然后,我们应用了大津二进制化的 Mahotas 实现,将来自实时视频源的帧转换为布尔二进制图像。 我们需要将其转换为<code>np.uint8</code>类型,然后将其乘以<code>255</code>(所有形式均采用二进制 8 位的形式),以便将其与<code>cv2.imshow()</code>。 输出如下:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7xByv84X-1681873301188)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_004.jpg)]</p><p>图 12.4 –输出窗口的屏幕截图</p><p>我们通常使用 OpenCV 功能从 USB 网络摄像头读取实时供稿。 然后,我们可以使用 Mahotas 或任何其他图像处理库中的函数来处理帧。 这样,我们可以合并来自两个不同图像处理库的代码。</p><p>在下一节中,我们将学习其他一些 Python 图像处理库的名称和 URL。</p><h2 id="51rsp" name="%E5%85%B6%E4%BB%96%E6%B5%81%E8%A1%8C%E7%9A%84%E5%9B%BE%E5%83%8F%E5%A4%84%E7%90%86%E5%BA%93">其他流行的图像处理库</h2><p>Python 3 有许多第三方库。 这些库中许多都使用 NumPy 来处理图像。 让我们来看看可用库的列表:</p><ul class="ul-level-0"><li><strong>Skimage</strong></li><li><strong>SimplelTK</strong></li><li><code>scipy.ndimage</code></li></ul><p>这些都是基于 NumPy 的图像处理库。 Python 图像库及其维护良好的分支 <strong>Pillow</strong> 是非基于 NumPy 的图像处理库。 它们还具有在 NumPy 和 PIL 图像格式之间转换图像的接口。</p><p>我们可以结合使用各种库的代码来创建具有所需功能的各种计算机视觉应用。</p><p>在下一部分中,我们将探索 Jupyter 笔记本。</p><h2 id="fctlg" name="%E6%8E%A2%E7%B4%A2%E9%80%82%E7%94%A8%E4%BA%8E-Python-3-%E7%BC%96%E7%A8%8B%E7%9A%84-Jupyter-%E7%AC%94%E8%AE%B0%E6%9C%AC">探索适用于 Python 3 编程的 Jupyter 笔记本</h2><p>Jupyter 笔记本是基于的基于 Web 的交互界面,其工作方式类似于 Python 3 的交互模式。Jupyter 笔记本具有 40 种编程语言,包括 Python 3,R,Scala 和 Julia。 它为编程提供了一个交互式环境,该环境也可以具有可视化,富文本,代码和其他组件。</p><p>Jupyter 是 IPython 项目的一个分支。 IPython 的所有与语言无关的部分已移至 Jupyter,而 Jupyter 的 Python 相关功能由 IPython 内核提供。 让我们看看如何在 Raspberry Pi 上安装 Jupyter:</p><p>在命令提示符中逐一运行以下命令:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">sudo pip3 uninstall ipykernel </code></pre></div></div><p>先前的命令将卸载<code>ipykernel</code>工具的早期版本。</p><p>以下命令将安装所有必需的库:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">sudo pip3 install ipykernel==4.8.0 </code></pre></div></div><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">sudo pip3 install jupyter </code></pre></div></div><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">sudo pip3 install prompt-toolkit==2.0.5</code></pre></div></div><p>这些命令将在 Raspberry Pi 上安装 Jupyter 和必需的组件。</p><p>要启动 Jupyter 笔记本,请登录 Raspberry Pi 的图形环境(直接或使用远程桌面),然后在<code>lxterminal</code>中运行以下命令:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">jupyter notebook</code></pre></div></div><p>这将启动 Jupyter 笔记本服务器进程并打开具有 Jupyter 笔记本界面的 Web 浏览器窗口,如下所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8ESTWpTu-1681873301189)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_005.jpg)]</p><p>图 12.5 –启动目录</p><p>上一个屏幕截图显示了运行命令以启动的目录的目录结构。 我在当前章节<code>/home/pi/book/chapter12</code>的代码目录中运行了它。 我们运行命令的 LXTerminal 窗口显示服务器日志,如下所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2QBKEnaN-1681873301189)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_006.jpg)]</p><p>图 12.6 – Jupyter 笔记本服务器日志</p><p>现在,让我们将返回到运行 Jupyter 的浏览器窗口。 在浏览器窗口的右上角,我们有注销和退出的选项。 在其下方,我们可以看到<strong>上传</strong>按钮,<strong>新建</strong>下拉菜单以及刷新符号。</p><p>在右侧,我们可以看到三个标签。 正如我们已经看到的,第一个显示了在命令提示符中启动 Jupyter 的目录结构。 第二个选项卡显示当前正在运行的进程。</p><p>让我们探索右侧的<strong>新建</strong>下拉选项。 以下屏幕截图显示了此菜单下可用的选项:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k6OEezFE-1681873301189)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_007.jpg)]</p><p>图 12.7 –新建菜单下拉菜单</p><p>在<strong>笔记本</strong>部分下,我们可以看到 <strong>Python 3</strong> 的选项。 如果您有使用 Jupyter 的任何其他编程语言,那么这些语言也会在此处显示。 我们将很快进行探讨。 其他选项是<strong>文本文件</strong>,<strong>文件夹</strong>和<strong>终端</strong>。 <strong>其它</strong>下的前两个选项分别创建一个空白文件和一个空白目录。 单击<strong>终端</strong>,将在浏览器窗口的新选项卡中启动 LXTerminal,如以下屏幕截图所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VyCx7AFE-1681873301189)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_008.jpg)]</p><p>图 12.8 –在 Web 浏览器选项卡中运行的命令提示符</p><p>如果我们单击原始选项卡(在浏览器选项卡中列为<strong>主页</strong>),然后在<strong>运行</strong>选项下进行检查,则可以看到对应于当前的终端窗口选项卡的一个条目,如以下屏幕截图所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cP8eLTMq-1681873301189)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_009.jpg)]</p><p>图 12.9 – Jupyter 中正在运行的子流程的列表</p><p>如我们所见,有选项可以查看在此服务器下启动的当前笔记本和终端。 我们可以从这里关闭它们。 转到<strong>文件</strong>,然后在<strong>新建</strong>下拉菜单下,选择 <strong>Python 3</strong>。 这将在同一浏览器窗口的新选项卡下打开 Python 3 笔记本:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c032nkyu-1681873301190)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_010.jpg)]</p><p>图 12.10 –新的 Jupyter 笔记本选项卡</p><p>我们可以看到笔记本的名称是<code>Untitled</code>。 我们可以单击名称,这将打开一个模式对话框,重命名笔记本,如下所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qq8svSQz-1681873301190)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_011.jpg)]</p><p>图 12.11 重命名笔记本</p><p>重命名笔记本。 之后,在<strong>主页</strong>主页中的<strong>文件</strong>下,我们可以看到<code>test01.ipynb</code>文件。 在这里,IPYNB 表示一个 IPython 笔记本。 您也可以在<strong>运行</strong>选项卡中看到一个条目。 在<code>/home/pi/book/chapter12/</code>目录中,我们可以找到<code>test01.ipynb</code>文件。 现在,让我们看看如何使用此文件进行 Python 3 编程。 再次在浏览器中切换到<code>test01</code>笔记本选项卡。 让我们详细研究界面:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KJ2iTY0T-1681873301190)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_012.jpg)]</p><p>图 12.12 – Jupyter 笔记本</p><p>在<code>In []:</code>文本之后,我们可以看到一个较长的文本区域。 我们可以在此处编写代码段。 确保从菜单的下拉菜单中选择了<strong>代码</strong>。 然后,将以下代码添加到文本区域:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">print(&#39;Hello World&#39;)</code></pre></div></div><p>我们可以通过单击菜单栏中的<strong>运行</strong>按钮来运行它。 输出将被打印在此处,并且将出现一个新的文本区域。 光标将在此处自动设置:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IcVfOV3G-1681873301190)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_013.jpg)]</p><p>图 12.13 –运行 HelloWorld 程序</p><p>关于此笔记本的最好的事情是我们可以编辑并重新执行较早的单元。 让我们尝试了解菜单中的图标:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mkOEanL5-1681873301190)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_014.jpg)]</p><p>图 12.14:菜单中的图标按钮</p><p>让我们从左到右。 第一个符号(软盘)将被保存。 <code>+</code>符号在当前突出显示的单元格之后添加一个文本区域单元格。 然后,我们有剪切,复制和粘贴选项。 上下箭头用于上下移动当前文本区域单元格。 然后,我们有<strong>运行</strong>并中断内核,重新启动内核,然后重新启动并运行整个笔记本按钮。 下拉框确定单元格的类型。 它具有以下四个选项:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mVGzxgnH-1681873301191)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_15.jpg)]</p><p>图 12.15 –单元格类型</p><p>如果希望单元运行代码,则选择<strong>代码</strong>。 <strong>Markdown</strong> 是用于 RTF 的标记语言。 选择一个空白文本区域单元,并将其更改为 <strong>Markdown</strong> 类型。 然后,在单元格中输入<code>#Test</code>并执行它。 这将创建一个一级标题,如下所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1MiOKlNo-1681873301191)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_016.jpg)]</p><p>图 12.16 – 1 级标题</p><p>我们可以将<code>##</code>用于第二级标题,将<code>###</code>用于三级标题,依此类推。</p><p>Jupyter 笔记本的主要功能之一是,我们甚至可以在笔记本中运行 OS 命令。 我们需要在命令前面加上<code>!</code>符号,然后将其作为<strong>代码</strong>在单元格中运行。 让我们看一下这个例子。 在笔记本中运行<code>!ls -la</code>命令,它将产生以下结果:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5FsQMdtf-1681873301191)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_017.jpg)]</p><p>图 12.17 –在 Jupyter 笔记本中运行 OS 命令</p><p>我们也可以在笔记本中使用 <strong>Matplotlib</strong> 显示的可视化效果和图像。 为此,我们需要使用神奇的<code>%matplotlib</code>函数。 我们可以使用将 <strong>Matplotlib</strong> 的后端设置为 Jupyter 笔记本的<strong>内联</strong>后端,如下所示:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">%matplotlib inline</code></pre></div></div><p>让我们看一下这个的简短演示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8GDjvHgN-1681873301191)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_018.jpg)]</p><p>图 12.18 –在 Jupyter 笔记本中显示图像</p><p>这就是我们可以在笔记本本身中显示可视化效果和图像的方式。</p><p>这是一个非常有用的概念。 我们可以在一个笔记本中使用富文本,操作系统命令,Python 代码和输出(包括可视化)。 我们甚至可以电子方式共享这些 <strong>IPYNB</strong> 笔记本文件。 就像 Python 3 一样,我们可以将 Jupyter 笔记本与许多语言一起使用,例如 Julia,R 和 Scala。 唯一的限制是我们不能在一个笔记本中混合使用多种编程语言的代码。</p><p>我要解释的最后一件事是如何清除输出。 单击<strong>单元格</strong>菜单。 它看起来如下:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Keu933nd-1681873301191)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B1608_12_019.jpg)]</p><p>图 12.19 –清除所有输出</p><p>在<strong>当前输出</strong>和<strong>所有输出</strong>下都具有<strong>清除</strong>选项。 这些分别清除当前单元格和整个笔记本的输出。</p><p>我建议您自己浏览菜单栏中的所有选项。</p><h2 id="1p69l" name="%E6%80%BB%E7%BB%93">总结</h2><p>在本章中,我们探讨了 Mahotas 的基础知识,它是一个基于 NumPy 的图像处理库。 我们研究了一些与图像处理相关的功能,并学习了如何结合 Mahotas 和 OpenCV 代码进行图像处理。 我们还了解了其他基于 NumPy 和非基于 NumPy 的图像处理库的名称。 您可以进一步探索这些库。</p><p>我们了解到的最后一个主题是 Jupyter 笔记本型原型和通过电子方式共享代码非常有用。 现在,许多计算机视觉和数据科学专业人员都将 Jupyter 笔记本用于其 Python 编程项目。</p><p>在本书的“附录”部分中,我解释了本章无法列出的所有主题。 这些主题对于将 Raspberry Pi 用于各种目的的任何人都将非常有用。</p><h2 id="35jft" name="%E5%8D%81%E4%B8%89%E3%80%81%E9%99%84%E5%BD%95">十三、附录</h2><p>本书主要章节中未涵盖的所有主题都将在此进行介绍。 本附录主要是有用的主题的集合,包括提示和技巧。 因此,让我们看一些与 Raspberry Pi,Python 3 和 OpenCV 有关的技巧。</p><h2 id="1176o" name="%E6%8A%80%E6%9C%AF%E8%A6%81%E6%B1%82">技术要求</h2><p>可以在 GitHub 上找到本章的代码文件。</p><p>观看以下视频,以查看这个页面 上的“正在执行的代码”。</p><h2 id="bdbvv" name="%E6%80%A7%E8%83%BD%E8%AF%84%E4%BC%B0%E5%92%8C-OpenCV-%E7%9A%84%E7%AE%A1%E7%90%86">性能评估和 OpenCV 的管理</h2><p>OpenCV 有很多优化和未优化的代码。 优化的代码使用了现代微处理器的功能,例如指令流水线和 AVX。</p><p>我们可以检查是否正在使用<code>cv2.useOptimized()</code>函数在当前使用的计算机上启用 OpenCV 优化。 我们还可以使用<code>cv2.setUseOptimized()</code>函数来切换优化。 <code>cv2.getTickCount()</code>函数返回自打开计算机后开始的时钟滴答数(也称为<strong>时钟周期</strong>)。 在执行我们感兴趣的代码段之前和之后调用此函数。</p><p>然后,我们计算时钟周期之间的差,并返回执行代码段所需的时钟周期数。 <code>cv2.getTickFrequency()</code>函数返回时钟周期的频率。 然后,我们可以将时钟周期之间的差除以时钟周期的频率,以获得执行代码段所需的时间:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">import cv2
cv2.setUseOptimized(True)
print(cv2.useOptimized())
img = cv2.imread(&#39;/home/pi/book/dataset/4.1.01.tiff&#39;, 0)
e1 = cv2.getTickCount()
img1 = cv2.medianBlur(img, 23)
e2 = cv2.getTickCount()
t = (e2 - e1)/cv2.getTickFrequency()
print(t)</code></pre></div></div><p>前面代码的输出如下:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">True
0.004361807</code></pre></div></div><p>我们还可以使用 Python 3 <code>time</code>库中的函数来确定运行任何代码段所需的时间。 试试看作为练习。 接下来,我们将看到如何重用 Raspbian OS microSD 卡。</p><h2 id="9mpa8" name="%E5%A4%8D%E7%94%A8-Raspbian-OS-microSD-%E5%8D%A1">复用 Raspbian OS microSD 卡</h2><p>我们已经学会了使用 <strong>Win32 Disk Imager</strong> 将 Raspbian 操作系统写入 microSD 卡。 现在,我们将了解如何将 microSD 卡重用于其他用途。 将 microSD 卡插入 microSD 卡读取器,然后将其连接到 Windows PC。 它将显示两个分区。 其中只有一个是可读的,并且将其标记为<strong>启动</strong>。 它还应具有<code>config.txt</code>文件,其大小约为 250 MB。 另一个分区不可读。 我们无法将此 microSD 卡用于其他目的。 因此,我们需要使用一些工具来格式化该卡,然后才能将其再次用于任何其他目的。</p><h3 id="10t6l" name="%E4%BD%BF%E7%94%A8-SD-%E5%8D%A1%E6%A0%BC%E5%BC%8F%E5%8C%96%E5%99%A8%E6%A0%BC%E5%BC%8F%E5%8C%96-SD-%E5%8D%A1">使用 SD 卡格式化器格式化 SD 卡</h3><p>有免费工具,用于格式化 SD 卡。 我们可以从这个页面下载。 安装此工具并打开它,它将显示以下窗口。 根据计算机上驱动器的数量,驱动器号可能会不同。 以下是该应用的屏幕截图:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Vv84t5lt-1681873301192)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_01.jpg)]</p><p>图 13.1 – SD 卡格式化程序</p><p>选择任何驱动器(它将始终格式化整个卡),然后单击<strong>格式化</strong>按钮上的。 这将显示以下确认框:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fQhndJ7G-1681873301192)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_02.jpg)]</p><p>图 13.2 –确认对话框</p><p>单击<strong>是</strong>按钮,它将格式化卡。 格式化后,与该卡相对应的只有一个驱动器号。 该卡现在已完全格式化为,我们可以将其当作新的一样使用。</p><h3 id="42kuc" name="Windows-%E4%B8%AD%E7%9A%84%E7%A3%81%E7%9B%98%E7%AE%A1%E7%90%86%E5%B7%A5%E5%85%B7">Windows 中的磁盘管理工具</h3><p>我们甚至可以在 Windows 中使用<strong>磁盘管理</strong>工具来格式化 microSD 卡。 在搜索栏中,键入<strong>磁盘</strong>,您将找到<strong>创建并格式化磁盘分区</strong>选项。 您也可以从 Windows 控制面板找到此工具。 再次将您要重复使用的 Raspbian OS microSD 卡插入 SD 卡读取器,并将其连接到 Windows 计算机。 打开<strong>磁盘管理</strong>工具,您将看到以下屏幕快照:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2fyq2EXq-1681873301192)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_03.jpg)]</p><p>图 13.3 –“磁盘管理”工具窗口</p><p>此列表列出了连接到系统的所有磁盘(可移动磁盘和不可移动磁盘)。 其中,可移动的(具有 256 MB 的启动分区)是 microSD 卡。 如您在前面的屏幕快照中所见,我在不扩展文件系统的情况下插入了 Raspbian OS microSD 卡(我的意思是,我为其编写了 Raspbian OS,但没有使用它来启动 Raspberry Pi 板)。 这就是为什么显示两个已分配分区和一个未分配分区的原因。 如果您使用该卡来启动 Raspberry Pi 板,则它将扩展文件系统,并且第二大分区占用未分配的部分。 因此,使用过的 Raspbian OS microSD 卡仅显示两个分区。 无论如何,我们可以右键单击分配的分区,然后选择<strong>删除卷</strong>选项。 对两个分配的分区执行此操作:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vexJU2OD-1681873301192)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_04.jpg)]</p><p>图 13.4 –删除 SD 卡的分区</p><p>删除所有分配的部分后,磁盘将如下所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-l0O1pLh9-1681873301192)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_05.jpg)]</p><p>图 13.5 –在 SD 卡上创建新分区</p><p>只需右键单击与 microSD 卡相对应的磁盘,然后单击<strong>新建简单卷</strong>。 这将启动向导到新卷。 使用所有默认选项完成向导向导,然后单击,您将获得新的磁盘以供重复使用。 您可以重写 Raspbian OS 或使用它存储您喜欢的 MP3 歌曲。 <strong>磁盘管理</strong>工具使我们可以更好地控制磁盘格式化和分区的各个方面。</p><h2 id="4i2ku" name="%E6%B5%8F%E8%A7%88raspi-config%E5%91%BD%E4%BB%A4%E8%A1%8C%E5%B7%A5%E5%85%B7">浏览<code>raspi-config</code>命令行工具</h2><p>我们可以通过以下三种方法之一使用来配置 Raspberry Pi:</p><ul class="ul-level-0"><li>Raspbian OS 菜单中的 Raspberry Pi 配置工具</li><li>通过更改<code>/boot/config.txt</code>的内容</li><li>使用<code>raspi-config</code>命令行工具</li></ul><p>我们将在本节中详细介绍<code>raspi-config</code>工具的。 打开 Raspberry Pi 命令提示符并运行以下命令:</p><div class="rno-markdown-code"><div class="rno-markdown-code-toolbar"><div class="rno-markdown-code-toolbar-info"><div class="rno-markdown-code-toolbar-item is-type"><span class="is-m-hidden">代码语言:</span>javascript</div></div><div class="rno-markdown-code-toolbar-opt"><div class="rno-markdown-code-toolbar-copy"><i class="icon-copy"></i><span class="is-m-hidden">复制</span></div></div></div><div class="developer-code-block"><pre class="prism-token token line-numbers language-javascript"><code class="language-javascript" style="margin-left:0">sudo raspi-config</code></pre></div></div><p>这将在命令提示符中打开 Raspberry Pi 配置工具,如以下屏幕截图所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aFg9DqrN-1681873301192)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_06.jpg)]</p><p>图 13.6 – <code>raspi-config</code>工具的主菜单</p><p>第一个选项用于更改<code>pi</code>用户的密码。 主菜单中的第二个选项<strong>网络选项</strong>可以更改 Raspberry Pi 板连接到网络的方式:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fHcmcKYv-1681873301193)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_07.jpg)]</p><p>图 13.7 –网络选项</p><p>主菜单中的第三个选项(<strong>引导选项</strong>)详细说明了引导选项,如下所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EhWFgKmL-1681873301193)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_08.jpg)]</p><p>图 13.8 –引导选项</p><p>主菜单中的第四个选项(<strong>本地化选项</strong>)使您可以如下设置区域设置,时区,键盘布局和 Wi-Fi 国家/地区:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SWC50Oud-1681873301193)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_09.jpg)]</p><p>图 13.9 –本地化选项</p><p>主菜单中的第五个选项是<strong>接口选项</strong>,显示如下:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rycy4ZRU-1681873301193)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_10.jpg)]</p><p>图 13.10 –接口选项</p><p>在前面的选项中,我们已经为演示启用了 <strong>P1 摄像机</strong>,<strong>P2 SSH</strong> 和 <strong>P3 VNC</strong>。</p><p>主菜单中的第六个选项用于对 Raspberry Pi 1 和 Raspberry Pi 2 超频。其他型号必须手动超频。</p><p>主菜单中的第七个选项是<strong>高级选项</strong>,如下所示:</p><p>[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NlQ3HS1a-1681873301193)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_11.jpg)]</p><p>图 13.11 –高级选项</p><p>在前面的屏幕截图中,<strong>A1 扩展文件系统</strong>扩展文件系统以确保 microSD 卡在中的所有空间均可用。 <strong>A3 内存分割</strong>用于为图形分配内存。</p><p>第八个选项更新<code>raspi-config</code>工具。 如果要访问 Raspberry Pi 板的命令提示符,则这是配置 Raspberry Pi 的最佳方法。</p><h2 id="3o858" name="Windows%EF%BC%8CDebian-%E5%92%8C-Ubuntu-%E4%B8%8A%E7%9A%84%E5%AE%89%E8%A3%85%E5%92%8C%E7%8E%AF%E5%A2%83%E8%AE%BE%E7%BD%AE">Windows,Debian 和 Ubuntu 上的安装和环境设置</h2><p>我们可以在和 Windows 和 Linux OS 上展示在其他台式计算机上学到的所有领域。 由于与台式机主板通常通常没有 DSI 端口,因此只有与 Raspberry Pi 摄像头模块相关的部件不能与其他计算机一起使用。 我们还可以在运行 Debian 或 Ubuntu 的其他单板计算机上运行代码示例。</p><p>在 Ubuntu,Debian 及其衍生产品上,安装包的过程是相同的。 所有现代 Linux 发行版均随附 Python3。我们只需要使用<code>apt</code>和<code>pip3</code>工具进行安装。</p><p>对于 Windows PC,我们需要从头开始安装所有内容。 让我们开始通过以下步骤来了解如何安装 Python 3:</p><ol class="ol-level-0"><li> 访问 www.python.org 并下载最新 Python 3 版本的安装文件:
 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pKJHillX-1681873301194)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_12.jpg)]
 图 13.12 – Python Foundation 主页
 运行下载的安装文件。 它将打开一个安装向导,如下所示:
 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4NVR3HpS-1681873301194)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_13.jpg)]
 图 13.13 – Python 3 安装选项
 确保选中“将 Python 3.8 添加到<code>PATH</code>”复选框。
 </li><li> 然后,点击“自定义安装”。 将出现以下窗口:
 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tDNw9wjE-1681873301194)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/raspi-cv-prog/img/B16208_Appendix_14.jpg)]
 图 13.14 –安装的可选功能
 选中所有复选框,然后单击<strong>下一个</strong>按钮。 在下一个窗口中,将所有选项保持为,并完成安装。
 </li><li> 安装完成后,我们可以通过在 Windows 搜索栏中搜索<code>IDLE</code>来进行验证。 另外,在<code>cmd</code>(Windows 的命令提示符)中,我们可以验证<code>python</code>和<code>pip3</code>命令是否正常运行。
 </li></ol><p>Python 3 解释器以 Windows 二进制可执行文件<code>python.exe</code>文件的形式出现,如果我们在安装过程中检查了适当的选项,则可以直接在命令提示符下调用它,如前所述。 我们可以通过命令提示符上的<code>pip3</code>工具安装本书前面各章中使用的所有包。</p><h2 id="c6ii3" name="Python-%E5%AE%9E%E7%8E%B0%E5%92%8C-Python-%E5%8F%91%E8%A1%8C%E7%89%88">Python 实现和 Python 发行版</h2><p>Python 实现是充当 Python 编程语言解释器的程序。 这个页面提供的解释器和 Linux 附带的是,称为 <strong>CPython</strong>。 其他流行的实现包括(但不限于)以下:</p><ul class="ul-level-0"><li>MicroPython</li><li>IronPython</li><li>Stackless Python</li><li>Jython</li><li>PyPy</li><li>CircuitPython</li></ul><p>我们可以在这个页面找到替代的实现及其项目 URL 的列表。</p><p>Python 发行版是 Python 解释器的实现,还有一组捆绑在一起的其他包。 一些 Python 实现本身就是发行版。 实际上,术语<em style="font-style:italic">实现</em>与分发之间没有明确的区别。 我们可以在这个页面上找到有关发行版的更多信息。</p>