当前位置: 首页 > news >正文

网站建设亮点百度广告官网

网站建设亮点,百度广告官网,网页设计实训报告3篇,建设茶叶网站目的原文#xff1a;Mastering OpenCV 4 with Python 协议#xff1a;CC BY-NC-SA 4.0 译者#xff1a;飞龙 本文来自【ApacheCN 计算机视觉 译文集】#xff0c;采用译后编辑#xff08;MTPE#xff09;流程来尽可能提升效率。 当别人说你没有底线的时候#xff0c;你最好真… 原文Mastering OpenCV 4 with Python 协议CC BY-NC-SA 4.0 译者飞龙 本文来自【ApacheCN 计算机视觉 译文集】采用译后编辑MTPE流程来尽可能提升效率。 当别人说你没有底线的时候你最好真的没有当别人说你做过某些事的时候你也最好真的做过。 第 3 部分OpenCV 中的机器学习和深度学习 在本书的第三部分中您将体验一下机器学习和深度学习。 我们将探索和利用 OpenCV 的机器学习模块。 此外您还将学习如何使用与人脸检测跟踪和识别相关的最新算法来创建人脸处理项目。 最后将向您介绍 OpenCV 和一些深度学习 Python 库TensorFlow 和 Keras的深度学习领域。 本节将介绍以下章节 第 10 章“使用 OpenCV 的机器学习” 第 11 章“人脸检测跟踪和识别” 第 12 章“深度学习简介” 十、使用 OpenCV 的机器学习 机器学习是人工智能的一种应用它为计算机以及具有一定计算能力的其他系统提供了自动根据经验进行预测或决策的能力而无需进行明确编程即可执行任务。 机器学习的概念已经存在了很长时间但是在过去的几年中它的发展势头强劲这主要归因于以下三个关键因素 数据量大大增加。有明显改进的算法。实质上有更强大的计算机硬件。 虚拟个人助理例如智能扬声器或移动应用通勤时的预测交通预测或导航服务视频系统监控摄像头系统或车牌识别系统以及电子商务应用推荐系统或自动价格识别系统 比较应用只是我们日常生活中机器学习应用的一些示例。 在本章中我们将看到 OpenCV 提供的一些最常见的机器学习算法和技术它们可以解决计算机视觉项目中的实际问题例如分类和回归问题。 我们将涵盖以下主题 机器学习入门K 均值聚类K 最近邻支持向量机 技术要求 技术要求如下 Python 和 OpenCV特定于 Python 的 IDENumPy 和 Matplotlib 包Git 客户端 有关如何安装这些要求的更多详细信息请参见第 1 章“设置 OpenCV”。 可通过 Github 访问《精通 Python OpenCV 4》的 GitHub 存储库其中包含从本书第一章到最后的所有必要的支持项目文件。 机器学习入门 在第 1 章“设置 OpenCV”中我们介绍了计算机视觉人工智能机器学习神经网络和深度学习的概念这些概念可以按层次结构进行构建 如下所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uOE6Zqrj-1681870549410)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/3066b65c-52cf-4467-9f9f-0412438e9759.png)] 可以看出人工智能主题包括所有其他主题。 在本章中我们将专注于机器学习。 如果您想对这些概念进行复习请参阅第 1 章“设置 OpenCV”。 机器学习是对计算机进行编程以从历史数据中学习以对新数据进行预测的过程。 机器学习是人工智能的一个子学科是指统计技术通过这些技术机器可以在学习到的相互关系的基础上执行操作。 基于收集或收集的数据算法是由计算机独立学习的*。* 在机器学习的上下文中有三种主要方法-监督机器学习无监督机器学习和半监督的机器学习技术。 这些方法可以在下图中看到。 为了完成它我们包含了三种最常见的技术来解决分类回归和聚类问题 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JsJE4eMO-1681870549411)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/159e6630-15f1-496e-820a-5a092994e8b5.png)] 这些方法之间的主要区别是学习过程我们将在下面讨论。 监督机器学习 使用样本集合进行监督学习每个样本具有相应的输出值期望的输出。 这些机器学习方法称为监督因为我们知道每个训练示例的正确答案并且有监督的学习算法会分析训练数据以便对训练数据做出预测。 此外可以基于预测与相应的期望输出之间的差异来校正这些预测。 基于这些更正该算法可以从误差中学习以调整其内部参数。 这样在监督学习中算法会迭代地调整一个函数该函数可以最佳地近似样本集合与相应所需输出之间的关系。 监督学习问题可以进一步分为以下几类 分类当输出变量是类别例如颜色红色绿色或蓝色尺寸大中或小或性别男性或女性时可能被视为分类问题。 在分类问题中该算法将输入映射到输出标签。回归当输出变量是真实值例如年龄或体重时监督学习问题可以归类为回归问题。 在回归问题中该算法将输入映射到连续输出。 在监督学习中有一些主要问题需要考虑为了完整起见接下来将进行评论 偏差方差权衡偏差方差折衷是机器学习中的一个常用术语指的是模型-数据不足的模型具有较高的偏差而模型的数据过拟合则偏高。 数据差异很大 偏差可以看作是学习算法中错误假设产生的误差可以定义为模型预测与我们尝试预测的正确值之间的差异。 这导致算法通过不考虑数据中的所有信息拟合不足来学习错误的东西。 因此具有高偏差的模型无法在数据中找到所有模式因此它不太适合训练集也不太适合测试集。方差可以定义为算法通过拟合模型来密切学习数据中的误差/噪声过拟合而该趋势倾向于学习错误的事物而与真实信号无关。 因此具有高方差的模型非常适合训练集但是由于它也已获悉数据中的误差/噪声因此无法推广到测试集。 请查看下图以更好地理解 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ReSNQggq-1681870549411)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/1debbbe1-ab51-48fb-9060-8104b2563135.png)] 函数复杂度和训练数据量模型复杂度是指机器学习算法正尝试以与多项式次数相似的方式学习函数的复杂性。 模型复杂性的适当级别通常由训练数据的性质决定。 例如如果您需要少量的数据来训练模型则低复杂度的模型是可取的。 这是因为高复杂度模型将适合较小的训练集。输入空间的维数在处理高/非常高维的特征空间时学习问题可能会非常困难因为许多额外的特征会混淆学习过程 结果差异很大。 因此当处理高/非常高维的特征空间时一种常见的方法是将学习算法修改为具有高偏差和低方差。 此问题与维度诅咒有关后者指的是在分析和组织在低维空间中找不到的高维空间中的数据时出现的各个方面。输出值中的噪声如果所需的输出值不正确由于人为或传感器误差则学习算法尝试过于紧密地拟合数据时可能会发生过拟合。 有几种常见的策略可用于减轻输出值中的误差/噪声影响。 例如在训练算法之前检测并去除嘈杂的训练示例是一种常见的方法。 另一个策略是尽早停止可以用来防止过拟合。 无监督机器学习 在无监督学习中没有标记输出。 从这个意义上说这里有一个样本集合但是每个样本的相应输出值都丢失了样本集合没有被标记分类或分类。 无监督学习的目标是对样本集合中的基础结构或分布进行建模和推断。 因此在无监督学习中该算法无法找到正确的输出但是可以探索数据并可以从数据中进行推断以试图揭示其中的隐藏结构。 聚类或降维是无监督学习中最常用的两种算法。 半监督机器学习 顾名思义半监督学习可以看作是监督学习和无监督学习之间的折衷因为它使用标记和未标记的数据进行训练。 从这个意义上讲您拥有大量输入数据并且仅对其中一些数据进行了标记的问题可以归类为半监督学习问题。 许多现实世界中的机器学习问题可以归类为半监督问题因为正确标记所有数据可能非常困难昂贵或耗时而未标记的数据更易于收集。 在这些情况下仅标记了少量的训练数据您可以探索有监督和无监督学习技术 您可以使用无监督学习技术来发现和学习输入变量中的结构。您可以使用监督学习技术来使用标记的数据训练分类器然后使用此模型对未标记的数据进行预测。 此时您可以将该数据作为训练数据反馈到监督学习算法中以迭代地增加标记数据的大小并使用重新训练的模型对新的未标记数据进行预测。 K 均值聚类 OpenCV 提供cv2.kmeans()函数该函数实现了 K 均值聚类算法该算法查找聚类的中心并对聚类周围的输入样本进行分组。 K 均值聚类算法的目标是将n个样本划分或聚类为K聚类其中每个样本将属于具有最均值的聚类。 cv2.kmeans()函数的签名如下 retval, bestLabels, centerscv.kmeans(data, K, bestLabels, criteria, attempts, flags[, centers])data代表用于聚类的输入数据。 它应为np.float32数据类型并且每个特征都应放在单个列中。 K指定最后所需的群集数。 使用criteria参数指定算法终止标准该参数设置最大迭代次数和/或所需的精度。 当满足这些条件时算法终止。 criteria是三个参数typemax_iterm和epsilon的元组 type这是终止条件的类型。 它具有三个标志 cv2.TERM_CRITERIA_EPS当达到指定的精度epsilon时算法停止。cv2.TERM_CRITERIA_MAX_ITER当达到指定的迭代次数max_iterm时算法停止。cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER达到两个条件中的任何一个时算法将停止。 max_iterm这是最大迭代次数。epsilon这是必需的精度。 条件的示例如下 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)在这种情况下最大迭代次数设置为20max_iterm 20所需的精度为1.0epsilon 1.0。 attempts参数指定使用不同的初始标签执行算法的次数。 该算法返回产生最佳紧密度的标签。 flags参数指定如何获取初始中心。 cv2.KMEANS_RANDOM_CENTERS标志在每次尝试中选择随机的初始中心。 cv2.KMEANS_PP_CENTERS标志使用 Arthur 和 Vassilvitskii 提出的 K 均值 中心初始化请参阅《K 均值精心播种的优势》2007。 cv2.kmeans()返回以下内容 bestLabels一个整数数组存储每个样本的聚类索引centers一个数组其中包含每个集群的中心compactness每个点到其相应中心的距离的平方之和 在本节中我们将看到两个如何在 OpenCV 中使用 K 均值聚类算法的示例。 在第一个示例中期望实现对 K 均值聚类的直观理解而在第二个示例中K 均值聚类将应用于颜色量化问题。 了解 K 均值聚类 在此示例中我们将使用 K 均值聚类算法对一组 2D 点进行聚类。 这组 2D 点可以看作是对象的集合已使用两个特征对其进行了描述。 可以使用k_means_clustering_data_visualization.py脚本创建和显示这组 2D 点。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-wyMYA4Os-1681870549412)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/9c824982-da30-44f3-bbf6-67fd4c79546f.png)] 这组 2D 点由150点组成它们是通过以下方式创建的 data np.float32(np.vstack((np.random.randint(0, 40, (50, 2)), np.random.randint(30, 70, (50, 2)), np.random.randint(60, 100, (50, 2)))))这将代表用于聚类的数据。 如前所述它应为np.float32类型并且每个特征都应放在单个列中。 在这种情况下每个点都有对应于(x, y)坐标的两个特征。 这些坐标可以表示例如每个150人的身高和体重或每个150房屋的卧室数量和大小。 在第一种情况下K 均值聚类算法将决定 T 恤的尺寸例如如果K3为小中或大而在第二种情况下K 均值聚类算法将决定房子的价格例如K 4便宜中等昂贵或非常昂贵。 总之data将是我们的聚类算法的输入。 在接下来的脚本中我们将看到如何使用K的不同值及其相应的可视化效果对其进行聚类。 为此我们编写了三个脚本 k_means_clustering_k_2.py在此脚本中data已分为两个组K 2。k_means_clustering_k_3.py在此脚本中data已分为三个组K 3。k_means_clustering_k_4.py在此脚本中data已分为四个组K 4。 在k_means_clustering_k_2.py脚本中数据已集群为2集群。 第一步是定义算法终止标准。 在这种情况下最大迭代次数设置为20max_iterm 20ε设置为1.0epsilon 1.0 criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)下一步是使用cv2.kmeans()函数应用 K 均值算法 ret, label, center cv2.kmeans(data, 2, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)此时我们可以使用label输出分离数据该输出存储每个样本的聚类索引。 因此我们可以根据标签将数据分为不同的群集 A data[label.ravel() 0] B data[label.ravel() 1]最后一步是在不进行聚类的情况下绘制A和B以及原始的data以便更好地了解聚类过程 # Create the dimensions of the figure and set title: fig plt.figure(figsize(12, 6)) plt.suptitle(K-means clustering algorithm, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)# Plot the original data: ax plt.subplot(1, 2, 1) plt.scatter(data[:, 0], data[:, 1], cc) plt.title(data)# Plot the clustered data and the centroids ax plt.subplot(1, 2, 2) plt.scatter(A[:, 0], A[:, 1], cb) plt.scatter(B[:, 0], B[:, 1], cg) plt.scatter(center[:, 0], center[:, 1], s100, cm, markers) plt.title(clustered data and centroids (K 2))# Show the Figure: plt.show()下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5wtZTU4p-1681870549412)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/4ea77223-5ebd-4fb8-82b6-33457e733868.png)] 您可以看到我们还绘制了center它是一个包含每个群集中心的数组。 在k_means_clustering_k_3.py脚本中采用了相同的步骤对数据进行聚类但是我们决定将数据分组为3聚类K 3。 因此在调用cv2.kmeans()函数时K参数设置为3 ret, label, center cv2.kmeans(data, 3, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)此外使用label输出分离数据时将获得三组 A data[label.ravel() 0] B data[label.ravel() 1] C data[label.ravel() 2]最后一步是显示AB和C以及质心和原始数据 # Create the dimensions of the figure and set title: fig plt.figure(figsize(12, 6)) plt.suptitle(K-means clustering algorithm, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)# Plot the original data: ax plt.subplot(1, 2, 1) plt.scatter(data[:, 0], data[:, 1], cc) plt.title(data)# Plot the clustered data and the centroids ax plt.subplot(1, 2, 2) plt.scatter(A[:, 0], A[:, 1], cb) plt.scatter(B[:, 0], B[:, 1], cg) plt.scatter(C[:, 0], C[:, 1], cr) plt.scatter(center[:, 0], center[:, 1], s100, cm, markers) plt.title(clustered data and centroids (K 3))# Show the Figure: plt.show()在上一个代码段中我们在同一图中绘制了原始数据和“聚类”数据以及质心。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6iloHcnW-1681870549412)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/87acc06a-a268-49a7-98ea-0ecade2b8df3.png)] 为了完整起见我们还对k_means_clustering_k_4.py脚本进行了编码其输出可以在下一个屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-O7J9jnc1-1681870549413)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/3d06f2a2-de2c-4ec0-9321-bbb1416f1560.png)] 可以看出群集的数量被设置为4K 4。 使用 K 均值聚类的颜色量化 在本小节中我们将 K 均值聚类算法应用于颜色量化问题可以将其定义为减少图像中颜色数量的过程。 对于在只能显示有限数量的颜色通常是由于内存限制的某些设备上显示图像颜色量化是至关重要的一点。 因此通常需要在相似度和颜色数量减少之间进行权衡。 这种权衡是通过正确设置K参数来建立的我们将在下面的示例中看到。 在k_means_color_quantization.py脚本中我们执行 K 均值聚类算法以执行颜色量化。 在这种情况下数据的每个元素都由3特征组成这些特征对应于图像每个像素的BG和R值。 因此关键步骤是通过以下方式将图像转换为data data np.float32(image).reshape((-1, 3))在这里image是我们先前加载的图像。 在此脚本中我们使用K351020和40的几个值执行了聚类过程以查看生成的图像如何变化。 例如如果我们希望生成的图像仅具有3颜色K 3则必须执行以下操作 加载 BGR 图片 img cv2.imread(landscape_1.jpg)使用color_quantization()函数执行色彩量化 color_3 color_quantization(img, 3)同时显示两个图像以查看结果。 color_quantization()函数执行颜色量化过程 def color_quantization(image, k):Performs color quantization using K-means clustering algorithm# Transform image into data:data np.float32(image).reshape((-1, 3))# print(data.shape)# Define the algorithm termination criteria (maximum number of iterations and/or required accuracy):# In this case the maximum number of iterations is set to 20 and epsilon 1.0criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)# Apply K-means clustering algorithm:ret, label, center cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)# At this point we can make the image with k colors# Convert center to uint8:center np.uint8(center)# Replace pixel values with their center value:result center[label.flatten()]result result.reshape(img.shape)return result在上一个函数中关键是要使用cv2.kmeans()方法。 最后我们可以用k种颜色构建图像用每种颜色的像素值替换其相应的中心值。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QpBkUOvZ-1681870549413)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/8c378955-0b9a-46e7-bbf6-b58cf7b43e25.png)] 可以将先前的脚本扩展为包括有趣的功能该功能显示分配给每个中心值的像素数。 可以在k_means_color_quantization_distribution.py脚本中看到。 color_quantization()函数已被修改为包括以下功能 def color_quantization(image, k):Performs color quantization using K-means clustering algorithm# Transform image into data:data np.float32(image).reshape((-1, 3))# print(data.shape)# Define the algorithm termination criteria (the maximum number of iterations and/or the desired accuracy):# In this case the maximum number of iterations is set to 20 and epsilon 1.0criteria (cv2.TERM_CRITERIA_EPS cv2.TERM_CRITERIA_MAX_ITER, 20, 1.0)# Apply K-means clustering algorithm:ret, label, center cv2.kmeans(data, k, None, criteria, 10, cv2.KMEANS_RANDOM_CENTERS)# At this point we can make the image with k colors# Convert center to uint8:center np.uint8(center)# Replace pixel values with their center value:result center[label.flatten()]result result.reshape(img.shape)# Build the color_distribution legend.# We will use the number of pixels assigned to each center value:counter collections.Counter(label.flatten())print(counter)# Calculate the total number of pixels of the input image:total img.shape[0] * img.shape[1]# Assign width and height to the color_distribution image:desired_width img.shape[1]# The difference between desired_height and desired_height_colors# will be the separation between the imagesdesired_height 70desired_height_colors 50# Initialize the color_distribution image:color_distribution np.ones((desired_height, desired_width, 3), dtypeuint8) * 255# Initialize start:start 0for key, value in counter.items():# Calculate the normalized value:value_normalized value / total * desired_width# Move end to the right position:end start value_normalized# Draw rectangle corresponding to the current color:cv2.rectangle(color_distribution, (int(start), 0), (int(end), desired_height_colors), center[key].tolist(), -1)# Update start:start endreturn np.vstack((color_distribution, result))如您所见我们利用collections.Counter()来计算分配给每个中心值的像素数 counter collections.Counter(label.flatten())例如如果K 3 — Counter({0: 175300, 2: 114788, 1: 109912})。 构建颜色分布图像后最后一步是将两个图像连接起来 np.vstack((color_distribution, result))下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-k9gqEtX0-1681870549413)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/17527994-5206-4865-ac7b-32207ba2633f.png)] 在上一个屏幕截图中您可以看到使用 K 均值聚类算法通过更改参数k351020和40应用颜色量化的结果。 k的值越大表示图像越真实。 K 最近邻 K 最近邻kNN被认为是有监督学习类别中最简单的算法之一。 kNN 可用于分类和回归问题。 在训练阶段kNN 同时存储所有训练样本的特征向量和类别标签。 在分类阶段将未标记向量与训练示例位于同一多维特征空间中的查询或测试向量分类为最接近要分类的未标记向量的k个训练样本中最频繁的类别标签其中k是用户定义的常数。 在下图中可以以图形方式看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c81gVG6l-1681870549413)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/01707af9-7288-45ba-ba80-f78b05b9ceb0.png)] 在上图中如果k 3则绿色圆圈未标记的测试样本将归类为三角形因为在内圈内有两个三角形并且只有一个正方形 。 如果k 5则绿色圆圈将归类为正方形因为虚线圆内只有三个正方形而只有两个三角形。 在 OpenCV 中使用此分类器的第一步是创建分类器。 cv2.ml.KNearest_create()方法创建一个空的 kNN 分类器应使用train()方法对其进行训练以同时提供数据和标签。 最后findNearest()方法用于查找邻居。 此方法的签名如下 retval, results, neighborResponses, distcv2.ml_KNearest.findNearest(samples, k[, results[, neighborResponses[, dist]]])这里samples是按行存储的输入样本k设置最近邻的数量应大于 1results存储每个输入样本的预测neighborResponses存储相应的邻居并且 dist存储从输入样本到相应邻居的距离。 在本节中我们将看到两个示例以了解如何在 OpenCV 中使用 kNN 算法。 在第一个示例中有望实现对 kNN 的直观理解而在第二个示例中kNN 将应用于手写数字识别问题。 了解 K 最近邻 knn_introduction.py脚本对 kNN 进行了简单介绍其中随机创建了一组点并分配了一个标签0或1。 标签0将代表红色三角形而标签1将代表蓝色正方形。 我们将使用 kNN 算法基于k最近邻对样本点进行分类。 因此第一步是创建带有相应标签的点集和用于分类的样本点 # The data is composed of 16 points: data np.random.randint(0, 100, (16, 2)).astype(np.float32)# We create the labels (0: red, 1: blue) for each of the 16 points: labels np.random.randint(0, 2, (16, 1)).astype(np.float32)# Create the sample point to be classified: sample np.random.randint(0, 100, (1, 2)).astype(np.float32)下一步是创建 kNN 分类器训练分类器并找到k最近的邻居 # KNN creation: knn cv2.ml.KNearest_create() # KNN training: knn.train(data, cv2.ml.ROW_SAMPLE, labels) # KNN find nearest: k 3 ret, results, neighbours, dist knn.findNearest(sample, k)# Print results: print(result: {}.format(results)) print(neighbours: {}.format(neighbours)) print(distance: {}.format(dist))在这种情况下并且与以下屏幕截图相对应获得的结果如下 result: [[0.]] neighbours: [[0\. 0\. 0.]] distance: [[ 80\. 100\. 196.]]因此绿点被分类为红色三角形。 在下图中可以看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RBVnY8gC-1681870549414)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/03177577-b410-4c8d-932e-06629393d361.png)] 前面的屏幕快照使您对 kNN 有了直观的了解。 在下一个示例中我们将把 kNN 应用于手写数字识别问题。 使用 K 最近邻识别手写数字 我们将看到如何使用 kNN 分类器执行手写数字识别。 我们将从获得可接受的准确率的基本脚本开始我们将对其进行修改以提高其表现。 在这些脚本中训练数据由手写数字组成。 OpenCV 提供了很多大图像里面没有手写数字而不是包含很多图像。 该图像的尺寸为2,000 x 1,000像素。 每个数字为20 x 20像素。 因此我们总共有 5,000 位数100 x 50 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HIoZQno3-1681870549414)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/d475a4ef-189f-4c00-ba54-7bf7fdfb0e08.png)] 在knn_handwritten_digits_recognition_introduction.py脚本中我们将执行第一种方法尝试使用 kNN 分类器识别数字。 在第一种方法中我们将使用原始像素值作为特征。 这样每个描述符的大小将为 40020 x 20。 第一步是从大图像中加载所有数字并为每个数字分配相应的标签。 这是通过load_digits_and_labels()函数执行的 digits, labels load_digits_and_labels(digits.png)load_digits_and_labels()函数的代码如下 def load_digits_and_labels(big_image):Returns all the digits from the big image and creates the corresponding labels for each image# Load the big image containing all the digits:digits_img cv2.imread(big_image, 0)# Get all the digit images from the big image:number_rows digits_img.shape[1] / SIZE_IMAGErows np.vsplit(digits_img, digits_img.shape[0] / SIZE_IMAGE)digits []for row in rows:row_cells np.hsplit(row, number_rows)for digit in row_cells:digits.append(digit)digits np.array(digits)# Create the labels for each image:labels np.repeat(np.arange(NUMBER_CLASSES), len(digits) / NUMBER_CLASSES)return digits, labels在上一个函数中我们首先加载“大”图像然后获取其中的所有数字。 上一个函数的最后一步是为每个数字创建标签。 在脚本中执行的下一步是为每个图像计算描述符。 在这种情况下原始像素是特征描述符 # Compute the descriptors for all the images. # In this case, the raw pixels are the feature descriptors raw_descriptors [] for img in digits:raw_descriptors.append(np.float32(raw_pixels(img))) raw_descriptors np.squeeze(raw_descriptors)此时我们将数据分为训练和测试各占 50%。 因此将使用 2500 位数字来训练分类器并使用 2500 位数字来测试经过训练的分类器 partition int(0.5 * len(raw_descriptors)) raw_descriptors_train, raw_descriptors_test np.split(raw_descriptors, [partition]) labels_train, labels_test np.split(labels, [partition])现在我们可以使用knn.train()方法训练 kNN 模型并使用get_accuracy()函数对其进行测试 # Train the KNN model: print(Training KNN model - raw pixels as features) knn cv2.ml.KNearest_create() knn.train(raw_descriptors_train, cv2.ml.ROW_SAMPLE, labels_train)# Test the created model: k 5 ret, result, neighbours, dist knn.findNearest(raw_descriptors_test, k)# Compute the accuracy: acc get_accuracy(result, labels_test) print(Accuracy: {}.format(acc))如我们所见k 5。 我们获得92.60的精度但我认为它可以提高。 我们要做的第一件事就是尝试使用k的不同值这是 kNN 分类器中的关键参数。 此修改在knn_handwritten_digits_recognition_k.py脚本中执行。 在此脚本中我们将创建一个字典来存储在测试k的不同值时的准确率 results defaultdict(list)请注意我们已经从collections导入了defaultdict from collections import defaultdict下一步是计算knn.findNearest()方法改变k参数在这种情况下在(1-9)的范围内并将结果存储在字典中 for k in np.arange(1, 10):ret, result, neighbours, dist knn.findNearest(raw_descriptors_test, k)acc get_accuracy(result, labels_test)print( {}.format(%.2f % acc))results[50].append(acc)最后一步是绘制结果 # Show all results using matplotlib capabilities: fig, ax plt.subplots(1, 1) ax.set_xlim(0, 10) dim np.arange(1, 10)for key in results:ax.plot(dim, results[key], linestyle--, markero, label50%)plt.legend(locupper left, title% training) plt.title(Accuracy of the KNN model varying k) plt.xlabel(number of k) plt.ylabel(accuracy) plt.show()为了显示结果我们让您使用 matplotlib 功能来绘制图形。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PI9ZgFaP-1681870549414)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/482f1b69-a04d-46c4-a907-a823c9c60ce1.png)] 如您在上一个屏幕截图中所见通过更改k参数获得的精度为-k1-93.72k2-91.96k3-93.00k4-92.64k5-92.60k6-92.40k7-92.28k8-92.44和k9-91.96。 如前所述获得的精度存在一些差异。 因此不要忘记在应用中适当调整k参数。 在这些示例中我们一直在训练和测试每个 2500 位数字的模型。 在机器学习中使用更多数据训练分类器通常是一个好主意因为分类器可以更好地学习特征的结构。 结合 kNN 分类器增加训练数字的数量也将增加在特征空间中找到测试数据正确匹配的可能性。 在knn_handwritten_digits_recognition_k_training_testing.py脚本中我们修改了图像的百分比以训练和测试模型如下所示 # Split data into training/testing: split_values np.arange(0.1, 1, 0.1)for split_value in split_values:# Split the data into training and testing:partition int(split_value * len(raw_descriptors))raw_descriptors_train, raw_descriptors_test np.split(raw_descriptors, [partition])labels_train, labels_test np.split(labels, [partition])# Train KNN modelprint(Training KNN model - raw pixels as features)knn.train(raw_descriptors_train, cv2.ml.ROW_SAMPLE, labels_train)# Store the accuracy when testing:for k in np.arange(1, 10):ret, result, neighbours, dist knn.findNearest(raw_descriptors_test, k)acc get_accuracy(result, labels_test)print( {}.format(%.2f % acc))results[int(split_value * 100)].append(acc)可以看出训练算法的数字百分比为 10%20%…90%测试算法的数字百分比为 90%80%…10%。 最后我们绘制结果 # Show all results using matplotlib capabilities: # Create the dimensions of the figure and set title: fig plt.figure(figsize(12, 5)) plt.suptitle(KNN handwritten digits recognition, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)ax plt.subplot(1, 1, 1) ax.set_xlim(0, 10) dim np.arange(1, 10)for key in results:ax.plot(dim, results[key], linestyle--, markero, labelstr(key) %)plt.legend(locupper left, title% training) plt.title(Accuracy of the KNN model varying both k and the percentage of images to train/test) plt.xlabel(number of k) plt.ylabel(accuracy) plt.show()下一个屏幕截图中可以看到knn_handwritten_digits_recognition_k_training_testing.py脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nLhCqHHZ-1681870549414)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/fb83a5cb-5178-4cec-a553-87175ff3c96a.png)] 随着训练图像数量的增加准确率也会提高。 此外当我们用 90% 的数字训练分类器时我们将用剩余的 10% 的数字测试分类器这等效于用500数字测试分类器这是一个可观的数字。 到目前为止我们一直在使用原始像素值作为特征来训练分类器。 在机器学习中训练分类器之前的常见过程是对输入数据进行某种预处理以在训练时帮助分类器。 在knn_handwritten_digits_recognition_k_training_testing_preprocessing.py脚本中我们正在进行预处理以减少输入数字的可变性。 此预处理在deskew()函数中执行 def deskew(img):Pre-processing of the imagesm cv2.moments(img)if abs(m[mu02]) 1e-2:return img.copy()skew m[mu11] / m[mu02]M np.float32([[1, skew, -0.5 * SIZE_IMAGE * skew], [0, 1, 0]])img cv2.warpAffine(img, M, (SIZE_IMAGE, SIZE_IMAGE), flagscv2.WARP_INVERSE_MAP | cv2.INTER_LINEAR)return imgdeskew()函数通过使用二阶矩来使数字偏斜。 更具体地可以通过两个中心矩之比mu11/mu02来计算偏斜的量度。 计算出的偏斜度用于计算仿射变换从而使数字偏斜。 请参阅下一个屏幕截图以欣赏此预处理的效果。 屏幕截图的顶部显示了原始数字蓝色边框屏幕截图的底部显示了预处理的数字绿色边框 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dmqK1aTo-1681870549415)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/7a3f8912-873a-448e-a9f0-37b27041a3e3.png)] 通过应用此预处理可以提高识别率如下面的屏幕快照所示图中绘制了识别率 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TpsjbF0s-1681870549415)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/05f50edc-520f-4765-bc14-7524e5366142.png)] 如果将在输入数据中执行预处理的此脚本与不执行任何预处理的前一个脚本进行比较则可以看到整体准确率有所提高。 在所有这些脚本中我们一直使用原始像素值作为特征描述符。 在机器学习中一种常见的方法是使用更高级的描述符。 定向梯度直方图HOG是一种流行的图像描述符。 特征描述符是图像的表示通过提取描述基本特征例如形状颜色纹理或运动的有用信息来简化图像。 通常特征描述符将图像转换为长度为n的特征向量/数组。 HOG 是一种用于计算机视觉的流行特征描述符最早用于人类在静态图像中的检测。 在knn_handwritten_digits_recognition_k_training_testing_preprocessing_hog.py脚本中我们将使用 HOG 特征代替原始像素值。 我们定义了get_hog()函数该函数获取 HOG 描述符 def get_hog():Get hog descriptor# cv2.HOGDescriptor(winSize, blockSize, blockStride, cellSize, nbins, derivAperture, winSigma, histogramNormType,# L2HysThreshold, gammaCorrection, nlevels, signedGradient)hog cv2.HOGDescriptor((SIZE_IMAGE, SIZE_IMAGE), (8, 8), (4, 4), (8, 8), 9, 1, -1, 0, 0.2, 1, 64, True)print(hog descriptor size: {}.format(hog.getDescriptorSize()))return hog在这种情况下每个图像的特征描述符都是144大小。 为了计算每个图像的 HOG 描述符我们必须执行以下操作 # Compute the descriptors for all the images. # In this case, the HoG descriptor is calculated hog_descriptors [] for img in digits:hog_descriptors.append(hog.compute(deskew(img))) hog_descriptors np.squeeze(hog_descriptors)如您所见我们将hog.compute()应用于每个不倾斜的数字。 结果可以在下一个屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mL4Z9Xss-1681870549415)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/b48411f8-9f1c-455f-b079-3eed6d08056d.png)] 当k2和 90% 的数字用于训练而 10% 的数字用于测试时则达到 98.60% 的精度。 因此我们将识别率从 92.60%在本小节的第一个脚本中获得提高到 98.60%在上一个脚本中获得。 在编写机器学习模型和应用时一种好的方法是从一个基本近似开始尝试尽快解决该问题。 然后如果获得的精度不够好可以通过添加更好的预处理更高级的特征描述符或其他机器学习技术来迭代地改进模型。 最后如果必要不要忘记收集更多数据来训练和测试模型。 支持向量机 支持向量机SVM是一种监督式学习技术通过根据分配的类最佳地分离训练示例在高维空间中构造一个超平面或一组超平面 。 可以在下一张图中看到其中绿线表示最能将两个类别分开的超平面的表示因为到两个类别中每个类别的最近元素的距离最大 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-s81GhKFy-1681870549415)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/35a06376-0101-4de5-ac3f-a0da003cd2d2.png)] 在第一种情况下决策边界是一条线而在第二种情况下决策边界是一条圆周。 虚线和虚线虚线表示其他决策边界但是它们不能最好地将这两个类别分开。 OpenCV 中的 SVM 实现基于《LIBSVM支持向量机库》2011。 要创建空模型请使用cv2.ml.SVM_create()函数。 接下来应将主要参数分配给模型 svmType这设置 SVM 的类型。 有关详细信息请参见 LibSVM。 可能的值如下 SVM_C_SVC可用于n类分类的 C-支持向量分类n ≥ 2NU_SVCν-支持向量分类ONE_CLASS分布估计一类 SVMEPS_SVRε-支持向量回归NU_SVRν-支持向量回归 kernelType设置 SVM 的核类型。 有关详细信息请参见 LibSVM。 可能的值如下 LINEAR线性核POLY多项式核RBF径向基函数RBF在大多数情况下是个不错的选择SIGMOIDSigmoid 核CHI2指数 Chi2 核类似于RBF核INTER直方图交点核 快速核 核函数选择可能很棘手并且取决于数据集。 从这个意义上讲RBF核通常被认为是一个不错的首选因为该核将样本非线性地映射到一个高维空间以处理类标签和属性之间的关系为非线性的情况。 有关更多详细信息请参见《支持向量分类的实用指南》2003。 degree核函数的参数度POLY gamma核函数的γ参数POLY/RBF/SIGMOID/CHI2 coef0核函数的coef0参数POLY/SIGMOID CvalueSVM 优化问题的C参数C_SVC/EPS_SVR/NU_SVR nuSVM 优化问题的ν参数NU_SVC/ONE_CLASS/NU_SVR pSVM 优化问题EPS_SVR的ε参数 classWeightsC_SVC问题中的可选权重分配给特定类别 termCritSVM 迭代训练过程的终止标准 默认构造器使用以下值初始化结构 svmType: C_SVC, kernelType: RBF, degree: 0, gamma: 1, coef0: 0, C: 1, nu: 0, p: 0, classWeights: 0, termCrit: TermCriteria(MAX_ITEREPS, 1000, FLT_EPSILON )在本节中我们将看到两个如何在 OpenCV 中使用 SVM 的示例。 在第一个示例中将给出对 SVM 的直观理解在第二个示例中SVM 将应用于手写数字识别问题。 了解 SVM svm_introduction.py脚本执行一个简单的示例以了解如何在 OpenCV 中使用 SVM。 首先我们创建训练数据和标签 # Set up training data: labels np.array([1, 1, -1, -1, -1]) data np.matrix([[500, 10], [550, 100], [300, 10], [500, 300], [10, 600]], dtypenp.float32)如您所见创建了五个点。 前两个点被分配为1类而其他三个点被分配为-1类。 下一步是使用svm_init()函数初始化 SVM 模型 # Initialize the SVM model: svm_model svm_init(C12.5, gamma0.50625)svm_init()函数创建一个空模型并分配主要参数并返回模型 def svm_init(C12.5, gamma0.50625):Creates empty model and assigns main parametersmodel cv2.ml.SVM_create()model.setGamma(gamma)model.setC(C)model.setKernel(cv2.ml.SVM_LINEAR)model.setType(cv2.ml.SVM_C_SVC)model.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6))return model在这种情况下SVM 核类型设置为LINEAR不执行任何映射而 SVM 的类型设置为C_SVC可用于n类分类其中n ≥ 2 。 然后我们使用svm_train()函数训练 SVM # Train the SVM: svm_train(svm_model, data, labels)在这里svm_train()函数使用样本和响应来训练模型然后返回训练后的模型 def svm_train(model, samples, responses):Trains the model using the samples and the responsesmodel.train(samples, cv2.ml.ROW_SAMPLE, responses)return model下一步是创建将在其中绘制 SVM 响应的图像 # Create the canvas (black image with three channels) # This image will be used to show the prediction for every pixel: img_output np.zeros((640, 640, 3), dtypeuint8)最后我们使用show_svm_response()函数显示 SVM 响应 # Show the SVM response: show_svm_response(svm_model, img_output)因此img_ouput图像显示了 SVM 响应。 show_svm_response()函数的代码如下 def show_svm_response(model, image):Show the prediction for every pixel of the image, the training data and the support vectorscolors {1: (255, 255, 0), -1: (0, 255, 255)}# Show the prediction for every pixel of the image:for i in range(image.shape[0]):for j in range(image.shape[1]):sample np.matrix([[j, i]], dtypenp.float32)response svm_predict(model, sample)image[i, j] colors[response.item(0)]# Show the training data:# Show samples with class 1:cv2.circle(image, (500, 10), 10, (255, 0, 0), -1)cv2.circle(image, (550, 100), 10, (255, 0, 0), -1)# Show samples with class -1:cv2.circle(image, (300, 10), 10, (0, 255, 0), -1)cv2.circle(image, (500, 300), 10, (0, 255, 0), -1)cv2.circle(image, (10, 600), 10, (0, 255, 0), -1)# Show the support vectors:support_vectors model.getUncompressedSupportVectors()for i in range(support_vectors.shape[0]):cv2.circle(image, (support_vectors[i, 0], support_vectors[i, 1]), 15, (0, 0, 255), 6)可以看出该函数显示以下内容 图像每个像素的预测所有五个训练数据点支持向量定义超平面的向量称为支持向量 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PZ1d8Fs3-1681870549416)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/3f1290c5-8aa8-4a69-9a6a-397720ad7246.png)] 如您所见已使用训练数据和由五个点组成的标签对两个点分配了类别1而对其他三个点分配了类别-1对 SVM 进行了训练之后将其用于分类图片中的所有像素。 这种分类导致将图像划分为黄色和青色区域。 此外您可以看到两个区域之间的边界对应于两个类别之间的最佳间隔因为到两个类别中每个类别的最近元素的距离最大。 支持向量以红线边框显示。 使用 SVM 的手写数字识别 我们刚刚看到了如何使用 kNN 分类器执行手写数字识别。 通过对数字进行预处理调用deskew()函数并计算 HOG 描述符作为用于描述每个数字的特征向量可以获得最佳的精度。 因此为简单起见接下来将要使用 SVM 对数字进行分类的脚本将使用上述近似值预处理和 HOG 特征。 svm_handwritten_digits_recognition_preprocessing_hog.py脚本使用 SVM 分类执行手写数字识别。 关键代码如下所示 # Load all the digits and the corresponding labels: digits, labels load_digits_and_labels(digits.png)# Shuffle data # Constructs a random number generator: rand np.random.RandomState(1234) # Randomly permute the sequence: shuffle rand.permutation(len(digits)) digits, labels digits[shuffle], labels[shuffle]# HoG feature descriptor: hog get_hog()# Compute the descriptors for all the images. # In this case, the HoG descriptor is calculated hog_descriptors [] for img in digits:hog_descriptors.append(hog.compute(deskew(img))) hog_descriptors np.squeeze(hog_descriptors)# At this point we split the data into training and testing (50% for each one): partition int(0.5 * len(hog_descriptors)) hog_descriptors_train, hog_descriptors_test np.split(hog_descriptors, [partition]) labels_train, labels_test np.split(labels, [partition])print(Training SVM model ...) model svm_init(C12.5, gamma0.50625) svm_train(model, hog_descriptors_train, labels_train)print(Evaluating model ... ) svm_evaluate(model, hog_descriptors_test, labels_test)在这种情况下我们使用了RBF核 def svm_init(C12.5, gamma0.50625):Creates empty model and assigns main parametersmodel cv2.ml.SVM_create()model.setGamma(gamma)model.setC(C)model.setKernel(cv2.ml.SVM_RBF)model.setType(cv2.ml.SVM_C_SVC)model.setTermCriteria((cv2.TERM_CRITERIA_MAX_ITER, 100, 1e-6))return model使用仅 50% 的数字来训练算法所获得的精度为 98.60%。 另外使用RBF核时有两个重要参数-C和γ。 在这种情况下为C12.5和γ0.50625。 和以前一样对于给定的问题取决于数据集C和γ并不为人所知。 因此必须进行某种参数搜索。 因此目标是确定推荐使用C和γ的网格搜索的良好C和γ。 与svm_handwritten_digits_recognition_preprocessing_hog.py脚本相比在svm_handwritten_digits_recognition_preprocessing_hog_c_gamma.py脚本中进行了两次修改。 第一个是使用 90% 的数字训练模型其余 10% 用于测试。 第二个修改是对C和γ进行网格搜索 # Create a dictionary to store the accuracy when testing: results defaultdict(list)for C in [1, 10, 100, 1000]:for gamma in [0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5]:model svm_init(C, gamma)svm_train(model, hog_descriptors_train, labels_train)acc svm_evaluate(model, hog_descriptors_test, labels_test)print( {}.format(%.2f % acc))results[C].append(acc)最后这是结果 # Create the dimensions of the figure and set title: fig plt.figure(figsize(10, 6)) plt.suptitle(SVM handwritten digits recognition, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)# Show all results using matplotlib capabilities: ax plt.subplot(1, 1, 1) ax.set_xlim(0, 1.5) dim [0.1, 0.3, 0.5, 0.7, 0.9, 1.1, 1.3, 1.5]for key in results:ax.plot(dim, results[key], linestyle--, markero, labelstr(key))plt.legend(locupper left, titleC) plt.title(Accuracy of the SVM model varying both C and gamma) plt.xlabel(gamma) plt.ylabel(accuracy) plt.show()下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-SRNQRCL9-1681870549416)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/b3c63e99-e551-4745-91b5-055d60704b2b.png)] 如图所示在某些情况下可获得 99.20% 的精度。 通过比较 kNN 分类器和 SVM 进行手写数字识别我们可以得出结论SVM 优于 kNN 分类器。 总结 在本章中我们涵盖了机器学习的完整介绍。 在第一部分中我们将机器学习的概念以及它与其他热门话题如人工智能神经网络和深度学习的关联性进行了背景研究。 此外我们总结了机器学习的三种主要方法并讨论了解决分类回归和聚类问题的三种最常用的技术。然后我们应用了最常用的机器学习技术来解决了一些现实世界中的问题。 更具体地说我们研究了 K 均值聚类算法K 最近邻分类器和 SVM。 在下一章中我们将探讨如何使用与人脸检测跟踪和识别相关的最新算法来创建人脸处理项目。 问题 机器学习中的三种主要方法是什么分类和回归问题有什么区别OpenCV 提供什么函数来实现 K 均值聚类算法OpenCV 提供什么函数来创建 kNN 分类器OpenCV 提供什么函数来找到最近的邻居OpenCV 提供什么函数来创建 SVM 分类器SVM 核的合理首选是什么 进一步阅读 如果您想深入研究机器学习请查看以下资源 《用于 OpenCV 的机器学习》2017作者Michael Beyeler 十一、人脸检测跟踪和识别 人脸处理是人工智能领域的热门话题因为可以使用计算机视觉算法从面部自动提取很多信息。 面部在视觉交流中起着重要作用因为可以从人脸中提取大量非语言信息例如身份意图和情感。 对于计算机视觉学习者来说面部处理是一个非常有趣的主题因为它涉及到不同的专业领域例如对象检测图像处理标志检测或对象跟踪。 在本章中将向您介绍与使用最新算法和技术进行面部处理有关的主要主题以达到令人印象深刻的效果。 我们将涵盖以下主题 人脸处理简介人脸检测检测人脸标志人脸追踪人脸识别 在本章中您将学习如何使用与人脸检测跟踪和识别相关的最新算法来创建人脸处理项目。 在第 12 章“深度学习简介”中将向您介绍使用 OpenCV 进行深度学习的领域以及一些深度学习 Python 库TensorFlow 和 Keras。 技术要求 技术要求如下 Python 和 OpenCV特定于 Python 的 IDENumPy 和 Matplotlib 包Git 客户端Dlib 包face_processing包 有关如何安装这些要求的更多详细信息请参见第 1 章“设置 OpenCV”。 《精通 Python OpenCV 4》的 GitHub 存储库其中包含贯穿本书的所有必要的支持项目文件从第一章到最后一章都可以在这里访问。 安装 Dlib Dlib 是一个 C 软件库包含计算机视觉机器学习和深度学习算法。 Dlib 也可以在您的 Python 应用中使用。 为了使用 PIP 安装dlib请使用以下命令 $ pip install dlib另外如果您想自己编译dlib请进入dlib根文件夹并运行以下命令 $ python setup.py install该命令运行完毕后就可以使用 Python 的dlib。 请注意您需要同时安装 CMake 和 C 编译器才能正常工作。 还要注意各种可选功能例如 GUI 支持例如dlib.image_window和 CUDA 加速将根据计算机上的可用状态启用或禁用。 安装dlib的第三个选项是访问这里并安装所需的dlib车轮包装。 就我而言我已经下载了dlib-19.8.1-cp36-cp36m-win_amd64.whl文件并使用以下命令进行了安装 $ pip install dlib-19.8.1-cp36-cp36m-win_amd64.whl车轮文件名是{distribution}-{version}(-{build tag})?-{python tag}-{abi tag}-{platform tag}.whl。 例如distribution-1.0-1-py27-none-any.whl是名为distribution的包的第一个版本它在任何 CPU 架构上都与 Python 2.7任何 Python 2.7 实现兼容而没有 ABI纯 Python。 请参阅这里了解有关 Wheel 二进制包格式的更多详细信息。 要确认安装是否正确执行只需打开 Python shell 并尝试导入dlib库 python import dlib请记住建议的方法是在虚拟环境中安装包。 有关如何创建和管理虚拟环境的信息请参见第 1 章“设置 OpenCV”。 例如在这种情况下我们将使用 Anaconda 提示符在虚拟环境中安装dlib 创建一个虚拟环境 (base) $ conda create -n dlib-env python3.6激活环境 (base) $ activate dlib-env查看(dlib-env)在此命令后的提示前的显示方式。 这表明虚拟环境已被激活。 使用以下命令安装dlib (dlib-env) $ pip install dlib安装face_recognition包 为了安装face_recognition包 请执行以下命令 $ pip install face_recognition要检查安装是否正确执行只需打开 Python shell 并尝试导入face_recognition库 python import face_recognition安装cvlib包 要安装cvlib包请先安装所需的包numpyopencv-pythonrequestsprogressbar pillowtensorflowkeras使用以下命令 $ pip install -r requirements.txt然后安装cvlib包 $ pip install cvlib要升级到最新版本请输入以下命令 pip install --upgrade cvlib请注意如果您使用的是 GPU则可以编辑requirements.txt文件以包括tensorflow-gpu而不是tensorflow。 要检查安装是否正确执行只需打开 Python shell 并尝试导入face_recognition库 python import cvlib人脸处理简介 在本章中我们将介绍与面部处理有关的主要主题。 为此我们将使用 OpenCV 库也将使用dlib主页和 PyPI dlib主页Github dlib主页PyPI face_recognition主页Github face_recognition主页和PyPI cvlib主页Github cvlib主页cvlib主页 Python 包。 在上一节中您了解了如何安装这些包。 为了介绍本章我们将在所有部分中使用不同的方法来了解您解决具体的面部处理任务时可能会遇到的不同可能性并且对所有这些都有一个较高的概述可能会有所帮助。 备择方案。 该图试图捕获前面提到的主题的概念 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpfn42Lw-1681870549417)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/62935876-9c7c-41c1-87ce-0e7722756f0b.png)] 如您所见这里要解决四个要点 人脸检测是对象检测的一种特殊情况其任务是查找图像中所有人脸的位置和大小。人脸标志检测是标志检测的一种特殊情况其任务是在面部定位主要标志。人脸跟踪是对象跟踪的一种特殊情况其中的任务是通过考虑可在连续帧的帧中提取的额外信息来查找视频中所有移动脸的位置和大小。 视频。人脸识别是对象识别的一种特殊情况其中使用从人脸提取的信息从图像或视频中识别或验证人 人脸识别1:N任务是在已知人脸的集合中查找与未知人物最接近的匹配项。面部验证1:1该任务是检查该人是否是他们所声称的那个人。 如上图所示本章将使用 OpenCVdlibface_recognition和cvlib。 人脸检测 人脸检测可以定义为确定数字图像中人脸的位置和大小的任务通常是构建人脸处理应用例如人脸表情识别嗜睡检测性别分类人脸识别 头姿势估计或人机交互。 这是因为上述应用需要将所检测到的面部的位置和大小作为输入。 因此自动面部检测起着至关重要的作用并且是人工智能界研究最多的主题之一。 面部检测对于人类来说似乎是一项轻松的任务但对计算机而言却是一项非常具有挑战性的任务因为通常涉及许多问题/挑战例如外观变化比例旋转面部表情遮挡或光照条件。 在 Viola 和 Jones 提出的工作之后人脸检测取得了令人瞩目的进展。 在本节中我们将看到 OpenCV 库以及dlib和face_processing包提供的一些最流行的面部检测技术包括上述的 Viola 和 Jones 算法以及其他机器学习和深度学习方法。 使用 OpenCV 的人脸检测 OpenCV 提供了两种面部检测方法 基于 Haar 级联的面部检测器基于深度学习的面部检测器 Viola 和 Jones 提出的框架请参见《使用简单特征的增强级联进行快速对象检测》2001是一种有效的对象检测方法。 该框架非常受欢迎因为 OpenCV 提供了基于该框架的面部检测算法。 另外该框架还可以用于检测其他物体而不是面部例如全身检测器车牌号检测器上身检测器或猫脸检测器。 在本节中我们将看到如何使用此框架检测人脸。 face_detection_opencv_haar.py脚本使用基于 Haar 特征的级联分类器执行面部检测。 从这个意义上说OpenCV 提供了四个用于正面人脸检测的级联分类器 haarcascade_frontalface_alt.xmlFA122 个阶段20 x 20 Haar 特征haarcascade_frontalface_alt2.xmlFA220 个阶段20 x 20 Haar 特征haarcascade_frontalface_alt_tree.xmlFAT47 个阶段20 x 20 Haar 特征haarcascade_frontalface_default.xmlFD25 个阶段24 x 24 Haar 特征 在一些可用的出版物中作者使用不同的标准和数据集评估了这些级联分类器的表现。 总体而言可以得出结论这些分类器达到了相似的准确率。 这就是为什么在此脚本中我们将使用其中两个以简化内容。 更具体地说在此脚本中加载了两个层叠分类器先前引入的FA2和FD # Load cascade classifiers: cas_alt2 cv2.CascadeClassifier(haarcascade_frontalface_alt2.xml) cas_default cv2.CascadeClassifier(haarcascade_frontalface_default.xml)cv2.CascadeClassifier()函数用于从文件加载分类器。 您可以从 OpenCV 信息库下载以下级联分类器文件。 此外我们在 GitHub 存储库中包含了两个已加载的层叠分类器文件haarcascade_frontalface_alt2.xml和haarcascade_frontalface_default.xml。 下一步是执行检测 faces_alt2 cas_alt2.detectMultiScale(gray) faces_default cas_default.detectMultiScale(gray)cv2.CascadeClassifier.detectMultiScale()函数检测对象并将其作为矩形列表返回。 最后一步是使用show_detection()函数关联结果 img_faces_alt2 show_detection(img.copy(), faces_alt2) img_faces_default show_detection(img.copy(), faces_default)show_detection()函数在每个检测到的面部上绘制一个矩形 def show_detection(image, faces):Draws a rectangle over each detected facefor (x, y, w, h) in faces:cv2.rectangle(image, (x, y), (x w, y h), (255, 0, 0), 5)return imageOpenCV 还提供cv2.face.getFacesHAAR()函数来检测面部 retval, faces_haar_alt2 cv2.face.getFacesHAAR(img, haarcascade_frontalface_alt2.xml) retval, faces_haar_default cv2.face.getFacesHAAR(img, haarcascade_frontalface_default.xml)应当注意cv2.CascadeClassifier.detectMultiScale()需要灰度图像而cv2.face.getFacesHAAR()需要 BGR 图像作为输入。 此外cv2.CascadeClassifier.detectMultiScale()将检测到的脸部输出为矩形列表。 例如两个检测到的面部的输出将如下所示 [[332 93 364 364] [695 104 256 256]]cv2.face.getFacesHAAR()函数以相似的格式返回人脸 [[[298 524 61 61]] [[88 72 315 315]]要摆脱无用的一维数组请调用np.squeeze() faces_haar_alt2 np.squeeze(faces_haar_alt2) faces_haar_default np.squeeze(faces_haar_default)用于检测和绘制已加载图像中的面部的完整代码如下 # Load image and convert to grayscale: img cv2.imread(test_face_detection.jpg) gray cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)# Load cascade classifiers: cas_alt2 cv2.CascadeClassifier(haarcascade_frontalface_alt2.xml) cas_default cv2.CascadeClassifier(haarcascade_frontalface_default.xml)# Detect faces: faces_alt2 cas_alt2.detectMultiScale(gray) faces_default cas_default.detectMultiScale(gray) retval, faces_haar_alt2 cv2.face.getFacesHAAR(img, haarcascade_frontalface_alt2.xml) faces_haar_alt2 np.squeeze(faces_haar_alt2) retval, faces_haar_default cv2.face.getFacesHAAR(img, haarcascade_frontalface_default.xml) faces_haar_default np.squeeze(faces_haar_default)# Draw face detections: img_faces_alt2 show_detection(img.copy(), faces_alt2) img_faces_default show_detection(img.copy(), faces_default) img_faces_haar_alt2 show_detection(img.copy(), faces_haar_alt2) img_faces_haar_default show_detection(img.copy(), faces_haar_default)最后一步是使用 OpenCV 或 Matplotlib 在这种情况下显示四个创建的图像。 完整的代码可以在face_detection_opencv_haar.py脚本中看到。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gsdnl7Ii-1681870549417)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/586f6104-36ae-41b3-9792-bf2966f1cb0c.png)] 如您所见通过使用基于 Haar 特征的叶栅分类器使用上述四个近似值检测到的面部会发生变化。 最后还应该指出cv2.CascadeClassifier.detectMultiScale()函数具有minSize和maxSize参数以便确定最小大小不会检测到小于minSize的对象和最大大小不会检测到大于maxSize的对象。 相反cv2.face.getFacesHAAR()函数不提供这种可能性。 基于 Haar 特征的级联分类器可用于检测人脸以外的对象。 OpenCV 库还提供了两个用于猫脸检测的级联文件。 为了完整起见cat_face_detection_opencv_haar.py脚本加载了两个层叠文件这些文件经过训练可以检测图像中的正面猫脸。 该脚本与face_detection_opencv_haar.py脚本非常相似。 确实关键的修改是已加载的两个层叠文件。 在这种情况下这是两个已加载的层叠文件 haarcascade_frontalcatface.xml一种正面猫脸检测器使用具有 20 个阶段的基本 Haar 特征集和24 x 24 Haar 特征haarcascade_frontalcatface_extended.xml正面猫脸检测器使用全套 20 个阶段的 Haar 特征和24 x 24 Haar 特征 有关这些级联文件的更多信息请参阅 Joseph Howse 的《面向秘密特工的 OpenCV》。 您可以从 OpenCV 信息库下载以下级联分类器文件。 此外我们已经在 GitHub 存储库中包含了这两个层叠分类器文件。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-J3Zhep5x-1681870549417)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/77b84524-168c-4d04-9602-af00d2b13e18.png)] 此外OpenCV 提供了基于深度学习的面部检测器。 更具体地说OpenCV 深度神经网络DNN面部检测器基于单发多盒检测器SSD框架 ResNet-10 网络。 从 OpenCV 3.1 开始提供了 DNN 模块该模块使用流行的深度学习框架例如 CaffeTensorFlowTorch 和 Darknet使用经过预训练的深度网络来实现前向传递推理。 在 OpenCV 3.3 中该模块已从opencv_contrib存储库升级到主存储库并得到了显着加速。 这意味着我们可以使用经过预训练的网络来执行完整的前向传递并利用输出在我们的应用中进行预测而不必花费数小时来训练网络。 在第 12 章“深度学习简介”中我们将进一步探索 DNN 模块 在本章中我们将重点介绍深度学习人脸检测器。 在本节中我们将使用库中包含的预训练深度学习人脸检测器模型执行人脸检测。 OpenCV 为此面部检测器提供了两种模型 人脸检测器FP16原始 Caffe 实现的浮点 16 版本5.1 MB人脸检测器UINT8使用 TensorFlow 的 8 位量化版本2.6 MB 在每种情况下您都需要两套文件模型文件和配置文件。 对于 Caffe 模型这些文件如下 res10_300x300_ssd_iter_140000_fp16.caffemodel此文件包含实际层的权重。 可以从这里下载它也包含在 GitHub 仓库中。deploy.prototxt此文件定义模型架构。 可以从这里下载并包含在该书的 GitHub 存储库中。 如果您使用 TensorFlow 模型则需要以下文件 opencv_face_detector_uint8.pb此文件包含实际层的权重。 可以从这里下载该文件该文件包含在本书的 GitHub 存储库中。opencv_face_detector.pbtxt此文件定义模型架构。 可以从这里下载并包含在该书的 GitHub 存储库中。 face_detection_opencv_dnn.py脚本向您展示如何通过使用面部检测和预训练的深度学习面部检测器模型来检测面部。 第一步是加载预训练的模型 # Load pre-trained model: net cv2.dnn.readNetFromCaffe(deploy.prototxt, res10_300x300_ssd_iter_140000_fp16.caffemodel) # net cv2.dnn.readNetFromTensorflow(opencv_face_detector_uint8.pb, opencv_face_detector.pbtxt)如您所见在此示例中原始 Caffe 实现的浮点 16 版本已加载。 为了获得最佳精度我们必须在大小分别为300 x 300的 BGR 图像上运行模型方法是分别对蓝色绿色和红色通道应用(104, 177, 123)值的均值减法。 此预处理是使用cv2.dnn.blobFromImage() OpenCV 函数执行的 blob cv2.dnn.blobFromImage(image, 1.0, (300, 300), [104., 117., 123.], False, False)在第 12 章“深度学习简介”中我们将更深入地研究此函数。 下一步是将 BLOB 设置为输入以获取结果并对整个网络执行前向传递以计算输出 # Set the blob as input and obtain the detections: net.setInput(blob) detections net.forward()最后一步是遍历所有检测并得出结果仅当相应的置信度大于固定的最小阈值时才考虑检测 # Iterate over all detections: for i in range(0, detections.shape[2]):# Get the confidence (probability) of the current detection:confidence detections[0, 0, i, 2]# Only consider detections if confidence is greater than a fixed minimum confidence:if confidence 0.7:# Increment the number of detected faces:detected_faces 1# Get the coordinates of the current detection:box detections[0, 0, i, 3:7] * np.array([w, h, w, h])(startX, startY, endX, endY) box.astype(int)# Draw the detection and the confidence:text {:.3f}%.format(confidence * 100)y startY - 10 if startY - 10 10 else startY 10cv2.rectangle(image, (startX, startY), (endX, endY), (255, 0, 0), 3)cv2.putText(image, text, (startX, y), cv2.FONT_HERSHEY_SIMPLEX, 0.9, (0, 0, 255), 2)下一个屏幕截图中可以看到face_detection_opencv_dnn.py脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tcbRVvlO-1681870549417)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/51f8839f-cff6-4788-a0df-5519c7ec6369.png)] 可以看出以高置信度检测到这三个脸。 使用 Dlib 的人脸检测 您可以使用dlib.get_frontal_face_detector()创建正面检测器该检测器基于定向梯度直方图HOG特征和滑动窗口检测方法中的线性分类器。 特别是HOG 训练器使用基于结构 SVM 的训练算法该训练算法使训练器可以在每个训练图像中的所有子窗口中进行训练。 此人脸检测器已使用来自带标签的野外数据集中的 3,000 张图像进行了训练。 应当注意的是该检测器也可以用于发现脸部以外的物体。 您可以查看dlib库中包含的train_object_detector.py脚本以了解如何轻松训练自己的对象检测器仅使用一些训练图像。 例如您可以仅使用八个停车标志图像来训练一个出色的停车标志检测器。 face_detection_dlib_hog.py脚本使用上述dlib正面面部检测器检测面部。 第一步是从dlib加载正面检测器 detector dlib.get_frontal_face_detector()下一步是执行检测 rects_1 detector(gray, 0) rects_2 detector(gray, 1)第二个参数表示在执行检测过程之前对图像进行了1上采样因为图像较大因此检测器可以检测更多的人脸。 相反执行时间将增加。 因此出于表现考虑应将其考虑在内。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1zRdJoep-1681870549418)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/c74eb9da-cf23-4943-a4ce-64be12bacd5c.png)] 如您所见如果我们使用原始灰度图像rects_1 detector(gray, 0)检测到面部则只会发现两个面部。 但是如果我们使用1时间rects_2 detector(gray, 1)上采样的灰度图像检测到人脸则可以正确检测到这三个人脸。 Dlib 库还提供了 CNN 人脸检测器。 您可以使用dlib.cnn_face_detection_model_v1()创建 CNN 人脸检测器。 构造器从文件中加载人脸检测模型。 您可以从这里下载预训练的模型712 KB。 创建 CNN 人脸检测器时应将相应的预训练模型传递给此方法 cnn_face_detector dlib.cnn_face_detection_model_v1(mmod_human_face_detector.dat)至此我们准备使用此检测器识别人脸 rects cnn_face_detector(img, 0)该检测器发现了mmod_rectangles对象该对象是mmod_rectangle对象的列表并且mmod_rectangle对象具有两个成员变量-dlib.rectangle对象和confidence分数。 因此为了显示检测结果对show_detection()函数进行了编码 def show_detection(image, faces):Draws a rectangle over each detected face# faces contains a list of mmod_rectangle objects# The mmod_rectangle object has two member variables, a dlib.rectangle object, and a confidence score# Therefore, we iterate over the detected mmod_rectangle objects accessing dlib.rect to draw the rectanglefor face in faces:cv2.rectangle(image, (face.rect.left(), face.rect.top()), (face.rect.right(), face.rect.bottom()), (255, 0, 0), 10)return imageshow_detection()函数应按以下方式调用 img_faces show_detection(img.copy(), rects)完整代码在face_detection_dlib_cnn.py脚本中。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3MDLKvvQ-1681870549418)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/21ec9ed8-4f88-43da-9579-ceb93eba64e5.png)] dlib CNN 面部检测器比dlib HOG 面部检测器更加精确但是运行起来需要更多的计算能力。 例如对于600 x 400图像HOG 面部检测器大约需要0.25秒而 CNN 面部检测器大约需要5秒。 实际上CNN 面部检测器旨在在 GPU 上执行以获得合理的速度。 如果您具有 GPU则可以启用 CUDA这将加快执行速度。 为此您需要从源代码编译dlib。 使用face_recognition的人脸检测 为了使用face_recognition检测人脸应调用face_locations()函数 rects_1 face_recognition.face_locations(rgb, 0, hog) rects_2 face_recognition.face_locations(rgb, 1, hog)第一个参数是输入RGB图像。 第二个参数设置在执行检测过程之前对输入图像进行上采样的次数。 第三个参数确定将使用哪种面部检测模型。 在这种情况下将使用hog检测模型。 该示例的完整代码可以在face_detection_fr_hog.py脚本中看到。 另外可以将face_processing配置为使用cnn面部检测器检测面部 rects_1 face_recognition.face_locations(rgb, 0, cnn) rects_2 face_recognition.face_locations(rgb, 1, cnn)您可以看到face_detection_fr_hog.py和face_detection_fr_cnn.py脚本如果需要更多详细信息它们分别使用hog和cnn面部检测器执行面部识别。 请记住face_processing库内部使用 HOG 和 CNN dlib人脸检测器。 使用cvlib的人脸检测 为了完整起见我们在本节中介绍cvlib包因为它还提供了面部检测算法。 该库是一个简单高级且易于使用的 Python 开源计算机视觉库。 为了使用cvlib检测人脸可以使用detect_face()函数该函数将为所有检测到的人脸返回边界框和相应的置信度 import cvlib as cv faces, confidences cv.detect_face(image)在后台此函数将 OpenCV DNN 面部检测器与经过预训练的 Caffe 模型一起使用。 有关更多详细信息请参见face_detection_cvlib_dnn.py脚本。 检测人脸标志 在计算机视觉中基准面部关键点也称为人脸标志的定位通常是许多面部分析方法和算法中的关键步骤。 面部表情识别头部姿势估计算法和嗜睡检测系统仅是几个示例它们严重依赖于通过检测地标而提供的面部形状信息。 人脸标志检测算法旨在自动识别图像或视频中人脸标志点的位置。 更具体地那些关键点或者是描述面部成分的唯一位置的优势点例如嘴角或眼睛的角或者是连接这些围绕面部成分和面部轮廓的优势点的内插点。 形式上给定表示为I的面部图像标志检测算法将检测D标志x {x1, y1, x2, y2, ..., xD, yD}其中x和y代表人脸标志的图像坐标。 在本节中我们将看到如何使用 OpenCV 和 Dlib 来检测人脸标志。 使用 OpenCV 检测人脸标志 OpenCV 人脸标志性 API 称为 Facemark。 它基于三篇不同的论文提供了三种不同的地标检测实现 FacemarkLBFFacemarkKameziFacemarkAAM 以下示例显示了如何使用这些算法检测人脸标志 # Import required packages: import cv2 import numpy as np# Load image: image cv2.imread(my_image.png,0)# Find faces: cas cv2.CascadeClassifier(haarcascade_frontalface_alt2.xml) faces cas.detectMultiScale(image , 1.5, 5) print(faces, faces)# At this point, we create landmark detectors and test them: print(testing LBF) facemark cv2.face.createFacemarkLBF() facemark .loadModel(lbfmodel.yaml) ok, landmarks facemark.fit(image , faces) print (landmarks LBF, ok, landmarks)print(testing AAM) facemark cv2.face.createFacemarkAAM() facemark .loadModel(aam.xml) ok, landmarks facemark.fit(image , faces) print (landmarks AAM, ok, landmarks)print(testing Kazemi) facemark cv2.face.createFacemarkKazemi() facemark .loadModel(face_landmark_model.dat) ok, landmarks facemark.fit(image , faces) print (landmarks Kazemi, ok, landmarks)此示例应使用 OpenCV 提供的三种不同算法来检测人脸标志。 但是为fit()函数生成的 Python 包装是不正确的。 因此在编写本文并使用OpenCV 4.0时此脚本在 Python 中不起作用。 要解决此问题我们需要修改fit()函数的 C 代码并从源代码安装 OpenCV。 例如以下是FacemarkLBFImpl::fit()方法的实际代码 // C codebool FacemarkLBFImpl::fit( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks ) {// FIXITstd::vectorRect faces *(std::vectorRect *)roi.getObj();if (faces.empty()) return false;std::vectorstd::vectorPoint2f landmarks *(std::vectorstd::vectorPoint2f *) _landmarks.getObj();landmarks.resize(faces.size());for(unsigned i0; ifaces.size();i){params.detectROI faces[i];fitImpl(image.getMat(), landmarks[i]);}return true; }应该使用以下代码对其进行修改 // C codebool FacemarkLBFImpl::fit( InputArray image, InputArray roi, OutputArrayOfArrays _landmarks ) {Mat roimat roi.getMat();std::vectorRect faces roimat.reshape(4,roimat.rows);if (faces.empty()) return false;std::vectorstd::vectorPoint2f landmarks(faces.size());for (unsigned i0; ifaces.size();i){params.detectROI faces[i];fitImpl(image.getMat(), landmarks[i]);}if (_landmarks.isMatVector()) { // pythonstd::vectorMat v *(std::vectorMat*) _landmarks.getObj();for (size_t i0; ifaces.size(); i)v.push_back(Mat(landmarks[i]));} else { // c, javastd::vectorstd::vectorPoint2f v *(std::vectorstd::vectorPoint2f *) _landmarks.getObj();v landmarks;}return true; }这样为fit()函数生成的 Python 包装器应该是正确的。 应该注意的是使用这三种算法提供的用于检测人脸标志的 Python 代码是正确的只有 Python 包装器无法生成正确的代码。 有关此问题的更多信息请参见以下两个链接 Using Facemark API (Python), Version 4.0.0 - pre : bad alloc error使用 Facemark API Python 使用 Dlib 检测人脸标志 另一种选择是使用dlib库来检测人脸标志。 在landmarks_detection_dlib.py脚本中我们使用dlib检测了人脸标志。 更具体地说我们使用从网络摄像头拍摄的图像使用dlib正面人脸检测进行人脸检测。 我们还提供从测试图像中获取图像的可能性。 下一步是使用形状预测器获得形状 p shape_predictor_68_face_landmarks.dat predictor dlib.shape_predictor(p) shape predictor(gray, rect)下一步是将shape转换为numpy数组。 从这个意义上讲shape是 Dlib full_object_detection对象它表示图像中对象的位置以及所有部分的位置。 shape_to_np()函数执行以下转换 def shape_to_np(dlib_shape, dtypeint):Converts dlib shape object to numpy array# Initialize the list of (x,y) coordinatescoordinates np.zeros((dlib_shape.num_parts, 2), dtypedtype)# Loop over all facial landmarks and convert them to a tuple with (x,y) coordinates:for i in range(0, dlib_shape.num_parts):coordinates[i] (dlib_shape.part(i).x, dlib_shape.part(i).y)# Return the list of (x,y) coordinates:return coordinates最后我们在图像中绘制了 68 个人脸标志。 为了在图像中绘制标志我们已经编码了几个函数这些函数提供了一种灵活的方式来以所需格式绘制所需的标志。 下一个屏幕截图显示了绘制检测到的人脸标志时的不同可能性 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qPE9BxSL-1681870549418)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/ea3f35a0-41b8-4656-9c78-5df8e03b171f.png)] 为了从左到右绘制每个图像中的地标我们执行了以下操作 第一张图片draw_shape_lines_all(shape, frame)第二张图片draw_shape_lines_range(shape, frame, JAWLINE_POINTS)第三张图片draw_shape_points_pos(shape, frame)第四张图片draw_shape_points_pos_range(shape, frame, LEFT_EYE_POINTS RIGHT_EYE_POINTS NOSE_BRIDGE_POINTS) 应当注意dlib还提供了检测与两只眼睛和鼻尖位置相对应的5人脸标志的可能性。 因此如果要使用此形状预测器则应相应地加载它 p shape_predictor_5_face_landmarks.dat使用face_recognition检测人脸标志 landmarks_detection_fr.py脚本显示了如何使用face_recognition包检测和绘制人脸标志。 为了检测标志调用face_recognition.face_landmarks()函数如下所示 # Detect 68 landmarks: face_landmarks_list_68 face_recognition.face_landmarks(rgb)此函数为图像中的每个脸部返回人脸标志例如眼睛和鼻子的字典。 例如如果我们打印检测到的地标则输出如下 [{chin: [(113, 251), (111, 283), (115, 315), (122, 346), (136, 376), (154, 402), (177, 425), (203, 442), (231, 447), (260, 442), (285, 426), (306, 403), (323, 377), (334, 347), (340, 315), (343, 282), (343, 251)], left_eyebrow: [(123, 223), (140, 211), (163, 208), (185, 211), (206, 220)], right_eyebrow: [(240, 221), (263, 212), (288, 209), (312, 211), (332, 223)], nose_bridge: [(225, 249), (225, 272), (225, 295), (226, 319)], nose_tip: [(201, 337), (213, 340), (226, 343), (239, 339), (252, 336)], left_eye: [(144, 248), (158, 239), (175, 240), (188, 254), (173, 255), (156, 254)], right_eye: [(262, 254), (276, 240), (293, 239), (308, 248), (295, 254), (278, 255)], top_lip: [(185, 377), (200, 370), (216, 364), (226, 367), (238, 364), (255, 370), (274, 377), (267, 378), (238, 378), (227, 380), (215, 379), (192, 378)], bottom_lip: [(274, 377), (257, 391), (240, 399), (228, 400), (215, 398), (200, 391), (185, 377), (192, 378), (215, 381), (227, 382), (239, 380), (267, 378)]}]最后一步是绘制检测到的地标 # Draw all detected landmarks: for face_landmarks in face_landmarks_list_68:for facial_feature in face_landmarks.keys():for p in face_landmarks[facial_feature]:cv2.circle(image_68, p, 2, (0, 255, 0), -1)需要说明的是face_recognition.face_landmarks()方法的签名如下 face_landmarks(face_image, face_locationsNone, modellarge)因此默认情况下会检测到 68 个特征点。 如果modelsmall将仅检测到 5 个特征点 # Detect 5 landmarks: face_landmarks_list_5 face_recognition.face_landmarks(rgb, None, small)如果打印face_landmarks_list_5则会得到以下输出 [{nose_tip: [(227, 343)], left_eye: [(145, 248), (191, 253)], right_eye: [(307, 248), (262, 252)]}]在这种情况下词典仅包含双眼和鼻尖的面部特征位置。 在以下屏幕截图中可以看到landmarks_detection_fr.py脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qXVnpOSi-1681870549418)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/79ce6934-2e0a-457a-a6e9-773f6935db45.png)] 在上面的屏幕截图中您可以看到使用face_recognition包绘制检测到的 68 个和 5 个人脸标志的结果。 人脸追踪 在仅知道目标的初始位置的情况下对象跟踪会尝试估计整个视频序列中目标的轨迹。 由于多种因素例如外观变化遮挡快速运动运动模糊和比例变化此任务确实具有挑战性。 在这种意义上基于判别相关过滤器DCF的视觉跟踪器可提供最新的表现。 此外这些跟踪器的计算效率很高这在实时应用中至关重要。 实际上可以在视觉对象跟踪VOT2014 挑战赛的结果中看到基于 DCF 的跟踪器的最新表现。 在 VOT2014 挑战赛中排名前三的跟踪器均基于相关性过滤器。 VOT2014 评估了 38 个跟踪器来自 VOT2014 委员会的 33 个跟踪器和 5 个基线。 因此DCF 跟踪器是当前基于边界框的跟踪的一种非常流行的选择方法。 Dlib 库实现了基于 DCF 的跟踪器该跟踪器易于用于对象跟踪。 在本节中我们将看到如何将此跟踪器用于面部跟踪和跟踪用户选择的任意对象。 在文献中此方法也称为判别尺度空间跟踪器DSST。 唯一需要的输入原始视频除外是第一帧目标的初始位置上的边界框然后跟踪器会自动预测目标的轨迹。 使用基于 Dlib DCF 的跟踪器的人脸跟踪 在face_tracking_correlation_filters.py脚本中我们使用 Dlib 正面人脸检测器进行初始化并使用基于dlib DCF 的跟踪器 DSST 进行人脸跟踪。 为了初始化相关跟踪器我们执行以下命令 tracker dlib.correlation_tracker()这将使用默认值filter_size 6num_scale_levels 5scale_window_size 23regularizer_space 0.001nu_space 0.025regularizer_scale 0.001nu_scale 0.025和scale_pyramid_alpha 1.020初始化跟踪器。 较高的filter_size和num_scale_levels值可以提高跟踪精度但是它需要更多的计算能力从而增加了 CPU 处理量。 filter_size的推荐值为56和7推荐的值为num_scale_levels45和6。 要开始跟踪该方法请使用tracker.start_track()。 在这种情况下我们执行面部检测。 如果成功我们将脸部位置传递给此方法如下所示 if tracking_face is False:gray cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# Try to detect a face to initialize the tracker:rects detector(gray, 0)# Check if we can start tracking (if we detected a face):if len(rects) 0:# Start tracking:tracker.start_track(frame, rects[0])tracking_face True这样对象跟踪器将开始跟踪边界框内的内容在这种情况下边界框是检测到的脸部。 另外为了更新被跟踪对象的位置调用tracker.update()方法 tracker.update(frame)此方法更新跟踪器并返回峰-旁瓣比该比值是衡量跟踪器置信度的指标。 此度量标准的较大值表示高置信度。 此度量标准可用于通过正面人脸检测重新初始化跟踪器。 要获取被跟踪对象的位置请调用tracker.get_position()方法 pos tracker.get_position()此方法返回被跟踪对象的位置。 最后我们可以绘制脸部的预测位置 cv2.rectangle(frame, (int(pos.left()), int(pos.top())), (int(pos.right()), int(pos.bottom())), (0, 255, 0), 3)在此脚本中我们编码了如果按下数字1重新初始化跟踪器的选项。 如果按下此数字我们将重新初始化跟踪器以尝试检测正面。 为了阐明此脚本的工作方式包括以下两个屏幕截图。 在第一个屏幕截图中跟踪算法正在等待直到执行正面人脸检测以初始化跟踪为止 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vFsRZfuf-1681870549418)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/3fa3ba82-f9c6-4a1e-a6c7-2bf0241c4456.png)] 在第二个屏幕截图中跟踪算法当前正在跟踪先前检测到的面部 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JRe1tEud-1681870549419)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/109b9bc9-f98f-453d-9a81-e0bef60d4651.png)] 在上一个屏幕截图中您可以看到该算法当前正在跟踪检测到的面部。 您还可以看到您还可以按数字1来重新初始化跟踪。 使用基于 Dlib DCF 的跟踪器的对象跟踪 可以修改face_tracking_correlation_filters.py脚本以跟踪任意对象。 在这种情况下我们将使用鼠标选择要跟踪的对象。 如果按1该算法将开始跟踪预定义边界框内的对象。 另外如果我们按2则预定义的边界框将被清空跟踪算法将停止从而允许用户选择另一个边界框。 为了阐明face_tracking_correlation_filters.py脚本的工作方式我们提供了以下两个屏幕截图。 在第一个中我们可以看到我们需要选择一个边界框来开始跟踪 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wjo0GFBi-1681870549419)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/385235bf-7d19-4ee8-92d3-6e40ca32a3c0.png)] 在第二篇文章中我们可以看到算法跟踪对象时任意帧的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ITIMprOa-1681870549419)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/e7f39864-b9d4-4476-9322-ecbf37eddff7.png)] 如您在上一个屏幕截图中所见该算法正在跟踪边界框内的对象。 人脸识别 随着计算机视觉机器学习和深度学习的发展人脸识别已成为热门话题。 人脸识别可广泛应用于各种用途包括预防犯罪监视法医应用生物识别以及最近在社交网络中的使用。 自动人脸识别面临各种挑战例如遮挡外观变化表情老化和比例变化。 在对象识别方面取得成功之后CNN 已被广泛用于面部识别。 在本章中我们将看到 OpenCV 提供的与面部识别相关的功能还将探索一些深度学习方法这些方法可以轻松地集成到您的计算机视觉项目中以执行最新的面部识别结果。 使用 OpenCV 的人脸识别 OpenCV 提供了执行面部识别的支持。 实际上OpenCV 提供了三种不同的实现方式 EigenfacesFisherfaces本地二进制模式直方图LBPH 这些实现以不同的方式执行识别。 但是您可以通过仅更改识别器的创建方式来使用它们中的任何一个。 更具体地说要创建这些识别器需要以下代码 face_recognizer cv2.face.LBPHFaceRecognizer_create() face_recognizer cv2.face.EigenFaceRecognizer_create() face_recognizer cv2.face.FisherFaceRecognizer_create()一旦创建并且与特定的内部算法无关OpenCV 将用于执行人脸识别应使用train()和predict()这两个关键方法来进行人脸识别系统的训练和测试 并且应注意我们使用这些方法的方式与创建的识别器无关。 因此尝试使用三个识别器并为特定任务选择表现最佳的识别器非常容易。 话虽如此当在通常涉及不同环境和光照条件的野外中识别图像时LBPH 应该比其他两种方法提供更好的结果。 此外LBPH 人脸识别器支持update()方法您可以在其中给定新数据来更新人脸识别器。 对于 Eigenfaces 和 Fisherfaces 方法此功能是不可能的。 为了训练识别器应调用train()方法 face_recognizer.train(faces, labels)cv2.face_FaceRecognizer.train(src, labels)方法训练特定的面部识别器其中src对应于图像面部的训练集而参数标签为训练集中的每个图像设置相应的标签。 要识别新人脸应调用predict()方法 label, confidence face_recognizer.predict(face)cv2.face_FaceRecognizer.predict(src)方法通过输出预测的标签和关联的置信度来输出预测新src图像的识别。 最后OpenCV 还提供write()和read()方法来分别保存创建的模型和加载先前创建的模型。 对于这两种方法filename参数都设置要保存或加载的模型的名称 cv2.face_FaceRecognizer.write(filename) cv2.face_FaceRecognizer.read(filename)如前所述可以使用update()方法更新 LBPH 人脸识别器 cv2.face_FaceRecognizer.update(src, labels)在这里src和labels设置了新的训练示例这些示例将用于更新 LBPH 识别器。 使用 Dlib 的人脸识别 Dlib 提供了基于深度学习的高质量人脸识别算法。 Dlib 实现了面部识别算法可提供最先进的准确率。 更具体地说该模型在野生数据库中带有标签的人脸上的准确率为 99.38%。 该算法的实现基于《用于图像识别的深度残差学习》2016中提出的 ResNet-34 网络该网络使用 300 万张人脸进行了训练。 可以从这里下载创建的模型21.4 MB。 该网络以生成用于量化面部的 128 维128D描述符的方式进行训练。 使用三元组执行训练步骤。 一个三元组训练示例由三个图像组成。 其中两个对应于同一个人。 网络为每个图像生成 128D 描述符略微修改神经网络权重以使与同一个人相对应的两个向量更近而与另一个人相对应的特征向量更远。 三元组损失函数将其形式化并尝试将同一个人的两个图像的 128D 描述符推近而将不同人的两个图像的 128D 描述符推向更远。 对于成千上万的人的数百万个图像此过程将重复数百万次最后它可以为每个人生成 128D 描述符。 因此由于以下原因最终的 128D 描述符是良好的编码 相同人的两个图像的生成的 128D 描述符彼此非常相似。不同人的两个图像生成的 128D 描述符非常不同。 因此利用dlib函数我们可以使用预先训练的模型将人脸映射到 128D 描述符中。 之后我们可以使用这些特征向量来进行面部识别。 encode_face_dlib.py脚本显示了如何计算用于量化人脸的 128D 描述符。 该过程非常简单如以下代码所示 # Load image: image cv2.imread(jared_1.jpg)# Convert image from BGR (OpenCV format) to RGB (dlib format): rgb image[:, :, ::-1]# Calculate the encodings for every face of the image: encodings face_encodings(rgb)# Show the first encoding: print(encodings[0])您可以猜到face_encodings()函数为图像中的每个面部返回 128D 描述符 pose_predictor_5_point dlib.shape_predictor(shape_predictor_5_face_landmarks.dat) face_encoder dlib.face_recognition_model_v1(dlib_face_recognition_resnet_model_v1.dat) detector dlib.get_frontal_face_detector()def face_encodings(face_image, number_of_times_to_upsample1, num_jitters1):Returns the 128D descriptor for each face in the image# Detect faces:face_locations detector(face_image, number_of_times_to_upsample)# Detected landmarks:raw_landmarks [pose_predictor_5_point(face_image, face_location) for face_location in face_locations]# Calculate the face encoding for every detected face using the detected landmarks for each one:return [np.array(face_encoder.compute_face_descriptor(face_image, raw_landmark_set, num_jitters)) forraw_landmark_set in raw_landmarks]如您所见关键是使用检测到的每个标志来计算每个检测到的脸部的脸部编码并调用dlib和face_encoder.compute_face_descriptor()函数。 num_jitters参数设置每个面部随机抖动的次数并返回每次计算的平均 128D 描述符。 在这种情况下输出编码 128D 描述符如下 [-0.08550473 0.14213498 0.01144615 -0.05947386 -0.05831585 0.01127038 -0.05497809 -0.03466939 0.14322688 -0.1001832 0.17384697 0.02444006 -0.25994921 0.13708787 -0.08945534 0.11796272 -0.25426617 -0.0829383 -0.05489913 -0.10409787 0.07074109 0.05810066 -0.03349853 0.07649824 -0.07817822 -0.29932317 -0.15986916 -0.087205 0.10356752 -0.12659372 0.01795856 -0.01736169 -0.17094864 -0.01318233 -0.00201829 0.0104903 -0.02453734 -0.11754096 0.2014133 0.12671679 -0.0271306 -0.02350519 0.08327188 0.36815098 0.12599576 0.04692561 0.03585262 -0.03999642 0.23675609 -0.28394884 0.11896492 0.11870296 0.20243752 0.2106981 0.03092775 -0.14315812 0.07708532 0.16536239 -0.19648902 0.22793224 0.06825032 -0.00117573 0.00304667 -0.01902146 0.2539638 0.09768397 -0.13558105 -0.15079053 0.11357955 -0.14893037 -0.09028706 0.03625216 -0.13004847 -0.16567475 -0.21958281 0.08687183 0.35941613 0.16637127 -0.08334676 0.02806632 -0.09188357 -0.10760318 0.02889947 0.08376379 -0.11524356 -0.00998984 -0.05582509 0.09372396 0.30287758 -0.01063644 -0.07903813 0.30418509 -0.01998731 0.0752025 -0.00424637 0.07463965 -0.12972119 -0.04034984 -0.08435905 -0.01642537 0.00847361 -0.09549874 -0.07568903 0.06476583 -0.19202243 0.16904426 -0.01247451 0.03941975 -0.01960869 0.02145611 -0.25607404 -0.03039071 0.20248309 -0.25835767 0.21397503 0.19302645 0.07284702 0.07879912 0.06171442 0.02366752 0.06781606 -0.06446165 -0.14713687 -0.0714087 0.11978403 -0.01525984 -0.04687868 0.00167655]面部被编码后下一步就是执行识别。 使用使用 128D 描述符计算的某种距离度量可以轻松地计算出识别率。 实际上如果两个面部描述符向量之间的欧式距离小于0.6则可以认为它们属于同一个人。 否则他们来自不同的人。 欧几里德距离可以使用numpy.linalg.norm()来计算。 在compare_faces_dlib.py脚本中我们将四个图像与另一个图像进行比较。 为了比较人脸我们编写了两个函数compare_faces()和compare_faces_ordered()。 compare_faces()函数将面部编码列表与候选者进行比较时返回距离以进行检查 def compare_faces(face_encodings, encoding_to_check):Returns the distances when comparing a list of face encodings against a candidate to checkreturn list(np.linalg.norm(face_encodings - encoding_to_check, axis1))compare_faces_ordered()函数在将人脸编码列表与候选者进行比较以进行检查时返回排序的距离和相应的名称 def compare_faces_ordered(face_encodings, face_names, encoding_to_check):Returns the ordered distances and names when comparing a list of face encodings against a candidate to checkdistances list(np.linalg.norm(face_encodings - encoding_to_check, axis1))return zip(*sorted(zip(distances, face_names)))因此将四个图像与另一个图像进行比较的第一步是加载所有图像并转换为 RGBdlib format # Load images: known_image_1 cv2.imread(jared_1.jpg) known_image_2 cv2.imread(jared_2.jpg) known_image_3 cv2.imread(jared_3.jpg) known_image_4 cv2.imread(obama.jpg) unknown_image cv2.imread(jared_4.jpg)# Convert image from BGR (OpenCV format) to RGB (dlib format): known_image_1 known_image_1[:, :, ::-1] known_image_2 known_image_2[:, :, ::-1] known_image_3 known_image_3[:, :, ::-1] known_image_4 known_image_4[:, :, ::-1] unknown_image unknown_image[:, :, ::-1]# Crate names for each loaded image: names [jared_1.jpg, jared_2.jpg, jared_3.jpg, obama.jpg]下一步是计算每个图像的编码 # Create the encodings: known_image_1_encoding face_encodings(known_image_1)[0] known_image_2_encoding face_encodings(known_image_2)[0] known_image_3_encoding face_encodings(known_image_3)[0] known_image_4_encoding face_encodings(known_image_4)[0] known_encodings [known_image_1_encoding, known_image_2_encoding, known_image_3_encoding, known_image_4_encoding] unknown_encoding face_encodings(unknown_image)[0]最后您可以使用以前的函数比较人脸。 例如让我们使用compare_faces_ordered()函数 computed_distances_ordered, ordered_names compare_faces_ordered(known_encodings, names, unknown_encoding) print(computed_distances_ordered) print(ordered_names)这样做将为我们带来以下好处 (0.3913191431497527, 0.39983264838593896, 0.4104153683230741, 0.9053700273411349) (jared_3.jpg, jared_1.jpg, jared_2.jpg, obama.jpg)前三个值0.39131914314975270.399832648385938960.4104153683230741小于0.6。 这意味着可以从与要检查的图像jared_4.jpg相同的人处考虑前三个图像jared_3.jpgjared_1.jpgjared_2.jpg。 获得的第四值0.9053700273411349表示第四张图像obama.jpg与要检查的图像不是同一个人。 在下一个屏幕截图中可以看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nh8MeDRi-1681870549419)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/1d8e0c02-939d-4a42-ab9e-36b33c901b99.png)] 在上一个屏幕截图中您可以看到可以从同一个人考虑前三张图像获取的值小于 0.6而可以从另一个人考虑第四张图像获取的值大于 0.6。 使用face_recognition的人脸识别 face_recognition的人脸识别使用dlib函数对人脸进行编码并计算编码人脸的距离。 因此您无需编码face_encodings()和compare_faces()函数而只需使用它们。 encode_face_fr.py脚本显示了如何创建使用face_recognition.face_encodings()函数的 128D 描述符 # Load image: image cv2.imread(jared_1.jpg)# Convert image from BGR (OpenCV format) to RGB (face_recognition format): image image[:, :, ::-1]# Calculate the encodings for every face of the image: encodings face_recognition.face_encodings(image)# Show the first encoding: print(encodings[0])要查看如何使用face_recognition比较人脸已对compare_faces_fr.py脚本进行了编码。 代码如下 # Load known images (remember that these images are loaded in RGB order): known_image_1 face_recognition.load_image_file(jared_1.jpg) known_image_2 face_recognition.load_image_file(jared_2.jpg) known_image_3 face_recognition.load_image_file(jared_3.jpg) known_image_4 face_recognition.load_image_file(obama.jpg)# Crate names for each loaded image: names [jared_1.jpg, jared_2.jpg, jared_3.jpg, obama.jpg]# Load unknown image (this image is going to be compared against all the previous loaded images): unknown_image face_recognition.load_image_file(jared_4.jpg)# Calculate the encodings for every of the images: known_image_1_encoding face_recognition.face_encodings(known_image_1)[0] known_image_2_encoding face_recognition.face_encodings(known_image_2)[0] known_image_3_encoding face_recognition.face_encodings(known_image_3)[0] known_image_4_encoding face_recognition.face_encodings(known_image_4)[0] known_encodings [known_image_1_encoding, known_image_2_encoding, known_image_3_encoding, known_image_4_encoding] unknown_encoding face_recognition.face_encodings(unknown_image)[0]# Compare the faces: results face_recognition.compare_faces(known_encodings, unknown_encoding)# Print the results: print(results)获得的结果为[True, True, True, False]。 因此前三个加载的图像jared_1.jpgjared_2.jpg和jared_3.jpg被视为与未知图像jared_4.jpg是同一个人而第四个加载的图像obama.jpg被视为一个不同的人。 总结 在本章中我们介绍了用于面部检测检测人脸标志面部跟踪和面部识别的最新算法和技术。 我们回顾了主要的 Python 库和包提供的面部处理方法。 更具体地说在面部处理的上下文中引入了 OpenCVdlibface_processing和cvlib。 其中一些经过审查的方法是基于深度学习技术的。 在下一章中我们将深入探讨深度学习。 问题 在本章中有关面部处理的包和库有哪些评论人脸识别和人脸验证之间的主要区别是什么cv2.face.getFacesHAAR() OpenCV 函数做什么cv2.dnn.blobFromImage()函数有什么作用cvlib包提供什么函数来检测人脸face_recognition提供什么函数来检测人脸标志dlib提供什么函数来初始化相关跟踪器dlib提供什么函数来启动相关跟踪器dlib提供什么函数来获取被跟踪对象的位置计算 128D 描述符以dlib对image BGR 图像进行人脸识别。 进一步阅读 以下资源将帮助您更深入地使用 Python 进行面部处理 《Python 人工智能》作者 Prateek Joshi2017 十二、深度学习简介 如今深度学习是机器学习中最受欢迎和增长最快的领域。 自 2012 年以来深度学习已经超越了传统的机器学习应用方法。这就是为什么将许多深度学习架构应用于许多领域包括计算机视觉的原因。 深度学习的常见应用包括自动语音识别图像识别视觉艺术处理自然语言处理推荐系统生物信息学和图像恢复。 大多数现代深度学习架构都基于人工神经网络深度学习中的深度指的是架构的层数。 在本章中将通过研究与传统机器学习方法的差异来向您介绍深度学习这些差异在第 10 章“使用 OpenCV 的机器学习”中进行了介绍。 此外您还将看到一些适用于图像分类和对象检测的常见深度学习架构。 最后将介绍两个深度学习 Python 库TensorFlow 和 Keras。 更具体地说本章将讨论以下主题 计算机视觉任务的深度学习概述OpenCV 中的深度学习TensorFlow 库Keras 库 在本章中将向您介绍 OpenCV 的深度学习领域以及一些深度学习的 Python 库TensorFlow 和 Keras。 在第 13 章“使用 Python 和 OpenCV 的移动和 Web 计算机视觉”中您将学习如何创建计算机视觉和深度学习 Web 应用。 技术要求 技术要求在这里列出 Python 和 OpenCV特定于 Python 的 IDENumPy 和 Matplotlib 包Git 客户端TensorFlow 库请参阅以下有关如何安装 TensorFlow 的部分Keras 库请参阅以下有关如何安装 Keras 的部分 有关如何安装这些要求的更多详细信息请参见第 1 章“设置 OpenCV”。 可以在 Github 中访问《精通 Python OpenCV 4》的 GitHub 存储库其中包含从本书第一章到最后的所有必要的支持项目文件。 安装 TensorFlow 为了安装 TensorFlow请使用以下命令 $ pip install tensorflow要检查安装是否已正确执行只需打开 Python shell 并尝试导入 TensorFlow 库如下所示 python import tensorflow安装 Keras 为了安装 Keras请使用以下命令 $ pip install keras要检查安装是否正确执行只需打开一个 Python shell 并尝试导入 Keras 库如下所示 python import keras请记住建议的方法是在虚拟环境中安装包。 请参阅第 1 章“设置 OpenCV”以了解如何创建和管理虚拟环境。 计算机视觉任务的深度学习概述 深度学习极大地促进了计算机视觉领域的发展。 在本章的这一部分中将介绍一些关键概念以向您介绍深度学习领域。 深度学习特征 与传统的机器学习方法相比深度学习具有一些关键差异。 此外在许多计算机视觉任务中深度学习技术都超越了机器学习但是应考虑一些关键因素以便知道何时应用每种技术来完成特定的计算任务。 所有这些注意事项简要总结如下 与可以在低端机器上运行的机器学习技术相反深度学习算法需要具有高端基础架构才能正确训练。 实际上深度学习算法固有地执行了大量计算而这些计算可以使用 GPU 进行优化。当对特征自省和工程都缺乏领域的了解时深度学习技术会胜过其他技术因为您不必担心特征工程。 特征工程可以定义为将领域知识应用于特征检测器和提取器创建的过程目的是降低数据的复杂性从而使传统的机器学习方法能够正确学习。 因此这些机器学习算法的表现取决于识别和提取特征的准确率。 另一方面深度学习技术试图从数据中提取高级特征这使得深度学习比传统的机器学习方法先进得多。 在深度学习中查找相关特征的任务是算法的一部分并且通过减少每个问题的特征自省和工程任务来实现自动化。机器学习和深度学习都能够处理海量数据集。 但是在处理小型数据集时机器学习方法更有意义。 从这个意义上说这两种方法之间的主要区别在于其表现随着数据规模的增加而增加。 例如当使用小型数据集时深度学习算法很难在数据中找到模式并且表现不佳因为它们需要大量数据来调整其内部参数。 经验法则是如果数据量很大则深度学习要胜过其他技术而当数据集较小时传统的机器学习算法是可取的。 在下一张图中我们尝试总结了上述关键点以便轻松记住它们 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yC19M98U-1681870549420)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/fc2c1ec7-1550-435f-aca4-5a6cf5213ef7.png)] 图中阐述的关键点如下 计算资源深度学习–高端机器与机器学习–低端机器特征工程深度学习–同一步骤中的特征提取和分类与机器学习–单独步骤中的特征提取和分类数据集大小深度学习–大/非常大的数据集与机器学习–中/大数据集 深度学习爆发 深度学习的概念并不是什么新鲜事物因为 Rina Dechter 于 1986 年以及 Igor Aizenberg 及其同事于 2000 年分别将其引入了机器学习领域和人工神经网络。 但是直到 2012 年发生深度学习革命时才出现了一些对研究界具有重大影响的杰出作品。 在计算机视觉方面 AlexNet 架构作者设计的卷积神经网络的名称是 ImageNet 大规模视觉识别挑战赛ILSVRC的获胜者2012 年的错误率非常低以巨大的错误率15.3% 对 26.2%第二名击败了所有其他竞争对手。 ImageNet 是一个大型视觉数据库包含超过 1400 万个带标签的高分辨率图像。 这些图像被人类标记。 ImageNet 包含 20,000 多个类别。 因此AlexNet 在解决 ILSVRC 2012 方面的 2012 年突破通常被认为是 2010 年代深度学习革命的开始。 用于图像分类的深度学习 继 AlexNet 在此竞赛中获得成功之后许多其他深度学习架构也已提交 ImageNet 挑战赛以实现更好的表现。 从这个意义上讲下一张图展示了提交给 ImageNet 挑战的最相关的深度学习方法的一站式准确率包括最左侧的 AlexNet2012架构以及迄今为止表现最佳的 Inception-V42016 对 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dh4xPs9Q-1681870549420)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/e9924fac-2310-4f9b-956c-9d819ea96850.png)] 这些深度学习架构的主要方面简要介绍如下重点介绍了它们引入的关键方面。 此外如果需要更多详细信息我们还提供了对每个出版物的引用 AlexNet2012 描述AlexNet 是 LSVRC-2012 的获胜者它是一种简单但功能强大的网络架构其中卷积层和池层一个接一个而顶层则是全连接层。 在将深度学习方法应用于计算机视觉任务时通常将该架构用作起点。参考Alex Krizhevsky, Ilya Sutskever, and Geoffrey E Hinton. ImageNet classification with deep convolutional neural networks. In Advances in neural information processing systems, pp. 1097–1105, 2012. VGG-16 和 -192014 描述VGGNet 由牛津大学的视觉几何组VGG提出。 通过在整个网络中仅使用3 x 3过滤器而不使用大型过滤器例如7 x 7和11 x 11。 这项工作的主要贡献在于它表明网络深度是在卷积神经网络中实现更好的识别或分类精度的关键组成部分。 VGGNet 被认为是对特定任务进行基准测试的良好架构。 但是它的主要缺点是训练速度非常慢并且网络架构的权重很大VGG-16 为 533 MBVGG-19 为 574 MB。 VGGNet-19 使用 1.38 亿个参数。参考Simonyan, K., and Zisserman, A. (2014). Very deep convolutional networks for large-scale image recognition. arXiv preprint arXiv:1409.1556. GoogLeNet/Inception V12014 说明 GoogLeNet也称为 Inception V1是 LSVRC-2014 的获胜者其前 5 名错误率达到 6.67%这非常接近人类水平的表现。 该架构比 VGGNet 更深入。 但是由于 9 个并行模块初始模块的架构是基于几个非常小的卷积的因此它仅使用 AlexNet 参数数量的十分之一从 6000 万到仅 400 万个参数目的是减少参数数量。参考Szegedy, C., Liu, W., Jia, Y., Sermanet, P., Reed, S., Anguelov, D., Dumitru, .E, Vincent, .V, and Rabinovich, A. (2015). Going deeper with convolutions. ResNet-18-34-50-101 和 -1522015 说明Microsoft 的残差网络ResNets是 LSVRC-2015 的获胜者并且是迄今为止最深的网络共有 153 个卷积层达到了最高 5 个分类误差为 4.9%这比人工精度略好。 此架构包括跳跃连接也称为门控单元或门控循环单元可实现增量学习更改。 ResNet-34 使用 2180 万个参数ResNet-50 使用 2560 万个参数ResNet-101 使用 4450 万个参数最后ResNet-152 使用 6020 万个参数。参考He, K., Zhang, X., Ren, S., and Sun, J. (2016). Deep residual learning for image recognition. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 770-778). Inception V32015 描述如前所示初始架构被引入为 GoogLeNet也称为 Inception V1。 后来对该架构进行了修改以引入批量规范化Inception-V2。 Inception V3 架构包括其他分解思想其目的是在不降低网络效率的情况下减少连接/参数的数量。参考Szegedy, C., Vanhoucke, V., Ioffe, S., Shlens, J., and Wojna, Z. (2016). Rethinking the inception architecture for computer vision. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 2818-2826). Inception V42016 说明从 GoogLeNet 演变而来的 Inception V4。 另外与 Inception-V3 相比此架构具有更统一的简化架构和更多的 Inception 模块。 Inception-V4 在 LSVRC 上能够达到 80.2% 的 top-1 精度和 95.2% 的 top-5 精度。参考Szegedy, C., Ioffe, S., Vanhoucke, V., and Alemi, A. A. (2017, February). Inception-V4, inception-resnet and the impact of residual connections on learning. In AAAI (Vol. 4, p. 12). 用于对象检测的深度学习 对象检测是深度学习中的一个热门话题它适合于在单个图像中识别和定位多个相关对象。 为了对对象检测算法进行基准测试通常使用三个数据库。 第一个是 PASCAL 视觉对象分类PASCAL VOC数据集其中包括 20 个类别和 10,000 个用于训练和验证的图像其中包含带有对象的边界框。 ImageNet 自 2013 年以来发布了一个对象检测数据集它由大约 500,000 张仅用于训练的图像和 200 个类别组成。 最后上下文中的常见对象COCO是大规模的对象检测分割和字幕数据集在 328,000 张图像中总共有 250 万个标记实例。 有关 COCO 数据集的更多信息您可以阅读出版物《Microsoft COCO上下文中的常见对象》2014。 为了评估对象检测算法通常使用平均平均精度mAP其计算方法是对所有类别和/或所有交并比IoU阈值计算 mAP具体取决于比赛。 在二分类中平均精度AP指标对应于精度正预测值-召回灵敏度曲线的摘要而 IoU 指标是预测框与地面真实框之间的重叠区域。 以下是此示例 PASCAL VOC2007 挑战–仅考虑了一个 IoU 阈值。 对于 PASCAL VOC 挑战如果 IoU 0.5则预测为肯定。 因此mAP 是对所有 20 个对象类平均的。在 2017 年 COCO 挑战赛中对所有 80 个物体类别和所有 10 个 IoU 阈值从 0.5 到 0.95步长为 0.05平均了 mAP。 在 10 个 IoU 阈值从 0.5 到 0.95步长为 0.05上求平均值而不是仅考虑一个 IoU≥0.5 的阈值倾向于奖励在精确定位方面更好的模型。 在下表中您可以看到使用上述三个数据集评估的用于对象检测的最新深度学习算法其中显示了 PASCAL VOC 和 COCO 数据集上的 mAP 得分 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c0WxiLz1-1681870549420)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/276707d9-8a1f-4a33-ab02-dc5a62e4175e.png)] 下面包括有关用于对象检测的最新深度学习算法的介绍 R-CNN2014 描述基于区域的卷积网络R-CNN是使用卷积神经网络进行对象检测的首批方法之一表明与基于类似 HOG 的简单特征的系统相比卷积神经网络可以提高目标检测表现。 该算法可以分解为以下三个步骤 创建一组区域提议对每个区域提议执行经过修订版的 AlexNet 的前向传递以提取特征向量潜在的对象通过几个 SVM 分类器进行检测此外线性回归器会更改边界框的坐标 参考Girshick, R., Donahue, J., Darrell, T., and Malik, J. (2014). Rich feature hierarchies for accurate object detection and semantic segmentation. In Proceedings of the IEEE conference on computer vision and pattern recognition (pp. 580-587). Fast R-CNN2015 描述基于快速区域的卷积网络Fast R-CNN方法是对先前方法的一种改进可以有效地对目标提议进行分类。 此外Fast R-CNN 采用了多项创新技术来提高训练和测试速度同时还提高了检测精度。参考Girshick, R. (2015). Fast r-cnn. In Proceedings of the IEEE international conference on computer vision and pattern recognition (pp. 1440-1448). Faster R-CNN2015 说明更快的 R-CNN 是对快速 R-CNN 的修改引入了一个区域提议网络RPN该网络与检测网络共享全图像卷积特征从而实现几乎免费的区域提议。参考Ren, S., He, K., Girshick, R., and Sun, J. (2015). Faster R-CNN – Towards real-time object detection with region proposal networks. In Advances in neural information processing systems (pp. 91-99). R-FCN2016 描述基于区域的全卷积网络R-FCN是仅包含卷积层的框架允许进行完整的反向传播以进行训练和推理从而获得准确而有效的对象检测。参考Dai, J., Li, Y., He, K., and Sun, J. (2016). R-FCN: Object Detection via Region-based Fully Convolutional Networks. In Advances in neural information processing systems (pp. 379-387). YOLO2016 描述只看一次YOLO是一种深度学习架构可在单个步骤中预测边界框和类概率。 与其他深度学习检测器相比YOLO 会产生更多的定位错误但是在背景中预测假正例的可能性较小。参考Redmon, J., Divvala, S., Girshick, R., and Farhadi, A. (2016). You only look once: Unified, Real-Time Object Detection. SSD2016 描述单发多盒检测器SSD是一个深层神经网络旨在通过端到端卷积神经网络架构的方法同时预测边界框和类概率。参考Liu, W., Anguelov, D., Erhan, D., Szegedy, C., Reed, S., Fu, C. Y., and Berg, A. C. (2016, October). SSD: Single Shot Multibox Detector. In European conference on Computer Vision (pp. 21-37). Springer, Cham. YOLO V22016 描述作者在同一出版物中介绍了 YOLO9000 和 YOLO V2。 YOLO9000 是一种实时对象检测系统可以检测 9,000 多个对象类别而 YOLO V2 是 YOLO 的改进版本致力于在提高准确率的同时仍是快速检测器。参考Redmon, J., and Farhadi, A. (2017). YOLO9000: Better, Faster, Stronger. arXiv preprint. NASNet2016 描述作者介绍了一种神经网络搜索这是使用循环神经网络构成神经网络架构的想法。 神经架构搜索网络NASNet包括学习模型的架构以优化层数同时还提高准确率。参考Zoph, B., and Le, Q. V. (2016). Neural Architecture Search with Reinforcement Learning. arXiv preprint arXiv:1611.01578. Mask R-CNN2017 描述基于遮罩区域的卷积网络遮罩 R-CNN是 Faster R-CNN 模型的另一个扩展它为边界框检测添加了并行分支目的是预测对象遮罩。 对象遮罩是按图像中的像素进行分割从而可以对对象实例进行分割。参考He, K., Gkioxari, G., Dollár, P., and Girshick, R. (2017, October). Mask r-cnn. In Computer Vision (ICCV), 2017 IEEE International Conference on Computer Vision (pp. 2980-2988). IEEE. OpenCV 中的深度学习 自 OpenCV 3.1 以来库中已有深层神经网络DNN模块可通过一些流行的深度学习框架进行预训练的深度网络实现前向传递推理 例如 CaffeTensorFlowTorch/PytorchDarknet 和 ONNX 格式的模型。 在 OpenCV 3.3 中该模块已从opencv_contrib存储库升级到主存储库并已进行了显着加速。 因此从 OpenCV 3.3 开始可以在我们的应用中使用经过预训练的网络进行预测并且在上一节中介绍的许多流行的网络架构都与 OpenCV 3.3 兼容。 在本节中我们将看到如何将这些架构中的一些应用于对象检测和图像分类但是在涵盖这一方面之前应先回顾一下 OpenCV 在 DNN 模块中提供的许多功能。 了解cv2.dnn.blobFromImage() 在第 11 章“人脸检测跟踪和识别”中我们看到了一些涉及深度学习计算的示例。 例如在face_detection_opencv_dnn.py脚本中使用了基于深度学习的面部检测器来检测图像中的人脸。 第一步是按以下方式加载预训练的模型 net cv2.dnn.readNetFromCaffe(deploy.prototxt, res10_300x300_ssd_iter_140000_fp16.caffemodel)提醒一下deploy.prototxt文件定义了模型架构res10_300x300_ssd_iter_140000_fp16.caffemodel文件包含了实际层的权重。 为了对整个网络执行前向传递以计算输出网络的输入应为 BLOB。 BLOB 可以看作是经过充分预处理以馈送到网络的图像集合。 此预处理由几个操作组成-调整大小裁剪减去平均值缩放以及交换蓝色和红色通道。 例如在上述面部检测示例中我们执行了以下命令 # Load image: image cv2.imread(test_face_detection.jpg)# Create 4-dimensional blob from image: blob cv2.dnn.blobFromImage(image, 1.0, (300, 300), [104., 117., 123.], False, False)在这种情况下这意味着我们要在调整为300 x 300的 BGR 图像上运行模型分别对蓝色绿色和红色通道应用(104, 117, 123)值的平均减法。 下表中对此进行了总结 | 模型 | 规模 | 尺寸WxH | 均值减法 | 通道顺序 | | — | — | — | — | | OpenCV 人脸检测器 | 1.0 |300 x 300| 104177123 | BGR | 此时我们可以将 BLOB 设置为输入并按以下方式获得检测结果 # Set the blob as input and obtain the detections: net.setInput(blob) detections net.forward()有关更多详细信息请参见face_detection_opencv_dnn.py脚本。 现在我们将详细了解cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()函数。 为此我们首先要看到两个函数的签名然后我们将看到blob_from_image.py和blob_from_images.py脚本。 这些脚本在理解这些函数时可能会有所帮助。 此外在这些脚本中我们还将使用 OpenCV cv2.dnn.imagesFromBlob()函数。 cv2.dnn.blobFromImage()的签名如下 retvalcv2.dnn.blobFromImage(image[, scalefactor[, size[, mean[, swapRB[, crop[, ddepth]]]]]])此函数从image创建一个二维 BLOB。 此外它还可以选择将图像调整为size大小并从中心裁剪输入图像减去mean值按scalefactor缩放值并交换蓝色和红色通道 image这是要预处理的输入图像。scalefactor这是image值的乘数。 此值可用于缩放我们的图像。 默认值为1.0这表示不执行缩放。size这是输出图像的空间大小。mean这是从图像中减去平均值的标量。 如果执行均值减法则在使用swapRB True时这些值应为mean-Rmean-Gmean-B。swapRB通过将该标志设置为True可以使用该标志交换图像中的R和B通道。crop这是一个标志指示在调整大小后是否将裁切图像。ddepth输出 BLOB 的深度。 您可以在CV_32F或CV_8U之间选择。如果为cropFalse则在不裁剪的情况下执行图像的调整大小。 否则如果cropTrue则首先应用调整大小然后从中心裁剪图像。默认值为scalefactor1.0size Size()mean Scalar()swapRB falsecrop false和ddepth CV_32F。 cv.dnn.blobFromImages()的签名如下 retvalcv.dnn.blobFromImages(images[, scalefactor[, size[, mean[, swapRB[, crop[, ddepth]]]]]])此函数从多个图像创建一个四维 BLOB。 这样您可以对整个网络执行前向传递以一次计算多个图像的输出。 以下代码显示了如何正确使用此函数 # Create a list of images: images [image, image2]# Call cv2.dnn.blobFromImages(): blob_images cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, False)# Set the blob as input and obtain the detections: net.setInput(blob_images) detections net.forward()至此我们介绍了cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()函数。 因此我们准备看blob_from_image.py和blob_from_images.py脚本。 在blob_from_image.py脚本中我们首先加载 BGR 图像然后使用cv2.dnn.blobFromImage()函数创建一个二维 BLOB。 您可以检查创建的 BLOB 的形状是否为(1, 3, 300, 300)。 然后我们调用get_image_from_blob()函数该函数可用于执行逆预处理转换以便再次获取输入图像。 这样您将更好地了解此预处理。 get_image_from_blob函数的代码如下 def get_image_from_blob(blob_img, scalefactor, dim, mean, swap_rb, mean_added):Returns image from blob assuming that the blob is from only one imageimages_from_blob cv2.dnn.imagesFromBlob(blob_img)image_from_blob np.reshape(images_from_blob[0], dim) / scalefactorimage_from_blob_mean np.uint8(image_from_blob)image_from_blob image_from_blob_mean np.uint8(mean)if mean_added is True:if swap_rb:image_from_blob image_from_blob[:, :, ::-1]return image_from_blobelse:if swap_rb:image_from_blob_mean image_from_blob_mean[:, :, ::-1]return image_from_blob_mean在脚本中我们利用此函数从 BLOB 获取不同的图像如以下代码片段所示 # Load image: image cv2.imread(face_test.jpg)# Call cv2.dnn.blobFromImage(): blob_image cv2.dnn.blobFromImage(image, 1.0, (300, 300), [104., 117., 123.], False, False)# The shape of the blob_image will be (1, 3, 300, 300): print(blob_image.shape)# Get different images from the blob: img_from_blob get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], False, True) img_from_blob_swap get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], True, True) img_from_blob_mean get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], False, False) img_from_blob_mean_swap get_image_from_blob(blob_image, 1.0, (300, 300, 3), [104., 117., 123.], True, False)创建的图像说明如下 img_from_blob图像对应于调整为(300,300)的原始 BGR 图像。img_from_blob_swap图像对应于调整为(300,300)尺寸的原始 BGR 图像并且蓝色和红色通道已交换。img_from_blob_mean图像对应于调整为(300,300)尺寸的原始 BGR 图像其中未将具有平均值的标量添加到图像中。img_from_blob_mean_swap图像对应于调整为(300,300)的原始 BGR 图像其中未将具有平均值的标量添加到该图像并且已交换了蓝色和红色通道。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jTWelmei-1681870549420)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/60a54618-3de9-4e3c-bcd1-965bd8e68eb1.png)] 在上一个屏幕截图中我们可以看到获得的四个图像img_from_blobimg_from_blob_swapimg_from_blob_mean和img_from_blob_mean_swap。 在blob_from_images.py脚本中我们首先加载两个 BGR 图像并使用cv2.dnn.blobFromImages()函数创建一个二维 BLOB。 您可以检查创建的 BLOB 的形状是否为(2, 3, 300, 300)。 然后我们调用get_images_from_blob()函数该函数可用于执行逆预处理转换以便再次获取输入图像。 get_images_from_blob函数的代码如下 def get_images_from_blob(blob_imgs, scalefactor, dim, mean, swap_rb, mean_added):Returns images from blobimages_from_blob cv2.dnn.imagesFromBlob(blob_imgs)imgs []for image_blob in images_from_blob:image_from_blob np.reshape(image_blob, dim) / scalefactorimage_from_blob_mean np.uint8(image_from_blob)image_from_blob image_from_blob_mean np.uint8(mean)if mean_added is True:if swap_rb:image_from_blob image_from_blob[:, :, ::-1]imgs.append(image_from_blob)else:if swap_rb:image_from_blob_mean image_from_blob_mean[:, :, ::-1]imgs.append(image_from_blob_mean)return imgs如前所示get_images_from_blob()函数使用 OpenCV cv2.dnn.imagesFromBlob()函数从 BLOB 返回图像。 在脚本中我们利用此函数从 BLOB 中获取不同的图像如下所示 # Load images and get the list of images: image cv2.imread(face_test.jpg) image2 cv2.imread(face_test_2.jpg) images [image, image2]# Call cv2.dnn.blobFromImages(): blob_images cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, False) # The shape of the blob_image will be (2, 3, 300, 300): print(blob_images.shape)# Get different images from the blob: imgs_from_blob get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], False, True) imgs_from_blob_swap get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], True, True) imgs_from_blob_mean get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], False, False) imgs_from_blob_mean_swap get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], True, False)在前面的代码中我们利用get_images_from_blob()函数从 BLOB 获取不同的图像。 创建的图像说明如下 imgs_from_blob图像对应于调整为(300,300)尺寸的原始 BGR 图像。imgs_from_blob_swap图像对应于调整为(300,300)尺寸的原始 BGR 图像并且蓝色和红色通道已交换。imgs_from_blob_mean图像对应于调整为(300,300)尺寸的原始 BGR 图像其中带有平均值的标量尚未添加到图像。imgs_from_blob_mean_swap图像对应于调整为(300,300)的原始 BGR 图像其中未将具有平均值的标量添加到图像中并且蓝色和红色通道已交换。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5k4cWWed-1681870549420)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/323d11a7-ef39-416d-a935-52a1aa4702f7.png)] cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()的最后一个考虑因素是crop参数该参数指示是否裁切图像。 在裁剪的情况下图像将从中心裁剪如以下屏幕截图所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tqHnqaTA-1681870549421)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/4d786113-f07b-412a-8235-5bf0d16a03a5.png)] 如您所见裁剪是从图像的中心进行的由黄线表示。 为了复制 OpenCV 在cv2.dnn.blobFromImage()和cv2.dnn.blobFromImages()函数内部执行的裁剪我们对get_cropped_img()函数进行了如下编码 def get_cropped_img(img):Returns the cropped image# calculate size of resulting image:size min(img.shape[1], img.shape[0])# calculate x1, and y1x1 int(0.5 * (img.shape[1] - size))y1 int(0.5 * (img.shape[0] - size))# crop and return the imagereturn img[y1:(y1 size), x1:(x1 size)]如您所见裁剪图像的大小将基于原始图像的最小尺寸。 因此在前面的示例中裁剪后的图像将具有(482, 482)的大小。 在blob_from_images_cropping.py脚本中我们看到了裁剪的效果并且还在get_cropped_img()函数中复制了裁剪过程 # Load images and get the list of images: image cv2.imread(face_test.jpg) image2 cv2.imread(face_test_2.jpg) images [image, image2]# To see how cropping works, we are going to perform the cropping formulation that # both blobFromImage() and blobFromImages() perform applying it to one of the input images: cropped_img get_cropped_img(image) # cv2.imwrite(cropped_img.jpg, cropped_img)# Call cv2.dnn.blobFromImages(): blob_images cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, False) blob_blob_images_cropped cv2.dnn.blobFromImages(images, 1.0, (300, 300), [104., 117., 123.], False, True)# Get different images from the blob: imgs_from_blob get_images_from_blob(blob_images, 1.0, (300, 300, 3), [104., 117., 123.], False, True) imgs_from_blob_cropped get_images_from_blob(blob_blob_images_cropped, 1.0, (300, 300, 3), [104., 117., 123.], False, True)在以下屏幕截图中可以看到blob_from_images_cropping.py脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rGoeqIQW-1681870549421)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/ccca028d-e5b7-4a5b-abfa-e5d1487bb06d.png)] 可以看到在两个加载的图像中裁剪的效果并且我们还可以理解保持了宽高比。 OpenCV DNN 人脸检测器的完整示例 接下来我们将看到如何修改face_detection_opencv_dnn.py脚本摘自第 11 章“人脸检测跟踪和识别”以便执行以下操作 当几张图像可能具有不同的大小馈送到网络时计算输出– face_detection_opencv_cnn_images.py脚本当cv2.dnn.blobFromImages()函数- face_detection_opencv_cnn_images_crop.py脚本中的cropTrue参数馈入网络时将几张图像可能具有不同的尺寸馈送到网络时计算输出 以下屏幕快照显示了face_detection_opencv_cnn_images.py脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-R04V217z-1681870549421)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/053d9827-6290-4caf-aec5-c3895160ace8.png)] 以下屏幕快照显示了face_detection_opencv_cnn_images_crop.py脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7GGUxUcO-1681870549421)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/d2bfb9de-0435-4583-a77b-092643fc5c0d.png)] 在上一个屏幕截图中您可以清楚地看到从中心裁剪图像时的区别。 OpenCV 深度学习分类 本节将介绍如何使用不同的预训练模型进行图像分类的几个示例。 请注意您可以通过使用net.getPerfProfile()方法获得推断时间如下所示 # Feed the input blob to the network, perform inference and get the output: net.setInput(blob) preds net.forward()# Get inference time: t, _ net.getPerfProfile() print(Inference time: %.2f ms % (t * 1000.0 / cv2.getTickFrequency()))如您所见在执行推断后将调用net.getPerfProfile()方法。 net.getPerfProfile()方法返回推理的总时间和层的计时以滴答为单位。 这样您可以使用不同的深度学习架构比较推理时间。 我们将从下一部分介绍的 AlexNet 架构开始了解主要的深度学习分类架构。 用于图像分类的 AlexNet 在image_classification_opencv_alexnet_caffe.py脚本中通过使用 AlexNet 和 Caffe 预训练模型使用 OpenCV DNN 模块进行图像分类。 第一步是加载类的名称。 第二步是从磁盘加载序列化的 Caffe 模型。 第三步是加载输入图像进行分类。 第四步是创建大小为(227, 2327)和(104, 117, 123)平均减法值的 BLOB。 第五步是将输入 BLOB 馈送到网络执行推理并获得输出。 第六步是获得概率最高降序排列的 10 个索引。 这样具有最高概率最高预测的索引将是第一个。 最后我们将在图像上绘制与最高预测相关的类和概率。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LjFKI0je-1681870549421)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/0e19379e-441b-4bfa-b7f9-ae57b01ed713.png)] 如前面的屏幕快照所示最高的预测对应于教堂的概率为 0.8325679898。 十大预测如下 1\. label: church, probability: 0.83256798982\. label: monastery, probability: 0.0436783883\. label: mosque, probability: 0.038279615344\. label: bell cote, probability: 0.024794898935\. label: beacon, probability: 0.012496204126\. label: dome, probability: 0.012230500587\. label: missile, probability: 0.0063239200978\. label: projectile, probability: 0.0052756355149\. label: palace, probability: 0.00428972067310\. label: castle, probability: 0.003241452388 还应注意在绘制类别和概率时我们执行以下操作 text label: {} probability: {:.2f}%.format(classes[indexes[0]], preds[0][indexes[0]] * 100) print(text) y0, dy 30, 30 for i, line in enumerate(text.split(\n)):y y0 i * dycv2.putText(image, line, (5, y), cv2.FONT_HERSHEY_SIMPLEX, 0.8, (0, 255, 255), 2)这样可以将文本拆分并在图像的不同行中绘制。 例如如果我们执行以下代码则文本将分两行绘制 text label: {}\nprobability: {:.2f}%.format(classes[indexes[0]], preds[0][indexes[0]] * 100)应当注意bvlc_alexnet.caffemodel文件未包含在本书的存储库中因为它超过了 GitHub 的文件大小限制 100.00 MB。 您必须从这里下载。 因此您必须在运行脚本之前下载bvlc_alexnet.caffemodel文件。 用于图像分类的 GoogLeNet 以与先前脚本类似的方式在image_classification_opencv_googlenet_caffe.py脚本中使用 GoogLeNet 和 Caffe 预训练模型使用 OpenCV CNN 模块进行图像分类。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pyBaNBzW-1681870549422)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/89583a6f-1a18-42eb-94df-622dd59fc7a0.png)] 如前面的屏幕快照所示最高的预测对应于一座教堂其概率为 0.9082632661。 十大预测如下 1\. label: church, probability: 0.90826326612\. label: bell cote, probability: 0.063509054483\. label: monastery, probability: 0.020469238984\. label: dome, probability: 0.0026247918145\. label: mosque, probability: 0.0010775009876\. label: fountain, probability: 0.0010114753397\. label: palace, probability: 0.00077509920818\. label: castle, probability: 0.00023492144839\. label: pedestal, probability: 0.000230657067710\. label: analog clock, probability: 0.0002107089822 用于图像分类的 ResNet 用于图像分类的 ResNet 脚本image_classification_opencv_restnet_50_caffe.py将使用带有 Caffe 预训练模型的 ResNet-50 进行图像分类。 在以下屏幕截图中可以看到输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8QrF8lny-1681870549422)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/c0c5f278-fa72-4409-8340-d85a458acb21.png)] 如前面的屏幕快照所示最高的预测对应于一座教堂的概率为 0.9955400825。 十大预测如下 1\. label: church, probability: 0.99554008252\. label: dome, probability: 0.0024299002253\. label: bell cote, probability: 0.00074244232384\. label: monastery, probability: 0.00037683139095\. label: picket fence, probability: 0.00032825497336\. label: mosque, probability: 0.0002583182657\. label: mobile home, probability: 0.00010836070588\. label: stupa, probability: 2.96174203e-059\. label: palace, probability: 2.621001659e-0510\. label: beacon, probability: 2.02897063e-05 用于图像分类的 SqueezeNet 在image_classification_opencv_squeezenet_caffe.py脚本中我们使用 SqueezeNet 架构执行图像分类该架构可将 AlexNet 级别的精度降低 50 倍。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eaWCu5Q2-1681870549422)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/fa426dba-88e1-4c61-a2b9-d58841e0b756.png)] 如前面的屏幕快照所示最高的预测对应于一座教堂的概率为 0.9967952371。 在此脚本中我们使用的是 SqueezeNet v1.1其计算量比 v1.0 少 2.4 倍但又不牺牲任何准确率。 十大预测如下 1\. label: church, probability: 0.99679523712\. label: monastery, probability: 0.0018990797693\. label: bell cote, probability: 0.00069247663494\. label: mosque, probability: 0.00026161412825\. label: dome, probability: 0.00018915272086\. label: palace, probability: 0.00010469520937\. label: stupa, probability: 8.239243471e-068\. label: vault, probability: 7.135886335e-069\. label: triumphal arch, probability: 6.732503152e-0610\. label: cinema, probability: 4.201304819e-06 OpenCV 深度学习对象检测 本节将介绍如何使用不同的预训练模型执行对象检测的几个示例。 对象检测尝试检测图像或视频中预定义类例如猫汽车和人的语义对象实例。 用于对象检测的 MobileNet-SSD 我们将结合使用 MobileNet 架构和 SSD 框架。 MobileNets 可以看作是用于移动视觉应用的高效卷积神经网络。 MobileNet-SSD 在 COCO 数据集上进行了训练并在 PASCAL VOC 上进行了微调达到了 72.27% 的 mAP请参阅汇总 mAP 的表格以了解对象检测算法以将该指标置于上下文中。 在 PASCAL VOC 上进行微调时可以检测到 20 个对象类如下所示 人人动物鸟猫牛狗马和羊车辆飞机自行车轮船公共汽车汽车摩托车和火车室内瓶子椅子餐桌盆栽沙发和电视/显示器 在object_detection_opencv_mobilenet_caffe.py脚本中我们使用 OpenCV DNN 模块通过使用 MobileNet-SSD 和 Caffe 预训练模型来执行对象检测。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-q7RoohAR-1681870549422)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/15ba0e5f-13b0-431f-94d9-fa672e3ef959.png)] 如上一个屏幕截图所示所有对象都可以高精度正确检测。 用于对象检测的 YOLO 在object_detection_opencv_yolo_darknet.py脚本中使用 YOLO v3 进行对象检测。 YOLO v3 使用了一些技巧来改善训练并提高表现其中包括多尺度预测和更好的主干分类器。 在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-NIGtQ5Cx-1681870549422)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/0a666017-27ad-4445-836d-568bd2ac7f0c.png)] 如上一个屏幕截图所示可以高度准确地检测到除一个绵羊以外的所有物体。 因此您必须在运行脚本之前下载yolov3.weights文件。 应当注意yolov3.weights文件未包含在本书的存储库中因为它超过了 GitHub 的文件大小限制 100.00 MB。 您必须从这里下载。 TensorFlow 库 TensorFlow 是 Google Brain 团队为内部使用而开发的用于机器学习和深度学习的开源软件平台。 随后TensorFlow 于 2015 年在 Apache 许可下发布。在本节中我们将看到一些示例以向您介绍 TensorFlow 库。 TensorFlow 的介绍示例 TensorFlow 库通过将操作链接到计算图中来表示要执行的计算。 创建此计算图后您可以打开 TensorFlow 会话并执行该计算图以获取结果。 可以在tensorflow_basic_op.py脚本中看到此过程该脚本执行在计算图中定义的乘法运算如下所示 # path to the folder that we want to save the logs for Tensorboard logs_path ./logs# Define placeholders: X_1 tf.placeholder(tf.int16, nameX_1) X_2 tf.placeholder(tf.int16, nameX_2)# Define a multiplication operation: multiply tf.multiply(X_1, X_2, namemy_multiplication)在会话中运行图时将提供占位符的值如以下代码片段所示 # Start the session and run the operation with different inputs: with tf.Session() as sess:summary_writer tf.summary.FileWriter(logs_path, sess.graph)print(2 x 3 {}.format(sess.run(multiply, feed_dict{X_1: 2, X_2: 3})))print([2, 3] x [3, 4] {}.format(sess.run(multiply, feed_dict{X_1: [2, 3], X_2: [3, 4]})))如您所见计算图已参数化以访问外部输入称为占位符。 在同一会话中我们将使用不同的输入执行两次乘法。 由于计算图是 TensorFlow 的关键点因此图的可视化可以帮助您使用 TensorBoard 来理解和调试图TensorBoard 是任何标准 TensorFlow 安装随附的可视化软件。 要使用 TensorBoard 可视化计算图您需要使用tf.summary.FileWriter()编写程序的日志文件如前所示。 如果执行此脚本则会在执行该脚本的相同位置创建logs目录。 要运行 TensorBoard您应该执行以下代码 $ tensorboard --logdir./logs这将生成一个链接http://localhost:6006/供您在浏览器中输入您将看到 TensorBoard 页面该页面可以在以下屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HNTtwnpT-1681870549423)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/1c45b552-eeb7-46bd-bad0-6b0fde503a29.png)] 您可以看到先前脚本的计算图。 另外由于 TensorFlow 图可以具有成千上万个节点因此可以创建范围以简化可视化并且 TensorBoard 使用此信息来定义图中节点的层次结构。 这个想法显示在tensorflow_basic_ops_scope.py脚本中其中我们在Operations范围内定义了两个操作加法和乘法如下所示 with tf.name_scope(Operations):addition tf.add(X_1, X_2, namemy_addition)multiply tf.multiply(X_1, X_2, namemy_multiplication)如果执行脚本并重复前面的步骤则可以在以下屏幕截图中看到 TensorBoard 中显示的计算图 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Tg1gLa1h-1681870549423)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/d750ac7b-0496-4ba1-8c27-e6d6da46eef5.png)] 请注意您还可以在脚本中使用常量tf.Constant和变量tf.Variable。 tf.Variable和tf.placeholder之间的差异在于传递值的时间。 如您在前面的示例中所看到的使用tf.placeholder不必提供初始值并且这些值在运行时使用会话内的feed_dict参数指定。 另一方面如果使用tf.Variable变量则在声明它时必须提供一个初始值。 占位符只是一个变量之后将向其分配数据。 在训练/测试算法时通常使用占位符将训练/测试数据输入到计算图中。 为了简化起见我们不会在下一个脚本中显示已创建的计算图但是推荐使用 TensorBoard 来可视化计算图的方法因为这将有助于您理解以及验证执行哪些计算。 TensorFlow 中的线性回归 在接下来的示例中我们将使用 TensorFlow 执行线性回归以帮助您了解训练和测试深度学习算法时所需的其他概念。 更具体地说我们将看到三个脚本。 在每个脚本中我们将涵盖以下主题 tensorflow_linear_regression_training.py此脚本生成线性回归模型。tensorflow_linear_regression_testing.py此脚本加载创建的模型并使用它进行新的预测。tensorflow_save_and_load_using_model_builder.py此脚本加载创建的模型并使用SavedModelBuilder()导出模型以进行推断。 此外此脚本还加载最终模型以做出新的预测。 线性回归是一种非常普遍的统计方法它使我们能够根据给定的二维样本点集对关系进行建模。 在这种情况下模型函数如下 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-GqM9lwa2-1681870549423)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/fa7cc8a7-9590-45be-a124-12357c83b63a.png)] 这描述了具有W斜率和y-截距b的线。 因此目标是找到W和b参数的值这些值将在某种意义上为二维采样点提供最佳拟合例如最小化均方误差。 在训练线性回归模型请参阅tensorflow_linear_regression_training.py时第一步是生成一些数据用于训练算法如下所示 x np.linspace(0, N, N) y 3 * np.linspace(0, N, N) np.random.uniform(-10, 10, N)下一步是定义占位符以便在训练过程中将我们的训练数据输入到优化器中如下所示 X tf.placeholder(float, nameX) Y tf.placeholder(float, nameY)此时我们为权重和偏差声明两个变量随机初始化如下所示 W tf.Variable(np.random.randn(), nameW) b tf.Variable(np.random.randn(), nameb)下一步是构建线性模型如下所示 y_model tf.add(tf.multiply(X, W), b, namey_model)我们还定义了成本函数。 在这种情况下我们将使用均方误差cost函数如以下代码片段所示 cost tf.reduce_sum(tf.pow(y_model - Y, 2)) / (2 * N)现在我们创建梯度下降优化器以最小化cost函数修改W和b变量的值。 传统的优化器称为梯度下降旨在查找函数最小值的迭代优化算法如下所示 optimizer tf.train.GradientDescentOptimizer(learning_rate).minimize(cost)学习率参数控制每次梯度下降算法更新时系数的变化量。 如前所述梯度下降是一种迭代优化算法因此在每次迭代中根据学习率参数修改参数。 创建模型的最后一步是执行变量的初始化如下所示 init tf.global_variables_initializer()此时我们可以在一个会话中开始训练过程如以下代码片段所示 # Start the training procedure inside a TensorFlow Session: with tf.Session() as sess:# Run the initializer:sess.run(init)# Uncomment if you want to see the created graph# summary_writer tf.summary.FileWriter(logs_path, sess.graph)# Iterate over all defined epochs:for epoch in range(training_epochs):# Feed each training data point into the optimizer:for (_x, _y) in zip(x, y):sess.run(optimizer, feed_dict{X: _x, Y: _y})# Display the results every display_step epochs:if (epoch 1) % disp_step 0:# Calculate the actual cost, W and b:c sess.run(cost, feed_dict{X: x, Y: y})w_est sess.run(W)b_est sess.run(b)print(Epoch, (epoch 1), : cost , c, W , w_est, b , b_est)# Save the final modelsaver.save(sess, ./linear_regression)# Storing necessary values to be used outside the sessiontraining_cost sess.run(cost, feed_dict{X: x, Y: y})weight sess.run(W)bias sess.run(b)print(Training finished!)如前面的代码片段所示一旦会话开始我们将运行初始化器然后对所有定义的时期进行迭代以训练线性回归模型。 此外我们为每个display_step周期打印结果。 最后训练完成后我们将保存最终模型。 至此训练结束我们可以显示结果了可以在以下屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1kfxdYkx-1681870549423)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/b6128011-c18f-4685-996e-42a2279ff6d7.png)] 在上图中我们可以看到训练数据左和与线性回归模型相对应的拟合线右。 保存最终模型saver.save(sess, ./linear_regression)时将创建四个文件 .meta文件包含 TensorFlow 图.data文件包含权重偏差梯度和所有其他已保存变量的值.index文件标识检查点checkpoint文件记录保存的最新检查点文件 此时我们可以加载预训练的模型并将其用于预测目的。 这在tensorflow_linear_regression_testing.py脚本中执行。 加载模型时要做的第一件事是从.meta文件中加载图如下所示 tf.reset_default_graph() imported_meta tf.train.import_meta_graph(linear_regression.meta)第二步是加载变量的值请注意这些值仅在会话中存在。 我们还运行模型以获取Wb的值和新的预测值如下所示 with tf.Session() as sess:imported_meta.restore(sess, ./linear_regression)# Run the model to get the values of the variables W, b and new prediction values:W_estimated sess.run(W:0)b_estimated sess.run(b:0)new_predictions sess.run([y_model:0], {X:0: new_x})此时我们可以显示训练数据回归线和新获得的预测可以在以下屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-setqJRLM-1681870549423)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/2ea35e61-e003-42ef-abea-5d49ffe9eb68.png)] 如上一个屏幕截图所示我们使用了预先训练的模型来进行新的预测蓝点。 但是在生产中提供模型时我们只希望将模型及其权重很好地打包在一个文件中以方便存储版本控制和更新不同模型。 结果将是一个扩展名为.pb的二进制文件其中包含受训网络的拓扑和权重。 在tensorflow_save_and_load_using_model_builder.py脚本中执行创建此二进制文件的过程以及如何将其用于推理。 在此脚本中我们对export_model()函数进行了编码以使用SaveModel导出训练后的模型如下所示 def export_model():Exports the modeltrained_checkpoint_prefix linear_regressionloaded_graph tf.Graph()with tf.Session(graphloaded_graph) as sess:sess.run(tf.global_variables_initializer())# Restore from checkpointloader tf.train.import_meta_graph(trained_checkpoint_prefix .meta)loader.restore(sess, trained_checkpoint_prefix)# Add signature:...signature_map {signature_constants.DEFAULT_SERVING_SIGNATURE_DEF_KEY: signature}# Export model:builder tf.saved_model.builder.SavedModelBuilder(./my_model)builder.add_meta_graph_and_variables(sess, signature_def_mapsignature_map,tags[tf.saved_model.tag_constants.SERVING])builder.save()这将在my_model文件夹内创建saved_model.pb。 在这一点上为了验证是否正确生成了导出的模型我们可以同时导入和使用它以便进行新的预测如下所示 with tf.Session(graphtf.Graph()) as sess:tf.saved_model.loader.load(sess, [tf.saved_model.tag_constants.SERVING], ./my_model)graph tf.get_default_graph()x graph.get_tensor_by_name(X:0)model graph.get_tensor_by_name(y_model:0)print(sess.run(model, {x: new_x}))调用load函数后该图将作为默认图被加载。 此外变量也已加载因此您可以开始对任何新数据运行推理。 这将输出[153.04472 166.54755 180.05037]数组它对应于我们的模型生成的预测值。 使用 TensorFlow 的手写数字识别 在此示例中我们将使用 TensorFlow 对图像进行分类。 更具体地说我们将创建一个简单的模型softmax 回归模型用于使用 MNIST 数据集学习和预测图像中的手写数字。 Softmax 回归是可用于多类分类的逻辑回归的概括。 MNIST 数据集包含各种手写的数字图像 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qtvUQCyV-1681870549424)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/1d2b0d6a-6ef9-45c8-b350-baf6e49a2747.png)] mnist_tensorflow_save_model.py脚本创建用于学习和预测图像中手写数字的模型。 主要步骤如下所示。 您可以使用以下代码自动导入此数据集 from tensorflow.examples.tutorials.mnist import input_data data input_data.read_data_sets(MNIST/, one_hotTrue)下载的数据集由三部分组成-55,000 行mnist.train训练数据10,000 行mnist.test测试数据和 5,000 行mnist.validation验证数据。 此外训练测试和验证部分还为每个数字包含相应的标签。 例如训练数据由mnist.train.images训练数据集图像和mnist.train.labels训练数据集标签组成。 每个图像由28 x 28像素组成从而形成784元素数组。 one_hotTrue选项意味着标签将以这样的方式表示特定数字的1只有一位。 例如对于9相应的标签将为[0 0 0 0 0 0 0 0 0 1]。 这项技术称为单热编码这意味着标签已从单个数字转换为向量向量的长度等于可能的类数。 这样除了i元素其值将是与i类相对应的1之外向量的所有元素都将设置为零。 在定义占位符时我们需要匹配其形状和类型以便将数据输入以下变量 x tf.placeholder(tf.float32, shape[None, 784], namemyInput) y tf.placeholder(tf.float32, shape[None, 10], nameY)当我们将None分配给占位符时这意味着可以根据需要为该占位符提供尽可能多的示例。 在这种情况下x占位符可以输入任何 784 维向量。 因此该张量的形状为[None, 784 ]。 此外我们还创建了y占位符以提供真实标签。 在这种情况下该张量的形状将为[None, 10]。 至此我们可以开始构建计算图了。 第一步是如下创建W和b变量 W tf.Variable(tf.zeros([784, 10])) b tf.Variable(tf.zeros([10]))创建W和b变量并将它们初始化为零因为 TensorFlow 会在训练时优化这些值。 W的尺寸为[784, 10]因为我们想将其乘以与某个图像表示形式相对应的 784 维数组以获得 10 维输出向量。 现在我们可以按以下方式实现我们的模型 output_logits tf.matmul(x, W) b y_pred tf.nn.softmax(output_logits, namemyOutput)tf.matmul()用于矩阵乘法tf.nn.softmax()用于将softmax函数应用于输入张量这意味着输出已归一化并且可以解释为概率。 在这一点上我们可以定义损失函数即创建优化器在本例中为AdamOptimizer模型的准确率如下 # Define the loss function, optimizer, and accuracy loss tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits_v2(labelsy, logitsoutput_logits), nameloss) optimizer tf.train.AdamOptimizer(learning_ratelearning_rate, nameAdam-op).minimize(loss) correct_prediction tf.equal(tf.argmax(output_logits, 1), tf.argmax(y, 1), namecorrect_pred) accuracy tf.reduce_mean(tf.cast(correct_prediction, tf.float32), nameaccuracy)最后我们可以训练模型并使用mnist.validation验证数据对其进行验证并按如下方式保存模型 with tf.Session() as sess:sess.run(tf.global_variables_initializer())for i in range(num_steps):# Get a batch of training examples and their corresponding labels.x_batch, y_true_batch data.train.next_batch(batch_size)# Put the batch into a dict to be fed into the placeholdersfeed_dict_train {x: x_batch, y: y_true_batch}sess.run(optimizer, feed_dictfeed_dict_train)# Validation:feed_dict_validation {x: data.validation.images, y: data.validation.labels}loss_test, acc_test sess.run([loss, accuracy], feed_dictfeed_dict_validation)print(Validation loss: {}, Validation accuracy: {}.format(loss_test, acc_test))# Save model:saved_path_model saver.save(sess, ./softmax_regression_model_mnist)print(Model has been saved in {}.format(saved_path_model))保存模型后我们可以使用它来识别图像中的手写数字。 在mnist_save_and_load_model_builder.py脚本中我们将在my_model文件夹中创建saved_model.pb并使用该模型对使用 OpenCV 加载图像进行新的预测。 为了保存模型我们使用了上一节介绍的export_model()函数。 为了做出新的预测我们使用以下代码 # Load some test images: test_digit_0 load_digit(digit_0.png) test_digit_1 load_digit(digit_1.png) test_digit_2 load_digit(digit_2.png) test_digit_3 load_digit(digit_3.png)with tf.Session(graphtf.Graph()) as sess:tf.saved_model.loader.load(sess, [tf.saved_model.tag_constants.SERVING], ./my_model)graph tf.get_default_graph()x graph.get_tensor_by_name(myInput:0)model graph.get_tensor_by_name(myOutput:0)output sess.run(model, {x: [test_digit_0, test_digit_1, test_digit_2, test_digit_3]})print(predicted labels: {}.format(np.argmax(output, axis1)))在此test_digit_0test_digit_1test_digit_2和test_digit_3是四个加载的图像每个图像包含一个数字。 要加载每个图像我们使用load_digit()函数如下所示 def load_digit(image_name):Loads a digit and pre-process in order to have the proper formatgray cv2.imread(image_name, cv2.IMREAD_GRAYSCALE)gray cv2.resize(gray, (28, 28))flatten gray.flatten() / 255.0return flatten如您所见我们必须对每个图像进行预处理以具有与 MNIST 数据库图像的格式相对应的正确格式。 如果执行此脚本则将为每个图像获得以下预测类 predicted labels: [0 1 2 3]Keras 库 Keras 是用 Python 编写的开放源代码高级神经网络 API与 Python 2.7-3.6 兼容。 它能够在 TensorFlowMicrosoft Cognitive ToolkitTheano 或 PlaidML 之上运行并且其开发重点是实现快速实验。 在本节中我们将看到两个示例。 在第一个示例中我们将看到如何使用与上一节中的 TensorFlow 示例相同的输入数据来解决线性回归问题。 在第二个示例中我们将使用 MNIST 数据集对一些手写数字进行分类就像在上一节中使用 TensorFlow 进行的操作一样。 这样当解决相同类型的问题时您可以清楚地看到两个库之间的差异。 Keras 中的线性回归 linear_regression_keras_training.py数据集执行线性回归模型的训练。 第一步是创建用于训练/测试算法的数据如下所示 # Generate random data composed by 50 (N 50) points: x np.linspace(0, N, N) y 3 * np.linspace(0, N, N) np.random.uniform(-10, 10, N)下一步是创建模型。 为此我们创建了create_model()函数如以下代码片段所示 def create_model():Create the model using Sequencial model# Create a sequential model:model Sequential()# All we need is a single connection so we use a Dense layer with linear activation:model.add(Dense(input_dim1, units1, activationlinear, kernel_initializeruniform))# Compile the model defining mean squared error(mse) as the lossmodel.compile(optimizerAdam(lr0.1), lossmse)# Return the created modelreturn model使用 Keras 时最简单的模型类型是Sequential模型该模型可以看作是线性的层栈并且在此示例中用于创建模型。 此外对于更复杂的架构可以使用 Keras 函数式 API该 API 允许构建任意的层图。 因此使用Sequential模型我们通过使用model.add()方法堆叠层来构建模型。 在此示例中我们使用具有线性激活函数的单个密集或全连接层。 接下来我们可以编译或配置将均方误差MSE定义为损失的模型。 在这种情况下将使用Adam优化器并设置学习率0.1。 此时我们现在可以使用model.fit()方法训练提供数据的模型如下所示 linear_reg_model.fit(x, y, epochs100, validation_split0.2, verbose1)训练后我们可以获得w和b的值学习的参数这些值将用于计算预测如下所示 w_final, b_final get_weights(linear_reg_model)get_weights()函数返回这些参数的值如下所示 def get_weights(model):Get weights of w and bw model.get_weights()[0][0][0]b model.get_weights()[1][0]return w, b在这一点上我们可以建立以下预测 # Calculate the predictions: predictions w_final * x b_final我们还可以如下保存模型 linear_reg_model.save_weights(my_model.h5)在以下屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kN6uNOru-1681870549424)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/ddf59653-c58c-407e-9823-d584607a3337.png)] 如前面的屏幕快照所示我们既可以看到训练数据在左侧也可以看到与线性回归模型相对应的拟合线在右侧。 我们可以加载预训练的模型进行预测。 可以在linear_regression_keras_testing.py脚本中看到此示例。 第一步是按以下方式加载权重 linear_reg_model.load_weights(my_model.h5)使用get_weights()函数我们可以获得如下学习的参数 m_final, b_final get_weights(linear_reg_model)此时我们获得了训练数据的以下预测并且还获得了新的预测 predictions linear_reg_model.predict(x) new_predictions linear_reg_model.predict(new_x)最后一步是显示获得的结果可以在以下屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KprDgC9d-1681870549424)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/a93deb66-88b8-4d49-bf65-b720c7ad3c64.png)] 如上一个屏幕截图所示我们使用了预先训练的模型来进行新的预测蓝点。 Keras 中的手写数字识别 在此示例中我们将看到如何使用 Keras 识别手写数字。 mnist_keras_training.py脚本使用四层神经网络创建模型如以下代码片段所示 def create_model():Create the model using Sequencial model# Create a sequential model (a simple NN is created) adding a softmax activation at the end with 10 units:model Sequential()model.add(Dense(units128, activationrelu, input_shape(784,)))model.add(Dense(units128, activationrelu))model.add(Dense(units128, activationrelu))model.add(Dense(units10, activationsoftmax))# Compile the model using the loss function categorical_crossentropy and Stocastic Gradient Descent optimizer:model.compile(optimizerSGD(0.001), losscategorical_crossentropy, metrics[accuracy])# Return the created modelreturn model在这种情况下我们使用categorical_crossentropy损失函数该损失函数非常适合比较两个概率分布并使用随机梯度下降SGD优化器。 要加载 MNIST 数据我们必须使用以下代码 (train_x, train_y), (test_x, test_y) mnist.load_data()此外我们必须重新调整加载的数据的形状以使其具有适当的形状如下所示 train_x train_x.reshape(60000, 784) test_x test_x.reshape(10000, 784) train_y keras.utils.to_categorical(train_y, 10) test_y keras.utils.to_categorical(test_y, 10)此时我们可以创建模型训练模型保存创建的模型并获得评估测试数据时获得的准确率如下所示 # Create the model: model create_model()# Use the created model for training: model.fit(train_x, train_y, batch_size32, epochs10, verbose1)# Save the created model: model.save(mnist-model.h5)# Get the accuracy when testing: accuracy model.evaluate(xtest_x, ytest_y, batch_size32)# Show the accuracy: print(Accuracy: , accuracy[1])此时我们准备使用预先训练的模型来预测图像中的新手写数字。 这是在mnist_keras_predicting.py脚本中执行的如下所示 # Note: Images should have black background: def load_digit(image_name):Loads a digit and pre-process in order to have the proper formatgray cv2.imread(image_name, cv2.IMREAD_GRAYSCALE)gray cv2.resize(gray, (28, 28))gray gray.reshape((1, 784))return gray# Create the model: model create_model()# Load parameters of the model from the saved mode file: model.load_weights(mnist-model.h5)# Load some test images: test_digit_0 load_digit(digit_0.png) test_digit_1 load_digit(digit_1.png) test_digit_2 load_digit(digit_2.png) test_digit_3 load_digit(digit_3.png) imgs np.array([test_digit_0, test_digit_1, test_digit_2, test_digit_3]) imgs imgs.reshape(4, 784)# Predict the class of the loaded images prediction_class model.predict_classes(imgs)# Print the predicted classes: print(Class: , prediction_class) 如您所见我们已经加载了四个图像并且使用了经过训练的模型来预测这些图像的类别。 获得的输出如下 Class: [0 1 2 3]总结 在本章中我们使用一些流行的库包括 OpenCVTensorFlow 和 Keras对深度学习进行了介绍。 在本章的第一部分我们概述了用于图像分类和对象检测的最新深度学习架构。 在第二部分中我们研究了 OpenCV 中的深度学习模块这些模块提供了 DNN 库该库通过使用一些流行的深度学习框架进行了预训练的深度网络来实现前向传递推理。 因此从 OpenCV 3.3 开始可以在我们的应用中使用经过预训练的网络进行预测。 在本章的后面我们对 TensorFlow 进行了介绍最后对 Keras 进行了介绍。 在下一章中我们将对移动和网络计算机视觉进行介绍。 更具体地说我们将看到如何使用 OpenCVKeras 和 Flask 创建 Web 计算机视觉以及 Web 深度学习应用并学习了如何与它们结合以提供 Web 应用机器学习和深度学习功能。 问题 本章开头所述的机器学习和深度学习之间的三个主要区别是什么哪一年被认为是深度学习的爆炸式增长以下函数执行什么功能 blob cv2.dnn.blobFromImage(image, 1.0, (300, 300), [104., 117., 123.], False, False)以下几行执行什么操作 net.setInput(blob) preds net.forward()TensorFlow 中的占位符是什么在 TensorFlow 中使用saver.save()保存模型时将创建四个文件单热编码是什么意思Keras 中的顺序模型是什么Keras 中model.fit()的作用是什么 进一步阅读 以下参考资料将帮助您深入了解 Python 的深度学习 《实用卷积神经网络》作者 Mohit SewakMd.Rezaul Karim 和 Pradeep Pujari2018 第 4 部分移动和 Web 计算机视觉 在本书的最后一部分中您将学习如何使用 Flask这是 BSD 许可下的一个功能强大的小型 Python Web 框架来创建计算机视觉和深度学习 Web 应用以构建计算机视觉和深度学习 Web 应用 。 此外您还将学习如何将 Flask 应用部署到云中。 本章将介绍以下章节 第 13 章“使用 Python 和 OpenCV 的移动和 Web 计算机视觉” 十三、使用 Python 和 OpenCV 的移动和 Web 计算机视觉 Web 计算是一个有趣的话题因为它允许我们利用云计算。 从这个意义上讲有许多 Python Web 框架可用于部署应用。 这些框架提供了包的集合使开发人员可以专注于应用的核心逻辑而不必处理底层细节例如协议套接字或进程和线程管理等。 在本章中我们将使用 Flask这是一个小型且功能强大的 Python Web 框架已获得 BSD 许可以建立计算机视觉和深度学习 Web 应用。 此外我们将了解如何利用云计算将应用部署到云中而不是在计算机上运行它们。 本章的主要部分如下 Python Web 框架简介Flask 简介使用 OpenCV 和 Flask 的 Web 计算机视觉应用使用 Keras 和 Flask 的深度学习 API将 Flask 应用部署到云上 技术要求 技术要求如下 Python 和 OpenCV特定于 Python 的 IDENumPy 和 Matplotlib 包Git 客户端Flask请参阅下一节“安装包中”的“如何安装 Flask”Keras请参阅下一节“安装包”中的“如何安装 Keras”TensorFlow请参阅下一节“安装包”中的“如何安装 TensorFlow”requests请参阅下一节“安装包”中的“如何安装requests”Pillow请参阅下一小节“安装包”的安装方法 有关如何安装这些要求的更多详细信息请参见第 1 章“设置 OpenCV”。 可以通过以下 URL 访问《精通 Python OpenCV 4》的 GitHub 存储库其中包含从本书第一章到最后一章所需的所有支持项目文件。 在接下来的小节中我们将介绍如何使用pip命令安装必要的包FlaskKerasTensorFlow 和请求。 安装包 让我们快速回顾一下如何安装所需的包 Flask您可以使用以下命令安装 Flask $ pip install flask要检查安装是否正确执行只需打开 Python shell 并尝试导入 Flask 库 python import FlaskTensorFlow您可以使用以下命令安装 TensorFlow $ pip install tensorflow要检查安装是否正确执行只需打开一个 Python shell 并尝试导入 TensorFlow 库 python import tensorflowKeras您可以使用以下命令安装 Keras $ pip install keras要检查安装是否正确执行只需打开一个 Python shell 并尝试导入 Keras 库 python import kerasrequests您可以使用以下命令安装requests $ pip install requests要检查安装是否正确执行只需打开 Python Shell 并尝试导入请求库 python import requestsPillow为了安装 Pillow请使用以下命令 pip install Pillow要检查安装是否正确执行只需打开 Python shell 并尝试导入 Pillow 库 python import PIL我想提一下推荐的方法是在虚拟环境中安装包。 请参阅第 1 章“设置 OpenCV”以了解有关创建和管理虚拟环境的更多信息。 Python Web 框架简介 Python Web 框架提供了一组包这些包使开发人员可以专注于应用的核心逻辑而不必处理底层细节 例如协议套接字或进程以及线程管理等。 此外可以将这些框架分为全栈和非全栈框架。 Django 和 Flask 是两个流行的 Python 网络框架我们将在本章稍后讨论 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lnjID1zy-1681870549424)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/5a63e63e-fdef-41c0-9135-5f602eb4922a.png)] 全栈框架的完美示例是 Django这是一个免费的开源全栈 Python 框架 尝试默认包含所有必需的功能而不是将它们作为单独的库提供。 Django 使创建 Web 应用更加容易并且比其他框架所需的时间更少。 它着重于通过遵循不要重复DRY的原理来实现尽可能的自动化。 如果您有兴趣学习 Django建议您阅读本教程该教程是关于编写第一个 Django 应用。 Flask已获得 BSD 许可可被视为非全栈框架的完美示例。 实际上Flask 被认为是一个微框架它是一个很少或完全不依赖外部库的框架。 Flask 具有以下依赖关系 WSGI 工具包 WSGI 工具库 Jinja2 模板引擎 Django 和 Flask 均可用于开发计算机视觉和深度学习应用。 但是公认的是Django 的学习曲线比 Flask 略陡。 此外Flask 专注于简约和简约。 例如Flask 的Hello World应用只有几行代码。 此外还建议将 Flask 用于较小和较不复杂的应用而 Django 通常用于较大和较复杂的应用。 在本章中我们将了解如何将 Flask 用于创建计算机视觉和深度学习 Web 应用。 Flask 简介 正如我们提到的这是 Flask 的Hello World应用仅包含几行代码。 可以在hello.py脚本中看到如下所示 # Import required packages: from flask import Flaskapp Flask(__name__)app.route(/) def hello():return Hello World!if __name__ __main__:app.run()导入所需的包后我们将创建Flask类的实例该实例将成为我们的 Web 服务器网关接口WSGI应用。 route()装饰器用于指示哪个 URL 应该触发hello()函数该函数将打印消息Hello World!。 在 Flask 中您可以使用route()装饰器将函数绑定到 URL。 使用以下命令执行此脚本 $ python hello.py您将在控制台中看到此消息告诉您 Web 服务器已启动 * Serving Flask app hello (lazy loading)* Environment: productionWARNING: Do not use the development server in a production environment.Use a production WSGI server instead.* Debug mode: off* Running on http://127.0.0.1:5000/ (Press CTRLC to quit)此时您可以打开浏览器并输入http://127.0.0.1:5000/。 这将对我们的服务器执行GET请求该请求将返回相应的消息使我们可以在浏览器中看到它。 这些步骤可以总结在下一个屏幕截图中 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-fpNTphs0-1681870549425)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/65afcc61-2b4f-4a2a-baf8-7805b9592f0e.png)] 如您所见浏览器显示Hello World!消息。 在前面的示例中脚本称为hello.py。 确保不要调用您的应用flask.py因为这可能导致与 Flask 本身发生冲突。 在前面的示例中只能从我们自己的计算机访问服务器而不能从网络中的任何其他服务器访问服务器。 为了使服务器公开可用在运行服务器应用时应添加参数host0.0.0.0。 可以在hello_external.py脚本中看到 # Import required packages: from flask import Flaskapp Flask(__name__)app.route(/) def hello():return Hello World!if __name__ __main__:# Add parameter host0.0.0.0 to run on your machines IP address:app.run(host0.0.0.0)这样我们可以从连接到该网络的任何其他设备执行请求如下面的屏幕快照所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-324mPps6-1681870549425)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/97836e1e-4f68-42ee-b809-7396f53846c8.png)] 如前所述可以使用route()装饰器将函数绑定到 URL如hello_routes_external.py脚本所示 # Import required packages: from flask import Flaskapp Flask(__name__)app.route(/) def hello():return Hello World!app.route(/user) def hello_user():return User: Hello World!if __name__ __main__:# Add parameter host0.0.0.0 to run on your machines IP address:app.run(host0.0.0.0)在下一个屏幕截图中对此进行了说明我们在其中请求http://192.168.1.101:5000/user URL并获得User: Hello World!消息 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xTDmlFAR-1681870549425)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/3a99ba0b-461f-4ef3-94cc-e893acc5b01a.png)] 在本节中我们介绍了一些使用 Flask 创建应用时必须考虑的基本概念。 在下一节中我们将看到不同的示例以了解如何使用 OpenCV 和 Flask 创建 Web 计算机视觉应用。 使用 OpenCV 和 Flask 的 Web 计算机视觉应用 在本节中我们将看到如何使用 OpenCV 和 Flask 创建 Web 计算机视觉应用。 我们将从使用 OpenCV 和 Flask 的等效Hello world应用开始。 OpenCV 和 Flask 的最小示例 对脚本hello_opencv.py进行了编码以显示如何使用 OpenCV 执行基本的 Web 计算机视觉应用。 该脚本的代码如下所示 # Import required packages: import cv2 from flask import Flask, request, make_response import numpy as np import urllib.requestapp Flask(__name__)app.route(/canny, methods[GET]) def canny_processing():# Get the image:with urllib.request.urlopen(request.args.get(url)) as url:image_array np.asarray(bytearray(url.read()), dtypenp.uint8)# Convert the image to OpenCV format:img_opencv cv2.imdecode(image_array, -1)# Convert image to grayscale:gray cv2.cvtColor(img_opencv, cv2.COLOR_BGR2GRAY)# Perform canny edge detection:edges cv2.Canny(gray, 100, 200)# Compress the image and store it in the memory buffer:retval, buffer cv2.imencode(.jpg, edges)# Build the response:response make_response(buffer.tobytes())response.headers[Content-Type] image/jpeg# Return the response:return responseif __name__ __main__:# Add parameter host0.0.0.0 to run on your machines IP address:app.run(host0.0.0.0)可以通过以下步骤来解释先前的代码 第一步是导入所需的包。 在此示例中我们使用route()装饰器将canny_processing()函数绑定到/canny URL。 此外还需要url参数才能正确执行GET请求。 为了获得该参数使用了request.args.get()函数。下一步是读取该 URL 持有的图像如下所示 with urllib.request.urlopen(request.args.get(url)) as url:image_array np.asarray(bytearray(url.read()), dtypenp.uint8)这样图像将作为数组读取。 下一步是将图像转换为 OpenCV 格式并执行 Canny 边缘处理该处理应在相应的灰度图像上执行 # Convert the image to OpenCV format: img_opencv cv2.imdecode(image_array, -1)# Convet image to grayscale: gray cv2.cvtColor(img_opencv, cv2.COLOR_BGR2GRAY)# Perform canny edge detection: edges cv2.Canny(gray, 100, 200)下一步是压缩图像并将其存储在内存缓冲区中如下所示 # Compress the image and store it in the memory buffer: retval, buffer cv2.imencode(.jpg, edges)最后一步是构建响应并将其返回给客户端如下所示 # Build and return the response: response make_response(buffer.tobytes()) response.headers[Content-Type] image/jpeg# Return the response: return response如果我们运行脚本$ python hello_opencv.py服务器将运行然后如果我们从客户端例如我们的手机执行GET请求我们将获得处理后的图像该图像可以在下一个屏幕截图中可以看到。 此外请考虑到您可能需要禁用防火墙在 Windows 上才能执行以下请求 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Csyrt5fR-1681870549425)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/dbf8ff98-1319-4766-b34e-5abf4bfcaa02.png)] 如图所示我们执行了以下GET请求 http://192.168.1.101:5000/canny?urlhttps://raw.githubusercontent.com/opencv/opencv/master/samples/data/lena.jpg在这里https://raw.githubusercontent.com/opencv/opencv/master/samples/data/lena.jpg是我们要使用 Web 计算视觉应用处理的图像。 使用 OpenCV 的最小人脸 API 在此示例中我们将看到如何使用 OpenCV 和 Flask 创建 Web Face API。 minimal_face_api项目对 Web 服务器应用进行编码。 main.py脚本负责解析请求并建立对客户端的响应。 该脚本的代码如下 # Import required packages: from flask import Flask, request, jsonify import urllib.request from face_processing import FaceProcessing# Initialize application and FaceProcessing(): app Flask(__name__) fc FaceProcessing()app.errorhandler(400) def bad_request(e):# return also the code errorreturn jsonify({status: not ok, message: this server could not understand your request}), 400app.errorhandler(404) def not_found(e):# return also the code errorreturn jsonify({status: not found, message: route not found}), 404app.errorhandler(500) def not_found(e):# return also the code errorreturn jsonify({status: internal error, message: internal error occurred in server}), 500app.route(/detect, methods[GET, POST, PUT]) def detect_human_faces():if request.method GET:if request.args.get(url):with urllib.request.urlopen(request.args.get(url)) as url:return jsonify({status: ok, result: fc.face_detection(url.read())}), 200else:return jsonify({status: bad request, message: Parameter url is not present}), 400elif request.method POST:if request.files.get(image):return jsonify({status: ok, result: fc.face_detection(request.files[image].read())}), 200else:return jsonify({status: bad request, message: Parameter image is not present}), 400else:return jsonify({status: failure, message: PUT method not supported for API}), 405if __name__ __main__:# Add parameter host0.0.0.0 to run on your machines IP address:app.run(host0.0.0.0)如您所见我们利用jsonify()函数创建具有application/json MIME 类型的给定参数的 JSON 表示形式。 可以将 JSON 视为信息交换的事实标准在此示例中我们将返回 JSON 响应而不是像在上一个示例中执行的那样返回图像。 您还可以看到此 API 支持GET和POST请求。 另外在main.py脚本中我们还通过使用errorhandler()装饰函数来注册错误处理器。 还记得在将响应返回给客户端时还要设置错误代码。 图像处理是在face_processing.py脚本中执行的其中FaceProcessing()类被编码为 # Import required packages: import cv2 import numpy as np import osclass FaceProcessing(object):def __init__(self):self.file os.path.join(os.path.join(os.path.dirname(__file__), data), haarcascade_frontalface_alt.xml)self.face_cascade cv2.CascadeClassifier(self.file)def face_detection(self, image):# Convert image to OpenCV format:image_array np.asarray(bytearray(image), dtypenp.uint8)img_opencv cv2.imdecode(image_array, -1)output []# Detect faces and build output:gray cv2.cvtColor(img_opencv, cv2.COLOR_BGR2GRAY)faces self.face_cascade.detectMultiScale(gray, scaleFactor1.1, minNeighbors5, minSize(25, 25))for face in faces:# face.tolist(): returns a copy of the array data as a Python listx, y, w, h face.tolist()face {box: [x, y, x w, y h]}output.append(face)# Return output:return outputface_detection()方法通过使用 OpenCV detectMultiScale()函数执行面部检测。 对于每个检测到的人脸我们将获取其坐标(x, y, w, h)并通过以适当格式对检测进行编码来构建box face {box: [x, y, x w, y h]}最后我们将编码的人脸检测添加到output中 output.append(face)将所有检测到的面部添加到输出后我们将返回它。 要使用此 API我们可以按照与前面示例相同的方式从浏览器执行GET请求。 此外由于我们的 API 也支持POST请求因此我们包含了两个脚本来测试此 API 的功能。 这些脚本同时执行GET和POST请求以了解如何与上述 Face API 进行交互。 更具体地说demo_request.py对面部 API 执行几个请求以获得不同的响应并查看错误处理的工作方式。 在此脚本中我们首先使用错误的 URL 执行GET请求 # Import required packages: import requestsFACE_DETECTION_REST_API_URL http://localhost:5000/detect FACE_DETECTION_REST_API_URL_WRONG http://localhost:5000/process IMAGE_PATH test_face_processing.jpg URL_IMAGE https://raw.githubusercontent.com/opencv/opencv/master/samples/data/lena.jpg# Submit the GET request: r requests.get(FACE_DETECTION_REST_API_URL_WRONG) # See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))在这种情况下我们得到以下信息 status code: 404headers: {Content-Type: application/json, Content-Length: 51, Server: Werkzeug/0.14.1 Python/3.6.6, Date: Sat, 16 Feb 2019 19:20:25 GMT}content: {message: route not found, status: not found}获得的状态码404意味着客户端可以与服务器通信但是服务器找不到请求的内容。 这是因为请求的 URLhttp://localhost:5000/process不正确。 我们执行的第二个请求是正确的GET请求 # Submit the GET request: payload {url: URL_IMAGE} r requests.get(FACE_DETECTION_REST_API_URL, paramspayload) # See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))在这种情况下我们得到以下信息 status code: 200headers: {Content-Type: application/json, Content-Length: 53, Server: Werkzeug/0.14.1 Python/3.6.6, Date: Sat, 16 Feb 2019 19:20:31 GMT}content: {result: [{box: [213, 200, 391, 378]}], status: ok}状态码200指示请求已成功执行。 此外您还可以看到已检测到与 Lenna 的脸相对应的一张脸。 我们执行的第三个请求也是GET请求但有效负载丢失 # Submit the GET request: r requests.get(FACE_DETECTION_REST_API_URL) # See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))在这种情况下我们得到的响应如下 status code: 400headers: {Content-Type: application/json, Content-Length: 66, Server: Werkzeug/0.14.1 Python/3.6.6, Date: Sat, 16 Feb 2019 19:20:32 GMT}content: {message: Parameter url is not present, status: bad request}状态码400表示请求错误。 如您所见url参数丢失。 我们执行的第四个请求是带有正确有效负载的POST请求 # Load the image and construct the payload: image open(IMAGE_PATH, rb).read() payload {image: image}# Submit the POST request: r requests.post(FACE_DETECTION_REST_API_URL, filespayload) # See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))我们得到以下回应 status code: 200headers: {Content-Type: application/json, Content-Length: 449, Server: Werkzeug/0.14.1 Python/3.6.6, Date: Sat, 16 Feb 2019 19:20:34 GMT}content: {result: [{box: [151, 29, 193, 71]}, {box: [77, 38, 115, 76]}, {box: [448, 37, 490, 79]}, {box: [81, 172, 127, 218]}, {box: [536, 47, 574, 85]}, {box: [288, 173, 331, 216]}, {box: [509, 170, 553, 214]}, {box: [357, 48, 399, 90]}, {box: [182, 179, 219, 216]}, {box: [251, 38, 293, 80]}, {box: [400, 174, 444, 218]}, {box: [390, 87, 430, 127]}, {box: [54, 89, 97, 132]}, {box: [499, 91, 542, 134]}, {box: [159, 95, 198, 134]}, {box: [310, 115, 344, 149]}, {box: [225, 116, 265, 156]}], status: ok}如您所见检测到许多面部。 这是因为test_face_processing.jpg包含很多人脸。 最终请求是PUT请求 # Submit the PUT request: r requests.put(FACE_DETECTION_REST_API_URL, filespayload) # See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))我们得到以下输出 status code: 405headers: {Content-Type: application/json, Content-Length: 66, Server: Werkzeug/0.14.1 Python/3.6.6, Date: Sat, 16 Feb 2019 19:20:35 GMT}content: {message: PUT method not supported for API, status: failure}如您所见不支持PUT方法。 此面部 API 仅支持GET和POST方法。 如您在前面的响应中看到的那样当请求成功执行时我们将检测到的面部作为 JSON 数据。 为了了解如何解析响应并使用它绘制检测到的面部我们可以对脚本demo_request_drawing.py进行如下编码 # Import required packages: import cv2 import numpy as np import requests from matplotlib import pyplot as pltdef show_img_with_matplotlib(color_img, title, pos):Shows an image using matplotlib capabilitiesimg_RGB color_img[:, :, ::-1]ax plt.subplot(1, 1, pos)plt.imshow(img_RGB)plt.title(title)plt.axis(off)FACE_DETECTION_REST_API_URL http://localhost:5000/detect IMAGE_PATH test_face_processing.jpg# Load the image and construct the payload: image open(IMAGE_PATH, rb).read() payload {image: image}# Submit the POST request: r requests.post(FACE_DETECTION_REST_API_URL, filespayload)# See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))# Get JSON data from the response and get result: json_data r.json() result json_data[result]# Convert the loaded image to the OpenCV format: image_array np.asarray(bytearray(image), dtypenp.uint8) img_opencv cv2.imdecode(image_array, -1)# Draw faces in the OpenCV image: for face in result:left, top, right, bottom face[box]# To draw a rectangle, you need top-left corner and bottom-right corner of rectangle:cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 255), 2)# Draw top-left corner and bottom-right corner (checking):cv2.circle(img_opencv, (left, top), 5, (0, 0, 255), -1)cv2.circle(img_opencv, (right, bottom), 5, (255, 0, 0), -1)# Create the dimensions of the figure and set title: fig plt.figure(figsize(8, 8)) plt.suptitle(Using face detection API, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)# Show the output image show_img_with_matplotlib(img_opencv, face detection, 1)# Show the Figure: plt.show()从上面可以看出我们首先加载图像并构造有效负载。 然后我们执行POST请求。 然后我们从响应中获取 JSON 数据并获得result # Get JSON data from the response and get result: json_data r.json() result json_data[result]在这一点上我们可以绘制检测到的人脸对所有检测到的人脸进行迭代如下所示 # Draw faces in the OpenCV image: for face in result:left, top, right, bottom face[box]# To draw a rectangle, you need top-left corner and bottom-right corner of rectangle:cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 255), 2)# Draw top-left corner and bottom-right corner (checking):cv2.circle(img_opencv, (left, top), 5, (0, 0, 255), -1)cv2.circle(img_opencv, (right, bottom), 5, (255, 0, 0), -1)对于每个检测到的脸部我们绘制一个矩形以及左上角和右下角点。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7jII5G9a-1681870549426)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/d35f132c-b6b6-407e-9717-00d7be7011fb.png)] 使用 OpenCV 使用 API​​检测到的人脸 如上一个屏幕截图所示已检测到所有面部。 使用 OpenCV 的深度学习猫检测 API 遵循在上一个示例中使用的相同方法-使用 OpenCV 的最小面部 API我们将使用 OpenCV 创建深度学习 API。 更具体地说我们将看到如何创建深度学习猫检测 API。 cat_detection_api项目对 Web 服务器应用进行编码。 main.py脚本负责解析请求并建立对客户端的响应。 该脚本的代码如下 # Import required packages: from flask import Flask, request, jsonify import urllib.request from image_processing import ImageProcessingapp Flask(__name__) ip ImageProcessing()app.errorhandler(400) def bad_request(e):# return also the code errorreturn jsonify({status: not ok, message: this server could not understand your request}), 400app.errorhandler(404) def not_found(e):# return also the code errorreturn jsonify({status: not found, message: route not found}), 404app.errorhandler(500) def not_found(e):# return also the code errorreturn jsonify({status: internal error, message: internal error occurred in server}), 500app.route(/catfacedetection, methods[GET, POST, PUT]) def detect_cat_faces():if request.method GET:if request.args.get(url):with urllib.request.urlopen(request.args.get(url)) as url:return jsonify({status: ok, result: ip.cat_face_detection(url.read())}), 200else:return jsonify({status: bad request, message: Parameter url is not present}), 400elif request.method POST:if request.files.get(image):return jsonify({status: ok, result: ip.cat_face_detection(request.files[image].read())}), 200else:return jsonify({status: bad request, message: Parameter image is not present}), 400else:return jsonify({status: failure, message: PUT method not supported for API}), 405app.route(/catdetection, methods[GET, POST, PUT]) def detect_cats():if request.method GET:if request.args.get(url):with urllib.request.urlopen(request.args.get(url)) as url:return jsonify({status: ok, result: ip.cat_detection(url.read())}), 200else:return jsonify({status: bad request, message: Parameter url is not present}), 400elif request.method POST:if request.files.get(image):return jsonify({status: ok, result: ip.cat_detection(request.files[image].read())}), 200else:return jsonify({status: bad request, message: Parameter image is not present}), 400else:return jsonify({status: failure, message: PUT method not supported for API}), 405if __name__ __main__:# Add parameter host0.0.0.0 to run on your machines IP address:app.run(host0.0.0.0)如您所见我们利用route()装饰器将detect_cat_faces()函数绑定到/catfacedetection URL还将detect_cats()函数绑定到/catdetection URL。 另外我们利用jsonify()函数创建具有application/json MIME 类型的给定参数的 JSON 表示形式。 您还可以看到此 API 支持GET和POST请求。 此外在main.py脚本中我们还通过使用errorhandler()装饰函数来注册错误处理器。 将响应返回给客户端时请记住还要设置错误代码。 图像处理在image_processing.py脚本中进行其中ImageProcessing()类被编码。 从这个意义上讲仅显示cat_face_detection()和cat_detection()方法 class ImageProcessing(object):def __init__(self):......def cat_face_detection(self, image):image_array np.asarray(bytearray(image), dtypenp.uint8)img_opencv cv2.imdecode(image_array, -1)output []gray cv2.cvtColor(img_opencv, cv2.COLOR_BGR2GRAY)cats self.cat_cascade.detectMultiScale(gray, scaleFactor1.1, minNeighbors5, minSize(25, 25))for cat in cats:# face.tolist(): returns a copy of the array data as a Python listx, y, w, h cat.tolist()face {box: [x, y, x w, y h]}output.append(face)return outputdef cat_detection(self, image):image_array np.asarray(bytearray(image), dtypenp.uint8)img_opencv cv2.imdecode(image_array, -1)# Create the blob with a size of (300,300), mean subtraction values (127.5, 127.5, 127.5):# and also a scalefactor of 0.007843:blob cv2.dnn.blobFromImage(img_opencv, 0.007843, (300, 300), (127.5, 127.5, 127.5))# Feed the input blob to the network, perform inference and ghe the output:self.net.setInput(blob)detections self.net.forward()# Size of frame resize (300x300)dim 300output []# Process all detections:for i in range(detections.shape[2]):# Get the confidence of the prediction:confidence detections[0, 0, i, 2]# Filter predictions by confidence:if confidence 0.1:# Get the class label:class_id int(detections[0, 0, i, 1])# Get the coordinates of the object location:left int(detections[0, 0, i, 3] * dim)top int(detections[0, 0, i, 4] * dim)right int(detections[0, 0, i, 5] * dim)bottom int(detections[0, 0, i, 6] * dim)# Factor for scale to original size of frameheightFactor img_opencv.shape[0] / dimwidthFactor img_opencv.shape[1] / dim# Scale object detection to frameleft int(widthFactor * left)top int(heightFactor * top)right int(widthFactor * right)bottom int(heightFactor * bottom)# Check if we have detected a cat:if self.classes[class_id] cat:cat {box: [left, top, right, bottom]}output.append(cat)return output如此处所示实现了两种方法。 cat_face_detection()方法使用 OpenCV detectMultiScale()函数执行猫脸检测。 cat_detection()方法使用在 Cafe-SSD 框架中训练的 MobileNet SSD 对象检测执行猫检测并且可以检测20类。 在此示例中我们将检测猫。 因此如果class_id是一只猫我们将把检测结果添加到输出中。 有关如何处理检测和使用预训练的深度学习模型的更多信息我们建议第 12 章“深度学习简介”该课程侧重于深度学习。 完整代码可在这个页面中找到。 为了测试此 API可以使用demo_request_drawing.py脚本如下所示 # Import required packages: import cv2 import numpy as np import requests from matplotlib import pyplot as pltdef show_img_with_matplotlib(color_img, title, pos):Shows an image using matplotlib capabilitiesimg_RGB color_img[:, :, ::-1]ax plt.subplot(1, 1, pos)plt.imshow(img_RGB)plt.title(title)plt.axis(off)CAT_FACE_DETECTION_REST_API_URL http://localhost:5000/catfacedetection CAT_DETECTION_REST_API_URL http://localhost:5000/catdetection IMAGE_PATH cat.jpg# Load the image and construct the payload: image open(IMAGE_PATH, rb).read() payload {image: image}# Convert the loaded image to the OpenCV format: image_array np.asarray(bytearray(image), dtypenp.uint8) img_opencv cv2.imdecode(image_array, -1)# Submit the POST request: r requests.post(CAT_DETECTION_REST_API_URL, filespayload)# See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))# Get JSON data from the response and get result: json_data r.json() result json_data[result]# Draw cats in the OpenCV image: for cat in result:left, top, right, bottom cat[box]# To draw a rectangle, you need top-left corner and bottom-right corner of rectangle:cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 0), 2)# Draw top-left corner and bottom-right corner (checking):cv2.circle(img_opencv, (left, top), 10, (0, 0, 255), -1)cv2.circle(img_opencv, (right, bottom), 10, (255, 0, 0), -1)# Submit the POST request: r requests.post(CAT_FACE_DETECTION_REST_API_URL, filespayload)# See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))# Get JSON data from the response and get result: json_data r.json() result json_data[result]# Draw cat faces in the OpenCV image: for face in result:left, top, right, bottom face[box]# To draw a rectangle, you need top-left corner and bottom-right corner of rectangle:cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 255), 2)# Draw top-left corner and bottom-right corner (checking):cv2.circle(img_opencv, (left, top), 10, (0, 0, 255), -1)cv2.circle(img_opencv, (right, bottom), 10, (255, 0, 0), -1)# Create the dimensions of the figure and set title: fig plt.figure(figsize(6, 7)) plt.suptitle(Using cat detection API, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)# Show the output image show_img_with_matplotlib(img_opencv, cat detection, 1)# Show the Figure: plt.show()在先前的脚本中我们执行两个POST请求以便同时检测猫的脸部以及cat.jpg图像中的猫。 此外我们还解析了两个请求的响应并绘制了结果这可以在此脚本的输出中看到如以下屏幕快照所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dbwrYBNy-1681870549426)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/ffc794a6-22a6-48f8-8c4f-b2f7749db1d2.png)] 如前面的屏幕快照所示绘制了猫脸检测和全身猫检测。 使用 Keras 和 Flask 的深度学习 API 在第 12 章“深度学习简介”中我们看到了如何同时使用 TensorFlow 和 Keras 创建深度学习应用。 在本节中我们将看到如何使用 Keras 和 Flask 创建深度学习 API。 更具体地说我们将看到如何与 Keras 中包含的经过预先训练的深度学习架构一起使用然后我们将看到如何使用这些经过预先训练的深度学习架构来创建深度学习 API。 Keras 应用 Keras 应用 与 python 2.7-3.6 兼容并以 MIT 许可分发是 Keras 深度学习库的应用模块 为许多流行的架构例如 VGG16ResNet50Xception 和 MobileNet 等提供深度学习模型定义和预训练权重这些架构可用于预测特征提取和微调。 在 Keras 安装期间会下载模型架构但是在实例化模型时会自动下载预先训练的权重。 此外所有这些深度学习架构都与所有后端TensorFlowTheano 和 CNTK兼容。 这些深度学习架构在 ImageNet 数据集上进行了训练和验证用于将图像分类为1,000类别或类别之一 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7mzNkI2p-1681870549426)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/fd1b50b0-fe4b-47eb-81b9-71c249121d58.png)] 在上一个屏幕截图中您可以在 Keras 应用模块中查看各个模型的文档 XceptionXception V1 模型权重在 ImageNet 上进行了预训练VGG16VGG16 模型权重在 ImageNet 上进行了预训练VGG19VGG19 模型权重在 ImageNet 上进行了预训练ResNet50ResNet50 模型权重在 ImageNet 上进行了预训练InceptionV3InceptionV3 模型权重在 ImageNet 上进行了预训练InceptionResNetV2InceptionResNetV2 模型权重在 ImageNet 上进行了预训练MobileNetMobileNet 模型权重在 ImageNet 上进行了预训练MobileNetV2MobileNetV2 模型权重在 ImageNet 上进行了预训练DenseNet121DenseNet121 模型权重在 ImageNet 上进行了预训练DenseNet169DenseNet169 模型权重在 ImageNet 上进行了预训练DenseNet201DenseNet201 模型权重在 ImageNet 上进行了预训练NASNetMobileNASNetMobile 模型权重在 ImageNet 上进行了预训练NASNetLargeNASNetLarge 模型权重在 ImageNet 上进行了预训练 在classification_keras_pretrained_imagenet_models.py脚本中我们展示了如何将这些经过预训练的模型用于预测。 这些预训练的模型还可以用于特征提取例如从任意中间层进行特征提取和微调例如在一组新的类上微调预训练的模型。 接下来可以看到classification_keras_pretrained_imagenet_models.py脚本的关键代码。 应该注意的是该脚本需要很长时间才能执行。 完整代码可以在这个页面 # Import required packages ...def preprocessing_image(img_path, target_size, architecture):Image preprocessing to be used for each Deep Learning architecture# Load image in PIL formatimg image.load_img(img_path, target_sizetarget_size)# Convert PIL format to numpy array:x image.img_to_array(img)# Convert the image/images into batch format:x np.expand_dims(x, axis0)# Pre-process (prepare) the image using the specific architecture:x architecture.preprocess_input(x)return xdef put_text(img, model_name, decoded_preds, y_pos):Show the predicted results in the imagecv2.putText(img, {}: {}, {:.2f}.format(model_name, decoded_preds[0][0][1], decoded_preds[0][0][2]),(20, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)# Path of the input image to be classified: img_path car.jpg# Load some available models: model_inception_v3 inception_v3.InceptionV3(weightsimagenet) model_vgg_16 vgg16.VGG16(weightsimagenet) model_vgg_19 vgg19.VGG19(weightsimagenet) model_resnet_50 resnet50.ResNet50(weightsimagenet) model_mobilenet mobilenet.MobileNet(weightsimagenet) model_xception xception.Xception(weightsimagenet) model_nasnet_mobile nasnet.NASNetMobile(weightsimagenet) model_densenet_121 densenet.DenseNet121(weightsimagenet)# Prepare the image for the corresponding architecture: x_inception_v3 preprocessing_image(img_path, (299, 299), inception_v3) x_vgg_16 preprocessing_image(img_path, (224, 224), vgg16) x_vgg_19 preprocessing_image(img_path, (224, 224), vgg19) x_resnet_50 preprocessing_image(img_path, (224, 224), resnet50) x_mobilenet preprocessing_image(img_path, (224, 224), mobilenet) x_xception preprocessing_image(img_path, (299, 299), xception) x_nasnet_mobile preprocessing_image(img_path, (224, 224), nasnet) x_densenet_121 preprocessing_image(img_path, (224, 224), densenet)# Get the predicted probabilities: preds_inception_v3 model_inception_v3.predict(x_inception_v3) preds_vgg_16 model_vgg_16.predict(x_vgg_16) preds_vgg_19 model_vgg_19.predict(x_vgg_19) preds_resnet_50 model_resnet_50.predict(x_resnet_50) preds_mobilenet model_mobilenet.predict(x_mobilenet) preds_xception model_xception.predict(x_xception) preds_nasnet_mobile model_nasnet_mobile.predict(x_nasnet_mobile) preds_densenet_121 model_nasnet_mobile.predict(x_densenet_121)# Print the results (class, description, probability): print(Predicted InceptionV3:, decode_predictions(preds_inception_v3, top5)[0]) print(Predicted VGG16:, decode_predictions(preds_vgg_16, top5)[0]) print(Predicted VGG19:, decode_predictions(preds_vgg_19, top5)[0]) print(Predicted ResNet50:, decode_predictions(preds_resnet_50, top5)[0]) print(Predicted MobileNet:, decode_predictions(preds_mobilenet, top5)[0]) print(Predicted Xception:, decode_predictions(preds_xception, top5)[0]) print(Predicted NASNetMobile:, decode_predictions(preds_nasnet_mobile, top5)[0]) print(Predicted DenseNet121:, decode_predictions(preds_densenet_121, top5)[0])# Show results: numpy_image np.uint8(image.img_to_array(image.load_img(img_path))).copy() numpy_image cv2.resize(numpy_image, (500, 500)) numpy_image_res numpy_image.copy()put_text(numpy_image_res, InceptionV3, decode_predictions(preds_inception_v3), 40) put_text(numpy_image_res, VGG16, decode_predictions(preds_vgg_16), 65) put_text(numpy_image_res, VGG19, decode_predictions(preds_vgg_19), 90) put_text(numpy_image_res, ResNet50, decode_predictions(preds_resnet_50), 115) put_text(numpy_image_res, MobileNet, decode_predictions(preds_mobilenet), 140) put_text(numpy_image_res, Xception, decode_predictions(preds_xception), 165) put_text(numpy_image_res, NASNetMobile, decode_predictions(preds_nasnet_mobile), 190) put_text(numpy_image_res, DenseNet121, decode_predictions(preds_densenet_121), 215)第一步是导入所需的包如下所示 from keras.preprocessing import image from keras.applications import inception_v3, vgg16, vgg19, resnet50, mobilenet, xception, nasnet, densenet from keras.applications.imagenet_utils import decode_predictions第二步是实例化不同的模型架构如下所示 # Load some available models: model_inception_v3 inception_v3.InceptionV3(weightsimagenet) model_vgg_16 vgg16.VGG16(weightsimagenet) model_vgg_19 vgg19.VGG19(weightsimagenet) model_resnet_50 resnet50.ResNet50(weightsimagenet) model_mobilenet mobilenet.MobileNet(weightsimagenet) model_xception xception.Xception(weightsimagenet) model_nasnet_mobile nasnet.NASNetMobile(weightsimagenet) model_densenet_121 densenet.DenseNet121(weightsimagenet)第三步是加载和预处理图像以进行分类。 为此我们具有preprocessing_image()函数 def preprocessing_image(img_path, target_size, architecture):Image preprocessing to be used for each Deep Learning architecture# Load image in PIL formatimg image.load_img(img_path, target_sizetarget_size)# Convert PIL format to numpy array:x image.img_to_array(img)# Convert the image/images into batch format:x np.expand_dims(x, axis0)# Pre-process (prepare) the image using the specific architecture:x architecture.preprocess_input(x)return xpreprocessing_image()函数的第一步是使用image.load_img()函数加载图像并指定目标尺寸。 应当注意Keras 以 PIL 格式(width, height)加载图像应使用image.img_to_array()函数将其转换为 NumPy 格式(height, width, channels)。 然后应使用 NumPy 的expand_dims()函数将输入图像转换为三维张量(batchsize, height, width, channels)。 预处理图像的最后一步是对图像进行规范化这是每种架构所特有的。 这可以通过调用preprocess_input()函数来实现。 我们使用上述preprocessing_image()如下所示 # Prepare the image for the corresponding architecture: x_inception_v3 preprocessing_image(img_path, (299, 299), inception_v3) x_vgg_16 preprocessing_image(img_path, (224, 224), vgg16) x_vgg_19 preprocessing_image(img_path, (224, 224), vgg19) x_resnet_50 preprocessing_image(img_path, (224, 224), resnet50) x_mobilenet preprocessing_image(img_path, (224, 224), mobilenet) x_xception preprocessing_image(img_path, (299, 299), xception) x_nasnet_mobile preprocessing_image(img_path, (224, 224), nasnet) x_densenet_121 preprocessing_image(img_path, (224, 224), densenet)一旦对图像进行了预处理我们可以使用model.predict()获得分类结果每个类的预测概率 # Get the predicted probabilities: preds_inception_v3 model_inception_v3.predict(x_inception_v3) preds_vgg_16 model_vgg_16.predict(x_vgg_16) preds_vgg_19 model_vgg_19.predict(x_vgg_19) preds_resnet_50 model_resnet_50.predict(x_resnet_50) preds_mobilenet model_mobilenet.predict(x_mobilenet) preds_xception model_xception.predict(x_xception) preds_nasnet_mobile model_nasnet_mobile.predict(x_nasnet_mobile) preds_densenet_121 model_nasnet_mobile.predict(x_densenet_121)可以将预测值解码为元组列表class IDdescription和confidence of prediction # Print the results (class, description, probability): print(Predicted InceptionV3:, decode_predictions(preds_inception_v3, top5)[0]) print(Predicted VGG16:, decode_predictions(preds_vgg_16, top5)[0]) print(Predicted VGG19:, decode_predictions(preds_vgg_19, top5)[0]) print(Predicted ResNet50:, decode_predictions(preds_resnet_50, top5)[0]) print(Predicted MobileNet:, decode_predictions(preds_mobilenet, top5)[0]) print(Predicted Xception:, decode_predictions(preds_xception, top5)[0]) print(Predicted NASNetMobile:, decode_predictions(preds_nasnet_mobile, top5)[0]) print(Predicted DenseNet121:, decode_predictions(preds_densenet_121, top5)[0])应该注意的是我们为批量中的每个图像获得一个元组列表class IDdescription和confidence of prediction。 在这种情况下仅一个图像用作输入。 获得的输出如下 Predicted InceptionV3: [(n04285008, sports_car, 0.5347126), (n03459775, grille, 0.26265427), (n03100240, convertible, 0.04198084), (n03770679, minivan, 0.030852199), (n02814533, beach_wagon, 0.01985116)]Predicted VGG16: [(n03770679, minivan, 0.38101497), (n04285008, sports_car, 0.11982699), (n04037443, racer, 0.079280525), (n02930766, cab, 0.063257575), (n02974003, car_wheel, 0.058513235)]Predicted VGG19: [(n03770679, minivan, 0.23455109), (n04285008, sports_car, 0.22764407), (n04037443, racer, 0.091262065), (n02930766, cab, 0.082842484), (n02974003, car_wheel, 0.07619765)]Predicted ResNet50: [(n04285008, sports_car, 0.2878513), (n03770679, minivan, 0.27558535), (n03459775, grille, 0.14996652), (n02974003, car_wheel, 0.07796249), (n04037443, racer, 0.050856136)]Predicted MobileNet: [(n04285008, sports_car, 0.2911019), (n03770679, minivan, 0.24308795), (n04037443, racer, 0.17548184), (n02814533, beach_wagon, 0.12273211), (n02974003, car_wheel, 0.065000646)]Predicted Xception: [(n04285008, sports_car, 0.3404192), (n03770679, minivan, 0.12870753), (n03459775, grille, 0.11251074), (n03100240, convertible, 0.068289846), (n03670208, limousine, 0.056636304)]Predicted NASNetMobile: [(n04285008, sports_car, 0.54606944), (n03100240, convertible, 0.2797665), (n03459775, grille, 0.037253976), (n02974003, car_wheel, 0.02682667), (n02814533, beach_wagon, 0.014193514)]Predicted DenseNet121: [(n04285008, sports_car, 0.65400195), (n02974003, car_wheel, 0.076283), (n03459775, grille, 0.06899618), (n03100240, convertible, 0.058678553), (n04037443, racer, 0.051732656)]最后我们使用put_text()函数显示图像中每种架构的获得的结果最佳预测 put_text(numpy_image_res, InceptionV3, decode_predictions(preds_inception_v3), 40) put_text(numpy_image_res, VGG16, decode_predictions(preds_vgg_16), 65) put_text(numpy_image_res, VGG19, decode_predictions(preds_vgg_19), 90) put_text(numpy_image_res, ResNet50, decode_predictions(preds_resnet_50), 115) put_text(numpy_image_res, MobileNet, decode_predictions(preds_mobilenet), 140) put_text(numpy_image_res, Xception, decode_predictions(preds_xception), 165) put_text(numpy_image_res, NASNetMobile, decode_predictions(preds_nasnet_mobile), 190) put_text(numpy_image_res, DenseNet121, decode_predictions(preds_densenet_121), 215)put_text()函数代码如下 def put_text(img, model_name, decoded_preds, y_pos):Show the predicted results in the imagecv2.putText(img, {}: {}, {:.2f}.format(model_name, decoded_preds[0][0][1], decoded_preds[0][0][2]),(20, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)在put_text()函数中我们调用cv2.putText()函数在图像中渲染相应的字符串。 下一个屏幕截图中可以看到classification_keras_pretrained_imagenet_models.py脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cFVtpP1M-1681870549426)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/a17b4bdf-eac5-4df0-a0b2-fb8ca67ea7b1.png)] 如前面的屏幕快照所示大多数模型将此图像分类为sport_car 但是VGG 模型VGG16 和 VGG19将此图像归类为minivan这可能是由于汽车的高度所致。 如果我们使用另一个输入图像在这种情况下为cat.jpg运行脚本则输出显示在下一个屏幕截图中 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mMjNElWH-1681870549426)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/3a096fe0-bf92-419b-8ed6-b0ff87114d63.png)] 在这种情况下大多数模型将此图像分类为tabby这是家猫虎斑猫。 Xception 模型将此图像分类为Egyptian_cat。 使用 Keras 应用的深度学习 REST API 在上一小节中我们已经了解了如何使用 Keras 深度学习库的应用模块为许多流行的架构提供了深度学习模型定义和预训练权重。 在本小节中我们将了解如何基于这些预先训练的架构之一创建深度学习 REST API。 Keras 深度学习 REST API 是一个名为keras_server.py的文件。 接下来可以看到此脚本的代码 # Import required packages: from keras.applications import nasnet, NASNetMobile from keras.preprocessing.image import img_to_array from keras.applications import imagenet_utils from PIL import Image import numpy as np import flask import io import tensorflow as tf# Initialize Flask app, Keras model and graph: app flask.Flask(__name__) graph None model Nonedef load_model():# Get default graph:global graphgraph tf.get_default_graph()# Load the pre-trained Keras model(pre-trained on ImageNet):global modelmodel NASNetMobile(weightsimagenet)def preprocessing_image(image, target):# Make sure the image mode is RGB:if image.mode ! RGB:image image.convert(RGB)# Resize the input image:image image.resize(target)# Convert PIL format to numpy array:image img_to_array(image)# Convert the image/images into batch format:image np.expand_dims(image, axis0)# Pre-process (prepare) the image using the specific architecture:image nasnet.preprocess_input(image)# Return the image:return imageapp.route(/predict, methods[POST]) def predict():# Initialize result:result {success: False}if flask.request.method POST:if flask.request.files.get(image):# Read input image in PIL format:image flask.request.files[image].read()image Image.open(io.BytesIO(image))# Pre-process the image to be classified:image preprocessing_image(image, target(224, 224))# Classify the input image:with graph.as_default():predictions model.predict(image)results imagenet_utils.decode_predictions(predictions)result[predictions] []# Add the predictions to the result:for (imagenet_id, label, prob) in results[0]:r {label: label, probability: float(prob)}result[predictions].append(r)# At this point we can say that the request was dispatched successfully:result[success] True# Return result as a JSON response:return flask.jsonify(result)app.route(/) def home():# Initialize result:result {success: True}# Return result as a JSON response:return flask.jsonify(result)if __name__ __main__:print(Loading Keras pre-trained model)load_model()print(Starting)app.run()第一步是导入所需的包如下所示 # Import required packages: from keras.applications import nasnet, NASNetMobile from keras.preprocessing.image import img_to_array from keras.applications import imagenet_utils from PIL import Image import numpy as np import flask import io import tensorflow as tf下一步是初始化 Flask 应用我们的模型和计算图如下所示 # Initialize Flask app, Keras model and graph: app flask.Flask(__name__) graph None model None我们定义load_model()函数该函数负责创建架构并加载所需的权重 def load_model():# Get default graph:global graphgraph tf.get_default_graph()# Load the pre-trained Keras model(pre-trained on ImageNet):global modelmodel NASNetMobile(weightsimagenet)可以看出NASNetMobile 权重已加载。 我们还定义了preprocessing_image()函数如下所示 def preprocessing_image(image, target):# Make sure the image mode is RGB:if image.mode ! RGB:image image.convert(RGB)# Resize the input image:image image.resize(target)# Convert PIL format to numpy array:image img_to_array(image)# Convert the image/images into batch format:image np.expand_dims(image, axis0)# Pre-process (prepare) the image using the specific architecture:image nasnet.preprocess_input(image)# Return the image:return image此函数准备输入图像-将图像转换为 RGB调整其大小将一个或多个图像转换为批量格式最后使用特定的架构对其进行预处理。 最后我们使用route()装饰器将predict()函数绑定到/predict URL。 predict()函数处理请求并将预测返回给客户端如下所示 app.route(/predict, methods[POST]) def predict():# Initialize result:result {success: False}if flask.request.method POST:if flask.request.files.get(image):# Read input image in PIL format:image flask.request.files[image].read()image Image.open(io.BytesIO(image))# Pre-process the image to be classified:image preprocessing_image(image, target(224, 224))# Classify the input image:with graph.as_default():predictions model.predict(image)results imagenet_utils.decode_predictions(predictions)result[predictions] []# Add the predictions to the result:for (imagenet_id, label, prob) in results[0]:r {label: label, probability: float(prob)}result[predictions].append(r)# At this point we can say that the request was dispatched successfully:result[success] True# Return result as a JSON response:return flask.jsonify(result)在predict()函数中处理图像的第一步是读取 PIL 格式的输入图像。 接下来我们对图像进行预处理并将其通过网络以获得预测。 最后我们将预测添加到结果中并将结果作为 JSON 响应返回。 以与前面各节相同的方式我们已经编码了两个脚本以便对 Keras 深度学习 REST API 执行POST请求。 request_keras_rest_api.py脚本执行POST请求并打印结果。 request_keras_rest_api_drawing.py脚本执行POST请求打印结果并创建图像以渲染获得的结果。 为了简化起见仅显示request_keras_rest_api_drawing.py脚本如下所示 # Import required packages: import cv2 import numpy as np import requests from matplotlib import pyplot as pltdef show_img_with_matplotlib(color_img, title, pos):Shows an image using matplotlib capabilitiesimg_RGB color_img[:, :, ::-1]ax plt.subplot(1, 1, pos)plt.imshow(img_RGB)plt.title(title)plt.axis(off)KERAS_REST_API_URL http://localhost:5000/predict IMAGE_PATH car.jpg# Load the image and construct the payload: image open(IMAGE_PATH, rb).read() payload {image: image}# Submit the POST request: r requests.post(KERAS_REST_API_URL, filespayload).json()# Convert the loaded image to the OpenCV format: image_array np.asarray(bytearray(image), dtypenp.uint8) img_opencv cv2.imdecode(image_array, -1) img_opencv cv2.resize(img_opencv, (500, 500))y_pos 40# Show the results: if r[success]:# Iterate over the predictionsfor (i, result) in enumerate(r[predictions]):# Print the results:print({}. {}: {:.4f}.format(i 1, result[label], result[probability]))# Render the results in the image:cv2.putText(img_opencv, {}. {}: {:.4f}.format(i 1, result[label], result[probability]),(20, y_pos), cv2.FONT_HERSHEY_SIMPLEX, 0.6, (255, 0, 255), 2)y_pos 30 else:print(Request failed)# Create the dimensions of the figure and set title: fig plt.figure(figsize(8, 6)) plt.suptitle(Using Keras Deep Learning REST API, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)# Show the output image show_img_with_matplotlib(img_opencv, Classification results (NASNetMobile), 1)# Show the Figure: plt.show()如您所见我们向 Keras 深度学习 REST API 执行POST请求。 为了显示结果我们迭代获得的预测。 对于每个预测我们都将打印结果并将结果呈现在图像中。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-m8I0buw4-1681870549427)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/27162867-0964-4949-aaa9-877887089566.png)] 在上一个屏幕截图中您可以看到与输入car.jpg图像相对应的顶部5预测。 将 Flask 应用部署到云上 如果您开发了 Flask 应用则可以在计算机上运行​​可以通过将其部署到云中轻松地将其公开。 如果要将应用部署到云中有很多选择例如Google App EngineMicrosoft AzureHeroku 和 Amazon Web Services 等。 另外您还可以使用 PythonAnywhere它是 Python 在线集成开发环境IDE。 和网络托管环境可轻松在云中创建和运行 Python 程序。 PythonAnywhere 非常简单也是托管基于机器学习的 Web 应用的推荐方法。 PythonAnywhere 提供了一些有趣的功能例如基于 WSGI 的网络托管例如 DjangoFlask 和 Web2py。 在本节中我们将看到如何创建 Flask 应用以及如何在 PythonAnywhere 上部署它。 为了向您展示如何使用 PythonAnywhere 将 Flask 应用部署到云中我们将使用mysite项目的代码。 该代码与本章先前所见的最小面部 API 非常相似进行了少量修改。 创建网站后将对这些修改进行说明 第一步是创建一个 PythonAnywhere 帐户。 对于此示例一个初学者帐户就足够了 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7e8GXoft-1681870549427)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/1d9f2c59-519b-405c-af81-115424109d2b.png)] 注册后您将可以访问仪表板。 在下一个屏幕截图中可以看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-W32DPkNZ-1681870549427)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/e913294d-dcea-4dce-bfb1-95721e37ef35.png)] 如您所见我已经创建了用户opencv。 下一步是单击“Web”菜单然后单击“添加新的 Web 应用”按钮如以下屏幕截图所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-5T9e6oaA-1681870549427)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/f9c45040-fcf6-463a-9581-1a8bc06a693e.png)] 此时您可以创建新的 Web 应用如下面的屏幕快照所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-DTC2MeOe-1681870549428)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/05982c75-8427-44bf-abbe-fad0c5e14a99.png)] 单击下一步然后单击 Flask然后单击最新版本的 Python。 最后单击“下一步”接受项目路径 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uBRPSoCi-1681870549428)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/cdf57442-fc8c-4ae8-b51d-35595612c702.png)] 这将创建一个Hello world Flask 应用如果您访问这里则可以看到该应用。 在我的情况下URL 为https://opencv.pythonanywhere.com。 至此我们准备上传自己的项目。 第一步是单击 Web 菜单的“代码”部分中的“转到目录”如下面的屏幕快照所示 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-gx4U0Ott-1681870549428)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/dd128e83-b04a-4ca2-a4bd-01eab26e933e.png)] 我们可以使用“上传文件”按钮将文件上传到我们的网站。 我们上传了三个文件如下所示 flask_app.pyface_processing.pyhaarcascade_frontalface_alt.xml 在下一个屏幕截图中可以看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-y3UQEEaI-1681870549428)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/e5281d74-15bc-49b8-8745-076b8c01e1fb.png)] 您可以通过单击下载图标查看这些文件的上载内容。 在这种情况下您可以在以下 URL 中查看这些文件的内容 下一步是设置虚拟环境。 为此应通过单击此处的“打开 Bash 控制台”来打开 bash 控制台请参阅上一个屏幕截图。 打开后运行以下命令 $ mkvirtualenv --python/usr/bin/python3.6 my-virtualenv您将看到提示从$更改为(my-virtualenv)$。 这意味着虚拟环境已被激活。 此时我们将安装所有必需的包flask和opencv-contrib-python (my-virtualenv)$ pip install flask(my-virtualenv)$ pip install opencv-contrib-python您会看到还安装了numpy。 所有这些步骤可以在下一个屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-rqhTKCWt-1681870549428)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/7ef2fe14-ccba-44e6-abd8-46e8f9f6710c.png)] 如果要安装其他包请不要忘记激活已创建的虚拟环境。 您可以使用以下命令重新激活它 $ workon my-virtualenv(my-virtualenv)$至此我们几乎完成了。 最后一步是通过单击菜单中的 Web 选项并重新加载站点来重新加载上载的项目这可以在下一个屏幕截图中看到 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kSQLBIJu-1681870549429)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/e79893b3-4216-48d6-92fc-5cb978f01dca.png)] 因此我们准备好测试上传到 PythonAnywhere 的 face API可以使用这个页面访问。 您将看到类似以下屏幕截图的内容 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-56dZCgFx-1681870549429)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/9542e783-4aaf-4881-af4e-3fbcacc806a4.png)] 您可以看到一个 JSON 响应。 之所以获得此 JSON 响应是因为我们已使用route()装饰器将info_view()函数绑定到 URL /。 与在本章中看到的最小人脸 API 相比这是我们在本示例中执行的修改之一。 因此我们修改了flask_app.py脚本使其包括 app.route(/, methods[GET]) def info_view():# List of routes for this API:output {info: GET /,detect faces via POST: POST /detect,detect faces via GET: GET /detect,}return jsonify(output), 200这样当访问这个页面时我们将获得此 API 的路由列表。 将项目上传到 PythonAnywhere 以便查看一切正常时这很有用。 第二次也是最后一次修改是在face_processing.py脚本中执行的。 在此脚本中我们更改了haarcascade_frontalface_alt.xml文件的路径该文件由人脸检测器使用 class FaceProcessing(object):def __init__(self):self.file /home/opencv/mysite/haarcascade_frontalface_alt.xmlself.face_cascade cv2.CascadeClassifier(self.file)查看文件的路径该路径与将haarcascade_frontalface_alt.xml文件上传到 PythonAnywhere 时分配的新路径匹配。 该路径应根据用户名在这种情况下为opencv进行更改。 与前面的示例相同我们可以对上传到 PythonAnywhere 的 Face API 执行POST请求。 这是在demo_request.py脚本中执行的 # Import required packages: import cv2 import numpy as np import requests from matplotlib import pyplot as pltdef show_img_with_matplotlib(color_img, title, pos):Shows an image using matplotlib capabilitiesimg_RGB color_img[:, :, ::-1]ax plt.subplot(1, 1, pos)plt.imshow(img_RGB)plt.title(title)plt.axis(off)FACE_DETECTION_REST_API_URL http://opencv.pythonanywhere.com/detect IMAGE_PATH test_face_processing.jpg# Load the image and construct the payload: image open(IMAGE_PATH, rb).read() payload {image: image}# Submit the POST request: r requests.post(FACE_DETECTION_REST_API_URL, filespayload)# See the response: print(status code: {}.format(r.status_code)) print(headers: {}.format(r.headers)) print(content: {}.format(r.json()))# Get JSON data from the response and get result: json_data r.json() result json_data[result]# Convert the loaded image to the OpenCV format: image_array np.asarray(bytearray(image), dtypenp.uint8) img_opencv cv2.imdecode(image_array, -1)# Draw faces in the OpenCV image: for face in result:left, top, right, bottom face[box]# To draw a rectangle, you need top-left corner and bottom-right corner of rectangle:cv2.rectangle(img_opencv, (left, top), (right, bottom), (0, 255, 255), 2)# Draw top-left corner and bottom-right corner (checking):cv2.circle(img_opencv, (left, top), 5, (0, 0, 255), -1)cv2.circle(img_opencv, (right, bottom), 5, (255, 0, 0), -1)# Create the dimensions of the figure and set title: fig plt.figure(figsize(8, 6)) plt.suptitle(Using face API, fontsize14, fontweightbold) fig.patch.set_facecolor(silver)# Show the output image show_img_with_matplotlib(img_opencv, face detection, 1)# Show the Figure: plt.show()除了以下几行之外此脚本中没有任何新内容 FACE_DETECTION_REST_API_URL http://opencv.pythonanywhere.com/detect请注意我们正在请求我们的云 API。 下一个屏幕截图中可以看到此脚本的输出 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDRQHPeP-1681870549429)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/22331a5a-9ed5-400a-9a57-2a3c73623049.png)] 这样我们可以确认我们的云 API 已启动并正在运行。 总结 在本书的最后一章中我们了解了如何使用 Python Web 框架创建 Web 应用并发现了 Flask 等 Web 框架的潜力。 更具体地说我们已经使用 OpenCVKeras 和 Flask 开发了多个 Web 计算机视觉和 Web 深度学习应用并学习了如何与它们结合以提供 Web 应用机器学习和深度学习功能。 此外我们还介绍了如何使用提供网络托管功能的 PythonAnywhere 将 Flask 应用部署到云中。 最后我们还了解了如何执行来自浏览器的请求例如 GET 和 POSTGET 请求以及如何以编程方式GET 和 POST 请求使用 OpenCV 和 Flask 创建 Web Face API以及如何使用 OpenCV 创建深度学习 API。 问题 Web 框架存在两种主要类别Flask 中route()装饰器的目的是什么如何运行 Flask 服务器应用以从网络上的任何其他计算机进行访问jsonify()函数的作用是什么Flask 中errorhandler()装饰器的目的是什么什么是 Keras 应用什么是 PythonAnywhere 进一步阅读 以下参考资料将帮助您更深入地研究 Flask以及 Django 《精通 Flask Web 开发第二版》作者 Daniel Gaspar 和 Jack Stouffer《Flask – 构建 Web 应用》《Flask 示例》《Python Web 开发2018 年的 Django VS Flask》作者 Aaron Lazar 十四、答案 第一章 Python 虚拟环境的主要目的是为 Python 项目创建一个隔离的环境。 这意味着每个项目都可以具有自己的依赖关系而不管每个其他项目都具有什么依赖关系。 换句话说它是 Python 的一个隔离工作副本使您可以在特定项目上工作而不必担心影响其他项目。pipvirtualenvpipenvAnaconda 和conda之间的连接如下 pipPython 包管理器 PyPA 推荐的用于安装 Python 包的工具您可以使用 PyPI 查找和发布 Python 包Python 包 索引 pyenvPython 版本管理器 pyenv 让您轻松在多个版本的 Python 之间切换如果您需要使用其他版本的 Pythonpyenv可让您轻松管理 virtualenvPython 环境管理器 virtualenv是用于创建隔离的 Python 环境的工具要创建virtualenv只需调用virtualenv ENV其中ENV是用于放置新虚拟环境的目录要初始化virtualenv您需要获取ENV/bin/activate要停止使用virtualenv只需致电deactivate激活virtualenv后您可以通过运行pip install -r requirements.txt 安装工作区的所有包要求。 anaconda包管理器环境管理器和其他科学库 Anaconda 包括易于安装的 Python并更新了 100 多个经过预先构建和测试的科学和分析 Python 包其中包括 NumPyPandasSciPyMatplotlib 和 IPython还可以通过简单的conda install packagename提供 620 多个包。conda是 Anaconda 发行版中包含的开源包管理系统和环境 管理系统提供虚拟环境功能。 因此您可以使用conda创建虚拟环境。尽管conda允许您安装包但是这些包与 PyPI 包是分开的因此根据您需要安装的包的类型您可能仍需要额外使用pip。 笔记本文档是 Jupyter 笔记本应用生成的文档其中包含计算机代码和富文本元素。 由于代码和文本元素的这种混合笔记本是将分析描述及其结果结合在一起的理想场所。 此外可以执行它们以实时执行数据分析。 Jupyter 笔记本 App 是一个服务器客户端应用它允许通过 Web 浏览器编辑和运行笔记本文档。 Jupyter 是一个缩写代表它设计的三种语言JuliaPython 和 R。它属于 Anaconda 发行版。 要使用图像需要的主要包如下Numpyopencvscikit-imagePILPillowSimpleCVMahotas 和 ilastik。 此外要解决机器学习问题您还可以使用 PandasScikit-learnOrangePyBrain 或 Milk。 最后如果您的计算机视觉项目涉及深度学习技术则还可以使用 TensorFlowPytorchTheano 或 Keras。 要根据本地目录中的requirements.txt文件使用pip安装包我们应执行pip install -r requirements.txt安装此文件中包含的所有包。 您还可以先创建一个虚拟环境然后安装所有必需的包 cd到requirements.txt所在的目录激活您的virtualenv运行pip install -r requirements.txt 集成开发环境IDE是一种软件应用为计算机程序员提供用于软件开发的全面功能。 IDE 通常包括源代码编辑器构建自动化工具和调试器。 大多数现代 IDE 具有智能的代码完成功能。 Python IDE 是开始使用 Python 编程的第一件事。 您可以在基本的文本编辑器如记事本中开始使用 Python 编程但是最好使用完整且功能丰富的 Python IDE。 PyCharm 是专业的 Python IDE有两种形式 专业用于 Python 和 Web 开发的全功能 IDE免费试用社区用于 Python 和科学开发的轻量级 IDE免费开源 它的大多数功能都以社区形式提供包括智能代码完成直观的项目导航即时错误检查和修复带有 PEP8 检查和智能重构的代码质量图调试器和测试运行程序。 它还与 IPython 笔记本集成并支持 Anaconda 以及其他科学包例如 Matplotlib 和 NumPy。 OpenCV 是在 BSD 许可下发布的。 因此它对于商业和学术用途都是免费的。 BSD 许可证可以分为三种类型 两条款 BSD 许可证三条款 BSD 许可证四条款 BSD 许可证 OpenCV 使用三节 BSD 许可证。 所有这些子句列出如下 Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:(1) Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.(2) Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.(3) Neither the name of the [organization] nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.(4) All advertising materials mentioning features or use of this software must display the following acknowledgement: This product includes software developed by the [organization].第二章 共有三个图像处理步骤 获取必要的信息以供使用例如图像或视频文件等通过应用图像处理技术处理图像以所需的方式显示结果例如将图像保存到磁盘显示图像等等 处理步骤可以分为三个处理级别 低级处理中级处理高级处理 灰度图像包含图像的每个像素的值该值与图像的亮度或灰度级成正比。 此值也称为强度或灰度级。 该值是属于[0, L-1]其中L 256对于 8 位图像。 另一方面黑白图像为图像的每个像素包含一个只能取两个值的值。 通常这些值为 0黑色和 255白色。 在许多情况下黑白图像是某些图像处理步骤和技术的结果例如阈值运算的结果。 数字图像是 2D 图像表示形式的有限数字值集称为像素。 像素是数字图像中可编程颜色的基本单位。 图像分辨率可以看作是图像的细节。 分辨率为800×1200的图像是具有 800 列和 1200 行的网格包含800×1200 960,000像素。 OpenCV 执行以下操作 加载读取图像cv2.imread() img cv2.imread(logo.png)gray_img cv2.imread(logo.png, cv2.IMREAD_GRAYSCALE) 显示图像cv2.imshow() cv2.imshow(bgr image, img ) 等待按键cv2.waitKey() cv2.waitKey(0) 拆分通道cv2.split() b, g, r cv2.split(img) 合并通道cv2.merge() img cv2.merge([r, g, b]) $ jupyter notebook。 将获得以下颜色 B 0, G 255, R 255黄色B 255, G 255, R 0青色B 255, G 0, R 255洋红色B 255, G 255, R 255白色 您可以在这个页面上进一步使用 RGB 颜色图表。 图像是彩色还是灰度均可通过其尺寸img.shape确定。 如本章所述如果加载了彩色图像则img.shape的长度将为3。 另一方面如果加载的图像是灰度图像则img.shape的长度将为2。 该代码如下所示 # load OpenCV logo image: img cv2.imread(logo.png)# Get the shape of the image: dimensions img.shape# Check the length of dimensions if len(dimensions) 3:print(grayscale image!) if len(dimensions) 3:print(color image!)# Load the same image but in grayscale: gray_img cv2.imread(logo.png, cv2.IMREAD_GRAYSCALE)# Get again the img.shape properties: dimensions gray_img.shape# Check the length of dimensions if len(dimensions) 3: print(grayscale image!) if len(dimensions) 3: print(color image!)第三章 列表的第二个元素是脚本的第一个参数即sys.argv[1]。代码如下 parser argparse.ArgumentParser() parser.add_argument(first_number, helpfirst number to be added, typeint)保存图像的代码如下 cv2.imwrite(image.png, img)capture对象的创建如下 capture cv2.VideoCapture(0)capture对象的创建如下 capture cv2.VideoCapture(0) print(CV_CAP_PROP_FRAME_WIDTH: {}.format(capture.get(cv2.CAP_PROP_FRAME_WIDTH)))读取图像的代码如下 image cv2.imread(logo.png) cv2.imwrite(logo_copy.png, gray_image)脚本编写如下 Example to introduce how to read a video file backwards and save it # Import the required packages import cv2 import argparsedef decode_fourcc(fourcc): Decodes the fourcc value to get the four chars identifying it fourcc_int int(fourcc)# We print the int value of fourcc print(int value of fourcc: {}.format(fourcc_int))# We can also perform this in one line: # return .join([chr((fourcc_int 8 * i) 0xFF) for i in range(4)])fourcc_decode for i in range(4): int_value fourcc_int 8 * i 0xFF print(int_value: {}.format(int_value)) fourcc_decode chr(int_value) return fourcc_decode# We first create the ArgumentParser object # The created object parser will have the necessary information # to parse the command-line arguments into data types. parser argparse.ArgumentParser()# We add video_path argument using add_argument() including a help. parser.add_argument(video_path, helppath to the video file)# We add output_video_path argument using add_argument() including a help. parser.add_argument(output_video_path, helppath to the video file to write)args parser.parse_args()# Create a VideoCapture object and read from input file # If the input is the camera, pass 0 instead of the video file name capture cv2.VideoCapture(args.video_path)# Get some properties of VideoCapture (frame width, frame height and frames per second (fps)): frame_width capture.get(cv2.CAP_PROP_FRAME_WIDTH) frame_height capture.get(cv2.CAP_PROP_FRAME_HEIGHT) fps capture.get(cv2.CAP_PROP_FPS) codec decode_fourcc(capture.get(cv2.CAP_PROP_FOURCC))print(codec: {}.format(codec))# FourCC is a 4-byte code used to specify the video codec and it is platform dependent! fourcc cv2.VideoWriter_fourcc(*codec)# Create VideoWriter object. We use the same properties as the input camera. # Last argument is False to write the video in grayscale. True otherwise (write the video in color) out cv2.VideoWriter(args.output_video_path, fourcc, int(fps), (int(frame_width), int(frame_height)), True)# Check if camera opened successfully if capture.isOpened()is False: print(Error opening video stream or file)# We get the index of the last frame of the video file frame_index capture.get(cv2.CAP_PROP_FRAME_COUNT) - 1 # print(starting in frame: {}.format(frame_index))# Read until video is completed while capture.isOpened() and frame_index 0:# We set the current frame position capture.set(cv2.CAP_PROP_POS_FRAMES, frame_index)# Capture frame-by-frame from the video file: ret, frame capture.read()if ret is True:# Print current frame number per iteration # print(CAP_PROP_POS_FRAMES : {}.format(capture.get(cv2.CAP_PROP_POS_FRAMES)))# Get the timestamp of the current frame in milliseconds # print(CAP_PROP_POS_MSEC : {}.format(capture.get(cv2.CAP_PROP_POS_MSEC)))# Display the resulting frame cv2.imshow(Original frame, frame)# Write the frame to the video out.write(frame)# Convert the frame to grayscale: gray_frame cv2.cvtColor(frame, cv2.COLOR_BGR2GRAY)# Display the grayscale frame cv2.imshow(Grayscale frame, gray_frame)frame_index frame_index - 1 # print(next index to read: {}.format(frame_index))# Press q on keyboard to exit the program: if cv2.waitKey(25) 0xFF ord(q): break # Break the loop else: break# Release everything: capture.release() out.release() cv2.destroyAllWindows()第四章 参数厚度可以取正值和负值。 如果该值为正则表示轮廓的粗细。 负值例如-1表示将绘制填充形状。 例如要绘制填充的椭圆请注意以下几点 cv2.ellipse(image, (80, 80), (60, 40), 0, 0, 360, colors[red], -1)您也可以使用cv2.FILLED cv2.ellipse(image, (80, 80), (60, 40), 0, 0, 360, colors[red], cv2.FILLED)lineType参数可以采用三个值cv2.LINE_4 4cv2.LINE_AA 16和cv2.LINE_8 8。 要绘制抗锯齿线必须使用cv2.LINE_AA cv2.line(image, (0, 0), (20, 20), colors[red], 1, cv2.LINE_AA)对角线是在以下代码的帮助下创建的 cv2.line(image, (0, 0), (512, 512), colors[green], 3)文本展示如下 cv2.putText(image, Hello OpenCV, (10, 30), cv2.FONT_HERSHEY_SIMPLEX, 0.9, colors[red], 2, cv2.LINE_4)此练习的代码对应于circle_polygon.py脚本。 要获取坐标可以使用圆的参数方程式请参见analog_clock_values.py。 下图显示了此多边形 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IZjDQugv-1681870549429)(https://gitcode.net/apachecn/apachecn-cv-zh/-/raw/master/docs/master-opencv4-py/img/b8ae1785-efec-4617-9c4d-ef397fba85aa.png)] circle_polygon.py文件的代码如下 Example to show how to draw a circle polygon # Import required packages: import cv2 import numpy as np import matplotlib.pyplot as pltdef show_with_matplotlib(img, title):Shows an image using matplotlib capabilities# Convert BGR image to RGBimg_RGB img[:, :, ::-1]# Show the image using matplotlib:plt.imshow(img_RGB)plt.title(title)plt.show()# Dictionary containing some colors colors {blue: (255, 0, 0), green: (0, 255, 0), red: (0, 0, 255), yellow: (0, 255, 255),magenta: (255, 0, 255), cyan: (255, 255, 0), white: (255, 255, 255), black: (0, 0, 0),gray: (125, 125, 125), rand: np.random.randint(0, high256, size(3,)).tolist(),dark_gray: (50, 50, 50), light_gray: (220, 220, 220)}# We create the canvas to draw: 640 x 640 pixels, 3 channels, uint8 (8-bit unsigned integers) # We set background to black using np.zeros() image np.zeros((640, 640, 3), dtypeuint8)# If you want another background color you can do the following: # image[:] colors[light_gray] image.fill(255)pts np.array([(600, 320), (563, 460), (460, 562), (320, 600), (180, 563), (78, 460), (40, 320), (77, 180), (179, 78), (319, 40),(459, 77), (562, 179)])# Reshape to shape (number_vertex, 1, 2) pts pts.reshape((-1, 1, 2))# Call cv2.polylines() to build the polygon: cv2.polylines(image, [pts], True, colors[green], 5)# Show image: show_with_matplotlib(image, polygon with the shape of a circle using 12 points)该代码对应于matplotlib_mouse_events_rect.py脚本。 关键是如何捕获双击鼠标左键 双击event.dblclick左键点击event.button 1 matplotlib_mouse_events_rect.py文件的代码如下 Example to show how to capture a double left click with matplotlib events to draw a rectangle # Import required packages: import cv2 import numpy as np import matplotlib as mpl import matplotlib.pyplot as plt# Dictionary containing some colors colors {blue: (255, 0, 0), green: (0, 255, 0), red: (0, 0, 255), yellow: (0, 255, 255),magenta: (255, 0, 255), cyan: (255, 255, 0), white: (255, 255, 255), black: (0, 0, 0),gray: (125, 125, 125), rand: np.random.randint(0, high256, size(3,)).tolist(),dark_gray: (50, 50, 50), light_gray: (220, 220, 220)}# We create the canvas to draw: 400 x 400 pixels, 3 channels, uint8 (8-bit unsigned integers) # We set the background to black using np.zeros() image np.zeros((400, 400, 3), dtypeuint8)# If you want another background color you can do the following: image[:] colors[light_gray]def update_img_with_matplotlib():Updates an image using matplotlib capabilities# Convert BGR to RGB image formatimg_RGB image[:, :, ::-1]# Display the image:plt.imshow(img_RGB)# Redraw the Figure because the image has been updated:figure.canvas.draw()# We define the event listener for the button_press_event: def click_mouse_event(event):# Check if a double left click is performed:if event.dblclick and event.button 1:# (event.xdata, event.ydata) contains the float coordinates of the mouse click event:cv2.rectangle(image, (int(round(event.xdata)), int(round(event.ydata))),(int(round(event.xdata)) 100, int(round(event.ydata)) 50), colors[blue], cv2.FILLED)# Call update_image() method to update the Figure:update_img_with_matplotlib()# We create the Figure: figure plt.figure() figure.add_subplot(111)# To show the image until a click is performed: update_img_with_matplotlib()# button_press_event is a MouseEvent where a mouse botton is click (pressed) # When this event happens the function click_mouse_event is called: figure.canvas.mpl_connect(button_press_event, click_mouse_event)# Display the figure: plt.show()该代码对应于meme_generator_opencv_python.py脚本。 这是一个简单的脚本在其中加载图像然后渲染一些文本 Example to show how to draw basic memes with OpenCV # Import required packages: import cv2 import numpy as np import matplotlib.pyplot as pltdef show_with_matplotlib(img, title):Shows an image using matplotlib capabilities# Convert BGR image to RGBimg_RGB img[:, :, ::-1]# Show the image using matplotlib:plt.imshow(img_RGB)plt.title(title)plt.show()# Dictionary containing some colors colors {blue: (255, 0, 0), green: (0, 255, 0), red: (0, 0, 255), yellow: (0, 255, 255),magenta: (255, 0, 255), cyan: (255, 255, 0), white: (255, 255, 255), black: (0, 0, 0),gray: (125, 125, 125), rand: np.random.randint(0, high256, size(3,)).tolist(),dark_gray: (50, 50, 50), light_gray: (220, 220, 220)}# We load the image lenna.png: image cv2.imread(lenna.png)# Write some text (up) cv2.putText(image, Hello World, (10, 30), cv2.FONT_HERSHEY_TRIPLEX, 0.8, colors[green], 1, cv2.LINE_AA)# Write some text (down) cv2.putText(image, Goodbye World, (10, 200), cv2.FONT_HERSHEY_TRIPLEX, 0.8, colors[red], 1, cv2.LINE_AA)# Show image: show_with_matplotlib(image, very basic meme generator)第五章 cv2.split()函数将源多通道图像分割为几个单通道图像 (b, g, r) cv2.split(image)。cv2.merge()函数将几个单通道图像合并为一个多通道图像image cv2.merge((b, g, r))。图像可以平移如下 height, width image.shape[:2] M np.float32([[1, 0, 150], [0, 1, 300]]) dst_image cv2.warpAffine(image, M, (width, height))可以按以下方式旋转图像 height, width image.shape[:2] M cv2.getRotationMatrix2D((width / 2.0, height / 2.0), 30, 1) dst_image cv2.warpAffine(image, M, (width, height))该图像可以如下构建 kernel np.ones((5, 5), np.float32) / 25 smooth_image cv2.filter2D(image, -1, kernel)灰度图像如下 M np.ones(image.shape, dtypeuint8) * 40 added_image cv2.add(image, M)COLORMAP_JET可以如下应用img_COLORMAP_JET cv2.applyColorMap(gray_img, cv2.COLORMAP_JET) 第六章 图像直方图是一种反映图像色调分布的直方图。 它绘制每个色调值的频率像素数通常在[0-255]范围内。在 OpenCV 中我们使用cv2.calcHist()函数来计算图像的直方图。 要使用64位计算灰度图像的直方图代码如下 hist cv2.calcHist([gray_image], [0], None, [64], [0, 256])我们首先构建具有与灰度图像gray_image相同形状的图像M然后为该图像的每个像素设置50值。 然后我们使用cv2.add()添加两个图像。 最后使用cv2.calcHist()计算直方图 M np.ones(gray_image.shape, dtypeuint8) * 50added_image cv2.add(gray_image, M)hist_added_image cv2.calcHist([added_image], [0], None, [256], [0, 256])在 BGR 图像中红色通道是第三通道index 2 cv2.calcHist([img], [2], None, [256], [0, 256])OpenCV 提供cv2.calcHist()numpy 提供np.histogram()而 matplotlib 提供plt.hist()。 如本章所述cv2.calcHist()比np.histogram()和plt.hist()都快。我们定义了一个函数get_brightness()该函数计算给定灰度图像的亮度。 此函数使用 numpy 函数np.mean()该函数返回数组元素的平均值。 因此此函数的代码如下 def get_brightness(img):Calculates the brightness of the imagebrightness np.mean(img)return brightness我们已经计算了三个图像的亮度 brightness_1 get_brightness(gray_image) brightness_2 get_brightness(added_image) brightness_3 get_brightness(subtracted_image)该示例的完整代码可以在grayscale_histogram_brightness.py脚本中看到。 首先我们必须导入default_timer from timeit import default_timer as timer然后我们必须测量两个函数的执行时间 start timer() gray_image_eq cv2.equalizeHist(gray_image) end timer() exec_time_equalizeHist (end - start) * 1000start timer() gray_image_clahe clahe.apply(gray_image) end timer() exec_time_CLAHE (end - start) * 1000该示例的完整代码可以在comparing_hist_equalization_clahe_time.py.脚本中看到。 第七章 ret, thresh cv2.threshold(gray_image, 100, 255, cv2.THRESH_BINARY)thresh cv2.adaptiveThreshold(gray_image, 255, cv2.ADAPTIVE_THRESH_GAUSSIAN_C, cv2.THRESH_BINARY, 9, 2)ret, th cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY cv2.THRESH_OTSU)ret, th cv2.threshold(gray_image, 0, 255, cv2.THRESH_BINARY cv2.THRESH_TRIANGLE)使用 scikit-image 的大津阈值可应用如下 thresh threshold_otsu(gray_image) binary gray_image thresh binary img_as_ubyte(binary)请记住threshold_otsu(gray_image)函数基于大津的二值化算法返回阈值。 然后使用此值构造二进制图像dtype bool应将其转换为 8 位无符号整数格式dtype uint8以进行适当的可视化。 img_as_ubyte()函数用于此目的。 可以使用 scikit-image 进行三角形阈值处理 thresh_triangle threshold_triangle(gray_image) binary_triangle gray_image thresh_triangle binary_triangle img_as_ubyte(binary_triangle)Niblack 使用 scikit-image 的阈值可按以下方式应用 thresh_niblack threshold_niblack(gray_image, window_size25, k0.8) binary_niblack gray_image thresh_niblack binary_niblack img_as_ubyte(binary_niblack)该算法最初是为文本识别而设计的。 有关更多详细信息请参见出版物《数字图像处理入门》1986。 使用 Scikit-image 和25窗口大小的 Sauvola 阈值可按以下方式应用 thresh_sauvola threshold_sauvola(gray_image, window_size25) binary_sauvola gray_image thresh_sauvola binary_sauvola img_as_ubyte(binary_sauvola)值得注意两个要点 Sauvola 是 Niblack 技术的改良版该算法最初是为文本识别而设计的 有关更多详细信息请参见出版物《自适应文档图像二值化》2000。 为了获得带有阈值图像的值的数组我们使用np.arange()。 由于我们需要一个带有10步骤的[60-130]范围内的值的数组因此以下行对此函数进行了编码 threshold_values np.arange(start60, stop140, step10)之后我们反复应用cv2.threshold()函数和threshold_values中定义的相应阈值 thresholded_images [] for threshold in threshold_values:ret, thresh cv2.threshold(gray_image, threshold, 255, cv2.THRESH_BINARY)thresholded_images.append(thresh)最后我们显示thresholded_images数组中包含的阈值图像。 完整的代码可以在thresholding_example_arange.py脚本中看到。 第八章 cv2.findContours()函数在二进制图像例如阈值运算后得到的图像中找到轮廓。OpenCV 提供的用于压缩轮廓的四个标志如下 cv2.CHAIN_APPROX_NONEcv2.CHAIN_APPROX_SIMPLEcv2.CHAIN_APPROX_TC89_KCOScv2.CHAIN_APPROX_TC89_L1 cv2.moments()函数计算直到多边形或栅格化形状的三阶的所有矩。矩m00给出轮廓的面积。OpenCV 提供cv2.HuMoments()函数来计算七个胡矩不变量。cv2.approxPolyDP()函数根据给定的精度返回给定轮廓的轮廓近似值。 此函数使用 Douglas-Peucker 算法。 epsilon参数指定用于在原始曲线与其近似之间建立最大距离的精度。可以以更紧凑的方式覆盖contour_functionality.py脚本中定义的extreme_points()函数如下所示 def extreme_points_2(contour):Returns extreme points of the contourextreme_left tuple(contour[contour[:, :, 0].argmin()][0])extreme_right tuple(contour[contour[:, :, 0].argmax()][0])extreme_top tuple(contour[contour[:, :, 1].argmin()][0])extreme_bottom tuple(contour[contour[:, :, 1].argmax()][0])return extreme_left, extreme_right, extreme_top, extreme_bottomOpenCV 提供cv2.matchShapes()函数可使用三种比较方法来比较两个轮廓。 所有这些方法都使用胡矩不变量。 三种实现的方法是cv2.CONTOURS_MATCH_I1cv2.CONTOURS_MATCH_I2和cv.CONTOURS_MATCH_I3。 第九章 带有 ORB 的已加载图像image中的关键点和计算描述符如下 orb cv2.ORB() keypoints orb.detect(image, None) keypoints, descriptors orb.compute(image, keypoints)先前检测到的关键点keypoints如下 image_keypoints cv2.drawKeypoints(image, keypoints, None, color(255, 0, 255), flags0)要绘制检测到的关键点请使用cv2.drawKeypoints()函数。 BFMatcher对象和先前已计算的描述符描述符_1和描述符_2的匹配如下创建 bf_matcher cv2.BFMatcher(cv2.NORM_HAMMING, crossCheckTrue) bf_matches bf_matcher.match(descriptors_1, descriptors_2)之前已进行匹配的前 20 个匹配如下 bf_matches sorted(bf_matches, keylambda x: x.distance) result cv2.drawMatches(image_query, keypoints_1, image_scene, keypoints_2, bf_matches[:20], None, matchColor(255, 255, 0), singlePointColor(255, 0, 255), flags0)要绘制计算出的匹配项请使用cv2.drawMatches()。 在图像gray_frame中使用 ArUco 进行标记的检测如下 corners, ids, rejected_corners cv2.aruco.detectMarkers(gray_frame, aruco_dictionary, parametersparameters)要检测标记请使用cv2.aruco.detectMarkers()函数。 使用 ArUco 时检测到的标记如下 frame cv2.aruco.drawDetectedMarkers(imageframe, cornerscorners, idsids, borderColor(0, 255, 0))要绘制标记请使用cv2.aruco.drawDetectedMarkers()函数。 使用 Aruco 时被拒绝的标记如下 frame cv2.aruco.drawDetectedMarkers(imageframe, cornersrejected_corners, borderColor(0, 0, 255))要绘制标记还可以使用cv2.aruco.drawDetectedMarkers()函数。 使用以下代码检测并解码图像中包含的 QR 代码 data, bbox, rectified_qr_code qr_code_detector.detectAndDecode(image)第十章 在机器学习的上下文中主要有三种方法和技术有监督无监督和半监督机器学习。监督学习问题可以进一步分为回归和分类问题。 当输出变量为类别时将发生分类问题而当输出变量为实值时将出现回归问题。 例如如果我们预测某些地区会下雨的可能性并分配两个标签降雨/不下雨这就是分类问题。 另一方面如果我们模型的输出是与降雨相关的概率则这是一个回归问题。OpenCV 提供cv2.kmeans()函数实现了 K 均值聚类算法该算法查找聚类的中心并对聚类周围的输入样本进行分组。 K 均值是可用于无监督学习的最重要的聚类算法之一。cv2.ml.KNearest_create()方法创建一个空的 KNN 分类器应使用train()方法对其进行训练同时提供数据和标签。cv2.findNearest()方法用于查找邻居。要创建空模型请使用cv2.ml.SVM_create()函数。通常RBF 核是合理的首选。 RBF 核将样本非线性地映射到更高维度的空间中因此与线性核不同RBF 核可以处理类标签和属性之间的关系为非线性的情况。 有关更多详细信息请参见《支持向量分类的实用指南》2003。 第十一章 我们已经看到了四个库和包OpenCV 库还有dlib主页和 PyPI dlib主页Github dlib主页PyPI face_recognition主页Github face_recognition主页和PyPI cvlib主页Github cvlib主页cvlib主页 Python 包。人脸识别是对象识别的一种特殊情况其中可以使用从人脸提取的信息从图像或视频中识别或验证人的身份可以分解为人脸识别和人脸验证 人脸验证是一个 1:1 匹配的问题试图回答以下问题“这是所要求的人吗” 例如使用面部解锁手机就是面部验证的一个示例。人脸识别是一个 1:N 匹配问题试图回答以下问题“这个人是谁” 例如可以在办公大楼中安装面部识别系统以识别所有雇员进入办公室时的身份。 cv2.face.getFacesHAAR()函数可用于检测图像中的人脸 retval, faces cv2.face.getFacesHAAR(img, haarcascade_frontalface_alt2.xml)此处img是 BGR 图像haarcascade_frontalface_alt2.xml是 Haar 级联文件的字符串变量。 cv2.dnn.blobFromImage()函数用于根据输入图像创建一个四维 BLOB。 可选地此函数执行预处理这是将 BLOB 输入网络以获得正确结果所必需的。 下一章将更深入地介绍此函数。cv.detect_face()函数可用于使用cvlib检测面部 import cvlib as cv faces, confidences cv.detect_face(image)在后台此函数将 OpenCV DNN 面部检测器与经过预训练的 Caffe 模型一起使用。 有关更多详细信息请参见face_detection_cvlib_dnn.py脚本。 要检测地标应调用face_recognition.face_landmarks()函数 face_landmarks_list_68 face_recognition.face_landmarks(rgb)此函数为图像中的每个脸部返回人脸标志例如眼睛鼻子等的字典。 应当注意face_recognition包适用于 RGB 图像。 要初始化相关跟踪器应调用dlib.correlation_tracker()函数 tracker dlib.correlation_tracker()这将使用默认值初始化跟踪器。 要开始跟踪请使用tracker.start_track()方法并且需要包含要跟踪的对象的边界框 tracker.start_track(image, rect)在此rect是要跟踪的对象的边界框。 要获取被跟踪对象的位置请调用tracker.get_position()方法 pos tracker.get_position()此方法返回被跟踪对象的位置。 使用 Dlib 执行 BGR 图像image的人脸识别的 128D 描述符的计算如下 # Convert image from BGR (OpenCV format) to RGB (dlib format): rgb image[:, :, ::-1] # Calculate the encodings for every face of the image: encodings face_encodings(rgb) # Show the first encoding: print(encodings[0])第十二章 机器学习和深度学习之间的三个主要区别如下 深度学习算法需要具有高端基础架构才能正确训练。 深度学习技术严重依赖高端机器这与可以在低端机器上运行的传统机器学习技术相反。当对特征自省和工程都缺乏领域的了解时深度学习技术会胜过其他技术因为您不必担心特征工程。机器学习和深度学习都能够处理海量数据集但是在处理小型数据集时机器学习方法更有意义。 经验法则是如果数据量很大则深度学习要胜过其他技术而当数据集较小时传统的机器学习算法更可取。 关于计算机视觉Alex KrizhevskyIlya Sutskever 和 Geoff Hinton 发表了《使用深度卷积神经网络的 ImageNet 分类》2012。 该出版物也称为 AlexNet 这是作者设计的卷积神经网络的名称被认为是计算机视觉中最具影响力的论文之一。 因此2012 年被认为是深度学习的爆炸式增长。 此函数从图像创建一个四维 BLOB这意味着我们要在调整大小为300x300的BGR图像上运行模型并对每个蓝色绿色和红色通道应用均值(104, 117, 123)的均值减法 分别。 第一行将输入的 BLOB 馈送到网络而第二行执行推理当推理完成时我们得到了预测。 占位符只是一个变量稍后我们将为其分配数据。 在训练/测试算法时通常使用占位符将训练/测试数据输入到计算图中。 保存最终模型例如saver.save(sess, ./linear_regression)时将创建四个文件 .meta文件包含 TensorFlow 图.data文件包含权重偏差梯度以及所有其他已保存变量的值.index文件确定检查点checkpoint文件记录保存的最新检查点文件 单热编码意味着标签已从单个数字转换为长度等于可能的类数的向量。 这样除i元素其值将为 1之外向量的所有元素都将设置为零对应于类别i。使用 Keras 时最简单的模型类型是顺序模型可以将其视为线性的层堆叠并在本示例中用于创建模型。 此外对于更复杂的架构可以使用 Keras 函数式 API该 API 允许您构建任意的层图。此方法可用于训练模型以获取固定数量的周期数据集上的迭代。 第十三章 Web 框架可以分为全栈和非全栈框架。 Django 是用于 Python 的全栈 Web 框架而 Flask 是 Python 的非全栈框架。在 Flask 中您可以使用route()装饰器将函数绑定到 URL。 换句话说route()装饰器用于向 Flask 指示应触发特定函数的 URL。为了使服务器可以公开在运行服务器应用时应添加host0.0.0.0参数 if __name__ __main__:# Add parameter host0.0.0.0 to run on your machines IP address:app.run(host0.0.0.0)jsonify()函数用于创建具有application/json mimetype 的给定参数的 JSON 表示形式。 JSON 被认为是信息交换的事实上的标准因此将 JSON 数据返回给客户端是一个好主意。我们可以通过用errorhandler()装饰函数来注册错误处理器。 例如请注意以下几点 app.errorhandler(500) def not_found(e):# return also the code errorreturn jsonify({status: internal error, message: internal error occurred in server}), 500 如果服务器中发生内部错误将为客户端提供500错误代码。 Keras 应用 是 Keras 深度学习库的应用模块它为许多流行的架构例如 VGG16ResNet50Xception 和 MobileNet 等可用于预测特征提取和微调。 这些深度学习架构在 ImageNet 数据集上进行了训练和验证用于将图像分类为1000类别或类别之一。PythonAnywhere 是 Python 在线集成开发环境IDE和 Web 托管环境可轻松在云中创建和运行 Python 程序。
文章转载自:
http://www.morning.pqqzd.cn.gov.cn.pqqzd.cn
http://www.morning.wrtbx.cn.gov.cn.wrtbx.cn
http://www.morning.wjxyg.cn.gov.cn.wjxyg.cn
http://www.morning.tsmxh.cn.gov.cn.tsmxh.cn
http://www.morning.rmfw.cn.gov.cn.rmfw.cn
http://www.morning.swimstaracademy.cn.gov.cn.swimstaracademy.cn
http://www.morning.kflzy.cn.gov.cn.kflzy.cn
http://www.morning.mkbc.cn.gov.cn.mkbc.cn
http://www.morning.lqgfm.cn.gov.cn.lqgfm.cn
http://www.morning.sxlrg.cn.gov.cn.sxlrg.cn
http://www.morning.btlsb.cn.gov.cn.btlsb.cn
http://www.morning.bgdk.cn.gov.cn.bgdk.cn
http://www.morning.rgqnt.cn.gov.cn.rgqnt.cn
http://www.morning.rbsmm.cn.gov.cn.rbsmm.cn
http://www.morning.tmbtm.cn.gov.cn.tmbtm.cn
http://www.morning.rqmqr.cn.gov.cn.rqmqr.cn
http://www.morning.jpwkn.cn.gov.cn.jpwkn.cn
http://www.morning.kncrc.cn.gov.cn.kncrc.cn
http://www.morning.svtxeu.com.gov.cn.svtxeu.com
http://www.morning.hzqjgas.com.gov.cn.hzqjgas.com
http://www.morning.gsdbg.cn.gov.cn.gsdbg.cn
http://www.morning.grxsc.cn.gov.cn.grxsc.cn
http://www.morning.tnnfy.cn.gov.cn.tnnfy.cn
http://www.morning.skdrp.cn.gov.cn.skdrp.cn
http://www.morning.rjyd.cn.gov.cn.rjyd.cn
http://www.morning.swzpx.cn.gov.cn.swzpx.cn
http://www.morning.tqbyw.cn.gov.cn.tqbyw.cn
http://www.morning.xsctd.cn.gov.cn.xsctd.cn
http://www.morning.sgrdp.cn.gov.cn.sgrdp.cn
http://www.morning.gbgdm.cn.gov.cn.gbgdm.cn
http://www.morning.qkcyk.cn.gov.cn.qkcyk.cn
http://www.morning.pslzp.cn.gov.cn.pslzp.cn
http://www.morning.ptxwg.cn.gov.cn.ptxwg.cn
http://www.morning.ktfbl.cn.gov.cn.ktfbl.cn
http://www.morning.skrh.cn.gov.cn.skrh.cn
http://www.morning.xqnzn.cn.gov.cn.xqnzn.cn
http://www.morning.nrzbq.cn.gov.cn.nrzbq.cn
http://www.morning.kgfsz.cn.gov.cn.kgfsz.cn
http://www.morning.wklhn.cn.gov.cn.wklhn.cn
http://www.morning.fflnw.cn.gov.cn.fflnw.cn
http://www.morning.zfkxj.cn.gov.cn.zfkxj.cn
http://www.morning.jwpcj.cn.gov.cn.jwpcj.cn
http://www.morning.grwgw.cn.gov.cn.grwgw.cn
http://www.morning.ghxtk.cn.gov.cn.ghxtk.cn
http://www.morning.knmby.cn.gov.cn.knmby.cn
http://www.morning.qgwdc.cn.gov.cn.qgwdc.cn
http://www.morning.jcrfm.cn.gov.cn.jcrfm.cn
http://www.morning.oumong.com.gov.cn.oumong.com
http://www.morning.rxfjg.cn.gov.cn.rxfjg.cn
http://www.morning.rgnq.cn.gov.cn.rgnq.cn
http://www.morning.nlygm.cn.gov.cn.nlygm.cn
http://www.morning.brcdf.cn.gov.cn.brcdf.cn
http://www.morning.gktds.cn.gov.cn.gktds.cn
http://www.morning.dnwlb.cn.gov.cn.dnwlb.cn
http://www.morning.kxrhj.cn.gov.cn.kxrhj.cn
http://www.morning.pxlsh.cn.gov.cn.pxlsh.cn
http://www.morning.fhddr.cn.gov.cn.fhddr.cn
http://www.morning.qczpf.cn.gov.cn.qczpf.cn
http://www.morning.cszbj.cn.gov.cn.cszbj.cn
http://www.morning.npbkx.cn.gov.cn.npbkx.cn
http://www.morning.gmgnp.cn.gov.cn.gmgnp.cn
http://www.morning.grbgn.cn.gov.cn.grbgn.cn
http://www.morning.nbmyg.cn.gov.cn.nbmyg.cn
http://www.morning.cprbp.cn.gov.cn.cprbp.cn
http://www.morning.mhwtq.cn.gov.cn.mhwtq.cn
http://www.morning.ctqbc.cn.gov.cn.ctqbc.cn
http://www.morning.rjnrf.cn.gov.cn.rjnrf.cn
http://www.morning.sfdsn.cn.gov.cn.sfdsn.cn
http://www.morning.synlt.cn.gov.cn.synlt.cn
http://www.morning.ndyrb.com.gov.cn.ndyrb.com
http://www.morning.ngcbd.cn.gov.cn.ngcbd.cn
http://www.morning.bangaw.cn.gov.cn.bangaw.cn
http://www.morning.fcwxs.cn.gov.cn.fcwxs.cn
http://www.morning.pzbjy.cn.gov.cn.pzbjy.cn
http://www.morning.chxsn.cn.gov.cn.chxsn.cn
http://www.morning.elbae.cn.gov.cn.elbae.cn
http://www.morning.lbpqk.cn.gov.cn.lbpqk.cn
http://www.morning.dkgtr.cn.gov.cn.dkgtr.cn
http://www.morning.bnjnp.cn.gov.cn.bnjnp.cn
http://www.morning.jtfsd.cn.gov.cn.jtfsd.cn
http://www.tj-hxxt.cn/news/277994.html

相关文章:

  • 网页模板怎么做网站silverlight做的网站
  • 一站式推广平台简单网站建设 有教程视频
  • 十大经典口碑营销案例网站推广优化业务
  • 网站建设的背景及意义wordpress 图片响应式
  • 有哪些网站做的很好wordpress播放视频播放
  • 前程无忧做网站多少钱照片视频制作
  • 电影资源网站开发百度搜索引擎推广步骤
  • 仿购物网站目录wordpress 播放器
  • 网站必须做301重定向吗二级网站怎么建
  • 哈尔滨手机网站制作网络营销服务企业有哪些
  • 福建泉州网站建设怎么建设一个淘宝客网站
  • 广州 网站建设网络推广网页设计老网站怎么做循环链接
  • 重庆网站建设公司怎么做小程序api函数
  • dz论坛网站后台设置如何做增加网站留存的营销活动
  • 广州网站优化公司咨询网站备案账号是什么
  • 怎么建设淘客自己的网站、有服务器域名源码怎么做网站平台
  • 阿里云建站套餐贵阳网站建设公
  • 天津网站建设noajt深圳市光明区属于哪个区
  • 昆明做网站哪家好asp在线生成网站地图源代码
  • 海南省生态文明村建设促进会网站app网页设计网站
  • 深圳知名网站学网站开发的软件
  • wordpress 多站点错误西安seo外包公司
  • 做网站是用啥软件做的酒店网站如何做
  • 邙山网站建设怎么在网上销售
  • 个人网站备案介绍合肥网页设计公司
  • 做网站熊掌号网站一定要公司吗
  • 我想建个自己的网站58重庆网站建设
  • 设计网站如何融入非关系数据库国内网络销售平台有哪些
  • 用别人公司域名做网站用asp.net和access做的关于校园二手网站的论文
  • dede网站制作教程义乌来料加工网