加载动态库
import 'dart:ffi' as FFI;
final scriptDir = File(Platform.resolvedExecutable).parent.path;
_algorithm_lib = FFI.DynamicLibrary.open(p.join(scriptDir, 'CylinderSDK.dll'));
声明dll中导出的函数
定义函数类型
//FFI.Pointer<Utf8> 表示函数返回参数是一个字符串
typedef Test1C = FFI.Pointer<Utf8> Function();
typedef Test1Dart = FFI.Pointer<Utf8> Function();
//接收返回参数为int的情况:
typedef Test2C = FFI.Int32 Function();
typedef Test2Dart = int Function();
//接收返回参数为字节数据的情况:
typedef Test3C = FFI.Pointer<FFI.Uint8> Function();
typedef Test3Dart = FFI.Pointer<FFI.Uint8> Function();
//传参int、字符串、字节数据的情况:
typedef Test4C = FFI.Int32 Function(FFI.Int32,FFI.Pointer<Utf8>,FFI.Pointer<FFI.Uint8>);
typedef Test4Dart = int Function(int,FFI.Pointer<Utf8>,FFI.Pointer<FFI.Uint8>);
初始化可直接调用Dll导出函数的方法
//()里的字符串参数就是dll导出函数的名称
Test1 = _algorithm_lib!.lookupFunction<Test1C, Test1Dart>('Test1');
Test2 = _algorithm_lib!.lookupFunction<Test2C, Test1Dart>('Test2');
Test3 = _algorithm_lib!.lookupFunction<Test3C, Test3Dart>('Test3');
Test4 = _algorithm_lib!.lookupFunction<Test1C, Test1Dart>('Test4');
调用方法
//调用Test1
//resolutionsPointer 是一个指针,不能直接使用,需要通过toDartString方法将其转为dart的字符串
final FFI.Pointer<Utf8> resolutionsPointer = Test1!();
//这里的resolutionsJSON就是dll中Test1方法返回的字符串值
final String resolutionsJSON = resolutionsPointer.toDartString();
//调用Test2
//这里的code就是dll中Test2方法返回的int类型值
int code = Test2!();
//调用Test3
//resolutionsPointer 是一个指针,不能直接使用,需要通过asTypedList方法将其转为dart的Uint8List类型
final FFI.Pointer<FFI.Uint8> resolutionsPointer = Test3!();
//这里传递的参数是一个整数,表示字节数据的长度,需要预先知道长度
final Uint8List Data = resolutionsPointer.asTypedList(1000);
//调用Test4
int intValue = 1;
//toNativeUtf8将dart字符串转为Pointer<Utf8>类型
final FFI.Pointer<Utf8> stringValue = "test".toNativeUtf8();
//将data 转为FFI.Pointer<FFI.Uint8>格式
//先创建对应长度的Pointer<FFI.Uint8>,然后通过setAll将数据写入
//通过calloc申请的内存需要在适当时间释放,比如函数调用完成后
Uint8List data = Uint8List(0);
final FFI.Pointer<FFI.Uint8> dataPointer = calloc.call<FFI.Uint8>(data.length);
dataPointer.asTypedList(data.length).setAll(0, data);
int code = Test4!(intValue,stringValue,dataPointer);
//释放内存
calloc.free(dataPointer);
上面的示例涵盖了大多数场景
完整示例
我封装了一个类来通用化,下面是完整示例
示例类:
// ignore: file_names
import 'dart:ffi' as FFI;
import 'dart:io';
// ignore: depend_on_referenced_packages
import 'package:ffi/ffi.dart';
import 'dart:convert';
// import 'dart:io' show Platform;
// ignore: depend_on_referenced_packages
import 'package:path/path.dart' as p;
import 'package:flutter/foundation.dart';
typedef SetResolutionC = FFI.Int32 Function(FFI.Int32, FFI.Int32);
typedef SetResolutionDart = int Function(int, int);
typedef GetAvailableResolutionsC = FFI.Pointer<Utf8> Function();
typedef GetAvailableResolutionsDart = FFI.Pointer<Utf8> Function();
//算法识别相关
//相机拍照
typedef CvCamera_Image_TakeC = FFI.Pointer<FFI.Uint8> Function(
FFI.Int32, FFI.Int32);
typedef CvCamera_Image_TakeDart = FFI.Pointer<FFI.Uint8> Function(int, int);
//扫描相机
typedef CvCamera_Scan_AllC = FFI.Int32 Function();
typedef CvCamera_Scan_AllDart = int Function();
//获取相机所有信息
typedef CvCamera_Scan_GetAllC = FFI.Pointer<Utf8> Function();
typedef CvCamera_Scan_GetAllDart = FFI.Pointer<Utf8> Function();
//打开相机
typedef CvCamera_OpenC = FFI.Int32 Function(FFI.Pointer<Utf8>);
typedef CvCamera_OpenDart = int Function(FFI.Pointer<Utf8>);
//启动相机,开始捕获
typedef CvCamera_StartC = FFI.Int32 Function(FFI.Int32);
typedef CvCamera_StartDart = int Function(int);
//设置相机参数<handel>,<name>,<value>
typedef CvCamera_SetParamC = FFI.Int32 Function(
FFI.Int32, FFI.Pointer<Utf8>, FFI.Pointer<Utf8>);
typedef CvCamera_SetParamDart = int Function(
int, FFI.Pointer<Utf8>, FFI.Pointer<Utf8>);
//通过句柄关闭相机<handel>
typedef CvCamera_CloseC = FFI.Int32 Function(FFI.Int32);
typedef CvCamera_CloseDart = int Function(int);
//通过句柄返回相机的描述信息,获取的这段数据记得释放
typedef CvCamera_GetDescC = FFI.Pointer<Utf8> Function(FFI.Int32);
typedef CvCamera_GetDescDart = FFI.Pointer<Utf8> Function(int);
//释放内存数据(字符串)
typedef CvCamera_String_FreeC = FFI.Int32 Function(FFI.Pointer<Utf8>);
typedef CvCamera_String_FreeDart = int Function(FFI.Pointer<Utf8>);
//释放内存数据(字节)
typedef CvCamera_Bytes_FreeC = FFI.Int32 Function(FFI.Pointer<FFI.Uint8>);
typedef CvCamera_Bytes_FreeDart = int Function(FFI.Pointer<FFI.Uint8>);
//初始化日志
typedef CylinderSDK_InitC = FFI.Int32 Function(FFI.Pointer<Utf8>);
typedef CylinderSDK_InitDart = int Function(FFI.Pointer<Utf8>);
typedef CvCamera_Image_ReadFileC = FFI.Pointer<FFI.Uint8> Function(
FFI.Pointer<Utf8>, FFI.Int32);
typedef CvCamera_Image_ReadFileDart = FFI.Pointer<FFI.Uint8> Function(
FFI.Pointer<Utf8>, int);
//终止日志
typedef CylinderSDK_TerminateC = FFI.Int32 Function();
typedef CylinderSDK_TerminateDart = int Function();
typedef CvCamera_Image_GrayToRGBC = FFI.Pointer<FFI.Uint8> Function(
FFI.Pointer<FFI.Uint8>, FFI.Int32, FFI.Int32, FFI.Int32);
typedef CvCamera_Image_GrayToRGBDart = FFI.Pointer<FFI.Uint8> Function(
FFI.Pointer<FFI.Uint8>, int, int, int);
//获取图片处理结果
//algorithm: 算法代码, 1,2,3,4类似
//data: 图片数据
//width: 图片宽度
//height: 图片高度
//format: 图片格式, 0:RGB, 1:GRAY
//stride: 图片数据的步长
//param: 算法参数
//返回值: 处理结果
typedef CvImage_Process_DetectC = FFI.Pointer<Utf8> Function(
FFI.Int32,
FFI.Pointer<FFI.Uint8>,
FFI.Int32,
FFI.Int32,
FFI.Int32,
FFI.Int32,
FFI.Pointer<Utf8>);
typedef CvImage_Process_DetectDart = FFI.Pointer<Utf8> Function(
int, FFI.Pointer<FFI.Uint8>, int, int, int, int, FFI.Pointer<Utf8>);
//保存图片
//data: 图片数据
//width: 图片宽度
//height: 图片高度
//format: 图片格式, 0:RGB, 1:GRAY
//stride: 图片数据的步长
//filename: 文件名
typedef CvImage_Save_FileC = FFI.Int32 Function(FFI.Pointer<FFI.Uint8>,
FFI.Int32, FFI.Int32, FFI.Int32, FFI.Int32, FFI.Pointer<Utf8>);
typedef CvImage_Save_FileDart = int Function(
FFI.Pointer<FFI.Uint8>, int, int, int, int, FFI.Pointer<Utf8>);
class RecognitionEngine {
FFI.DynamicLibrary? _lib;
FFI.DynamicLibrary? _algorithm_lib;
CvCamera_Image_TakeDart? CvCamera_Image_Take;
CvCamera_Scan_AllDart? CvCamera_Scan_All;
CvCamera_Scan_GetAllDart? CvCamera_Scan_GetAll;
CvCamera_OpenDart? CvCamera_Open;
CvCamera_SetParamDart? CvCamera_SetParam;
CvCamera_CloseDart? CvCamera_Close;
CvCamera_GetDescDart? CvCamera_GetDesc;
CvCamera_String_FreeDart? CvCamera_String_Free;
CvCamera_Bytes_FreeDart? CvCamera_Bytes_Free;
CvImage_Process_DetectDart? CvImage_Process_Detect;
CylinderSDK_InitDart? CylinderSDK_Init;
CylinderSDK_TerminateDart? CylinderSDK_Terminate;
CvCamera_Image_ReadFileDart? CvCamera_Image_ReadFile;
CvCamera_Image_GrayToRGBDart? CvCamera_Image_GrayToRGB;
CvCamera_StartDart? CvCamera_Start;
CvImage_Save_FileDart? CvImage_Save_File;
// 构造函数
RecognitionEngine() {
loadLibrary();
initDllFunctions();
}
void init() {
loadLibrary();
initDllFunctions();
}
// 加载动态库
void loadLibrary() {
//获取exe所在的路径
final scriptDir = File(Platform.resolvedExecutable).parent.path;
_lib = FFI.DynamicLibrary.open(p.join(scriptDir, 'SetResolution.dll'));
_algorithm_lib =
FFI.DynamicLibrary.open(p.join(scriptDir, 'CylinderSDK.dll'));
}
//初始化函数
Future<void> initDllFunctions() async {
//初始化拍照
CvCamera_Image_Take = _algorithm_lib!
.lookupFunction<CvCamera_Image_TakeC, CvCamera_Image_TakeDart>(
'CvCamera_Image_Take');
//初始化扫描相机
CvCamera_Scan_All = _algorithm_lib!
.lookupFunction<CvCamera_Scan_AllC, CvCamera_Scan_AllDart>(
'CvCamera_Scan_All');
//初始化获取当前所有相机的信息
CvCamera_Scan_GetAll = _algorithm_lib!
.lookupFunction<CvCamera_Scan_GetAllC, CvCamera_Scan_GetAllDart>(
'CvCamera_Scan_GetAll');
//初始化通过名字打开相机,返回句柄
CvCamera_Open = _algorithm_lib!
.lookupFunction<CvCamera_OpenC, CvCamera_OpenDart>('CvCamera_Open');
//初始化通过句柄设置相机的参数
CvCamera_SetParam = _algorithm_lib!
.lookupFunction<CvCamera_SetParamC, CvCamera_SetParamDart>(
'CvCamera_SetParam');
//启动相机,开始捕获
CvCamera_Start = _algorithm_lib!
.lookupFunction<CvCamera_StartC, CvCamera_StartDart>('CvCamera_Start');
//初始化通过句柄关闭相机
CvCamera_Close = _algorithm_lib!
.lookupFunction<CvCamera_CloseC, CvCamera_CloseDart>('CvCamera_Close');
//初始化通过句柄返回相机的描述信息,获取的这段数据记得释放
CvCamera_GetDesc = _algorithm_lib!
.lookupFunction<CvCamera_GetDescC, CvCamera_GetDescDart>(
'CvCamera_GetDesc');
//初始化释放内存数据(字符串)
CvCamera_String_Free = _algorithm_lib!
.lookupFunction<CvCamera_String_FreeC, CvCamera_String_FreeDart>(
'CvCamera_String_Free');
//初始化释放内存数据(字节)
CvCamera_Bytes_Free = _algorithm_lib!
.lookupFunction<CvCamera_Bytes_FreeC, CvCamera_Bytes_FreeDart>(
'CvCamera_Bytes_Free');
//初始化获取图片处理结果
CvImage_Process_Detect = _algorithm_lib!
.lookupFunction<CvImage_Process_DetectC, CvImage_Process_DetectDart>(
'CvImage_Process_Detect');
//初始化日志
CylinderSDK_Init = _algorithm_lib!
.lookupFunction<CylinderSDK_InitC, CylinderSDK_InitDart>(
'CylinderSDK_Init');
//终止日志
CylinderSDK_Terminate = _algorithm_lib!
.lookupFunction<CylinderSDK_TerminateC, CylinderSDK_TerminateDart>(
'CylinderSDK_Terminate');
//读取文件
CvCamera_Image_ReadFile = _algorithm_lib!
.lookupFunction<CvCamera_Image_ReadFileC, CvCamera_Image_ReadFileDart>(
'CvCamera_Image_ReadFile');
//gray转为rgb
CvCamera_Image_GrayToRGB = _algorithm_lib!.lookupFunction<
CvCamera_Image_GrayToRGBC,
CvCamera_Image_GrayToRGBDart>('CvCamera_Image_GrayToRGB');
//保存图片
CvImage_Save_File = _algorithm_lib!.lookupFunction<
CvImage_Save_FileC,
CvImage_Save_FileDart>('CvImage_Save_File');
}
//相机扫描
Future<int> Camera_Scan_All() async {
debugPrint("------------->开始扫描相机");
int code = CvCamera_Scan_All!();
debugPrint("------------->相机扫描结果:$code");
return code;
}
//初始化日志
void initlog() {
final FFI.Pointer<Utf8> name = "D:/test.txt".toNativeUtf8();
CylinderSDK_Init!(name);
}
//获取当前所有相机信息
Future<List<Map<String, dynamic>>> Camera_Scan_GetAll() async {
debugPrint("------------->开始获取当前所有相机信息");
final FFI.Pointer<Utf8> resolutionsPointer = CvCamera_Scan_GetAll!();
final String resolutionsJSON = resolutionsPointer.toDartString();
// Map<String, dynamic> resolutions = json.decode(resolutionsJSON);
final List<dynamic> decodedResolutions = json.decode(resolutionsJSON);
List<Map<String, dynamic>> resolutions =
decodedResolutions.cast<Map<String, dynamic>>();
debugPrint("------------->相机信息:${jsonEncode(resolutions)}");
return resolutions;
}
//通过名字打开相机
Future<int> Camera_Open(String name) async {
debugPrint("------------->开始打开相机:$name");
final FFI.Pointer<Utf8> namePointer = name.toNativeUtf8();
int code = CvCamera_Open!(namePointer);
debugPrint("------------->相机打开返回的句柄:$code");
return code;
}
//通过句柄设置相机的参数
Future<int> Camera_SetParam(int handle, String name, String param) async {
debugPrint("------------->开始设置相机参数:$name");
final FFI.Pointer<Utf8> namePointer = name.toNativeUtf8();
final FFI.Pointer<Utf8> paramPointer = param.toNativeUtf8();
int code = CvCamera_SetParam!(handle, namePointer, paramPointer);
debugPrint("------------->相机设置参数返回值:$code");
return code;
}
//通过句柄启动相机,开始捕获图像
Future<int> Camera_Start(int handle) async {
debugPrint("------------->通过句柄启动相机:$handle");
int code = CvCamera_Start!(handle);
debugPrint("------------->通过句柄启动相机返回值:$code");
return code;
}
//通过句柄关闭相机
Future<int> Camera_Close(int handle) async {
debugPrint("------------->开始关闭相机,句柄:$handle");
int code = CvCamera_Close!(handle);
debugPrint("------------->相机关闭返回值:$code");
return code;
}
//通过句柄返回相机的描述信息,获取的这段数据记得释放
Future<Map<String, dynamic>> Camera_GetDesc(int handle) async {
debugPrint("------------->开始通过句柄获取相机描述信息:$handle");
final FFI.Pointer<Utf8> resolutionsPointer = CvCamera_GetDesc!(handle);
final String resolutionsJSON = resolutionsPointer.toDartString();
debugPrint("------------->相机描述信息:$resolutionsJSON");
//释放内存
int code = CvCamera_String_Free!(resolutionsPointer);
debugPrint("内存释放结果:$code");
return jsonDecode(resolutionsJSON);
}
//相机拍照
Future<Uint8List> Camera_Image_Take(int handle, int timeout,
{required int width, required int height, required int stride}) async {
debugPrint("------------->开始相机拍照,句柄:$handle");
final FFI.Pointer<FFI.Uint8> resolutionsPointer =
CvCamera_Image_Take!(handle, timeout);
//先判断resolutionsPointer返回的是否是空地址
// 检查是否返回了有效的指针
if (resolutionsPointer.address == 0) {
debugPrint("-------------> 相机拍照返回的指针为空:${resolutionsPointer.address}");
return Uint8List(0);
}
//获取rgb数据
final Uint8List rgb_data =
await Camera_Image_GrayToRGB(resolutionsPointer, width, height, stride);
return rgb_data;
}
Future<Uint8List> Camera_Image_GrayToRGB(
FFI.Pointer<FFI.Uint8> Pointer, int width, int height, int stride) async {
debugPrint("------------->dataPointer:${Pointer}");
// //转为dart的字节数组
// final Uint8List data = Pointer.asTypedList(width * height);
// //保存到D:// 存为yuv
// // await File('D:/test.yuv').writeAsBytes(data);
// debugPrint("------------->相机拍照结果长度:${data.length}");
// final FFI.Pointer<FFI.Uint8> dataPointer =
// calloc.call<FFI.Uint8>(data.length);
debugPrint("------------->开始转换格式");
final FFI.Pointer<FFI.Uint8> resolutionsPointer =
CvCamera_Image_GrayToRGB!(Pointer, width, height, stride);
//转为dart的字节数组
final Uint8List Data = resolutionsPointer.asTypedList(width * height * 3);
debugPrint("------------->开始转换格式结果长度:${Data.length}");
//拷贝data
final Uint8List dataCopy = Uint8List.fromList(Data);
// 释放内存
int flag = CvCamera_Bytes_Free!(resolutionsPointer);
debugPrint("释放返回值:$flag");
int flag1 = CvCamera_Bytes_Free!(Pointer);
debugPrint("释放返回值:$flag1");
// calloc.free(dataPointer);
return dataCopy;
}
//获取图片处理结果
Future<Map<String, dynamic>?> Image_Process_Detect(
int algorithm,
Uint8List data,
int width,
int height,
int format,
int stride,
String param) async {
debugPrint("------------->开始获取图片处理结果");
try {
//自动释放池
// Arena arena = Arena();
// final FFI.Pointer<FFI.Uint8> dataPointer1 =
// arena.call<FFI.Uint8>(data.length);
// dataPointer1.asTypedList(data.length).setAll(0, data);
// //回收内存
// arena.releaseAll();
//将data 转为FFI.Pointer<FFI.Uint8>格式
final FFI.Pointer<FFI.Uint8> dataPointer =
calloc.call<FFI.Uint8>(data.length);
dataPointer.asTypedList(data.length).setAll(0, data);
final FFI.Pointer<Utf8> paramPointer = param.toNativeUtf8();
final FFI.Pointer<Utf8> resolutionsPointer = CvImage_Process_Detect!(
algorithm, dataPointer, width, height, format, stride, paramPointer);
final String resolutionsJSON = resolutionsPointer.toDartString();
Map<String, dynamic>? resolutions;
resolutions = json.decode(resolutionsJSON);
debugPrint("------------->图片处理结果:${jsonEncode(resolutions)}");
//释放内存
calloc.free(dataPointer);
return resolutions;
} catch (e) {
debugPrint("------------->图片处理结果解析失败:$e");
return null;
}
}
//保存图片
Future<int> Image_Save_File(Uint8List data, int width, int height, int format,
int stride, String filename) async {
debugPrint("------------->开始保存图片");
//将data 转为FFI.Pointer<FFI.Uint8>格式
final FFI.Pointer<FFI.Uint8> dataPointer =
calloc.call<FFI.Uint8>(data.length);
dataPointer.asTypedList(data.length).setAll(0, data);
final FFI.Pointer<Utf8> filenamePointer = filename.toNativeUtf8();
int code = CvImage_Save_File!(
dataPointer, width, height, format, stride, filenamePointer);
debugPrint("------------->保存图片结果:$code");
//释放内存
calloc.free(dataPointer);
return code;
}
//通过图片路径获取源数据
Future<Uint8List> Camera_Image_ReadFile(
String filename, int format, int size) async {
// debugPrint("------------->开始读取本地图片,文件:$filename");
final FFI.Pointer<Utf8> filenamePointer = filename.toNativeUtf8();
final FFI.Pointer<FFI.Uint8> resolutionsPointer =
CvCamera_Image_ReadFile!(filenamePointer, format);
//转为dart的字节数组
final Uint8List data = resolutionsPointer.asTypedList(size);
//拷贝data
final Uint8List dataCopy = Uint8List.fromList(data);
// debugPrint("------------->读取本地图片结果长度:${data.length}");
// 释放内存
int flag = CvCamera_Bytes_Free!(resolutionsPointer);
// debugPrint("释放返回值:$flag");
return dataCopy;
}
// 设置分辨率
int setResolution(int w, int h) {
var setResolution = _lib
?.lookupFunction<SetResolutionC, SetResolutionDart>("SetResolution");
// 调用函数设置分辨率
print('Setting resolution to $w x $h'); // 调试输出
// 调用函数设置分辨率
final int result = setResolution!(w, h);
if (result == 0) {
print('Resolution set successfully');
} else {
print('Failed to set resolution');
}
// lib.close();
return result;
}
// 获取可用分辨率
Future<List<Map<String, dynamic>>> getResolutions() async {
List<Map<String, dynamic>> resolutions = [];
try {
final GetAvailableResolutionsC getAvailableResolutionsFunc = _lib!
.lookupFunction<GetAvailableResolutionsC,
GetAvailableResolutionsDart>('GetAvailableResolutions');
print('Getting available resolutions'); // 调试输出
final FFI.Pointer<Utf8> resolutionsPointer =
getAvailableResolutionsFunc();
final String resolutionsJSON = resolutionsPointer.toDartString();
final List<dynamic> decodedResolutions = json.decode(resolutionsJSON);
resolutions = decodedResolutions.cast<Map<String, dynamic>>();
// 注意:这里没有释放内存,因为toDartString()应该不会保留指向原生内存的引用
} catch (e) {
print('Error getting resolutions: $e'); // 打印异常信息
}
return resolutions;
}
}
使用类
late RecognitionEngine REngine;
@override
void initState() {
super.initState();
REngine = RecognitionEngine();
Test();
}
void Test(){
await REngine.Camera_Scan_All();
}
//如果需要所有组件访问一个示例对象(可以避免多次加载dll)
//使用单例模式
//例如:
//用到了Provider插件
runApp(
MultiProvider(
providers: [
// 创建RecognitionEngine的单例
Provider<RecognitionEngine>.value(
value: RecognitionEngine(),
),
],
child: MyApp(),
),
);
//在其余组件获取已经初始化的RecognitionEngine实例
RecognitionEngine REngine = Provider.of<RecognitionEngine>(context, listen: false);
//其它方法:
//修改类结构
RecognitionEngine._();
static final RecognitionEngine instance = RecognitionEngine._();
Future<void> init() async {
}
//main中调用
await RecognitionEngine.instance.init();
//其它组件访问
final REngine = RecognitionEngine.instance;