目录
(1)不要企图直接用sam2官网代码,会出错的。具体有什么差别我后面再研究。
(2)自动选择Segment Anything 2 Video (Large)这个模型
一、需求分析和方案
Tobii Pro Lab 是一款用于眼动追踪研究的实验软件,不过这玩意没有目标跟踪算法用于定位目标框,所以我们打算使用SAM2进行目标跟踪以避免手动标注。
Tobii Pro Lab 官网:https://www.tobii.cn/products/software/data-analysis-tools/tobii-pro-lab#resources
找到“下载与支持”,把第二个用户手册下载下来,
https://go.tobii.com/tobii_pro_lab_user_manual
看到第10.8开始就有介绍关于AOI的内容,AOI其实就是目标区域,在用户手册中我们获知了原本是去这里绘制AOI的:
“通过点击“项目概览”选项卡顶部的“AOI工具”按钮或从顶部导航栏的“分析”下拉菜单中选择它来激活AOI工具”,然后在这个界面右键点击Export AOIs就可以导出AOI, Import AOIs就可以导入AOI,这在10.9.3 Export and import AOIs有介绍。导出的文件格式是.aois文件
之所以说如何导入导出AOI是因为我不知道怎么二次开发。所以我的方案是这样的(尽管这个方案比较low):
1.先导出原视频 => 2.使用目标跟踪算法对原视频进行跟踪,获得json标注文件之后,再将json转.aois文件 => 3.导入.aois文件到软件中即完成了目标跟踪。
对于第2步我们使用的是X-AnyLabeling这个标注软件,里面内置了SAM2的视频跟踪算法。我是在他的3.1.1版本上改代码的。
注意:这里面实现的SAM2跟踪在掩码输出选择和记忆机制方面有需要改进的地方,掩码输出有时候范围过大,在长时间跟踪的场景中如果目标丢失会出现找不回来等情况,如何改进将在后面研究。
https://github.com/CVHub520/X-AnyLabeling下面是计划:
基于SAM2的眼动数据跟踪1:介绍怎么实现自动载入SAM跟踪模型
基于SAM2的眼动数据跟踪2:介绍如何将目标跟踪的结果转为.aois,然后导入Tobii Pro Lab软件中
基于SAM2的眼动数据跟踪3:介绍如何将python代码转为exe。
基于SAM2的眼动数据跟踪4:可能会介绍整体代码的运行路径,以及SAM2的视频跟踪算法是如何实现和改进
二、使用X-AnyLabeling进行目标跟踪
1、X-AnyLabeling安装和使用
安装教程:
https://github.com/CVHub520/X-AnyLabeling/blob/main/docs/en/get_started.md
重点:
git clone https://github.com/CVHub520/segment-anything-2
cd segment-anything-2
pip install -e .
(1)不要企图直接用sam2官网代码,会出错的。具体有什么差别我后面再研究。
(2)如果是内网环境,要自己下载模型并放到指定位置。
2025-07-31 11:13:49,978 | INFO | model:get_mode_abs_path:232 - Downloading https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt to C:\Users\guan.linyi\xanylabeling_data\models\sam2_hiera_large_video-r20240901\sam2_hiera_large_video-r20240901\sam2_hiera_large.pt
视频跟踪安装教程里面Note: If the model fails to load due to network issues展开有介绍模型默认的存放位置:
# Windows
C:\Users\${User}\xanylabeling_data\models\sam2_hiera_large_video-r20240901
# Linux or macOS
~/xanylabeling_data/models/sam2_hiera_large_video-r20240901
(3)怎么把默认位置改到自己的项目下面呢?
看上面信息显示model:get_mode_abs_path:232,我们先搜索一下这个函数,发现是在:
anylabeling/services/auto_labeling/model.py
def get_model_abs_path(self, model_config, model_path_field_name):
......
# Create model folder
# home_dir = os.path.expanduser("~")
# 改为:
if getattr(sys, 'frozen', False):
# 如果是打包的话,home_dir就在X-AnyLabeling-GPU/_internal
# 打包后的环境(PyInstaller)
base_dir = os.path.dirname(sys.executable) # 即X-AnyLabeling-GPU文件夹
home_dir = os.path.join(base_dir, '_internal')
else:
# 如果是未打包的话,指定开发时的模型路径,就是我们的项目路径
# 开发环境(未打包),
home_dir = r"D:\zero_track\X-AnyLabeling-3.1.1"
print("home_dir:", home_dir)
model_path = os.path.abspath(os.path.join(home_dir, data_dir))
# 模型存放路径:D:\zero_track\X-AnyLabeling-3.1.1\xanylabeling_data
print("model_path:", model_path)
改好之后我们只要把模型放在自己项目目录下面的这个位置就可以了:
D:\zero_track\X-AnyLabeling-3.1.1\xanylabeling_data\models\sam2_hiera_large_video-r20240901\sam2_hiera_large.pt
D:\zero_track\X-AnyLabeling-3.1.1\xanylabeling_data\models\sam2_hiera_tiny_video-r20240901\sam2_hiera_tiny.pt
sam2的模型有不同大小尺寸的,建议用sam2_hiera_large.pt就够可以了,效果不错又不会过于慢。
(4)装sam2的时候遇到报错
File "D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera\sam2\sam2_camera_predictor.py", line 274, in add_new_prompt
current_out, _ = self._run_single_frame_inference(
File "D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera\sam2\sam2_camera_predictor.py", line 990, in _run_single_frame_inference
pred_masks_gpu = fill_holes_in_mask_scores(
File "D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera\sam2\utils\misc.py", line 238, in fill_holes_in_mask_scores
labels, areas = get_connected_components(mask <= 0)
File "D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera\sam2\utils\misc.py", line 61, in get_connected_components
from sam2 import _C
ImportError: cannot import name '_C' from 'sam2'
(D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera\sam2\__init__.py)
解决:
1)确定已经安装CUDA,并设置环境变量
windows下:
设置BUILD_WITH_CUDA为True
设置CUDA_HOME为
C:\Program Files\NVIDIA GPU Computing Toolkit\CUDA\v12.2
path里面新增:%CUDA_HOME%\bin
2)运行
(X_AnyLabeling_win_env) D:\zero_track\X-AnyLabeling-3.1.1>
cd segment-anything-2-camera
(X_AnyLabeling_win_env) D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera>
python setup.py build_ext --inplace
(X_AnyLabeling_win_env) D:\zero_track\X-AnyLabeling-3.1.1\segment-anything-2-camera>
pip install -e ".[demo]"
解决方案来自sam2的issue:
https://github.com/facebookresearch/sam2/issues/131
Contributor
Hi, we have added INSTALL.md as an installation FAQ.
Regarding this error:
This is usually because you haven't run the pip install -e ".[demo]" step above or the installation failed. Please install SAM 2 first, and see the other issues if your installation fails. (You may also try python setup.py build_ext --inplace in the repo root as others suggested in #77)
这启示我们,一般遇到这种源代码的问题可以将问题复制到issue,查一下有没有解决方案。
(5)运行主程序
(X_AnyLabeling_win_env) D:\zero_track\X-AnyLabeling-3.1.1>
python anylabeling/app.py
2、自动加载SAM视频跟踪模型
的Getting Started的Step 1: Load the SAM 2 Video model 有演示怎么加载SAM2视频模型。
但是如果我们想一进去就自动加载呢?(因为我们只用这个功能,如果能自动加载,就避免每次进来都要选择模型)

(1)自动点击左侧栏倒数第二个AI的按钮
1)首先,必须得先找到界面的按钮是在哪里设置的:
X-AnyLabeling/anylabeling/views/labeling/label_widget.py
这个AI的按钮,鼠标停留的时候显示的是“auto_labeling”,所以我们在这个文件搜索一下。
找到了按钮的动作定义位置:
# AI Actions
toggle_auto_labeling_widget = action(
self.tr("&Auto Labeling"),
self.toggle_auto_labeling_widget,
shortcuts["auto_label"],
"brain",
self.tr("Auto Labeling"),
)
2)搜索toggle_auto_labeling_widget这个函数的位置
def toggle_auto_labeling_widget(self):
"""Toggle auto labeling widget visibility."""
if self.auto_labeling_widget.isVisible():
self.auto_labeling_widget.hide()
self.actions.run_all_images.setEnabled(False)
else:
self.auto_labeling_widget.show()
self.actions.run_all_images.setEnabled(True)
self.update_thumbnail_display()
3)因为toggle_auto_labeling_widget()在class LabelingWidget里面
在class LabelingWidget右键Find Usages找到创建LabelingWidget实例的地方
anylabeling/views/labeling/label_wrapper.py
# Create a labeling widget
self.view = LabelingWidget(
self,
config=config,
filename=filename,
output=output,
output_file=output_file,
output_dir=output_dir,
)
在这里后面加上:
# 直接加载AI模型
self.view.toggle_auto_labeling_widget()
(2)自动选择Segment Anything 2 Video (Large)这个模型
在上方有个地方是有个选择模型的按钮的,现在我们希望自动选择Segment Anything 2 Video (Large)这个模型。
在X-AnyLabeling/anylabeling/views/labeling/label_widget.py
找到self.auto_labeling_widget = AutoLabelingWidget(self),我们追溯到了这个对象原来是用这个类创建的,ctrl点进AutoLabelingWidget
在anylabeling/views/labeling/widgets/auto_labeling/auto_labeling.py
找到了AutoLabelingWidget这个类的定义。我们应该是希望在这个初始化的时候加载SAM2视频模型。但是我们现在不知道怎么加载。这时候我们的思路是想办法找到SAM2视频算法的类,然后找这个类是在哪里被使用的。但是首先我们得看到AutoLabelingWidget的初始化函数里面有一句:
self.new_model_selected.connect(self.model_manager.load_model),这似乎是用于关联用户选择的模型,所以随后了解一下self.model_manager.load_model做了什么也很有必要。
class AutoLabelingWidget(QWidget):
new_model_selected = pyqtSignal(str)
new_custom_model_selected = pyqtSignal(str)
auto_segmentation_requested = pyqtSignal()
auto_segmentation_disabled = pyqtSignal()
auto_labeling_mode_changed = pyqtSignal(AutoLabelingMode)
clear_auto_labeling_action_requested = pyqtSignal()
finish_auto_labeling_object_action_requested = pyqtSignal()
cache_auto_label_changed = pyqtSignal()
auto_decode_mode_changed = pyqtSignal(bool)
clear_auto_decode_requested = pyqtSignal()
mask_fineness_changed = pyqtSignal(float)
def __init__(self, parent):
super().__init__()
self.parent = parent
current_dir = os.path.dirname(__file__)
# 注意这里
uic.loadUi(os.path.join(current_dir, "auto_labeling.ui"), self)
self.model_manager = ModelManager()
self.model_manager.new_model_status.connect(self.on_new_model_status)
# 注意这里
self.new_model_selected.connect(self.model_manager.load_model)
self.new_custom_model_selected.connect(
self.model_manager.load_custom_model
)
self.model_manager.model_loaded.connect(self.update_visible_widgets)
self.model_manager.model_loaded.connect(self.on_new_model_loaded)
我们注意到在
anylabeling/anylabeling/services/auto_labeling/segment_anything_2_video.py
这里似乎是SAM2视频的类定义的地方(这个全局搜索一下segment_anything_2_video或者翻阅到anylabeling/services/auto_labeling有很多定义算法的文件,就能翻到这里。
找到class SegmentAnything2Video(Model):右键Find Usages,找到
在anylabeling/services/auto_labeling/model_manager.py
def _load_model(self, model_id):
.......
elif model_config["type"] == "segment_anything_2_video":
try:
from .segment_anything_2_video import SegmentAnything2Video
model_config["model"] = SegmentAnything2Video(
model_config, on_message=self.new_model_status.emit
)
self.auto_segmentation_model_selected.emit()
logger.info(
f"✅ Model loaded successfully: {model_config['type']}"
)
except Exception as e: # noqa
logger.error(
f"❌ Error in loading model: {model_config['type']} with error: {str(e)}"
)
template = "Error in loading model: {error_message}"
translated_template = self.tr(template)
error_text = translated_template.format(error_message=str(e))
self.new_model_status.emit(error_text)
return
# Request next files for prediction
self.request_next_files_requested.emit()
_load_model右键Find Usages,找到
def load_model(self, config_file):
"""Run model loading in a thread"""
if (
self.model_download_thread is not None
and self.model_download_thread.isRunning()
):
logger.info(
"Another model is being loaded. Please wait for it to finish."
)
return
if not config_file:
if self.model_download_worker is not None:
try:
self.model_download_worker.finished.disconnect(
self.on_model_download_finished
)
except TypeError:
pass
self.unload_model()
self.new_model_status.emit(self.tr("No model selected."))
return
# Check and get model id
model_id = None
for i, model_config in enumerate(self.model_configs):
if model_config["config_file"] == config_file:
model_id = i
break
if model_id is None:
logger.error(
"An error occurred while loading the model: "
"The model name is invalid."
)
self.new_model_status.emit(
self.tr("Error in loading model: Invalid model name.")
)
return
self.model_download_thread = QThread()
template = "Loading model: {model_name}. Please wait..."
translated_template = self.tr(template)
message = translated_template.format(
model_name=self.model_configs[model_id]["display_name"]
)
self.new_model_status.emit(message)
# 这里调用了self._load_model
self.model_download_worker = GenericWorker(self._load_model, model_id)
self.model_download_worker.finished.connect(
self.on_model_download_finished
)
self.model_download_worker.finished.connect(
self.model_download_thread.quit
)
self.model_download_worker.moveToThread(self.model_download_thread)
self.model_download_thread.started.connect(
self.model_download_worker.run
)
self.model_download_thread.start()
刚才是反着找,现在正着看,调用顺序是:
1) 在 AutoLabelingWidget 类中,load_model 方法通过信号 new_model_selected 被调用: self.new_model_selected.connect(self.model_manager.load_model)
2) load_model(self, config_file)
-
检查是否有其他模型正在加载。如果有,返回。
-
检查传入的
config_file是否为空。如果为空,卸载当前模型并返回。 -
通过
config_file找到对应的model_id。 -
如果
model_id无效,记录错误并返回。 -
创建一个新线程
model_download_thread和一个工作线程model_download_worker。 -
将
_load_model方法绑定到工作线程,并启动线程。
3) _load_model(self, model_id)
-
如果当前有模型加载,卸载当前模型。
-
根据
model_id获取模型配置。 -
根据模型类型加载相应的模型。
-
更新用户界面,显示模型加载成功或失败的信息。
于是我们回到:
在anylabeling/views/labeling/widgets/auto_labeling/auto_labeling.py
AutoLabelingWidget初始化函数中增加
# self.model_configs[1]["config_file"]:获取模型 ID 为 1 的配置文件路径。
self.model_manager.load_model(self.model_manager.model_configs[1]["config_file"])
self.model_selection_button.setText("Segment Anything 2 Video (Large)")
注:
如果直接在 ModelManager 的初始化中调用 self._load_model(1)是不行的,按钮的显示状态会保持No Model,不会更新按钮的显示。因为 _load_model 方法实际上是被 load_model 方法通过线程调用的。直接调用 _load_model 会跳过线程相关的逻辑,导致一些必要的动作没有执行,比如更新用户界面或处理线程结束后的信号。
load_model逻辑梳理:
1) 初始化时加载模型:
在 ModelManager 的初始化中,调用 load_model 方法,传入模型的配置文件路径。这样可以确保 _load_model 在正确的线程中被调用。
self.model_configs[1]["config_file"]:获取模型 ID 为 1 的配置文件路径。
2) 线程逻辑:
load_model 方法负责创建线程并启动 _load_model 方法。
GenericWorker:一个工作线程类,用于在后台线程中执行 _load_model 方法。
self.model_download_thread:线程对象,用于管理后台线程。
self.model_download_worker:工作线程对象,负责执行 _load_model 方法。
self.model_download_thread.started.connect(self.model_download_worker.run):连接线程的 started 信号到工作线程的 run 方法,确保 _load_model 在线程启动时被调用。
self.model_download_worker.finished.connect(self.on_model_download_finished):连接工作线程的 finished 信号到 on_model_download_finished 方法,确保在模型加载完成后更新用户界面。
3)模型加载完成后更新用户界面:
on_model_download_finished 方法在模型加载完成后被调用,用于更新用户界面。
如果模型加载成功,更新按钮文本并发射相关信号。
如果模型加载失败,发射一个空的信号,表示没有模型被加载。
问题:如何理解self.model_manager.load_model(self.model_manager.model_configs[1]["config_file"])?
在anylabeling/services/auto_labeling/model_manager.py
class ModelManager(QObject):
def __init__(self):
super().__init__()
self.model_configs = []
self.loaded_model_config = None
self.loaded_model_config_lock = Lock()
self.model_download_worker = None
self.model_download_thread = None
self.model_execution_thread = None
self.model_execution_thread_lock = Lock()
self.load_model_configs()
def load_model_configs(self):
"""Load model configs"""
# Load list of default models
# 注意这里
with pkg_resources.open_text(
auto_labeling_configs, "models.yaml"
) as f:
model_list = yaml.safe_load(f)
# Load list of custom models
custom_models = get_config().get("custom_models", [])
for custom_model in custom_models:
custom_model["is_custom_model"] = True
# Remove invalid/not found custom models
custom_models = [
custom_model
for custom_model in custom_models
if os.path.isfile(custom_model.get("config_file", ""))
]
config = get_config()
config["custom_models"] = custom_models
save_config(config)
model_list += custom_models
# Load model configs
model_configs = []
for model in model_list:
model_config = {}
config_file = model["config_file"]
if config_file.startswith(":/"): # Config file is in resources
config_file_name = config_file[2:]
resource_path = pkg_resources.files(
auto_labeling_configs
).joinpath("auto_labeling", config_file_name)
with open(resource_path, "r", encoding="utf-8") as f:
model_config = yaml.safe_load(f)
model_config["config_file"] = str(config_file)
else: # Config file is in local file system
with open(config_file, "r", encoding="utf-8") as f:
model_config = yaml.safe_load(f)
model_config["config_file"] = os.path.normpath(
os.path.abspath(config_file)
)
is_custom = model.get("is_custom_model", False)
model_config["is_custom_model"] = is_custom
if is_custom and not model_config["name"].startswith("_custom_"):
model_config["name"] = f"_custom_{model_config['name']}"
model_configs.append(model_config)
# Sort by last used
for i, model_config in enumerate(model_configs):
# Keep order for integrated models
if not model_config.get("is_custom_model", False):
model_config["last_used"] = -i
else:
model_config["last_used"] = model_config.get(
"last_used", time.time()
)
model_configs.sort(key=lambda x: x.get("last_used", 0), reverse=True)
self.model_configs = model_configs
self.model_configs_changed.emit(model_configs)
其实是读取了models.yaml然后将config_file里面的内容和yaml的内容整合放到python字典里
我们看一下 anylabeling\configs\models.yaml 里面的内容,只关注sam2有关的,其他的删了
- model_name: "sam2_hiera_base_video-r20240901"
config_file: ":/sam2_hiera_base_video.yaml"
- model_name: "sam2_hiera_large_video-r20240901"
config_file: ":/sam2_hiera_large_video.yaml"
- model_name: "sam2_hiera_small_video-r20240901"
config_file: ":/sam2_hiera_small_video.yaml"
- model_name: "sam2_hiera_tiny_video-r20240901"
config_file: ":/sam2_hiera_tiny_video.yaml"
我们再看看anylabeling\configs\auto_labeling\sam2_hiera_large_video.yaml里面的内容
type: segment_anything_2_video
name: sam2_hiera_large_video-r20240901
provider: Meta
display_name: Segment Anything 2 Video (Large)
model_cfg: sam2_hiera_l.yaml
device_type: cuda # cpu, cuda, mps
model_path: https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt
在load_model_configs加入调试输出语句,可以知道,原来model_configs是下面这样的一个列表,model_configs[1]就是对应的从第0个开始数的第1个{...}字典,字典的内容一部分是来自sam2_hiera_large_video.yaml里面的内容,然后还新增了三个,其中 config_file是来自于models.yaml。由此我们理解了model_configs[1]["config_file"]实际上就是获取到了这个里面的'config_file': ':/sam2_hiera_large_video.yaml', 这个:':/sam2_hiera_large_video.yaml'
[
{
'type': 'segment_anything_2_video'
'name': 'sam2_hiera_base_video-r20240901'
......
}
{
# sam2_hiera_large_video.yaml里面的内容
'type': 'segment_anything_2_video'
'name': 'sam2_hiera_large_video-r20240901'
'provider': 'Meta'
'display_nam'e: 'Segment Anything 2 Video (Large)'
'model_cfg': 'sam2_hiera_l.yaml'
'device_type': 'cuda' # cpu, cuda, mps
'model_path': 'https://dl.fbaipublicfiles.com/segment_anything_2/072824/sam2_hiera_large.pt'
# 新增三个 config_file是来自于models.yaml
'config_file': ':/sam2_hiera_large_video.yaml',
'is_custom_model': False,
'last_used': -1
}
{
...
}
{
...
}
]
(3)自动选择矩形框
如果我们要使用矩形框作为提示进行跟踪,可以自动选择矩形框,避免每次都要选择。
在anylabeling/views/labeling/widgets/auto_labeling/auto_labeling.py中,
导入
from PyQt5.QtCore import QTimer
class AutoLabelingWidget(QWidget):
......
# --- Configuration for: button_add_rect ---
self.button_add_rect.clicked.connect(
lambda: self.set_auto_labeling_mode(
AutoLabelingMode.ADD, AutoLabelingMode.RECTANGLE
)
)
# 在 __init__ 里界面还没完全 show,直接 click() 会被 Qt 忽略。
# 用零延时 QTimer 推迟到 下一次事件循环。保证界面就绪、按钮可用时再触发。
QTimer.singleShot(0, lambda: self.button_add_rect.click() if self.button_add_rect.isEnabled() else None)
如果要改成默认加载点提示,可以变成这句
QTimer.singleShot(0, lambda: self.button_add_point.click() if self.button_add_point.isEnabled() else None)
注意:这一句是控制sam2默认加载什么类型的提示,但是加了这句之后好像会导致编辑多边形的快捷键失效(第3节改快捷键要成功,这句要注释掉,也就是说不能设置这句)
如果我们要指定提示生成的结果默认是矩形框,如果要指定默认是多边形,则不用改。但是后面我试了一下多边形跟踪的效果比较差,有时间看看怎么回事。
在anylabeling/anylabeling/services/auto_labeling/segment_anything_2_video.py
class SegmentAnything2Video(Model):
class Meta:
......
output_modes = {
"polygon": QCoreApplication.translate("Model", "Polygon"),
"rectangle": QCoreApplication.translate("Model", "Rectangle"),
"rotation": QCoreApplication.translate("Model", "Rotation"),
}
# default_output_mode = "polygon"
# 改为
default_output_mode = "rectangle"
3、改快捷键
anylabeling/views/labeling/label_widget.py
edit_mode = action(
self.tr("Edit Object"),
self.set_edit_mode,
# shortcuts["edit_polygon"],
"v",
"edit",
self.tr("Move and edit the selected polygons"),
enabled=False,
)
原先的shortcuts["edit_polygon"]是在配置文件中定义的:
anylabeling/configs/xanylabeling_config.yaml
create_polygon: [P, Ctrl+N]
create_rectangle: [R, Ctrl+R]
create_rotation: O
create_circle: null
create_line: null
create_point: null
create_linestrip: null
edit_polygon: Ctrl+J
原先的edit_polygon快捷键是ctrl+j,我们改成v,本来我想改成w的,但是w冲突了。v就可以。
我说实在的,这些快捷键的默认设置是搞笑的吧,谁会按ctrl+j就为了打开一个编辑模式,这两个键在键盘上隔多远不知道吗
4、总结
到这里,我们已经实现了自动载入SAM2跟踪模型,不再需要每次都手动选择模型了。通过
python anylabeling/app.py进入主界面之后,我们发现SAM2跟踪模型已经自动加载了。现在我们只要通过点击菜单栏的“文件”,选择“打开文件夹”,选择一个图片文件夹导入,然后用一个提示框或者提示点分割出第一帧的目标,然后按 f 弹出标签写入的窗口,写入标签名,然后再点击左侧最后一个按钮,就可以开始跟踪图片文件夹里面的所有图片了了。
但是我们之前提到的方案中,拿到的是一个视频。所以其实应该是先要点击菜单栏的“文件”,选择“打开视频”,然后将视频切分为多个视频帧,然后在视频的同级目录下会生成跟视频名称相同的图片文件夹,然后才是打开图片文件夹进行目标跟踪。
但是将视频切分为多个视频帧的时候,会涉及一个跟fps有关的问题,就是说如果不是你切分视频帧的手段跟软件的不一样,就会出现帧不同步的问题。这个问题,我们会在后面json转.aois中说到如何解决。
2669

被折叠的 条评论
为什么被折叠?



