计算机视觉——基于 Yolov8 目标检测与 OpenCV 光流实现目标追踪
1. 概述
目标检测(Object Detection)和目标追踪(Object Tracking)是计算机视觉中的两个关键技术,它们在多种实际应用场景中发挥着重要作用。
目标检测指的是在静态图像或视频帧中识别出特定类别的目标对象,并通常以矩形框(bounding box)的形式标出其位置。目标检测算法通常只处理单个图像帧,其任务是检测出该帧中所有感兴趣的对象。
目标追踪则涉及对视频中连续帧里的目标对象进行识别和跟踪。与目标检测不同,目标追踪需要在视频序列中维持对目标的识别,确保同一目标在不同帧中被连续检测和标记,即使目标在场景中移动或变换姿态。目标追踪算法需要处理整个视频序列,确保目标的一致性和唯一性。
本文旨在帮助读者理解“目标检测”和“目标追踪”这两个概念,并指导如何在编程中实现这些技术以及如何对它们进行可视化展示。这些技术在商业领域的应用非常广泛,包括但不限于自动化农业监控、机器人导航、交通流量分析等。
2. 检测和跟踪
目标检测是针对单个视频帧或图像的。对象检测算法的任务是在这张静态图片中识别并定位一个或多个特定的对象。这通常涉及到使用矩形框(bounding boxes)来标出对象的位置,并且可能会包括对象的类别信息。
对象跟踪则是一项更为复杂的任务,它涉及视频序列中的连续帧。对象跟踪算法不仅要识别出视频中的每一帧中的对象,还要能够在连续的帧之间关联同一个对象,确保对同一个对象的跟踪是连续和一致的。这通常需要为每个对象分配一个唯一的标识符(ID),并在整个视频序列中维持这个 ID 的一致性。
在对象跟踪中,可能会用到以下几种技术:
- 卡尔曼滤波器(Kalman Filter):这是一种数学算法,用于从一系列的测量中估计对象的位置和速度,即使在测量中含有噪声或误差时也能做出准确的预测。
- 光流(Optical Flow):这是一种计算图像中物体运动模式的技术,它可以估计图像中每个像素或一组特征点的运动速度,从而帮助跟踪移动的对象。
对象跟踪的算法示例包括 DeepSort、Sort 以及 OpenCV 中的原生跟踪算法等。这些算法利用了不同的策略和技术来有效地在视频帧之间跟踪对象,即使在目标遮挡、快速运动或光照变化等复杂情况下也能有效工作。
3. 光流追踪
光流是一种描述和估计图像中物体运动的计算机视觉技术。当物体或相机在三维空间中移动时,图像序列中的相应物体会出现视觉上的移动。光流利用这种视觉变化来推断物体在连续视频帧或图像序列中的运动。
光流的关键特点包括:
- 二维向量场:光流表示为一个二维向量场,其中每个向量代表一个位移向量。
- 位移向量:每个向量显示了图像中一个点从第一帧到第二帧的位移。这些向量的方向和大小通常用来表示物体的运动方向和速度。
- 稀疏与密集光流:光流可以是稀疏的,即只计算图像中关键特征点的位移;也可以是密集的,即计算图像中每个像素的位移。
- 应用领域:光流在多个领域有广泛应用,包括但不限于:
- 结构从运动(Structure from Motion)
- 视频压缩
- 视频稳定化
- 物体跟踪
- 运动检测
- 机器人导航
- 计算方法:光流可以通过多种算法计算,包括基于梯度的方法、基于频域的方法、以及基于能量最小化的方法。
- 局限性:光流可能受到多种因素的影响,如光照变化、遮挡、快速运动等,这些因素可能导致光流计算的不准确或失败。

4. Yolov8 算法
YOLOv8 是基于 YOLO(You Only Look Once)的模型,由 Ultralytics 开发。通常,这个模型专门用于:
- 检测对象
- 分割
- 分类对象
YOLOv8 系列模型被广泛认为是该领域最好的模型之一,提供卓越的准确性和更快的性能。它的易用性归功于它由五个独立的模型组成,每个模型都满足不同的需求、时间限制和范围。

在比较 YOLOv8 不同版本时,可以看到模型在多个方面存在差异:
- 平均精度(mAP):不同版本的 YOLOv8 在平均精度上有所不同,这是一个衡量模型在目标检测任务上性能的指标。
- 参数数量:各模型拥有的参数数量也不同,参数数量越多,模型的复杂度通常越高,这可能会影响模型的推理速度和资源消耗。
- 资源消耗和速度:一些模型可能在运行时更为资源密集,这可能会影响处理速度。例如,X 模型被认为是最先进的,提供了更高的精度,但可能会导致视频或图像渲染速度变慢。而 Nano 模型(N)则是最快的选择,但在准确性上做了一些妥协。
YOLOv8 系列提供了不同版本的模型以适应不同的应用场景和需求。用户可以根据自己的具体需求,如对速度或准确性的偏好,来选择最合适的模型版本。例如,如果应用场景对速度要求极高,可能更适合选择 Nano 模型;而如果对检测精度有较高要求,X 模型可能是更好的选择。
4.1 SORT 算法
SORT 算法由 Alex Bewley 提出,是一种用于视频序列中二维多目标跟踪的跟踪算法。它是其他跟踪算法(如 DeepSort)的基础。由于其极简主义的特点,该算法非常易于使用和实现。您可以通过这里了解更多关于该算法的信息,甚至可以查看源代码。
4.2 数学原理
4.2.1 卷积神经网络(CNN)

卷积神经网络结构 [来源:点击这里 ]
卷积神经网络(CNN)是基于卷积层和池化层的神经网络。正如在《卷积神经网络全面指南——简单易懂的方式》中所述,“卷积操作的目标是从输入图像中提取高级特征,例如边缘”。简单来说,卷积层负责从初始输入中提取最重要的特征。而池化层则负责简化内容,即“负责减少卷积特征的空间尺寸”。通过这种过程,机器能够理解初始输入的特征。因此,我们得到了一个复杂的特征学习过程,其中卷积层和池化层相互堆叠[4]。
4.2.1 光流数学原理
连续两帧之间的像素运动被称为光流。无论是相机在运动还是场景在运动,都取决于运动的主体。
光流的基本目标是计算由于相机运动或物体运动而导致的物体的位移向量。为了计算所有图像像素或稀疏特征集合的运动向量,我们的主要目标是确定它们的位移。
如果我们用一张图片来说明光流问题,它看起来会像这样:

光流通过定义一个密集的向量场来发挥作用,它是计算机视觉和机器学习应用中的关键组成部分,包括目标跟踪、目标识别、运动检测和机器人导航等。在这个场中,每个像素都被赋予了一个位移向量,这有助于确定输入视频序列中每个移动目标像素的方向和速度[5]。
5. 实践操作
5.1 使用 YOLOv8 和 SORT 进行目标跟踪
首先,需要了解如何使用 YOLOv8 模型。
pip install ultralytics
# 如果在 Jupyter Notebook 中,请使用 !pip install ultralytics
然后:
from ultralytics import YOLO
# 假设你已经安装了 opencv
import cv2 MODEL = "yolov8x.pt" # 创建你选择的模型实例
model = YOLO(MODEL) results = model("people.jpg",show=True)
# "0" 将会一直显示窗口,直到有按键按下(适用于视频)
# waitKey(1) 将会显示一帧图像 1 毫秒
cv2.waitKey(0)

这是从 YOLOv8x 版本获得的结果。
现在,已经了解了基础内容,接下来将进入真正的目标检测和跟踪。
import cv2
from ultralytics import YOLO
import math
# 使用 cvzone,它比 cv2 更美观且易于使用
import cvzone
# 从 SORT 导入所有函数
from sort import *
# cap = cv2.VideoCapture(0) #用于网络摄像头
# cap.set(3,1280)
# cap.set(4,720)
cap = cv2.VideoCapture("data/los_angeles.mp4")
model = YOLO("yolos/yolov8n.pt")
cap 变量是我们使用的视频实例,model 是 YOLOv8 模型的实例。
classes = {0: 'person',
1: 'bicycle',
2: 'car',
...
78: 'hair drier',
79: 'toothbrush'}result_array = [classes[i] for i in range(len(classes))]
最初,从 YOLOv8 API 获取的类别是以浮点数或类别 ID 的形式。当然,每个数字都对应一个类别名称。创建一个字典会更简单,如果需要的话,还可以将其转换为数组(我懒得手动做了)。
# 线条坐标(稍后解释)
l = [593,500,958,500]
while True:# 按帧读取视频内容_, frame = cap.read() # 每一帧都通过 YOLO 模型results = model(frame,stream=True) for r in results: # 创建边界框boxes = r.boxes for box in boxes:# 提取坐标x1, y1, x2, y2 = box.xyxy[0] x1, y1, x2, y2 = int(x1),int(y1),int(x2),int(y2)# 创建实例的宽度和高度w,h = x2-x1,y2-y1 cvzone.cornerRect(frame,(x1,y1,w,h),l=5, rt = 2, colorC=(255,215,0), colorR=(255,99,71))# 每个边界框的置信度或准确度conf = math.ceil((box.conf[0]*100))/100 # 类别 ID(数字)cls = int(box.cls[0])
这是检测每个对象的部分。现在是时候跟踪并统计道路上的每一辆车了:
while True:_, frame = cap.read()results = model(frame,stream=True)detections = np.empty((0,5)) #创建一个空数组 for r in results: boxes = r.boxes for box in boxes:''' 其余代码 '''ins = np.array([x1,y1,x2,y2,conf]) #每个对象都应该这样记录detections = np.vstack((detections,ins)) # 然后将它们堆叠在一起形成一个公共数组 tracks = tracker.update(detections) #将我们的检测结果发送到跟踪函数cv2.line(frame, (l[0],l[1]),(l[2],l[3]),color=(255,0,0),thickness=3) #作为阈值的线条
现在,将创建一个数组来存储我们所有的检测结果。接下来,将这个数组发送到跟踪函数中,在那里可以提取唯一的 ID 和边界框坐标(这些坐标与之前的相同)。重要的是 cv2.line 实例:正在使用特定的坐标生成一条线。如果某些 ID 的车辆穿过这条线,总计数将增加。本质上,正在建立一个根据车辆 ID 运行的车辆计数器。
for result in tracks:x1,y1,x2,y2,id = resultx1,y1,x2,y2 = int(x1),int(y1),int(x2),int(y2)#.putTextRect 是为了在边界框上方放置一个矩形cvzone.putTextRect(frame,f'{result_array[cls]} {conf} id:{int(id)} ',(max(0,x1),max(35,y1-20)),scale=1, thickness=1, offset=3, colorR=(255,99,71))#边界框中心的坐标cx,cy = x1+w//2, y1+h//2 if l[0]<cx<l[2] and l[1]-10<cy<l[3]+10:if totalCount.count(id) == 0:#统计每一个新穿过线的车辆totalCount.append(id)#当一个对象穿过线时,线条会改变颜色cv2.line(frame, (l[0],l[1]),(l[2],l[3]),color=(127,255,212),thickness=5)#显示计数车辆数量的矩形cvzone.putTextRect(frame,f' Total Count: {len(totalCount)} ',(70,70),scale=2, thickness=1, offset=3, colorR=(255,99,71))m.write(frame)cv2.imshow("Image",frame)cv2.waitKey(1)
现在,最有趣的部分来了:cx 和 cy 是边界框中心的坐标。通过使用这些值,可以判断车辆是否穿过了指定的线条(查看代码)。如果车辆确实穿过了线条,我们的下一步是验证分配给这辆车的 ID 是否唯一,也就是说,这辆车之前没有穿过线条。
5.1 结果
车辆跟踪和计数
如图所示,我在这里显示了边界框,以及对应的目标类别、置信度和唯一 ID。左上角显示了总目标数量。
5.2 使用光流进行目标跟踪
SORT 算法通过分配 ID 并将当前帧与上一帧建立联系来进行跟踪,而光流过程则更侧重于运动估计。换句话说,它估计哪些物体在移动,并估计物体的运动方向或向量。
光流有两种类型:
- 稀疏光流(Sparse Optical Flow)
- 密集光流(Dense Optical Flow)
虽然密集光流会计算每一帧中每个像素的光流(从而创建一个仅包含移动物体的图像),但稀疏光流则计算物体的主要特征点的流向量。
现在我们来看看代码:
flow = cv2.calcOpticalFlowFarneback(prevgray, gray, None, 0.5, 3, 15, 3, 5, 1.2, 0)
首先,我们需要计算光流。我们可以通过 OpenCV 库来实现,它已经包含了这个算法。当然,不要忘记将这个函数放在一个while循环中,以便算法能够持续计算光流(读取视频和处理视频的过程与使用 YOLOv8 和 SORT 进行目标跟踪小节中的内容相同)。
def draw_flow(img, flow, step=16):# 获取图像的高和宽h, w = img.shape[:2]# 在图像上以网格模式创建点y, x = np.mgrid[step/2:h:step, step/2:w:step].reshape(2,-1).astype(int)# 获取网格点处的光流方向fx, fy = flow[y,x].T# 创建线条以显示光流方向lines = np.vstack([x, y, x-fx, y-fy]).T.reshape(-1, 2, 2)lines = np.int32(lines + 0.5)# 将灰度图像转换为彩色img_bgr = cv2.cvtColor(img, cv2.COLOR_GRAY2BGR)# 绘制线条以表示光流方向cv2.polylines(img_bgr, lines, 0, (0, 255, 0))# 在线条的起始点绘制小圆圈 for (x1, y1), (_x2, _y2) in lines:cv2.circle(img_bgr, (x1, y1), 1, (0, 255, 0), -1)return img_bgr
这是负责稀疏光流的def函数。我们只需要从图像中提取点,将其转换为流向量,然后简单地进行可视化。
def draw_hsv(flow):# 获取流向量矩阵的高和宽h, w = flow.shape[:2]# 将流向量矩阵分解为其 x 和 y 分量fx, fy = flow[:,:,0], flow[:,:,1]# 计算流向量的角度并转换为度ang = np.arctan2(fy, fx) + np.pi# 计算流向量的大小v = np.sqrt(fx*fx + fy*fy)# 创建一个空的 HSV 图像hsv = np.zeros((h, w, 3), np.uint8)# 根据流向量的角度设置 HSV 图像的色调通道hsv[...,0] = ang * (180 / np.pi / 2)# 将 HSV 图像的饱和度通道设置为最大值hsv[...,1] = 255# 根据流向量的大小设置 HSV 图像的亮度通道hsv[...,2] = np.minimum(v * 4, 255)# 将 HSV 图像转换为 BGR 色彩空间bgr = cv2.cvtColor(hsv, cv2.COLOR_HSV2BGR)# 返回最终的 BGR 图像,其中包含光流可视化结果 return bgr
这是负责密集光流的def函数。在这里,该函数将流向量转换为色调值,因此最终结果将仅显示移动的像素[7]。
相关文章:
计算机视觉——基于 Yolov8 目标检测与 OpenCV 光流实现目标追踪
1. 概述 目标检测(Object Detection)和目标追踪(Object Tracking)是计算机视觉中的两个关键技术,它们在多种实际应用场景中发挥着重要作用。 目标检测指的是在静态图像或视频帧中识别出特定类别的目标对象࿰…...
PHP使用pandoc把markdown文件转为word
文章目录 首先安装pandocPHP处理 服务器操作系统是Linux,centos 首先安装pandoc yum install -y pandoc安装完成后输入如下代码,检查安装是否成功 pandoc --versionPHP处理 我把markdown内容存到了数据库里,所以要从数据库读取内容。对内容…...
第二十四天 - 分布式任务队列 - Celery高级应用 - 练习:分布式监控任务系统
一、Celery核心机制解析 1.1 分布式架构四要素 # celery_config.py BROKER_URL redis://:passwordlocalhost:6379/0 # 消息中间件 RESULT_BACKEND redis://:passwordlocalhost:6379/1 # 结果存储 TASK_SERIALIZER json ACCEPT_CONTENT [json] TIMEZONE Asia/Shanghai核…...
针对MCP认证考试中的常见技术难题进行实战分析与解决方案分享
一、身份与权限管理类难题 场景1:Active Directory组策略(GPO)不生效 问题现象:客户端计算机未应用新建的组策略。排查步骤: 检查GPO链接顺序:使用gpresult /r查看策略优先级,确保目标OU的GPO…...
【滑动窗口】最⼤连续 1 的个数 III(medium)
⼤连续 1 的个数 III(medium) 题⽬描述:解法(滑动窗⼝):算法思路:算法流程: C 算法代码:Java 算法代码: 题⽬链接:1004. 最⼤连续 1 的个数 III …...
OBS 日期时间.毫秒时间脚本 date-and-time.lua
文章目录 OBS 日期时间.毫秒时间脚本:效果 OBS 日期时间.毫秒时间脚本: obs obslua source_name ""last_text "" format_string "" activated false-- 此函数用于获取精确的毫秒级时间戳&#…...
探索大语言模型(LLM):目标、原理、挑战与解决方案
提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档 文章目录 前言语言模型的目标语言模型的数学表示语言模型面临的挑战解决参数量巨大的方法1. 马尔可夫假设2. 神经网络语言模型3.自监督学习4. 分布式表示 脑图总结 前言 在自…...
ES基本操作(Java API)
1. 导入restClient依赖 <!-- es --><dependency><groupId>org.elasticsearch.client</groupId><artifactId>elasticsearch-rest-high-level-client</artifactId><version>7.12.1</version></dependency> <!…...
得物官网sign签名逆向分析
打开得物官网,点击鞋类,可以看到请求 直接搜sign function p(e) {return f()("".concat(e ? s()(e).sort().reduce(function(t, n) {return "".concat(t).concat(n).concat(e[n])}, "") : "", "048a9…...
Agent的九种设计模式 介绍
Agent的九种设计模式 介绍 一、ReAct模式 原理:将推理(Reasoning)和行动(Acting)相结合,使Agent能够在推理的指导下采取行动,并根据行动的结果进一步推理,形成一个循环。Agent通过生成一系列的思维链(Thought Chains)来明确推理步骤,并根据推理结果执行相应的动作,…...
vivado 时钟IP核(MMCM PLL)
CMT简介 FPGA中时钟管理模块(CMT)包括PLL和MMCM,用于将时钟倍频(比如输入时钟25M,我们要产生50M时钟)、分频(在不影响系统功能的前提下,较低的工作时钟,能够降低系统功耗)、改变相位偏移或占空比等。 当需要…...
hackmyvm-airbind
收集信息 arp-scan -l nmap -sS -v 192.168.195.162 访问扫描到的ip,直接跳转到登录页面,利用admin/admin弱口令登录 在settings.php中找到一处文件上传,上传一句话木马,上传成功 反弹shell 上传php-reverse-shell.php 抓包&am…...
知识了解03——怎么解决使用npm包下载慢的问题?
1、为什么使用npm下载包会下载的慢 因为使用npm下载包时,默认使用国外服务器进行下载,此时的网络传输需要经过漫长的海底电缆,因此下载速度会变慢 2、怎么解决?(切换镜像源) (1)方…...
[晕事]今天做了件晕事71,_GNU_SOURCE
今天碰到一件晕,从别的地方搬运来一段代码,里面有使用in6_pktinfo这个结构体: struct in6_pktinfo pktinfo; 通过搜索发现需要include的头文件就是:netinet/in.h。加上这个头文件,还是出现找不到结构体的错误。最后通过仔细查看头文件,发现,这个结构体定义是在宏判断里…...
【算法数据结构】leetcode37 解数独
37. 解数独 - 力扣(LeetCode) 题目描述: 题目要求每一行 ,每一列,每个3*3 的子框只能出现一次。每个格子的数字范围1-9. 需要遍历每个空格填入可能的数字,并验证符合规则。如果符合就填入,不符…...
招商信诺原点安全:一体化数据安全管理解决方案荣获“鑫智奖”!
近日,“鑫智奖 2025第七届金融数据智能优秀解决方案评选”榜单发布,原点安全申报的《招商信诺:数据安全一体化管理解决方案》荣获「信息安全创新优秀解决方案」。 “鑫智奖第七届金融数据智能优秀解决方案评选”活动由金科创新社主办&#x…...
楼宇自控系统如何为现代建筑打造安全、舒适、节能方案
在科技飞速发展的当下,现代建筑对功能和品质的要求日益提升。楼宇自控系统作为建筑智能化的核心技术,宛如一位智慧的“管家”,凭借先进的技术手段,为现代建筑精心打造安全、舒适、节能的全方位解决方案,让建筑真正成为…...
吃透LangChain(四):消息管理与聊天历史存储
消息存储在内存 下面我们展示一个简单的示例,其中聊天历史保存在内存中,此处通过全局 Python 字典实现。我们构建一个名为 get_session_history 的可调用对象,引用此字典以返回chatMessageHistory实例。通过在运行时向 RunnablewithMessageHi…...
【差分隐私相关概念】瑞丽差分隐私(RDP)命题4
命题4的证明详解(分情况讨论) 背景与设定 机制: f : D → R f: \mathcal{D} \to \mathcal{R} f:D→R 是由 n n n 个 ϵ \epsilon ϵ-差分隐私机制自适应组合而成。相邻输入: D D D 和 D ′ D D′ 是相邻数据集。目标…...
RoBoflow数据集的介绍
https://public.roboflow.com/object-detection(该数据集的网址) 可以看到一些基本情况 如果我们想要下载,直接点击 点击图像可以看到一些基本情况 可以点击红色箭头所指,右边是可供选择的一些yolo模型的格式 如果你想下载…...
免费将AI生成图像放大4倍的方法
有些人不需要任何高级工具和花哨的技巧;他们只需要一种简单的方法来提升图像分辨率而不损失任何质量 — 今天,我们将学习如何做到这一点。 生成AI图像最大的问题之一是什么?最终结果通常分辨率非常低。 这会导致很多不同的问题,特别是对于那些想要在内容或项目中使用这些…...
滑动过期机制——延长 Token有效期
文章目录 1. Flask 后端代码(支持 WebSocket)2. Android Studio Java 前端代码(使用 Socket.IO)代码说明后端前端 注意事项 前端使用 Android Studio(Java)和 Socket.IO 库,后端使用 Flask。 1…...
《JVM考古现场(二十三):归零者·重启奇点的终极奥义》
目录 楔子:归零者文明觉醒 上卷十维弦理论破译 第一章:JVM弦论代码考古 第二章:超膜引用解析算法 第三章:量子真空涨落监控 中卷归零者心法实战 第四章:宇宙重启倒计时引擎 第五章:内存奇点锻造术 第…...
k8s中sidecar死循环
序言 怎么发现我的同事们很上进呢,估计做了下贱的事儿吧。 伤不到我,不代表不疼! sidecar产生的问题 1 背景 在k8s的环境中,pod的使用越来越多了,也就产生了sidecar容器,在现在的环境中,一个pod…...
Linux `init 4` 相关命令的完整使用指南
Linux init 4 相关命令的完整使用指南—目录 一、init 系统简介二、init 4 的含义与作用三、不同 Init 系统下的 init 4 行为1. SysVinit(如 CentOS 6、Debian 7)2. systemd(如 CentOS 7、Ubuntu 16.04)3. Upstart(如 …...
Java Web 之 简介 100问
DAO 层的作用是什么? DAO 层作用: 与数据库直接交互,封装所有数据访问的细节(即CRUD操作),不包含业务逻辑,只关注数据的持久化。 DAO的全拼是什么 Data Access Object,数据连接实…...
06-libVLC的视频播放器:推流RTMP
创建媒体对象 libvlc_media_t* m = libvlc_media_new_path(m_pInstance, inputPath.toStdString().c_str()); if (!m) return -1; // 创建失败返回错误 libvlc_media_new_path:根据文件路径创建媒体对象。注意:toStdString().c_str() 在Qt中可能存在临时字符串析构问题,建议…...
【物联网】基于LORA组网的远程环境监测系统设计
基于LORA组网的远程环境监测系统设计 演示视频: 简介: 1.本系统有一个主机,两个从机。 2.一主多从的LORA组网通信,主机和两个从机都配备了STM32F103单片机与 LoRa 模块,主机作为中心设备及WIFI网关,负责接收和发送数据到远程物联网平台和手机APP,两个从机则负责采集数…...
少儿编程路线规划
少儿编程路线规划—一文写明白 现在有很多的编程机构,五花八门的。我有幸也见识到了大家的营销策略。这些策略有黑有白吧,从业几年,沉淀下来一些客户角度的干货,分享给大家。 如果是想以很远很远的就业为目的,毕业就…...
第3章 垃圾收集器与内存分配策略《深入理解Java虚拟机:JVM高级特性与最佳实践(第3版)》
第3章 垃圾收集器与内存分配策略 3.2 对象已死 Java世界中的所有对象实例,垃圾收集器进行回收前就是确定对象哪些是活着的,哪些已经死去。 3.2.1 引用计数算法 常见的回答是:给对象中添加一个引用计数器,有地方引用࿰…...
