TiXL 的 Lib.io.dmx.helpers 运算符详解:用 Video2DPointScanner 做投影仪-相机标定与 LED 跟踪,用 VisualizeSpotLights 定位聚光灯
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
本篇技术指南围绕 TiXL(实时动态图形软件)运算符库中Lib.io.dmx.helpers命名空间下的两个辅助运算符展开:Video2DPointScanner(通过"输出亮点-回读视频"的方式扫描并记录 2D 亮点坐标,服务于投影仪-相机标定、LED/像素跟踪、结构化光映射)与VisualizeSpotLights(在 3D 视口中可视化并辅助摆放聚光灯点位)。读完本文,你将掌握这两个运算符的完整参数语义、底层图像处理算法(OpenCvSharp 轮廓检测与质心计算)、点数据与相机标定文件的持久化格式,以及它们在 DMX/Art-Net 灯光控制工作流中的实际配合方法。
1. helpers 命名空间在 DMX 工作流中的定位
在 TiXL 的运算符库中,Lib.io.dmx 命名空间负责 DMX/Art-Net/sACN 的收发与灯光控制,包含ArtnetInput、ArtnetOutput、DmxOutput、SacnInput、SacnOutput、PointsToDmxLights等核心运算符;其子命名空间Lib.io.dmx.helpers则提供两个不直接发 DMX 数据、但服务于灯光现场工作流的辅助运算符:
- Video2DPointScanner — 扫描视频输入,检测亮点像素的 2D 位置;
- VisualizeSpotLights — 可视化聚光灯的位置与朝向。
ArtnetAndDMX 实操文档 描述了 TiXL 驱动灯具的模块化链路:用Points承载灯具信息 →PointsToArtnetLight(在仓库中对应 PointsToDmxLights)把点的Color、Orientation、FX1、FX2映射为 DMX 通道值 →ArtnetOutput广播。而VisualizeSpotLights正是这条链路中"把灯具点摆放准确"的关键工具,Video2DPointScanner则是"让投影画面与相机视野对齐"的前置标定工具。
2. Video2DPointScanner:原理与整体流程
2.1 核心思想:结构化光回读
根据运算符文档(Video2DPointScanner.md),该运算符的工作方式是对输出像素逐个测试:先向投影/输出端发出一个高亮像素,再检查这个亮点是否在相机视频源中可见,从而建立"输出像素坐标 ↔ 相机视野坐标"的映射关系。扫描得到的 2D 点图可以保存,之后交给 CameraCalibrator 生成 3D 空间映射。文档给出的别名(AKA)直观概括了它的应用场景:projector calibration(投影仪标定)、led tracking(LED 跟踪)、blob detection(光斑检测)、camera mapping(相机映射)、structured light(结构化光)。
2.2 源码中的四态状态机
从实现文件 Video2DPointScanner.cs 可以清晰看到该运算符是一个带内部状态机的图像处理循环,每一帧的Update都会执行:
- 读取
PixelCount,若变化则调用InitializeCurrentScan重置点列表(L102-L106); HandleInputsAndTriggers处理各触发输入(L242-L320);- 将
VideoIn纹理读回 CPU(ConvertTextureToMat,经 Staging 纹理拷贝为 OpenCvSharp 的Mat,见 L459-L480); - 若已加载标定且
ApplyCorrection开启,则用Cv2.Undistort先做镜头畸变校正(L125-L138); - 依据状态执行不同分支,最后更新三个输出。
四个状态分支如下(对应 L145-L206):
| 状态 | 触发方式 | 行为 |
|---|---|---|
| TestFullMode | TestFullMode=true | 一次性输出全白画面,统计相机视野中能看到多少个亮点,用于检查投影仪对齐与覆盖范围 |
| TestPixelMode | TestPixelMode=true | 按ScanIntervallum间隔循环点亮单个像素(索引由PixelCount控制),逐个确认输出与相机视野的对应关系 |
| Scanning | ScanTrigger边沿触发切换 | 自动扫描:每次间隔找到下一个缺失的点(FindNextMissingLedIndex),点亮它并从视频中检测回读位置 |
| Idle | 其余情况 | 全部输出置黑,在调试画面上显示已存储点数/完成状态 |
触发输入全部通过MathUtils.WasTriggered做边沿检测(false→true 跳变才生效),这是 TiXL 运算符"按钮式触发"的通用约定。
3. Video2DPointScanner:输入输出参数全解
以下表格完整继承自 Video2DPointScanner.md,并补充了默认值与源码层面的行为说明(默认值来自运算符定义文件 Video2DPointScanner.t3):
3.1 输入参数
| 名称(相关性 & 类型) | 说明 | 默认值/补充说明 |
|---|---|---|
| ApplyCorrection(Boolean) | 启用后,对扫描到的点应用已加载的相机标定,得到畸变校正后的 2D 坐标 | 默认false;仅在成功加载标定文件后生效,内部调用Cv2.Undistort(L125-L138) |
| CalibrationPath(String) | 相机标定文件路径(通常是标定工具生成的文本/JSON 文件) | 默认"";在 .t3ui 中标记为 FilePath 类型 |
| DebugMode(Boolean) | 开启调试可视化与日志输出,便于排查扫描过程 | 默认false;开启后内部使用Log.Debug记录扫描进度 |
| FilePath(String) | 扫描得到的 2D 点数据保存/加载的文件路径 | 默认"";文件内容为 JSON 格式的List<Vector2>(见第 5 节) |
| Load(Boolean) | 触发:从FilePath加载已扫描的点数据 | 边沿触发;路径为空时输出警告Load failed: File path is empty.(L265-L289) |
| LoadCalibration(Boolean) | 触发:从CalibrationPath加载相机标定文件 | 边沿触发;解析失败会输出警告(L313-L319) |
| PixelBrightness(Single) | 被测试像素的亮度(0-1) | 默认0.0;由GenerateLightOutput写入点颜色的 RGB 分量(L530-L541) |
| PixelCount(Int32) | 要扫描的像素/点数总数 | 默认0;改变该值会自动重置扫描进度 |
| ResetScan(Boolean) | 触发:重置扫描进程,从头开始 | 边沿触发;清空_currentLed2DPositions(L244-L249) |
| Save(Boolean) | 触发:把当前扫描点数据保存到FilePath | 边沿触发;序列化为带缩进的 JSON(L291-L311) |
| ScanIntervallum(Single) | 使用ScanTrigger时每扫描一个像素之间的等待秒数 | 默认0.0;实际节奏受帧率影响,源码以DateTime.Now差值判断(L173) |
| ScanTrigger(Boolean) | 触发:开始自动扫描,按ScanIntervallum间隔逐像素扫描 | 边沿触发、可切换启停(toggle),再次触发即停止(L251-L263) |
| TestFullMode(Boolean) | 调试模式:输出全白纹理,用于检查投影仪对齐与覆盖 | 默认false |
| TestPixelMode(Boolean) | 调试模式:逐个测试单个像素,像素索引由PixelCount控制 | 默认false;状态栏显示Test Pixel Mode: Cycling LED i/N |
| Threshold(Single) | 判定视频中某像素"点亮"的亮度阈值(0-1) | 默认0.0;阈值乘以 255 后用于二值化(见第 4 节) |
| VideoIn(Texture2D Required) | 指向投影表面/灯具的相机视频纹理输入 | 必需输入(.t3ui 标记为 Required);未连接时输出空缓冲区 |
3.2 输出
| 名称 | 类型 | 说明 |
|---|---|---|
| DebugTexture | T3.Core.DataTypes.Texture2D | 叠加了亮点标记圆点与状态文字的调试画面,可连到任意 Texture2D 显示 |
| PixelOutput | T3.Core.DataTypes.BufferWithViews | 当前帧要"点亮"的像素点列表(含亮度),用于驱动投影/灯具输出 |
| ScannedPoints2D | T3.Core.DataTypes.BufferWithViews | 扫描得到的 2D 点图,Point.Position的 XY 为归一化坐标、Z 为 0 |
两个 BufferWithViews 输出内部使用ResourceManager.SetupStructuredBuffer创建 GPU 结构化缓冲(含 SRV/UAV),并复用缓冲区避免每帧重建(L432-L457)。
4. 底层亮点检测算法:从视频帧到 2D 坐标
扫描能否成功,取决于FindBrightSpots(L375-L419)对视频帧的处理质量。其算法链为:
- 灰度化:
Cv2.CvtColor(frame, gray, BGR2GRAY); - 二值化:
Cv2.Threshold(gray, thresh, threshold*255, 255, Binary),Threshold参数在此被放大 255 倍; - 轮廓提取:
Cv2.FindContours(..., RetrievalModes.External, ApproxSimple),只取外轮廓; - 质心计算:对面积大于 2 像素的轮廓用图像矩
Cv2.Moments,以m10/m00、m01/m00求质心(光斑中心); - 坐标归一化:把像素坐标映射到
[-1, 1]区间并翻转 Y 轴:x = (cx / frame.Width - 0.5f) * 2fy = (cy / frame.Height - 0.5f) * -2f
这一归一化约定与 TiXL 内部Point数据的坐标习惯一致(ScannedPoints2D输出中Position的 XY 即使用该坐标),同时DebugTexture上的圆点绘制(L421-L425)会把归一化坐标反向映射回像素坐标进行可视化。
FindBrightSpots有findAll与单点两种模式:单点模式取面积最大的轮廓作为唯一目标(适合背景干净、每次只有一个亮点的情况);findAll模式返回所有面积大于 2 的轮廓质心(供TestFullMode统计可见亮点总数)。
5. 数据持久化:点图 JSON 与相机标定文件
5.1 点图文件(FilePath)
Save/Load使用System.Text.Json序列化List<Vector2>,选项为WriteIndented = true, IncludeFields = true(L17),因此生成的是人类可读的缩进 JSON,例如:
[ { "X": -0.123456, "Y": 0.456789 }, { "X": 0.234567, "Y": -0.123456 } ]加载时按PixelCount对齐:不足则补零,超出则截断(L278-L282),因此修改PixelCount后重新Load也能兼容旧数据。
5.2 相机标定文件(CalibrationPath)
LoadCalibrationData(L329-L357)解析的是自定义文本格式,而非标准 JSON。文件需满足:
- 第 1 行固定为
CameraMatrix; - 第 2-4 行为 3×3 内参矩阵,每行 3 个逗号分隔的浮点数;
- 第 5 行固定为
DistortionCoeffs; - 第 6 行为逗号分隔的畸变系数(个数不限,取决于所用畸变模型)。
示例:
CameraMatrix 1520.5, 0, 640.0 0, 1520.5, 360.0 0, 0, 1 DistortionCoeffs -0.24, 0.06, 0.0005, -0.0003, 0解析成功后在ApplyCorrection=true时经Cv2.Undistort对每一帧做去畸变处理,再执行亮点检测,从而得到校正后的准确坐标。标定数据本身可由 CameraCalibrator 生成——该运算符通过棋盘格(ChessboardSize、SquareInMm)采集 10-15 张不同角度的画面后Calibrate,得到焦距与镜头畸变参数,并支持Save到文件。若此处标定文件格式与 CameraCalibrator 的输出格式不完全一致,可通过脚本转换后再加载(文档中提到"usually a .json file generated by a calibration tool")。
6. 实操:一次完整的投影-相机标定流程
结合运算符文档(Video2DPointScanner.md)的 Tips 与源码行为,推荐流程如下:
- 接线:将相机画面接入
VideoIn(文档明确为必需输入),相机对准投影表面或待跟踪的 LED 阵列; - 设置扫描目标:
PixelCount设为要扫描的灯光/像素数量;PixelBrightness设为足够被相机分辨的亮度(建议先用较高值,如 1.0);Threshold根据现场亮度调试,使背景不误检而亮点清晰; - 对齐检查:开启
TestFullMode,观察DebugTexture,确认投影覆盖范围与相机视野是否对齐,统计可见点数; - 单点验证:开启
TestPixelMode,按ScanIntervallum间隔循环点亮单个像素,确认输出像素与相机坐标的对应关系是否稳定; - 开始扫描:关闭两个 Test 模式,触发
ScanTrigger启动自动扫描。源码会逐点点亮像素、回读视频、按阈值检测光斑质心并写入_currentLed2DPositions(L170-L198); - 查看进度:
DebugTexture上会以黄点叠加已找到的亮点,并显示Scanning... Found n/N状态文本;全部找到后状态变为Scan Complete. Found N/N.; - 保存结果:触发
Save,点图以 JSON 写入FilePath;下次使用直接Load,或配合ResetScan重新扫描; - (可选)镜头校正:若相机有明显畸变,用 CameraCalibrator 生成标定文件,填
CalibrationPath并触发LoadCalibration,再打开ApplyCorrection重新扫描; - (可选)3D 化:将扫描结果与 CameraCalibrator 结合,把 2D 点图映射为 3D 空间映射,用于精确的投影映射或 LED 定位。
排障提示:DebugMode开启后,扫描进度、保存/加载成功与否都会通过Log.Debug输出;文件路径为空时Load/Save会在日志中给出明确警告。
7. VisualizeSpotLights:聚光灯位置与朝向可视化
7.1 参数与输出
VisualizeSpotLights.md 定义的接口如下,实现见 VisualizeSpotLights.cs:
| 名称 | 类型 | 说明 |
|---|---|---|
| GPoints | BufferWithViews | 要可视化的灯具点数据(Point列表,含位置与朝向) |
| Color | Vector4 | 可视化颜色 |
| ShowBody | Boolean | 是否绘制灯具本体 |
| VisualizeAxis | Boolean | 是否可视化坐标轴(朝向指示) |
| Visibility | GizmoVisibility | Gizmo 可见性控制(枚举类型,来自T3.Core.Operator) |
| ReferencePoints | BufferWithViews | 参考点数据(用于与效果点合并显示,见 7.2) |
| Output(输出) | T3.Core.DataTypes.Command | 绘制命令,接入渲染链路 |
从源码结构看,该运算符是一个纯绘制辅助节点:接收 BufferWithViews 点数据与若干开关/颜色参数,输出一个 Command 用于在 3D 视口绘制 Gizmo 与灯具示意,本身不产生 DMX 数据。
7.2 在 ArtNet 灯光工作流中的标准用法
ArtnetAndDMX 文档 给出了使用VisualizeSpotLights精确摆放摇头灯(moving head)的步骤,这也是仓库示例 HowToDmx.t3 所演示的链路:
- 在 Blender 等工具中建立约 5cm 精度的现场 3D 模型;
- 用VisualizeSpotLights可视化定位灯光点;
- 在输出窗口启用Show Gizmos,选中点后用变换手柄移动;
- 调整朝向(对应
VisualizeAxis辅助); - 对点应用效果(如让灯光跟随某目标点);
- 将效果点与参考点合并(
ReferencePoints输入即用于此); - 在 PointsToDmxLights 中启用
WithReferencePoints,让 DMX 换算参考真实的灯具朝向。
因此VisualizeSpotLights的价值在于:把"灯具位置/朝向"从抽象的点数据变成可交互、可目视校验的 3D 场景元素,确保 DMX 通道算出的 pan/tilt 与现场物理布局一致。
8. 与周边运算符的协作关系小结
从 Lib.io.dmx 的整体结构看,helpers中的两个运算符分别解决了 DMX 灯光工作流的两端问题:
- 输入端(感知):
Video2DPointScanner把真实世界的亮点(投影画面、LED)映射为归一化 2D 点数据,可与CameraCalibrator联动升级为 3D 空间映射; - 输出端(控制):
VisualizeSpotLights把控制端的灯具点数据可视化,服务于PointsToDmxLights→ArtnetOutput/DmxOutput/SacnOutput的 DMX 通道生成链路。
两者共同构成了"视觉反馈校准 → 3D 布局 → DMX 控制"的闭环,是 TiXL 中连接"图像处理"与"舞台灯光"两个领域的桥梁模块。若需要更完整的 DMX 生态背景(协议基础、通道映射约定等),可继续阅读 ArtnetAndDMX.md。
9. 参考文件索引
- 运算符文档:helpers/README.md | Video2DPointScanner.md | VisualizeSpotLights.md
- 实现源码:Video2DPointScanner.cs | VisualizeSpotLights.cs
- 运算符定义与 UI:Video2DPointScanner.t3 | Video2DPointScanner.t3ui
- 关联生态:Lib.io.dmx 索引 | CameraCalibrator | ArtnetAndDMX 使用指南 | HowToDmx 示例
【免费下载链接】t3TiXL is an open source software to create realtime motion graphics.项目地址: https://gitcode.com/GitHub_Trending/t3/t3
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考