【python】OpenCV—Tracking(10.4)—Centroid

在这里插入图片描述

文章目录

  • 1、任务描述
  • 2、人脸检测模型
  • 3、完整代码
  • 4、结果展示
  • 5、涉及到的库函数
  • 6、参考

1、任务描述

基于质心实现多目标(以人脸为例)跟踪

人脸检测采用深度学习的方法

核心步骤:

步骤#1:接受边界框坐标并计算质心
步骤#2:计算新边界框和现有对象之间的欧几里得距离
步骤 #3:更新现有对象的 (x, y) 坐标
步骤#4:注册新对象
步骤#5:注销旧对象
当旧对象无法与任何现有对象匹配总共 N 个后续帧时,我们将取消注册。

2、人脸检测模型

在这里插入图片描述

输入 1x3x300x300

在这里插入图片描述
resnet + ssd,分支还是挺多的

3、完整代码

质心跟踪代码

# import the necessary packages
from scipy.spatial import distance as dist
from collections import OrderedDict
import numpy as npclass CentroidTracker():def __init__(self, maxDisappeared=65):# initialize the next unique object ID along with two ordered# dictionaries used to keep track of mapping a given object# ID to its centroid and number of consecutive frames it has# been marked as "disappeared", respectivelyself.nextObjectID = 0# 用于为每个对象分配唯一 ID 的计数器。如果对象离开帧并且在 maxDisappeared 帧中没有返回,则将分配一个新的(下一个)对象 ID。self.objects = OrderedDict()# 对象 ID 作为键,质心 (x, y) 坐标作为值的字典self.disappeared = OrderedDict()# 保存特定对象 ID(键)已被标记为“丢失”的连续帧数(值)# store the number of maximum consecutive frames a given# object is allowed to be marked as "disappeared" until we# need to deregister the object from trackingself.maxDisappeared = maxDisappeared# 在我们取消注册该对象之前,允许将对象标记为“丢失/消失”的连续帧数。def register(self, centroid):# when registering an object we use the next available object# ID to store the centroid# 注册新的 idself.objects[self.nextObjectID] = centroidself.disappeared[self.nextObjectID] = 0self.nextObjectID += 1def deregister(self, objectID):# to deregister an object ID we delete the object ID from# both of our respective dictionaries# 注销掉 iddel self.objects[objectID]del self.disappeared[objectID]def update(self, rects):# check to see if the list of input bounding box rectangles# is empty# rects = (startX, startY, endX, endY)if len(rects) == 0: # 没有检测到目标# loop over any existing tracked objects and mark them# as disappeared# 我们将遍历所有对象 ID 并增加它们的disappeared计数for objectID in list(self.disappeared.keys()):self.disappeared[objectID] += 1# if we have reached a maximum number of consecutive# frames where a given object has been marked as# missing, deregister itif self.disappeared[objectID] > self.maxDisappeared:self.deregister(objectID)# return early as there are no centroids or tracking info# to updatereturn self.objects# initialize an array of input centroids for the current frameinputCentroids = np.zeros((len(rects), 2), dtype="int")# loop over the bounding box rectanglesfor (i, (startX, startY, endX, endY)) in enumerate(rects):# use the bounding box coordinates to derive the centroidcX = int((startX + endX) / 2.0)  # 检测框中心横坐标cY = int((startY + endY) / 2.0)  # 检测框中心纵坐标inputCentroids[i] = (cX, cY)# if we are currently not tracking any objects take the input# centroids and register each of them# 如果当前没有我们正在跟踪的对象,我们将注册每个新对象if len(self.objects) == 0:for i in range(0, len(inputCentroids)):self.register(inputCentroids[i])# otherwise, are are currently tracking objects so we need to# try to match the input centroids to existing object# centroidselse:# grab the set of object IDs and corresponding centroidsobjectIDs = list(self.objects.keys())objectCentroids = list(self.objects.values())# compute the distance between each pair of object# centroids and input centroids, respectively -- our# goal will be to match an input centroid to an existing# object centroidD = dist.cdist(np.array(objectCentroids), inputCentroids)# in order to perform this matching we must (1) find the# smallest value in each row and then (2) sort the row# indexes based on their minimum values so that the row# with the smallest value as at the *front* of the index# listrows = D.min(axis=1).argsort()# next, we perform a similar process on the columns by# finding the smallest value in each column and then# sorting using the previously computed row index listcols = D.argmin(axis=1)[rows]# in order to determine if we need to update, register,# or deregister an object we need to keep track of which# of the rows and column indexes we have already examinedusedRows = set()usedCols = set()# loop over the combination of the (row, column) index# tuplesfor (row, col) in zip(rows, cols):# if we have already examined either the row or# column value before, ignore it# val# 老目标被选中过或者新目标被选中过if row in usedRows or col in usedCols:continue# otherwise, grab the object ID for the current row,# set its new centroid, and reset the disappeared# counterobjectID = objectIDs[row]  # 老目标 idself.objects[objectID] = inputCentroids[col]  # 更新老目标 id 的质心为新目标的质心,因为两者距离最近self.disappeared[objectID] = 0  # 丢失索引重置为 0# indicate that we have examined each of the row and# column indexes, respectivelyusedRows.add(row)usedCols.add(col)# compute both the row and column index we have NOT yet# examinedunusedRows = set(range(0, D.shape[0])).difference(usedRows)unusedCols = set(range(0, D.shape[1])).difference(usedCols)# in the event that the number of object centroids is# equal or greater than the number of input centroids# we need to check and see if some of these objects have# potentially disappeared# 如果老目标不少于新目标if D.shape[0] >= D.shape[1]:# loop over the unused row indexesfor row in unusedRows:# grab the object ID for the corresponding row# index and increment the disappeared counterobjectID = objectIDs[row]  # 老目标 idself.disappeared[objectID] += 1  # 跟踪帧数 +1# check to see if the number of consecutive# frames the object has been marked "disappeared"# for warrants deregistering the object# 检查disappeared计数是否超过 maxDisappeared 阈值,如果是,我们将注销该对象if self.disappeared[objectID] > self.maxDisappeared:self.deregister(objectID)# otherwise, if the number of input centroids is greater# than the number of existing object centroids we need to# register each new input centroid as a trackable objectelse:  # 新目标多于老目标for col in unusedCols:self.register(inputCentroids[col])  # 注册新目标# return the set of trackable objectsreturn self.objects

self.nextObjectID = 0用于为每个对象分配唯一 ID 的计数器。如果对象离开帧并且在 maxDisappeared 帧中没有返回,则将分配一个新的(下一个)对象 ID。

self.objects = OrderedDict() 对象 ID 作为键,质心 (x, y) 坐标作为值的字典

self.disappeared = OrderedDict() 保存特定对象 ID(键)已被标记为“丢失”的连续帧数(值)

self.maxDisappeared = maxDisappeared,在我们取消注册该对象之前,允许将对象标记为“丢失/消失”的连续帧数。

有初始化,注册,注销,更新过程

核心代码在 def update

如果没有检测到目标,当前所有 id 的 self.disappeared 会加 1

如果注册的 id 为空,这当前帧所有质心会被注册上新的 id,否则会计算历史质心和当前帧质心的距离,距离较近的匹配上,更新 id 的质心,没有被分配上的历史质心对应 id 的 self.disappeared 会加 1,没有被分配的当前帧质心会被注册新的 id

超过 self.maxDisappeared 的 id 会被注销掉

根据 D 计算 rowscols 的时候有点绕,看看下面这段注释就会好理解一些

"""
Darray([[  2.        , 207.48252939],[206.65188119,   1.41421356]])D.min(axis=1)  每行最小值array([2.        , 1.41421356])rows 每行最小值再排序,表示按从小到大行数排序,比如最小的数在rows[0]所在的行,第二小的数在rows[1]所在的行array([1, 0])D.argmin(axis=1) 返回指定轴最小值的索引,也即每行最小值的索引array([0, 1])cols  每行每列最小值,按行从小到大排序array([1, 0])
"""

再看看一个 shape 不一样的例子

import numpy as npD = np.array([[2., 1, 3],[1.1, 1.41421356, 0.7]])print(D.min(axis=1))
print(D.argmin(axis=1))rows = D.min(axis=1).argsort()cols = D.argmin(axis=1)[rows]print(rows)
print(cols)"""
[1.  0.7]
[1 2][1 0]
[2 1]
"""

实际运用的时候,rows 是历史 ids,cols 是当前帧的 ids


人脸检测+跟踪+绘制结果 pipeline

# USAGE
# python object_tracker.py --prototxt deploy.prototxt --model res10_300x300_ssd_iter_140000.caffemodel# import the necessary packages
from CentroidTracking.centroidtracker import CentroidTracker
import numpy as np
import argparse
import imutils
import cv2# construct the argument parse and parse the arguments
ap = argparse.ArgumentParser()
ap.add_argument("-p", "--prototxt", default="deploy.prototxt.txt",help="path to Caffe 'deploy' prototxt file")
ap.add_argument("-m", "--model", default="res10_300x300_ssd_iter_140000.caffemodel",help="path to Caffe pre-trained model")
ap.add_argument("-c", "--confidence", type=float, default=0.5,help="minimum probability to filter weak detections")
args = vars(ap.parse_args())# initialize our centroid tracker and frame dimensions
ct = CentroidTracker()
(H, W) = (None, None)# load our serialized model from disk
print("[INFO] loading model...")
net = cv2.dnn.readNetFromCaffe(args["prototxt"], args["model"])# initialize the video stream
print("[INFO] starting video stream...")
vs = cv2.VideoCapture("4.mp4")index = 0# loop over the frames from the video stream
while True:index += 1# read the next frame from the video stream and resize itrect, frame = vs.read()if not rect:breakframe = imutils.resize(frame, width=600)(H, W) = frame.shape[:2]# construct a blob from the frame, pass it through the network,# obtain our output predictions, and initialize the list of# bounding box rectanglesblob = cv2.dnn.blobFromImage(frame, 1.0, (W, H),(104.0, 177.0, 123.0))net.setInput(blob)detections = net.forward()  # (1, 1, 200, 7)"""detections[0][0][0]array([0.        , 1.        , 0.9983138 , 0.418003  , 0.22666326,0.5242793 , 0.50829136], dtype=float32)第三个维度是 score最后四个维度是左上右下坐标"""rects = []  # 记录所有检测框的左上右下坐标# loop over the detectionsfor i in range(0, detections.shape[2]):# filter out weak detections by ensuring the predicted# probability is greater than a minimum thresholdif detections[0, 0, i, 2] > args["confidence"]:# compute the (x, y)-coordinates of the bounding box for# the object, then update the bounding box rectangles listbox = detections[0, 0, i, 3:7] * np.array([W, H, W, H])rects.append(box.astype("int"))# draw a bounding box surrounding the object so we can# visualize it(startX, startY, endX, endY) = box.astype("int")cv2.rectangle(frame, (startX, startY), (endX, endY),(0, 0, 255), 2)# update our centroid tracker using the computed set of bounding# box rectanglesobjects = ct.update(rects)# loop over the tracked objectsfor (objectID, centroid) in objects.items():# draw both the ID of the object and the centroid of the# object on the output frametext = "ID {}".format(objectID)cv2.putText(frame, text, (centroid[0] - 10, centroid[1] - 10),cv2.FONT_HERSHEY_SIMPLEX, 0.5, (0, 0, 255), 2)cv2.circle(frame, (centroid[0], centroid[1]), 4, (0, 0, 255), -1)# show the output framecv2.imshow("Frame", frame)# cv2.imwrite(f"./frame4/{str(index).zfill(3)}.jpg", frame)key = cv2.waitKey(1) & 0xFF# if the `q` key was pressed, break from the loopif key == ord("q"):break# do a bit of cleanup
cv2.destroyAllWindows()
vs.release()

网络前向后的结果,第三个维度是 score,最后四个维度是左上右下坐标

核心跟新坐标的过程在 objects = ct.update(rects)

4、结果展示

单个人脸

tracking-face1

多个人脸

tracking-face2

多个人脸,存在漏检情况,id 还是持续了很长一段时间(算法中配置的是 50 帧)

tracking-face3

多个人脸

tracking-face4

5、涉及到的库函数

scipy.spatial.distance.cdist 是 SciPy 库中 spatial.distance 模块的一个函数,用于计算两个输入数组之间所有点对之间的距离。这个函数非常有用,特别是在处理大规模数据集时,需要计算多个点之间的距离矩阵。

函数的基本用法如下:

from scipy.spatial.distance import cdist  # 假设 XA 和 XB 是两个二维数组,其中每一行代表一个点的坐标  
XA = ...  # 形状为 (m, n) 的数组,m 是点的数量,n 是坐标的维度  
XB = ...  # 形状为 (p, n) 的数组,p 是另一个集合中点的数量  # 计算 XA 和 XB 中所有点对之间的距离  
# metric 参数指定使用的距离度量,默认为 'euclidean'(欧氏距离)  
D = cdist(XA, XB, metric='euclidean')

在这个例子中,D 将是一个形状为 (m, p) 的数组,其中 D[i, j] 表示 XA 中第 i 个点和 XB 中第 j 个点之间的距离。

cdist 函数支持多种距离度量,通过 metric 参数指定。除了默认的欧氏距离外,还可以选择如曼哈顿距离(‘cityblock’)、切比雪夫距离(‘chebyshev’)、闵可夫斯基距离(‘minkowski’,需要额外指定 p 值)、余弦距离(注意:虽然余弦通常用作相似度度量,但可以通过 1 - cosine(u, v) 转换为距离度量,不过 cdist 不直接支持负的余弦距离,需要手动计算)、汉明距离(‘hamming’,仅适用于布尔数组或整数数组)等。

6、参考

  • https://github.com/gopinath-balu/computer_vision
  • https://pyimagesearch.com/2018/07/23/simple-object-tracking-with-opencv/
  • 链接:https://pan.baidu.com/s/1UX_HmwwJLtHJ9e5tx6hwOg?pwd=123a
    提取码:123a
  • 目标跟踪(7)使用 OpenCV 进行简单的对象跟踪
  • https://pixabay.com/zh/videos/woman-phone-business-casual-154833/

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/web/57781.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

萤火虫算法优化BILSTM神经网络多输入回归分析

目录 LSTM的基本定义 LSTM实现的步骤 BILSTM神经网络 代码 结果分析 展望 完整代码下载:的MATALB代码(代码完整,数据齐全)资源-CSDN文库 https://download.csdn.net/download/abc991835105/88755564 背影 bp神经网络是一种成熟的神经网络,应用非常广,本文用萤火虫算法…

通过嵌套循环输出二维矩阵

输出以下4*5的矩阵。 1 2 3 4 52 4 6 8 103 6 9 12 154 8 12 16 20 输入格式: 无。 输出格式: 二维矩阵 代码如下&#xff1a; #include <stdio.h> int main() {for (int i 1; i < 4; i) {//行for (int j 1; j < 5; …

【maven】idea执行了maven的install命令给本地安装项目依赖包 安装后删除

目录 事件起因环境和工具操作过程解决办法1、找到对应的目录下的文件&#xff0c;手动去删除&#xff0c;比如我的依赖库的路径是D:\qc_code\apache-maven-3.8.2\repository 我只需要找到这个目录下对应的依赖包进行手动删除即可&#xff08;不推荐&#xff0c;强行删除文件夹文…

PostgreSQL 到 PostgreSQL 数据迁移同步

简述 PostgreSQL 是一个历史悠久且广泛使用的数据库&#xff0c;不仅具备标准的关系型数据库能力&#xff0c;还具有相当不错的复杂 SQL 执行能力。用户常常会将 PostgreSQL 应用于在线事务型业务&#xff0c;以及部分数据分析工作&#xff0c;所以 PostgreSQL 到 PostgreSQL …

Java项目实战II基于Java+Spring Boot+MySQL的智能推荐的卫生健康系统(开发文档+数据库+源码)

目录 一、前言 二、技术介绍 三、系统实现 四、文档参考 五、核心代码 六、源码获取 全栈码农以及毕业设计实战开发&#xff0c;CSDN平台Java领域新星创作者&#xff0c;专注于大学生项目实战开发、讲解和毕业答疑辅导。获取源码联系方式请查看文末 一、前言 基于Java、…

免费插件集-illustrator插件-Ai插件-闭合开放路径

文章目录 1.介绍2.安装3.通过窗口>扩展>知了插件4.功能解释5.总结 1.介绍 本文介绍一款免费插件&#xff0c;加强illustrator使用人员工作效率&#xff0c;实现图形编辑中闭合开放路径。首先从下载网址下载这款插件https://download.csdn.net/download/m0_67316550/8789…

WPF 实现冒泡排序可视化

WPF 实现冒泡排序可视化 实现冒泡排序代码就不过多讲解,主要是实现动画效果思路,本demo使用MVVM模式编写,读者可自行参考部分核心代码,即可实现如视频所示效果。 对于新手了解算法相关知识应该有些许帮助,至于其它类型排序,也可按该思路自行修改实现。 直接上代码,页面布…

老电脑不能装纯净版windows

手上有一台2016年的老笔记本电脑&#xff0c;用了8年&#xff0c;基本上能换的都换了一遍&#xff0c;散热风扇换了&#xff0c;键盘换了&#xff0c;屏幕换了&#xff0c;扬声器也换了&#xff0c;内存也换大了&#xff0c;甚至都换过固态硬盘&#xff0c;但是CPU没法换&#…

angular登录按钮输入框监听

说明&#xff1a;angular实现简单的登录页面&#xff0c;监听输入框的值&#xff0c;打印出来&#xff0c;按钮监听&#xff0c;打印数据 效果图: step1:E:\projectgood\ajnine\untitled4\src\app\app.config.ts import { ApplicationConfig, provideZoneChangeDetection } …

Java基于SpringBoot+Vue框架的房屋租赁管理系统(附源码,文档)

博主介绍&#xff1a;✌Java徐师兄、7年大厂程序员经历。全网粉丝13w、csdn博客专家、掘金/华为云等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取源码联系&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb; 不…

【前端】CSS知识梳理

基础&#xff1a;标签选择器、类选择器、id选择器和通配符选择器 font:font-style(normal) font-weight(400) font-size(16px) /line-height(0) font-family(宋体&#xff09; 复合&#xff1a; 后代选择器&#xff08; &#xff09;、子选择器&#xff08;>)、并集选择器(…

盖电子章的软件

e章宝&#xff08;也称为e-章宝&#xff09;是一款专业的电子印章管理和应用软件&#xff0c;它可以帮助用户创建、管理和使用电子印章。这款软件特别适用于需要频繁处理文件盖章的企业和个人&#xff0c;比如在合同签署、文件审批等场景中。 e章宝的主要功能包括&#xff1a;…

第16课 核心函数(方法)

掌握常用的内置函数及其用法。 数学类函数&#xff1a;abs、divmod、max、min、pow、round、sum。 类型转换函数&#xff1a;bool、int、float、str、ord、chr、bin、hex、tuple、list、dict、set、enumerate、range、object。 序列操作函数&#xff1a;all、any、filter、m…

基于AI深度学习的中医针灸实训室腹针穴位智能辅助定位系统开发

在中医针灸的传统治疗中&#xff0c;穴位取穴的精确度对于治疗效果至关重要。然而&#xff0c;传统的定位方法&#xff0c;如体表标志法、骨度折量法和指寸法&#xff0c;由于观察角度、个体差异&#xff08;如人体姿态和皮肤纹理&#xff09;以及环境因素的干扰&#xff0c;往…

C++ 实现俄罗斯方块游戏

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

《高频电子线路》—— 相位平衡条件判断准则

文章内容来源于【中国大学MOOC 华中科技大学通信&#xff08;高频&#xff09;电子线路精品公开课】&#xff0c;此篇文章仅作为笔记分享。 相位平衡条件判断准则 反馈电压或者从电感上获得&#xff0c;或者从电容上获得。分别为电感反馈三端振荡器&#xff0c;或者是电容反馈…

Chromium 在WebContents中添加自定义数据c++

为了能在WebContents中添加自定义数据先看下几个关键类的介绍。 一、WebContents 介绍&#xff1a; WebContents是content模块核心&#xff0c;是呈现 Web 内容&#xff08;通常为 HTML&#xff09;位于矩形区域中。 最直观的是一个浏览器标签对应一个WebContents&#xff0c…

公众号黑名单(资源类)仅个人备份,便于查看

公众号黑名单&#xff08;资源类&#xff09; 如标题&#xff0c;仅作为云备份供我本人参考&#xff0c;我本地还有一个备份&#xff0c;只是为了方便本人在不同设备查看。 本来有一个内容更多的列表&#xff0c;后来无意间丢失了&#xff0c;因此重建了一个表 名称原因CAE仿真…

砥砺十年风雨路,向新而行创新程丨怿星科技十周年庆典回顾

10月24日&#xff0c;是一年中的第256天&#xff0c;也是程序员节&#xff0c;同时也是怿星的生日。2014年到2024年&#xff0c;年华似水匆匆一瞥&#xff0c;多少岁月轻描淡写&#xff0c;怿星人欢聚一堂&#xff0c;共同为怿星科技的十周年庆生&#xff01; 01.回忆往昔&…

Android——横屏竖屏

系统配置变更的处理机制 为了避免横竖屏切换时重新加载界面的情况&#xff0c;Android设计了一中配置变更机制&#xff0c;在指定的环境配置发生变更之时&#xff0c;无需重启活动页面&#xff0c;只需执行特定的变更行为。该机制的视线过程分为两步&#xff1a; 修改 Androi…