fixSharedMemory_src/) 的区别FIX 同时提供了源码集成方案 (fixSharedMemory_src/),以下是两者的主要区别:
| DLL 方案(本目录,推荐) | 源码方案 (fixSharedMemory_src/) |
|
|---|---|---|
| 集成方式 | 链接 .lib + 部署 .dll |
将 .h/.cpp 编译到自己项目中 |
| API 风格 | 纯 C 函数 (extern "C"),opaque handle |
C++ 类 (fixSharedMemoryManager),std::function 回调 |
| 编译器要求 | 任意 C/C++ 编译器 | 需与 FIX 相同的 MSVC 版本(ABI 兼容) |
| 语言兼容 | C / C++ / C# (P/Invoke) / Python (ctypes) 等 | 仅 C++ |
| 外部依赖 | 无 | 无(FS_LOG_* 默认空操作) |
| 接口复杂度 | 15 个函数,同步/异步二选一 | 底层接口,需自行管理共享内存读写细节 |
| 状态反馈 | 有完整的双向通信(见下) | 单向发送,无返回状态 |
| 适用场景 | 大多数客户集成 | 需要深度定制或直接访问底层协议 |
源码方案的 fixSharedMemoryManager 是单向通信——sendVolumeData() 仅返回数据是否成功写入共享内存(FSMemoryStatus),之后客户端无法得知 FIX 是否收到数据、处理是否成功、失败原因是什么。发送投影数据时,如果同时接收重建后的体数据,可以间接确认处理完成;但如果不需要接收体数据,或处理中途出错,客户端同样无法感知。
DLL 方案在内部自动建立了双向响应通道(response channel),解决了这些问题:
SendVolumeAndWait / SendProjectionAndWait)阻塞等待直到 FIX 处理完成,返回完整结果:成功/失败状态码、错误消息文本、总耗时SendVolumeAsync / SendProjectionAsync)通过回调实时通知:处理进度(0%~100%)、完成/失败状态、错误消息不确定选哪个?选 DLL 方案——代码最少、集成最快、无编译器版本限制、有完整的状态反馈。
FIX安装目录/src/fixSharedMemory_dll/
fixSharedMemory_api.h <- 头文件(唯一需要 include 的文件)
fixSharedMemoryDll.dll <- 动态链接库
fixSharedMemoryDll.lib <- 导入库(链接用)
fixSharedMemory_dll_README.md <- 本文档
example.cpp <- 完整示例代码
main.cpp <- 测试程序源码(来自 test_fixSharedMemoryDll)
test_fixSharedMemoryDll.exe <- 预编译测试程序(可直接运行验证连接)
fixSharedMemory_api.h 所在路径fixSharedMemoryDll.lib 所在路径fixSharedMemoryDll.libfixSharedMemoryDll.dll 复制到可执行文件所在目录也可以使用
#pragma comment(lib, "fixSharedMemoryDll.lib")替代第 3 步。所有文件在同一目录下,头文件和库路径可以指向同一个目录。
安装后可直接运行 test_fixSharedMemoryDll.exe 验证与 FIX 软件的连接:
test_fixSharedMemoryDll.exe -d E:\3D\data\tif -n myVolume -t teaching1 -r ResultPath -w 120
test_fixSharedMemoryDll.exe -d D:\data.raw -n rawVol -x 1536 -y 1536 -z 256
test_fixSharedMemoryDll.exe -d E:\3D\proj\tif -n PROJ1 -p --recon-config D:\rc.json -w 300
该测试程序支持 TIFF 文件夹和 RAW 文件两种数据输入方式,可指定体数据或投影数据模式。
只需 3 个函数就能完成一次体数据检测:fsChannelCreate → fsChannelSendVolumeAndWait → fsChannelDestroy。
#include "fixSharedMemory_api.h"
#pragma comment(lib, "fixSharedMemoryDll.lib")
int main(void)
{
/* ① 连接 FIX 软件(确保 FIX 已启动) */
FSChannelHandle ch = fsChannelCreate(NULL);
if (!ch) {
printf("连接失败! 请先启动 FIX 检测软件\n");
return -1;
}
/* ② 发送体数据, 等待检测完成 */
int ret = fsChannelSendVolumeAndWait(
ch,
myVolumeData, /* 你的体数据指针 */
"sample_001", /* 给这份数据起个名字 */
"TeachingFileName", /* 教学文件名称, 没有就传 NULL */
"D:\\Results", /* 结果(Results, XML)保存到哪个文件夹, 不需要就传 NULL */
256, 256, 100, /* 宽, 高, 深度(切片数) */
FS_IMAGE_C_GRAY16U, /* 像素类型: 16位无符号 (最常用) */
0.05, 0.05, 0.05, /* 体素尺寸 X/Y/Z, 单位 mm */
60000, /* 超时 60 秒, 传 0 也是 60 秒 */
NULL, NULL, 0 /* 不需要详细结果, 全传 NULL/0 */
);
if (ret == FS_API_OK)
printf("检测完成!\n");
else
printf("检测失败, 错误码: %d\n", ret);
/* ③ 用完释放 */
fsChannelDestroy(ch);
return 0;
}
就这么简单。 下面的内容是进阶用法和参数详解,需要时再看。
#include <stdio.h>
#include "fixSharedMemory_api.h"
#pragma comment(lib, "fixSharedMemoryDll.lib")
int doInspection(void* volumeData,
uint32_t width, uint32_t height, uint32_t depth)
{
/* 连接 */
FSChannelHandle ch = fsChannelCreate(NULL);
if (!ch) {
printf("连接失败! 请确认 FIX 软件已启动\n");
return -1;
}
/* 发送并等待 */
FSResultC result = {0}; /* 用于接收耗时等详细信息 */
char msg[512] = {0}; /* 用于接收错误/完成消息文本 */
int ret = fsChannelSendVolumeAndWait(
ch,
volumeData, "my_sample",
"C:\\Teaching\\my.tch", /* 教学文件, 没有传 NULL */
"C:\\Results", /* 结果目录, 不需要传 NULL */
width, height, depth,
FS_IMAGE_C_GRAY16U, /* 16-bit 灰度, 最常见 */
0.05, 0.05, 0.05, /* 体素尺寸 mm */
120000, /* 大数据建议加长超时 */
&result, /* [输出] 详细结果 */
msg, sizeof(msg) /* [输出] 消息文本 */
);
/* 检查结果 */
switch (ret) {
case FS_API_OK:
printf("检测完成! 耗时 %lld ms, 消息: %s\n",
(long long)result.elapsedMs, msg);
break;
case FS_API_ERROR_TIMEOUT:
printf("超时了! 已等待 %lld ms\n", (long long)result.elapsedMs);
break;
default:
printf("失败! 错误码=%d, 消息: %s\n", ret, msg);
break;
}
/* 释放 */
fsChannelDestroy(ch);
return ret;
}
#include <stdio.h>
#include <windows.h> /* Sleep */
#include "fixSharedMemory_api.h"
#pragma comment(lib, "fixSharedMemoryDll.lib")
/* === 回调函数 (DLL 内部线程调用, 不是主线程!) === */
void onProgress(uint32_t requestId, double progress,
const char* message, void* userData)
{
printf("进度: %.0f%% - %s\n", progress * 100.0, message ? message : "");
}
void onDone(uint32_t requestId, uint32_t taskStatus,
const char* message, int64_t elapsedMs, void* userData)
{
volatile int* flag = (volatile int*)userData;
if (taskStatus == FS_TASK_C_COMPLETED)
printf("完成! 耗时 %lld ms\n", (long long)elapsedMs);
else
printf("失败! 状态=%u, 消息: %s\n", taskStatus, message);
*flag = 1; /* 通知主线程 */
}
int main(void)
{
/* 连接 */
FSChannelHandle ch = fsChannelCreate(NULL);
if (!ch) { printf("连接失败\n"); return -1; }
/* 设置回调 */
volatile int done = 0;
fsChannelSetProgressCallback(ch, onProgress, NULL);
fsChannelSetCompletionCallback(ch, onDone, (void*)&done);
/* 发送 (立即返回, 不阻塞) */
uint32_t reqId = fsChannelSendVolumeAsync(
ch, myVolumeData, "sample",
"C:\\Teaching\\my.tch", "C:\\Results",
256, 256, 100, FS_IMAGE_C_GRAY16U,
0.05, 0.05, 0.05);
if (reqId == 0) {
printf("发送失败: %s\n", fsChannelGetLastError(ch));
fsChannelDestroy(ch);
return -1;
}
printf("已发送, requestId=%u, 主线程可以做其他事...\n", reqId);
/* 主线程等回调通知 (实际项目中用事件/消息循环代替) */
while (!done) Sleep(100);
fsChannelDestroy(ch);
return 0;
}
FSChannelHandle ch = NULL;
/* 连接函数, 可反复调用 */
int connectToFIX(void)
{
/* 如果旧句柄还在, 先释放 */
if (ch) {
fsChannelDestroy(ch);
ch = NULL;
}
ch = fsChannelCreate(NULL);
if (!ch) return -1; /* FIX 没启动 */
/* 重连后回调需要重新设置 */
fsChannelSetProgressCallback(ch, onProgress, NULL);
fsChannelSetCompletionCallback(ch, onDone, NULL);
return 0;
}
/* 使用时: 发现断了就重连 */
void sendData(void* data, uint32_t w, uint32_t h, uint32_t d)
{
if (!ch || !fsChannelIsConnected(ch)) {
if (connectToFIX() != 0) {
printf("FIX 未启动, 无法发送\n");
return;
}
}
fsChannelSendVolumeAsync(ch, data, "sample", NULL, NULL,
w, h, d, FS_IMAGE_C_GRAY16U,
0.05, 0.05, 0.05);
}
fsChannelCreateFSChannelHandle fsChannelCreate(const char* serverName);
创建通道并连接到 FIX 服务端(一步到位)。内部自动分配资源、连接共享内存、启动接收线程。
| 参数 | 类型 | 说明 |
|---|---|---|
| serverName | const char* | 要连接的共享内存名称。传 NULL 使用默认值 "FIXSharedMemory"(连接 FIX 检测软件) |
| 返回值 | 说明 |
|---|---|
| 非 NULL | 通道句柄,后续所有 API 调用都需要传入此句柄 |
| NULL | 连接失败(通常是 FIX 检测软件未启动),所有资源已自动释放 |
通常直接传 NULL 即可:
fsChannelCreate(NULL)如需重连,先
fsChannelDestroy再重新fsChannelCreate(回调需重新设置)。
fsChannelIsConnectedint fsChannelIsConnected(FSChannelHandle handle);
检查通道是否已连接到服务端。
| 参数 | 类型 | 说明 |
|---|---|---|
handle |
FSChannelHandle |
通道句柄(传 NULL 安全,返回 0) |
| 返回值 | 说明 |
|---|---|
1 |
已连接 |
0 |
未连接或句柄无效 |
fsChannelDestroyvoid fsChannelDestroy(FSChannelHandle handle);
销毁通道,释放所有资源。调用后句柄不可再使用。传 NULL 是安全的(无操作)。
fsChannelSendVolumeAndWaitint fsChannelSendVolumeAndWait(
FSChannelHandle handle,
const void* volumeData,
const char* volumeName,
const char* teachingFile,
const char* resultPath,
uint32_t width, uint32_t height, uint32_t depth,
int imageType,
double voxelSizeX, double voxelSizeY, double voxelSizeZ,
uint32_t timeoutMs,
FSResultC* outResult,
char* outMsg, size_t msgBufSize);
发送 3D 体数据到 FIX 软件进行检测,阻塞等待处理完成后返回。
参数说明:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
handle |
FSChannelHandle |
是 | 通道句柄 |
volumeData |
const void* |
是 | 体数据指针。数据在内存中按切片连续排列,即 slice[0] 的全部像素紧接着 slice[1],以此类推。总大小 = width x height x depth x 每像素字节数 |
volumeName |
const char* |
是 | 体数据名称标识符,用于 FIX 软件内部区分不同数据 |
teachingFile |
const char* |
否 | 教学文件路径。如果指定,FIX 会按此教学文件的配置进行检测。传 NULL 或空字符串表示不使用教学文件 |
resultPath |
const char* |
否 | 检测结果保存目录。FIX 会将检测报告输出到此路径。传 NULL 或空字符串表示不保存 |
width |
uint32_t |
是 | 体数据 X 维度(像素数) |
height |
uint32_t |
是 | 体数据 Y 维度(像素数) |
depth |
uint32_t |
是 | 体数据 Z 维度(切片数) |
imageType |
int |
是 | 像素类型,使用 FSImageTypeC 枚举值(见下方图像类型表) |
voxelSizeX |
double |
是 | X 方向体素物理尺寸,单位 mm |
voxelSizeY |
double |
是 | Y 方向体素物理尺寸,单位 mm |
voxelSizeZ |
double |
是 | Z 方向体素物理尺寸,单位 mm |
timeoutMs |
uint32_t |
否 | 超时时间(毫秒)。传 0 使用默认值 60000 (60 秒)。根据数据大小和检测复杂度适当调大 |
outResult |
FSResultC* |
否 | [输出] 接收结果详情的结构体指针。可传 NULL 表示不关心 |
outMsg |
char* |
否 | [输出] 接收状态消息的缓冲区。可传 NULL 表示不关心 |
msgBufSize |
size_t |
否 | outMsg 缓冲区大小(字节),建议 512 |
返回值:
| 返回值 | 说明 |
|---|---|
FS_API_OK (0) |
检测完成 |
FS_API_ERROR_INVALID_HANDLE (-1) |
句柄无效 |
FS_API_ERROR_NOT_CONNECTED (-2) |
未连接到服务端 |
FS_API_ERROR_INVALID_PARAM (-3) |
参数错误(volumeData 为 NULL 或尺寸为 0) |
FS_API_ERROR_SEND_FAILED (-4) |
发送失败或服务端处理报错 |
FS_API_ERROR_TIMEOUT (-5) |
等待超时 |
体数据内存布局:
volumeData 指向的内存:
偏移 0: slice[0] — width * height 个像素
偏移 sliceSize: slice[1]
偏移 sliceSize * 2: slice[2]
...
偏移 sliceSize * (depth-1): slice[depth-1]
sliceSize = width * height * bytesPerPixel
totalSize = sliceSize * depth
每像素字节数取决于 imageType:
| imageType | 每像素字节数 |
|---|---|
| FS_IMAGE_C_GRAY8 | 1 |
| FS_IMAGE_C_GRAY16U / FS_IMAGE_C_GRAY16S | 2 |
| FS_IMAGE_C_FLOAT32 | 4 |
| FS_IMAGE_C_RGB24 | 3 |
| FS_IMAGE_C_RGBA32 | 4 |
fsChannelSendProjectionAndWaitint fsChannelSendProjectionAndWait(
FSChannelHandle handle,
const void* projectionData,
const char* projectionName,
const char* teachingFile,
const char* resultPath,
const char* reconConfigJson,
uint32_t width, uint32_t height, uint32_t depth,
int imageType,
double voxelSizeX, double voxelSizeY, double voxelSizeZ,
uint32_t timeoutMs,
FSResultC* outResult,
char* outMsg, size_t msgBufSize,
void* outVolumeData, size_t outVolumeSize,
FSVolumeInfoC* outVolumeInfo);
发送投影数据到 FIX 软件进行重建 + 检测。参数大部分与 fsChannelSendVolumeAndWait 相同,以下仅说明差异参数:
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
projectionData |
const void* |
是 | 投影数据指针,内存布局与体数据相同。depth 表示投影张数 |
projectionName |
const char* |
是 | 投影数据名称标识符 |
reconConfigJson |
const char* |
否 | JSON 格式的重建配置参数。传 NULL 或空字符串表示使用默认重建参数 |
timeoutMs |
uint32_t |
否 | 默认值 120000 (120 秒)。重建耗时较长,建议根据实际情况设置 |
outVolumeData |
void* |
否 | [输出] 接收重建体数据的缓冲区。传 NULL 表示不需要体数据。需先调用 fsChannelEnableVolumeReceiver |
outVolumeSize |
size_t |
否 | outVolumeData 缓冲区大小(字节) |
outVolumeInfo |
FSVolumeInfoC* |
否 | [输出] 接收体数据信息(尺寸、体素大小等),可传 NULL |
reconConfigJson 获取方式:
在 FIX 软件的重建界面中调试好参数后,保存的 .fixrc 文件即为 JSON 格式的重建配置。可以直接将该文件内容作为 reconConfigJson 参数传入。
提示:建议先在 FIX 软件中完成一次完整的重建流程,确认参数正确后,再将
.fixrc文件内容用于 SDK 调用。
reconConfigJson 完整示例:
{
"geometry": {
"trajectory": "CBCT",
"sdAxis": "Z+",
"rotation": "Zccw",
"sourceToObject": 500.0,
"objectToDetector": 500.0,
"obliqueAngle": 0.0,
"startAngle": 0.0,
"scanAngle": 360.0,
"scanCount": 720
},
"detector": {
"dataType": "UInt16",
"sizeU": 1536,
"sizeV": 1536,
"physicalSizeU": 153.6,
"physicalSizeV": 153.6,
"offsetU": 0.0,
"offsetV": 0.0,
"tiltAngleU": 0.0,
"tiltAngleV": 0.0,
"tiltAngleN": 0.0,
"flipU": false,
"flipV": false
},
"volume": {
"dataType": "UInt16",
"sizeX": 512,
"sizeY": 512,
"sizeZ": 512,
"physicalSizeX": 15.36,
"physicalSizeY": 15.36,
"physicalSizeZ": 15.36,
"offsetX": 0.0,
"offsetY": 0.0,
"offsetZ": 0.0,
"flipX": false,
"flipY": false,
"flipZ": false,
"intensityMin": 0.0,
"intensityMax": 1.0
},
"despeckle": {
"filterType": "Bilateral",
"bilateralRadius": 2,
"bilateralSigmaSpace": 3.0,
"bilateralSigmaRange": 30.0
},
"_metadata": {
"version": "1.0",
"dataName": "MySample",
"loadToViewer": true
}
}
reconConfigJson 字段详解:
| 字段 | 类型 | 默认值 | 可选值 | 说明 |
|---|---|---|---|---|
trajectory |
string | "Planar" |
"CBCT", "OCT", "Planar", "Linear_Parallel", "Linear_Cross" |
扫描方式。CT 重建通常用 "CBCT" |
sdAxis |
string | "Z+" |
"X+", "X-", "Y+", "Y-", "Z+", "Z-" |
射线源-探测器轴线方向 |
rotation |
string | "Zccw" |
"Xccw", "Xcw", "Yccw", "Ycw", "Zccw", "Zcw" |
旋转轴与方向(ccw=逆时针, cw=顺时针) |
sourceToObject |
double | 500.0 |
> 0 | 射线源到样品中心距离 (mm) |
objectToDetector |
double | 500.0 |
> 0 | 样品中心到探测器距离 (mm)。注意:SDD = sourceToObject + objectToDetector |
obliqueAngle |
double | 0.0 |
-180 ~ 180 | 倾斜角 (度) |
startAngle |
double | 0.0 |
0 ~ 360 | 起始扫描角度 (度) |
scanAngle |
double | 360.0 |
> 0 | 总扫描角度 (度) |
scanCount |
int | 360 |
>= 1 | 投影张数(通常与投影数据的 depth 一致) |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dataType |
string | "UInt16" |
投影数据类型:"UInt8", "UInt16", "Int16", "Float32" |
sizeU |
int | 1536 |
探测器水平像素数 |
sizeV |
int | 1536 |
探测器垂直像素数 |
physicalSizeU |
double | 153.6 |
探测器水平物理尺寸 (mm),像素间距 = physicalSizeU / sizeU |
physicalSizeV |
double | 153.6 |
探测器垂直物理尺寸 (mm),像素间距 = physicalSizeV / sizeV |
offsetU |
double | 0.0 |
探测器中心水平偏移 (mm) |
offsetV |
double | 0.0 |
探测器中心垂直偏移 (mm) |
tiltAngleU |
double | 0.0 |
探测器 U 方向倾斜角 (度) |
tiltAngleV |
double | 0.0 |
探测器 V 方向倾斜角 (度) |
tiltAngleN |
double | 0.0 |
探测器法线方向倾斜角 (度) |
flipU |
bool | false |
水平翻转探测器 |
flipV |
bool | false |
垂直翻转探测器 |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
dataType |
string | "UInt16" |
输出体素类型:"UInt8", "UInt16", "Int16", "Float32" |
sizeX |
int | 1536 |
输出体数据 X 维度(体素数) |
sizeY |
int | 1536 |
输出体数据 Y 维度(体素数) |
sizeZ |
int | 1536 |
输出体数据 Z 维度(体素数) |
physicalSizeX |
double | 15.36 |
X 方向物理尺寸 (mm),体素间距 = physicalSizeX / sizeX |
physicalSizeY |
double | 15.36 |
Y 方向物理尺寸 (mm) |
physicalSizeZ |
double | 15.36 |
Z 方向物理尺寸 (mm) |
offsetX |
double | 0.0 |
体数据中心 X 偏移 (mm) |
offsetY |
double | 0.0 |
体数据中心 Y 偏移 (mm) |
offsetZ |
double | 0.0 |
体数据中心 Z 偏移 (mm) |
flipX |
bool | false |
X 方向翻转 |
flipY |
bool | false |
Y 方向翻转 |
flipZ |
bool | false |
Z 方向翻转 |
intensityMin |
double | 0.0 |
灰度裁剪最小值 (0.0~1.0 归一化范围) |
intensityMax |
double | 1.0 |
灰度裁剪最大值 (0.0~1.0 归一化范围) |
对重建后的体数据进行降噪处理。如果省略整个 despeckle 对象,默认使用 Bilateral 滤波器。
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
filterType |
string | "Bilateral" |
滤波算法:"None", "Hampel", "Median", "Bilateral", "NLM" |
以下参数根据选择的滤波算法填写对应部分即可,其余可省略:
Hampel 滤波器参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
hampelRadius |
int | 1 |
核半径 |
hampelThreshold |
double | 2.0 |
异常值检测阈值 |
Median 中值滤波器参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
medianRadius |
int | 1 |
核半径 |
Bilateral 双边滤波器参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
bilateralRadius |
int | 2 |
核半径 |
bilateralSigmaSpace |
double | 3.0 |
空间域标准差 |
bilateralSigmaRange |
double | 30.0 |
灰度域标准差 |
NLM 非局部均值滤波器参数:
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
nlmPatchRadius |
int | 1 |
匹配块半径 |
nlmSearchRadius |
int | 2 |
搜索窗口半径 |
nlmH |
double | 5000.0 |
滤波强度 |
nlmSigma |
double | 3.0 |
噪声标准差 |
| 字段 | 类型 | 默认值 | 说明 |
|---|---|---|---|
version |
string | "1.0" |
配置版本号 |
dataName |
string | "" |
配置名称(仅标识用) |
loadToViewer |
bool | true |
重建完成后是否自动加载到 FIX 查看器 |
注意:
configJson字段存储在共享内存固定缓冲区中(2048 字节),JSON 字符串长度不能超过此限制。建议精简格式(去掉缩进),只包含需要修改的参数。未指定的参数组(如省略整个geometry对象)将使用 FIX 软件当前的默认值。
异步 API 发送数据后立即返回一个 requestId,处理进度和完成结果通过回调函数通知。
fsChannelSetProgressCallbackvoid fsChannelSetProgressCallback(
FSChannelHandle handle, FSProgressFunc callback, void* userData);
设置进度回调函数。
| 参数 | 类型 | 说明 |
|---|---|---|
handle |
FSChannelHandle |
通道句柄 |
callback |
FSProgressFunc |
回调函数指针,传 NULL 取消回调 |
userData |
void* |
用户自定义数据指针,每次回调时原样传回 |
回调函数签名:
void myProgressCallback(uint32_t requestId, // 请求 ID
double progress, // 进度 0.0~1.0
const char* message, // 进度描述文本
void* userData); // 用户数据
注意:回调函数在 DLL 内部线程中调用,不在主线程。如需更新 UI,请使用线程安全的方式(如 PostMessage、信号量等)转发到主线程。
fsChannelSetCompletionCallbackvoid fsChannelSetCompletionCallback(
FSChannelHandle handle, FSCompletionFunc callback, void* userData);
设置完成回调函数。
| 参数 | 类型 | 说明 |
|---|---|---|
handle |
FSChannelHandle |
通道句柄 |
callback |
FSCompletionFunc |
回调函数指针,传 NULL 取消回调 |
userData |
void* |
用户自定义数据指针 |
回调函数签名:
void myCompletionCallback(uint32_t requestId, // 请求 ID
uint32_t taskStatus, // 任务状态码 (FSTaskStatusC)
const char* message, // 状态描述文本
int64_t elapsedMs, // 总耗时 (毫秒)
void* userData); // 用户数据
taskStatus 值:
| 状态码 | 枚举值 | 说明 |
|---|---|---|
| 100 | FS_TASK_C_STARTED | 任务已开始 |
| 101 | FS_TASK_C_PROGRESS | 进度更新(仅通过 progressCallback 回调) |
| 200 | FS_TASK_C_COMPLETED | 处理完成,有数据返回 |
| 201 | FS_TASK_C_COMPLETED_NODATA | 处理完成,无数据返回 |
| 400 | FS_TASK_C_FAILED | 处理失败 |
| 401 | FS_TASK_C_CANCELLED | 已取消 |
| 408 | FS_TASK_C_TIMEOUT | 超时 |
fsChannelEnableVolumeReceiverint fsChannelEnableVolumeReceiver(FSChannelHandle handle, size_t volumeDataSize);
启用体数据接收器,用于接收重建后的体数据。调用一次后,后续所有投影请求共用同一接收器。
| 参数 | 类型 | 说明 |
|---|---|---|
handle |
FSChannelHandle |
通道句柄 |
volumeDataSize |
size_t |
预期体数据大小(字节),如 512*512*512*2(16-bit 512^3 体数据)。DLL 内部自动加上协议头开销。传 0 关闭并释放接收器 |
| 返回值 | 说明 |
|---|---|
FS_API_OK (0) |
成功 |
FS_API_ERROR_INVALID_HANDLE (-1) |
句柄无效 |
FS_API_ERROR_INTERNAL (-6) |
创建接收器失败 |
fsChannelSetVolumeCallbackvoid fsChannelSetVolumeCallback(FSChannelHandle handle,
FSVolumeDataFunc callback, void* userData);
设置体数据到达回调(异步模式使用)。当回调已设置且接收器已启用时,异步投影请求会自动向服务端请求体数据返回。
| 参数 | 类型 | 说明 |
|---|---|---|
handle |
FSChannelHandle |
通道句柄 |
callback |
FSVolumeDataFunc |
回调函数指针,传 NULL 取消回调 |
userData |
void* |
用户自定义数据指针 |
回调函数签名:
void myVolumeCallback(uint32_t requestId, // 请求 ID
const void* volumeData, // 体数据指针 (仅回调期间有效)
const FSVolumeInfoC* volInfo, // 体数据信息
void* userData); // 用户数据
注意:
volumeData指向 DLL 内部缓冲区,仅在回调函数执行期间有效。如需保留体数据,必须在回调内将数据复制到自己的缓冲区。
fsChannelSendVolumeAsyncuint32_t fsChannelSendVolumeAsync(
FSChannelHandle handle,
const void* volumeData,
const char* volumeName,
const char* teachingFile,
const char* resultPath,
uint32_t width, uint32_t height, uint32_t depth,
int imageType,
double voxelSizeX, double voxelSizeY, double voxelSizeZ);
异步发送体数据,立即返回。参数含义与 fsChannelSendVolumeAndWait 相同(无 timeout 和输出参数)。
| 返回值 | 说明 |
|---|---|
> 0 |
requestId,可用于 fsChannelCancelRequest 取消。结果通过完成回调通知 |
0 |
发送失败,用 fsChannelGetLastError 获取详情 |
重要:异步发送返回后,
volumeData指向的内存已被复制到共享内存中,调用方可以立即释放。
fsChannelSendProjectionAsyncuint32_t fsChannelSendProjectionAsync(
FSChannelHandle handle,
const void* projectionData,
const char* projectionName,
const char* teachingFile,
const char* resultPath,
const char* reconConfigJson,
uint32_t width, uint32_t height, uint32_t depth,
int imageType,
double voxelSizeX, double voxelSizeY, double voxelSizeZ);
异步发送投影数据。参数含义与 fsChannelSendProjectionAndWait 相同。返回值与 fsChannelSendVolumeAsync 相同。
fsChannelCancelRequestvoid fsChannelCancelRequest(FSChannelHandle handle, uint32_t requestId);
取消一个待处理的异步请求。这是客户端侧取消,仅停止等待服务端响应,不会中断服务端正在进行的处理。
| 参数 | 类型 | 说明 |
|---|---|---|
handle |
FSChannelHandle |
通道句柄 |
requestId |
uint32_t |
异步发送时返回的请求 ID |
fsChannelGetLastErrorconst char* fsChannelGetLastError(FSChannelHandle handle);
获取最近一次错误消息。返回的指针指向 DLL 内部缓冲区,在下一次 API 调用前有效。传 NULL 句柄返回空字符串。
fsChannelPendingCountsize_t fsChannelPendingCount(FSChannelHandle handle);
返回当前待处理的异步请求数量。
fsGetVersionconst char* fsGetVersion(void);
返回 SDK 版本号字符串,如 "1.0.0"。返回静态字符串,始终有效。
typedef struct FSResultC {
uint32_t taskStatus; // FSTaskStatusC 枚举值
uint32_t requestId; // 请求 ID
double progress; // 最终进度 (通常为 1.0)
int64_t elapsedMs; // 从发送到收到响应的总耗时 (毫秒)
} FSResultC;
同步 API 通过 outResult 参数填充此结构体。
typedef struct FSVolumeInfoC {
uint32_t width; // X 维度
uint32_t height; // Y 维度
uint32_t depth; // Z 维度
int imageType; // 像素类型 (FSImageTypeC 值)
double voxelSizeX; // X 方向体素尺寸 (mm)
double voxelSizeY; // Y 方向体素尺寸 (mm)
double voxelSizeZ; // Z 方向体素尺寸 (mm)
size_t dataSize; // 实际体数据字节数
} FSVolumeInfoC;
通过 fsChannelSendProjectionAndWait 的 outVolumeInfo 参数或异步体数据回调的 volumeInfo 参数返回。
| 枚举值 | 值 | 每像素字节数 | 对应 OpenCV 类型 | 说明 |
|---|---|---|---|---|
| FS_IMAGE_C_GRAY8 | 1 | 1 | CV_8UC1 | 8 位无符号灰度 |
| FS_IMAGE_C_GRAY16U | 2 | 2 | CV_16UC1 | 16 位无符号灰度(最常用) |
| FS_IMAGE_C_GRAY16S | 3 | 2 | CV_16SC1 | 16 位有符号灰度 |
| FS_IMAGE_C_FLOAT32 | 4 | 4 | CV_32FC1 | 32 位浮点灰度 |
| FS_IMAGE_C_RGB24 | 5 | 3 | CV_8UC3 | 24 位 RGB 彩色 |
| FS_IMAGE_C_RGBA32 | 6 | 4 | CV_8UC4 | 32 位 RGBA 彩色 |
| 错误码 | 值 | 说明 |
|---|---|---|
| FS_API_OK | 0 | 成功 |
| FS_API_ERROR_INVALID_HANDLE | -1 | 句柄为 NULL 或无效 |
| FS_API_ERROR_NOT_CONNECTED | -2 | 未连接到服务端 |
| FS_API_ERROR_INVALID_PARAM | -3 | 参数错误(数据指针为 NULL、尺寸为 0 等) |
| FS_API_ERROR_SEND_FAILED | -4 | 发送失败或服务端返回处理失败 |
| FS_API_ERROR_TIMEOUT | -5 | 等待超时(同步 API) |
| FS_API_ERROR_INTERNAL | -6 | 内部错误(初始化失败等) |
fsChannelCreate(NULL) 创建句柄 + 连接 (失败返回 NULL)
|
fsChannelSendVolumeAndWait 发送数据, 阻塞等待
| (检查返回值和 outResult)
|
fsChannelDestroy 释放资源
fsChannelCreate(NULL) 创建句柄 + 连接 (失败返回 NULL)
|
fsChannelSetProgressCallback 设置进度回调
fsChannelSetCompletionCallback 设置完成回调
|
fsChannelSendVolumeAsync 发送数据, 立即返回 requestId
|
|--- (主线程继续做其他事情) ---
|
| onProgress(requestId, 0.3, "处理中...", userData) <- DLL 内部线程
| onProgress(requestId, 0.7, "处理中...", userData)
| onCompletion(requestId, 200, "完成", 1234, userData) <- DLL 内部线程
|
fsChannelDestroy 释放资源
fsChannelCreate(NULL) 创建句柄 + 连接 (失败返回 NULL)
|
fsChannelEnableVolumeReceiver(size) 启用体数据接收器 (一次)
|
fsChannelSendProjectionAndWait(..., 发送投影, 阻塞等待
buf, bufSize, &volInfo) 最后 3 个参数接收体数据
|
| ret == FS_API_OK && volInfo.dataSize > 0
| → buf 中已有重建体数据
|
| (可多次调用 sendProjectionAndWait, 接收器可复用)
|
fsChannelDestroy 释放资源
fsChannelCreate(NULL) 创建句柄 + 连接 (失败返回 NULL)
|
fsChannelEnableVolumeReceiver(size) 启用体数据接收器 (一次)
fsChannelSetVolumeCallback(onVolume, ud) 设置体数据回调
fsChannelSetCompletionCallback(onDone, ud)
|
fsChannelSendProjectionAsync(...) 异步发送投影
|
| onVolume(reqId, data, &info, ud) <- 体数据先到达
| onDone(reqId, 200, "完成", ms, ud) <- 完成状态后到达
|
fsChannelDestroy 释放资源
const char* 参数传 NULL 是安全的(等同于空字符串)fsChannelCreate 返回 NULL,怎么办?最常见原因:FIX 检测软件没有启动。 请先启动 FIX 软件,等它完全加载后再调用 fsChannelCreate。
其他可能:
serverName(通常传 NULL 就行)fsChannelSendVolumeAndWait 返回 FS_API_ERROR_TIMEOUT (-5)体数据太大或检测配置复杂,在超时时间内没处理完。解决方法:
/* 把超时从 60 秒改大, 比如 5 分钟 */
fsChannelSendVolumeAndWait(ch, ..., 300000, ...);
/* ^^^^^^ 300000 毫秒 = 5 分钟 */
fsChannelSendVolumeAndWait 返回 FS_API_ERROR_SEND_FAILED (-4)服务端处理报错了。用最后三个参数获取错误消息:
FSResultC result = {0};
char msg[512] = {0};
int ret = fsChannelSendVolumeAndWait(ch, ..., &result, msg, sizeof(msg));
if (ret != FS_API_OK) {
printf("错误消息: %s\n", msg); /* 看服务端返回了什么 */
printf("状态码: %u\n", result.taskStatus); /* 400=失败, 401=取消, 408=超时 */
}
体数据在内存中是一个连续的一维数组,按切片顺序排列:
内存布局 (以 256x256x100, 16-bit 为例):
总大小 = 256 * 256 * 100 * 2 = 13107200 字节 (12.5 MB)
byte[0 ~ 131071] = 第 0 张切片 (256*256*2 字节)
byte[131072 ~ 262143] = 第 1 张切片
...
byte[12975128 ~ 13107199] = 第 99 张切片
用 C 代码准备数据:
uint32_t width = 256;
uint32_t height = 256;
uint32_t depth = 100;
/* 16-bit 灰度: 每像素 2 字节 */
size_t totalSize = (size_t)width * height * depth * 2;
uint16_t* data = (uint16_t*)malloc(totalSize);
/* 访问第 z 张切片、第 y 行、第 x 列的像素: */
uint16_t pixel = data[z * width * height + y * width + x];
imageType 应该传什么?看你的数据是什么类型的:
| 你的数据 | 传什么 | 每像素字节数 |
|---|---|---|
uint8_t 数组(8 位灰度) |
FS_IMAGE_C_GRAY8 |
1 |
uint16_t 数组(最常见) |
FS_IMAGE_C_GRAY16U |
2 |
int16_t 数组 |
FS_IMAGE_C_GRAY16S |
2 |
float 数组 |
FS_IMAGE_C_FLOAT32 |
4 |
90% 的 CT 数据都是 16-bit 无符号,传
FS_IMAGE_C_GRAY16U。
voxelSizeX/Y/Z 不知道填什么?体素尺寸是每个像素在物理空间中代表多少 mm。这个值取决于你的 CT 设备。
例如:扫描视野 (FOV) 是 12.8mm,重建分辨率 256 像素 → 体素尺寸 = 12.8 / 256 = 0.05 mm。
如果真不知道,先随便填个值(如 0.05, 0.05, 0.05),检测功能能正常工作,只是物理尺寸标注不准确。
teachingFile 和 resultPath 可以不传吗?可以。这两个参数都是可选的:
/* 不用教学文件, 也不保存结果 */
fsChannelSendVolumeAndWait(ch, data, "sample",
NULL, /* teachingFile: 不使用教学文件 */
NULL, /* resultPath: 不保存结果 */
...);
teachingFile = NULL → FIX 使用当前加载的教学文件(如果有的话)resultPath = NULL → 不保存检测结果文件| 场景 | 推荐 | 理由 |
|---|---|---|
| 命令行工具 / 批处理脚本 | 同步 (SendVolumeAndWait) |
简单,发了就等结果 |
| 有 UI 的程序 | 异步 (SendVolumeAsync + 回调) |
不会卡死界面 |
| 需要显示进度条 | 异步 + SetProgressCallback |
进度回调里更新进度条 |
| 不确定 / 先试试 | 同步 | 代码最少,先跑起来再说 |
只要调 fsChannelDestroy 就行,它会自动断开连接、释放所有资源:
fsChannelDestroy(ch); /* 之后 ch 不能再用 */
ch = NULL; /* 好习惯: 置空防止误用 */
可以。每个句柄独立工作,互不干扰:
FSChannelHandle ch1 = fsChannelCreate(NULL); /* 连接默认 FIX */
FSChannelHandle ch2 = fsChannelCreate("MyServer"); /* 连接另一个服务 */
/* ... 各自独立使用 ... */
fsChannelDestroy(ch1);
fsChannelDestroy(ch2);