YOLO v8目标跟踪详细解读(一)

在此之前,我们已经对yolo系列做出了详细的探析,有兴趣的朋友可以参考yolov8等文章。YOLOV8对生态进行了优化,目前已经支持了分割,分类,跟踪等功能,这对于我们开发者来说,是十分便利。今天我们对YOLO v8中支持的目标跟踪进行详细解读。
在这里插入图片描述代码地址:yolov8

一、算法简介

目标跟踪现阶段是强烈依赖目标检测结果的,主流的目标跟踪pipeline,简单的总结一下:

首先,目标检测模型输出的检测框是我们跟踪的对象。假设我们在第一帧确定了跟踪对象,即给定检测框id,那当检测模型输出第2帧检测结果,我们需要将第2帧的检测框与第1帧检测框的id进行匹配。那么匹配的过程是怎样的呢?

有人说,直接用两帧检测框的IOU去匹配呗,将IOU看作cost_matrix,利用匈牙利算法去匹配两帧的id。理想情况下,是没问题的,但是,当我们处于crowd场景下,遮挡,光照等给检测带来误差,那么,IOU直接匹配就不那么靠谱了。

远古神器卡尔曼滤波器有神奇的疗效,我们将检测框的坐标看作状态变量,通过状态转移矩阵可以预测出下一帧检测框的位置。然后,下一帧的检测框坐标作为观测量,可以对预测量进行更新并矫正,从而更好的预测下下帧的检测框位置。

总结来说,就是利用卡尔曼滤波器预测检测框位置,构建合适的cost_matrix并利用匈牙利算法匹配轨迹与当前帧检测框的id,同时加入适当的逻辑,就能构建一个效果不错的跟踪器。

二、代码详解

YOLOv8采用2022年提出的跟踪算法BoT-SORT和ByteTrack两种算法实现目标跟踪。如果你想体验该算法的跟踪效果只需要执行以下代码。

from ultralytics import YOLO
	
model = YOLO('yolov8n.pt')
results = model.track(source=".avi", show=True,  save=True)

在Yolo V8中,实现对一个视频中运动目标跟踪的核心代码块为文件:.\ultralytics\tracker\trackers\byte_tracker.py 中类BYTETracker.update()。下面我们不赘述卡尔曼以及匈牙利算法的原理,仅从代码逻辑来拆解。

在这之前,需要了解一个重要的类class BaseTrack,该对象为跟踪的基类,用于处理基本的跟踪属性与操作。class STrack(BaseTrack),class BOTrack(STrack),BoT-SORT和ByteTrack的track都是继承于此。从代码中我们可以发现该类记录了跟踪id,is_activated 激活状态等等操作与属性。在跟踪过程中,每帧的检测框都会分配一个track

class BaseTrack:
    """Base class for object tracking, handling basic track attributes and operations."""

    _count = 0

    track_id = 0
    is_activated = False
    state = TrackState.New

    history = OrderedDict()
    features = []
    curr_feature = None
    score = 0
    start_frame = 0
    frame_id = 0
    time_since_update = 0

    # Multi-camera
    location = (np.inf, np.inf)

    @property
    def end_frame(self):
        """Return the last frame ID of the track."""
        return self.frame_id

    @staticmethod
    def next_id():
        """Increment and return the global track ID counter."""
        BaseTrack._count += 1
        return BaseTrack._count

    def activate(self, *args):
        """Activate the track with the provided arguments."""
        raise NotImplementedError
	 ## ........
   

在跟踪前,先初始化跟踪器,其中self.tracked_stracks列表保存了可跟踪的轨迹,self.lost_stracks保存丢失的轨迹,self.removed_stracks保存被移除的轨迹。当self.lost_stracks的成员在满足被移除的条件后则变为self.removed_stracks的成员,而过程中若被新的track匹配上,则变为self.tracked_stracks的成员。self.frame_id用来记录帧数,self.kalman_filter表示卡尔曼滤波器,后面称之为KF。

def __init__(self, args, frame_rate=30):
        """Initialize a YOLOv8 object to track objects with given arguments and frame rate."""
        self.tracked_stracks = []  # type: list[STrack]
        self.lost_stracks = []  # type: list[STrack]
        self.removed_stracks = []  # type: list[STrack]

        self.frame_id = 0
        self.args = args
        self.max_time_lost = int(frame_rate / 30.0 * args.track_buffer)
        self.kalman_filter = self.get_kalmanfilter()
        self.reset_id()

接下来正式进入update逻辑讲解。每检测一帧就会执行一次update,因此self.frame_id帧数+1,activated_starcks 用来保存该帧激活的轨迹,refind_stracks保存该帧重新激活的轨迹,lost_stracks 保存该帧丢失的轨迹,removed_stracks 保存该帧移除的轨迹。results为YOLOv8的检测结果,bboxes 是检测框坐标,后面接上了索引。

def update(self, results, img=None):
        """Updates object tracker with new detections and returns tracked object bounding boxes."""
        self.frame_id += 1
        activated_starcks = []
        refind_stracks = []
        lost_stracks = []
        removed_stracks = []

        scores = results.conf
        bboxes = results.xyxy
        cls = results.cls
        # Add index
        bboxes = np.concatenate([bboxes, np.arange(len(bboxes)).reshape(-1, 1)], axis=-1)

byte_tracker根据置信度将检测框分成两类,当置信度高于self.args.track_high_thresh(0.5)时,我们将其称为第一检测框,当置信度低于0.5且高于0.1时,称为第二检测框。

		remain_inds = scores > self.args.track_high_thresh
        inds_low = scores > self.args.track_low_thresh
        inds_high = scores < self.args.track_high_thresh

        inds_second = np.logical_and(inds_low, inds_high)
        dets_second = bboxes[inds_second]
        dets = bboxes[remain_inds]
        scores_keep = scores[remain_inds]
        scores_second = scores[inds_second]
        cls_keep = cls[remain_inds]
        cls_second = cls[inds_second]

为每个检测框分配track,上面讲到,track类提供了跟踪属性与操作。目前,YOLOV8中的self.args.with_reid=False,与特征相关的还未实现。

detections = self.init_track(dets, scores_keep, cls_keep, img) 
def init_track(self, dets, scores, cls, img=None):
        """Initialize track with detections, scores, and classes.为每一个det分配一个track"""
        if len(dets) == 0:
            return []
        if self.args.with_reid and self.encoder is not None:
            features_keep = self.encoder.inference(img, dets)
            return [BOTrack(xyxy, s, c, f) for (xyxy, s, c, f) in zip(dets, scores, cls, features_keep)]  # detections
        else:
            return [BOTrack(xyxy, s, c) for (xyxy, s, c) in zip(dets, scores, cls)]  # detections

步骤一:将可跟踪轨迹与第一检测框进行关联匹配。首先,遍历self.tracked_stracks可跟踪轨迹,将已激活的轨迹添加到tracked_stracks中,未被激活的轨迹加入unconfirmed未证实轨迹。将丢失轨迹与激活轨迹合并到strack_pool中,注意剔除重复track_id。重点来了,self.multi_predict(strack_pool),我们需要利用KF将strack_pool中前一帧轨迹通过状态转移矩阵预测出该帧最优估计以及状态协方差矩阵,并计算轨迹池中的轨迹与该帧检测出的bbox的距离作为cost_matrix,利用匈牙利算法进行关联匹配。

通过匈牙利算法,我们获得matches(关联成功后轨迹与检测框的索引),u_track是未关联上的轨迹,u_detection未关联上的检测框。

		unconfirmed = []
        tracked_stracks = []  # type: list[STrack]
        for track in self.tracked_stracks:
            if not track.is_activated:
                unconfirmed.append(track)
            else:
                tracked_stracks.append(track)
        # Step 2: First association, with high score detection boxes
        strack_pool = self.joint_stracks(tracked_stracks, self.lost_stracks) ##将丢失track与激活track合并到strack_pool中,注意剔除重复track_id。
        # Predict the current location with KF
        self.multi_predict(strack_pool) ## 第一帧step4已初始化KF,第二帧的track通过KF的状态转移方程预测最优估计以及状态协方差矩阵
        if hasattr(self, 'gmc') and img is not None:
            warp = self.gmc.apply(img, dets)
            STrack.multi_gmc(strack_pool, warp)
            STrack.multi_gmc(unconfirmed, warp)

        dists = self.get_dists(strack_pool, detections) ## 计算轨迹池中的轨迹与该帧检测出的bbox的距离,并利用匈牙利算法进行匹配。
        matches, u_track, u_detection = matching.linear_assignment(dists, thresh=self.args.match_thresh)

遍历matches,已激活的轨迹需要根据该帧检测框的信息去更新轨迹属性,KF根据测量值(该帧的检测框坐标)矫正最优估计,并更新状态协方差矩阵。而未激活的轨迹重新关联上检测框,需要重新激活,re_activate与update功能类似。

 		for itracked, idet in matches:
            track = strack_pool[itracked]
            det = detections[idet]
            if track.state == TrackState.Tracked: ## 已激活的track
                track.update(det, self.frame_id)  ## 确定匹配后更新track信息,KF根据测量值矫正最优估计,并更新状态协方差矩阵
                activated_starcks.append(track)
            else: ## 未激活的track重新匹配上需要重新激活
                track.re_activate(det, self.frame_id, new_id=False)
                refind_stracks.append(track)
                
     def update(self, new_track, frame_id):
        """
        Update a matched track
        :type new_track: STrack
        :type frame_id: int
        :return:
        """
        self.frame_id = frame_id
        self.tracklet_len += 1

        new_tlwh = new_track.tlwh
        self.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance,self.convert_coords(new_tlwh))
        self.state = TrackState.Tracked
        self.is_activated = True

        self.score = new_track.score
        self.cls = new_track.cls
        self.idx = new_track.idx
        
     def re_activate(self, new_track, frame_id, new_id=False):
        """Reactivates a previously lost track with a new detection."""
        self.mean, self.covariance = self.kalman_filter.update(self.mean, self.covariance, self.convert_coords(new_track.tlwh))
        self.tracklet_len = 0
        self.state = TrackState.Tracked
        self.is_activated = True
        self.frame_id = frame_id
        if new_id:
            self.track_id = self.next_id()
        self.score = new_track.score
        self.cls = new_track.cls
        self.idx = new_track.idx

步骤二: 将可跟踪轨迹与第二检测框进行关联匹配。首先,为第二检测框分配track,将轨迹池strack_pool中在步骤一中未关联上的轨迹存放在r_tracked_stracks中。计算r_tracked_stracks与第二检测框的IOU,将IOU作为cost_matric进行匈牙利匹配,注意与步骤一匹配过程相比,步骤二的匹配阈值从0.8降低至0.5。由于第二检测框置信度较低,坐标回归质量较差,为了捞回更多的轨迹,适当降低阈值是必要的。遍历matches,更新可激活轨迹,并重新激活未激活轨迹。若步骤二中仍存在未关联上的轨迹,需将其状态改成lost丢失

		detections_second = self.init_track(dets_second, scores_second, cls_second, img) ## 0.1<置信度<0.5的box分配track
        r_tracked_stracks = [strack_pool[i] for i in u_track if strack_pool[i].state == TrackState.Tracked] ## 第一次未匹配上的track
        # TODO
        dists = matching.iou_distance(r_tracked_stracks, detections_second)
        matches, u_track, u_detection_second = matching.linear_assignment(dists, thresh=0.5)
        for itracked, idet in matches:
            track = r_tracked_stracks[itracked]
            det = detections_second[idet]
            if track.state == TrackState.Tracked:
                track.update(det, self.frame_id)
                activated_starcks.append(track)
            else:
                track.re_activate(det, self.frame_id, new_id=False)
                refind_stracks.append(track)

        for it in u_track: ## 第2次还未匹配上的track,将其状态改成lost丢失
            track = r_tracked_stracks[it]
            if track.state != TrackState.Lost:
                track.mark_lost()
                lost_stracks.append(track)

u_detection是步骤一中没有关联上的track(即检测框),unconfirmed未证实的轨迹是可跟踪轨迹中is_tracked=False的轨迹,这里需要与u_detection进行匹配尝试。如若关联成功,则需要更新轨迹将is_tracked置为True,并更新其KF相关参数,否则该轨迹被移除

 # Deal with unconfirmed tracks, usually tracks with only one beginning frame
        detections = [detections[i] for i in u_detection]
        dists = self.get_dists(unconfirmed, detections) ## 这里的detections是第一检测框未匹配上的
        matches, u_unconfirmed, u_detection = matching.linear_assignment(dists, thresh=0.7) 
        for itracked, idet in matches: ## unconfirmed未证实的轨迹与步骤一未匹配上的检测框关联成功,更新轨迹并加入激活轨迹
            unconfirmed[itracked].update(detections[idet], self.frame_id)
            activated_starcks.append(unconfirmed[itracked])
        for it in u_unconfirmed: ## 未证实轨迹仍然关联失败,则移除未证实轨迹
            track = unconfirmed[it]
            track.mark_removed()
            removed_stracks.append(track)

步骤三:初始化新轨迹。当我们在处理第一帧时,因为可跟踪轨迹等列表均为空,无法匹配,因此直接进入步骤三,将检测框置信度高于self.args.new_track_thresh的track激活,is_tracked置为True,初始化KF,并将track存放于列表activated_starcks中,等待合并到self.tracked_stracks可跟踪轨迹中。当frame_id != 1时,activate不会将is_tracked置为True,未匹配上的track存于activated_starcks中,等待合并到self.tracked_stracks。注意由于is_tracked=False,这部分在步骤一中被归纳为unconfirmed未证实轨迹。

		## 处理第一帧时,因为无法匹配,直接进入Step 4,激活track,将is_tracked置True,初始化KF,并将track存放于列表activated_starcks中,等待合并到self.tracked_stracks可跟踪轨迹中。
        ## frame_id!=1时,activate不会将is_tracked置为True,未匹配上的track存于activated_starcks中,等待合并到self.tracked_stracks。注意这部分在220行被归纳到unconfirmed中。
        for inew in u_detection:
            track = detections[inew]
            if track.score < self.args.new_track_thresh:
                continue
            track.activate(self.kalman_filter, self.frame_id)
            activated_starcks.append(track)

步骤四: 可跟踪轨迹在经过步骤一与步骤二后,仍未与检测框关联成功,其状态会变为lost丢失,并从可跟踪轨迹中移除,并入self.lost_stracks丢失轨迹中。如果丢失轨迹中的track在30帧内仍未匹配上,则将其移除。

 # Step 5: Update state
        for track in self.lost_stracks: ## 如果超过30帧lost_track仍未匹配上,则移除
            if self.frame_id - track.end_frame > self.max_time_lost:
                track.mark_removed()
                removed_stracks.append(track)

步骤五: 更新self.tracked_stracks,self.lost_stracks,self.removed_stracks列表。将activated_starcks,refind_stracks合并到可跟踪轨迹中,继续在下一帧进行关联跟踪。将lost_stracks在self.tracked_stracks列表中出现的track剔除(丢失已找回),将lost_stracks在self.removed_stracks列表中出现的track剔除(丢失已移除)。remove_duplicate_stracks将self.tracked_stracks, self.lost_stracks列表中IOU接近的track去重,保留最新出现的track。最后return被激活的轨迹,其坐标是KF修正后的值。

		self.tracked_stracks = [t for t in self.tracked_stracks if t.state == TrackState.Tracked]
        self.tracked_stracks = self.joint_stracks(self.tracked_stracks, activated_starcks) ## 将激活的tracks合并到self.tracked_stracks列表中
        self.tracked_stracks = self.joint_stracks(self.tracked_stracks, refind_stracks)
        self.lost_stracks = self.sub_stracks(self.lost_stracks, self.tracked_stracks) ## 将lost_stracks在self.tracked_stracks列表中出现的track剔除(丢失已找回)
        self.lost_stracks.extend(lost_stracks)
        self.lost_stracks = self.sub_stracks(self.lost_stracks, self.removed_stracks) ## 将lost_stracks在self.removed_stracks列表中出现的track剔除(丢失已移除)
        self.tracked_stracks, self.lost_stracks = self.remove_duplicate_stracks(self.tracked_stracks, self.lost_stracks)
        self.removed_stracks.extend(removed_stracks)
        if len(self.removed_stracks) > 1000:
            self.removed_stracks = self.removed_stracks[-999:]  # clip remove stracks to 1000 maximum
        return np.asarray(
            [x.tlbr.tolist() + [x.track_id, x.score, x.cls, x.idx] for x in self.tracked_stracks if x.is_activated],
            dtype=np.float32)

代码详解过于冗长,文字单调,缺少精炼的图释,有不理解的或者讲解错误的地方还望指出,接下来会对目标跟踪中的卡尔曼滤波进行剖析,有兴趣的朋友可以关注留言。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

应用在家庭影院触摸屏中的高性能低功耗触摸芯片

家庭影院的主要思想是获得清晰的画面和令人惊叹的环绕声。这可以通过多个电子元件的组合轻松实现&#xff0c;为您提供真正的剧院体验。家庭影院系统所需的电子设备&#xff0c;主要有&#xff1a;扬声器、电视或投影仪、媒体设备和接收器。这些设备以不同的方式工作&#xff0…

Python web实战之Django的AJAX支持详解

关键词&#xff1a;Web开发、Django、AJAX、前端交互、动态网页 今天和大家分享Django的AJAX支持。AJAX可实现在网页上动态加载内容、无刷新更新数据的需求。 1. AJAX简介 AJAX&#xff08;Asynchronous JavaScript and XML&#xff09;是一种在网页上实现异步通信的技术。通过…

【MybatisPlus】LambdaQueryWrapper和QueryWapper的区别

个人主页&#xff1a;金鳞踏雨 个人简介&#xff1a;大家好&#xff0c;我是金鳞&#xff0c;一个初出茅庐的Java小白 目前状况&#xff1a;22届普通本科毕业生&#xff0c;几经波折了&#xff0c;现在任职于一家国内大型知名日化公司&#xff0c;从事Java开发工作 我的博客&am…

移动端预览指定链接的pdf文件流

场景 直接展示外部系统返回的获取文件流时出现了跨域问题&#xff1a; 解决办法 1. 外部系统返回的请求头中调整&#xff08;但是其他系统不会给你改的&#xff09; 2. 我们系统后台获取文件流并转为新的文件流提供给前端 /** 获取传入url文件流 */ GetMapping("/get…

如何在 3Ds Max 中准确地将参考图像调整为正确的尺寸?

您是否想知道如何在 3Ds Max 中轻松直观地调整参考图像的大小&#xff0c;而无需借助第三方解决方案、插件或脚本&#xff1f; 我问自己这个问题&#xff0c;并高兴地发现了FFD Box 2x2x2&#xff0c;我无法停止钦佩这个修改器的多功能性。 在本文中&#xff0c;我想与您分享一…

image has dependent child images

问题&#xff1a;很多none的镜像无法被删除 解决过程&#xff1a; 1、通过 docker image prune -f 提示可删除为 0 2、直接进行删除报错&#xff1a; docker rmi 8f5116cbc201Error response from daemon: conflict: unable to delete 8f5116cbc201 (cannot be forced) - im…

时序预测 | MATLAB实现WOA-CNN-BiLSTM鲸鱼算法优化卷积双向长短期记忆神经网络时间序列预测

时序预测 | MATLAB实现WOA-CNN-BiLSTM鲸鱼算法优化卷积双向长短期记忆神经网络时间序列预测 目录 时序预测 | MATLAB实现WOA-CNN-BiLSTM鲸鱼算法优化卷积双向长短期记忆神经网络时间序列预测预测效果基本介绍程序设计学习总结参考资料 预测效果 基本介绍 时序预测 | MATLAB实现…

如何正确下载tomcat???

亲爱的小伙伴&#xff0c;千万别再去找下网站下载啦&#xff0c;这样詪容易携带病毒。 我们去官方网址下载。 Apache Tomcat - Welcome! 最后下载解压即可。。。

学习C语言的好处:

基础编程语言&#xff1a;C语言是其他编程语言的基础&#xff0c;学习C语言可为后续学习打下坚实基础&#xff0c;广泛应用于嵌入式系统、操作系统、网络协议等。 简单易学&#xff1a;C语言语法简单易懂&#xff0c;适合初学者。只需文本编辑器和编译器&#xff0c;即可开始编…

集简云推出的全国第一款 AI+连接器解决方案产品语聚AI

语聚AI是集简云推出的全国第一款 AI连接器解决方案产品&#xff0c;官网&#xff1a;https://yuju.jijyun.cn 语聚AI包括了多个不同的AI功能&#xff0c;协助企业和个人更好的使用AI语言模型所带来的能力&#xff0c;包括&#xff1a; 应用助手 希望通过AI智能助手帮助您查询C…

掌握网络设备,畅游网络世界!

网络的搭建离不开网络设备&#xff0c;物理连接&#xff0c;以及设备之间的多种协议。其中在实现网络互通时&#xff0c;最常见的网络设备是路由器和交换机。 如今在各种级别的网络随处可见各种低、中、高端的路由器、交换机&#xff0c;种类繁多&#xff0c;这些不同种类的设备…

UGUI基础游戏对象Canvas

一.画布Canvas对象概述 画布是一种带有画布组件的游戏对象&#xff0c;所有 UI 元素都必须是此类画布的子项。 创建新的 UI 元素&#xff08;如使用菜单 GameObject > UI > Image 创建图像&#xff09;时&#xff0c;如果场景中还没有画布&#xff0c;则会自动创建画布。…

uniapp开发小程序-有分类和列表时,进入页面默认选中第一个分类

一、效果&#xff1a; 如下图所示&#xff0c;进入该页面后&#xff0c;默认选中第一个分类&#xff0c;以及第一个分类下的列表数据。 二、代码实现&#xff1a; 关键代码&#xff1a; 进入页面时&#xff0c;默认调用分类的接口&#xff0c;在分类接口里做判断&#xff…

Redis 缓存过期及删除

一、Redis缓存过期策略 物理内存达到上限后&#xff0c;像磁盘空间申请虚拟内存(硬盘与内存的swap),甚至崩溃。 内存与硬盘交换 (swap) 虚拟内存&#xff0c;频繁I0 性能急剧下降&#xff0c;会造成redis内存急剧下降&#xff1b; 一般设置物理内存的3/4&#xff0c;在redis…

【双指针_有效三角形的个数_C++】

题目解析 有效三角形的个数 判断三角形&#xff1a;任意两边之和大于第三边 需要重复计算&#xff1a; 知识点 1、需要判断三次&#xff1a; 2、只需要判断一次 已经知道这三个数的大小&#xff08;先进行排序&#xff09; 只需要判断 较小的两个数之和 是否 大于最大的…

考研408 | 【计算机网络】 传输层

导图 传输层的功能 传输层的两个协议 传输层的寻址与端口 UDP协议 UDP的主要特点 UDP首部格式&#xff1a; UDP校验&#xff1a; TCP协议 TCP协议的特点 TCP报文段首部格式 TCP连接管理 TCP的连接建立 SYN洪泛攻击 TCP的连接释放 TCP可靠传输 序号&#xff1a; 确认&#xff1…

支持M1 Syncovery for mac 文件备份同步工具

Syncovery for Mac 是一款功能强大、易于使用的文件备份和同步软件&#xff0c;适用于需要备份和同步数据的个人用户和企业用户。Syncovery 提供了一个直观的用户界面&#xff0c;使用户可以轻松设置备份和同步任务。用户可以选择备份的文件类型、备份目录、备份频率等&#xf…

ArcGIS Pro暨基础入门、制图、空间分析、影像分析、三维建模、空间统计分析与建模、python融合、案例应用

GIS是利用电子计算机及其外部设备&#xff0c;采集、存储、分析和描述整个或部分地球表面与空间信息系统。简单地讲&#xff0c;它是在一定的地域内&#xff0c;将地理空间信息和 一些与该地域地理信息相关的属性信息结合起来&#xff0c;达到对地理和属性信息的综合管理。GIS的…

大数据大屏的分析

今天又进行了大屏的训练&#xff0c;就是很多的报表头是最难的&#xff0c;因为确定了头&#xff0c;就确定了大屏的风格了。 今天的还是有点丑但是也是学习了。报班报班~~~~

抖音小程序开发,收银台支付回调通知

大家好&#xff0c;我是小悟 关于抖音小程序收银台支付&#xff0c;可阅读【抖音小程序开发&#xff0c;唤起收银台&#xff0c;包括抖音支付、支付宝支付、微信支付】。 做支付功能最重要的一步就是异步回调通知&#xff0c;所谓回调通知就是唤起收银台支付&#xff0c;支付…