doc_scanner.dart 8.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  1. import 'dart:ffi';
  2. import 'dart:io';
  3. import 'dart:isolate';
  4. import 'package:async/async.dart';
  5. import 'package:camera/camera.dart';
  6. import 'package:ffi/ffi.dart';
  7. import 'package:flutter/material.dart';
  8. import 'package:native_vision/native/dynamic_lib_singleton.dart';
  9. import 'package:native_vision/edge_detection.dart';
  10. import 'package:native_vision/native_vision_helper.dart';
  11. import 'package:path_provider/path_provider.dart';
  12. class NativeROI extends Struct {
  13. @Int32()
  14. external int left;
  15. @Int32()
  16. external int top;
  17. @Int32()
  18. external int width;
  19. @Int32()
  20. external int height;
  21. }
  22. class NativeCameraImage extends Struct {
  23. external Pointer<Uint8> buf;
  24. @Int32()
  25. external int height;
  26. @Int32()
  27. external int width;
  28. external Pointer<Utf8> format;
  29. @Int32()
  30. external int bytesPerRow;
  31. external Pointer<NativeROI> roiPtr;
  32. static Pointer<NativeCameraImage> New(imgSizeInByte, String format) {
  33. final nativeCameraImgPtr =
  34. malloc.allocate<NativeCameraImage>(sizeOf<NativeCameraImage>());
  35. final ref = nativeCameraImgPtr.ref;
  36. ref.buf = malloc.allocate<Uint8>(imgSizeInByte);
  37. ref.roiPtr = malloc.allocate<NativeROI>(sizeOf<NativeROI>());
  38. ref.format = format.toNativeUtf8();
  39. return nativeCameraImgPtr;
  40. }
  41. }
  42. extension CameraImageExtention on CameraImage {
  43. void fillNativeCameraImage(int left, int top, int cropW, int cropH,
  44. Pointer<NativeCameraImage> cameraImgPtr) {
  45. final imgSize = planes[0].bytes.length;
  46. cameraImgPtr.ref.height = height;
  47. cameraImgPtr.ref.width = width;
  48. final buf = cameraImgPtr.ref.buf;
  49. buf.asTypedList(imgSize).setRange(0, imgSize, planes[0].bytes);
  50. cameraImgPtr.ref.bytesPerRow = planes[0].bytesPerRow;
  51. final roiPtr = cameraImgPtr.ref.roiPtr;
  52. roiPtr.ref.left = left;
  53. roiPtr.ref.top = top;
  54. roiPtr.ref.width = cropW;
  55. roiPtr.ref.height = cropH;
  56. }
  57. }
  58. extension NativeCameraImageExtention on Pointer<NativeCameraImage> {
  59. void release() {
  60. malloc.free(ref.buf);
  61. malloc.free(ref.format);
  62. malloc.free(ref.roiPtr);
  63. malloc.free(this);
  64. }
  65. }
  66. class DocScanner {
  67. static final DynamicLibrary _docScan = DynamicLibSingleton().dl;
  68. static final int Function(
  69. Pointer<Utf8> docModelPath, Pointer<Utf8> cornerModelPath, double)
  70. _nativeDocFinder = _docScan
  71. .lookup<
  72. NativeFunction<
  73. IntPtr Function(
  74. Pointer<Utf8>, Pointer<Utf8>, Float)>>('initDocFinder')
  75. .asFunction();
  76. static final void Function(int docFinderPtr) _nativeDeleteDocFinder = _docScan
  77. .lookup<NativeFunction<Void Function(IntPtr)>>('deleteDocFinder')
  78. .asFunction();
  79. static final bool Function(Pointer<CoordinateArray>,
  80. Pointer<NativeCameraImage>, int docFinderPtr, Pointer<Utf8> tmpDirPtr)
  81. _findDocWithCameraImg = _docScan
  82. .lookup<
  83. NativeFunction<
  84. Bool Function(
  85. Pointer<CoordinateArray> coordsPtr,
  86. Pointer<NativeCameraImage>,
  87. IntPtr,
  88. Pointer<Utf8>)>>('findDocWithCameraImg')
  89. .asFunction();
  90. static final bool Function(Pointer<Utf8> imgPathPtr, Pointer<Utf8> outPathPtr,
  91. int docFinderPtr, Pointer<CoordinateArray>, Pointer<NativeROI>)
  92. _findDocWithImgPath = _docScan
  93. .lookup<
  94. NativeFunction<
  95. Bool Function(
  96. Pointer<Utf8>,
  97. Pointer<Utf8>,
  98. IntPtr,
  99. Pointer<CoordinateArray>,
  100. Pointer<NativeROI>)>>("findDocWithImgPath")
  101. .asFunction();
  102. SendPort? _sendport;
  103. StreamQueue? _events;
  104. bool _release = true;
  105. Pointer<NativeCameraImage>? _cameraImgPtr;
  106. Future<void> init(double precise) async {
  107. final docModelPath =
  108. await NativeVisionHelper.getModelPath("doc_model_20220802.tflite");
  109. final cornerModelPath =
  110. await NativeVisionHelper.getModelPath("corner_model_20220802.tflite");
  111. final tmpDir = (await getTemporaryDirectory()).path;
  112. final p = ReceivePort();
  113. Isolate.spawn(isolateHandler, {
  114. "sendPort": p.sendPort,
  115. "docModelPath": docModelPath,
  116. "cornerModelPath": cornerModelPath,
  117. "tmpDir": tmpDir,
  118. "precise": precise,
  119. });
  120. _events = StreamQueue<dynamic>(p);
  121. _sendport = await _events?.next;
  122. _release = false;
  123. }
  124. Future<List<Offset>?> scanWithCameraImage(CameraImage image, int left,
  125. int top, int cropW, int cropH, int width, int height) async {
  126. try {
  127. if (_release) throw "The DocScanner has release!";
  128. _cameraImgPtr ??= NativeCameraImage.New(
  129. image.planes[0].bytes.length, image.format.group.name);
  130. image.fillNativeCameraImage(left, top, cropW, cropH, _cameraImgPtr!);
  131. _sendport!.send({
  132. "data_source": "CameraImage",
  133. "CameraImagePtr": _cameraImgPtr!.address
  134. });
  135. final msg = await _events?.next;
  136. if (_release) throw "The DocScanner has release!";
  137. if (msg["succ"] == true) {
  138. Pointer<CoordinateArray> coordArr =
  139. Pointer<CoordinateArray>.fromAddress(msg["coordsPtr"]);
  140. final coords = List.generate(
  141. 4,
  142. (index) => Offset(coordArr.ref.coordsPtr[index].x * width,
  143. coordArr.ref.coordsPtr[index].y * height));
  144. return coords;
  145. }
  146. } catch (e, st) {
  147. debugPrint("exception:${e.toString()}");
  148. debugPrintStack(stackTrace: st);
  149. }
  150. return null;
  151. }
  152. Future<EdgeDetectionResult?> singleScan(
  153. String imgPath, String outPath, ROI roi) async {
  154. Pointer<Utf8>? imgPathPtr;
  155. Pointer<NativeROI>? roiPtr;
  156. Pointer<Utf8>? outPathPtr;
  157. try {
  158. if (_release) throw "The DocScanner has release!";
  159. outPathPtr = outPath.toNativeUtf8();
  160. imgPathPtr = imgPath.toNativeUtf8();
  161. roiPtr = roi.toNativeROI();
  162. _sendport!.send({
  163. "data_source": "File",
  164. "imgPathPtr": imgPathPtr.address,
  165. "roiPtr": roiPtr.address,
  166. "outPathPtr": outPathPtr.address
  167. });
  168. final msg = await _events?.next;
  169. if (_release) throw "The DocScanner has release!";
  170. Pointer<CoordinateArray> coordArr =
  171. Pointer<CoordinateArray>.fromAddress(msg["coordsPtr"]);
  172. return coordArr.toEdgeDetectionResult();
  173. } catch (e, st) {
  174. debugPrintStack(stackTrace: st);
  175. } finally {
  176. if (imgPathPtr != null) malloc.free(imgPathPtr);
  177. if (roiPtr != null) malloc.free(roiPtr);
  178. if (outPathPtr != null) malloc.free(outPathPtr);
  179. }
  180. return null;
  181. }
  182. bool available() {
  183. return !_release;
  184. }
  185. Future<void> release() async {
  186. _release = true;
  187. _cameraImgPtr?.release();
  188. _sendport?.send(null);
  189. await _events?.cancel();
  190. debugPrint("DocScanner release!");
  191. }
  192. static Future<void> isolateHandler(dynamic args) async {
  193. final commandPort = ReceivePort();
  194. SendPort p = args["sendPort"];
  195. String docModelPath = args["docModelPath"];
  196. String cornerModelPath = args["cornerModelPath"];
  197. double precise = args["precise"];
  198. final tmpDirPtr = (args["tmpDir"] as String).toNativeUtf8();
  199. p.send(commandPort.sendPort);
  200. final docFinderPtr =
  201. await _initNativeDocFinder(docModelPath, cornerModelPath, precise);
  202. final coordsPtr = CoordinateArrayExtention.createCoordsPtr();
  203. await for (final msg in commandPort) {
  204. if (msg == null) break;
  205. final type = msg["data_source"];
  206. var succ = false;
  207. if (type == "CameraImage") {
  208. succ = _findDocWithCameraImg(
  209. coordsPtr,
  210. Pointer<NativeCameraImage>.fromAddress(msg["CameraImagePtr"]),
  211. docFinderPtr,
  212. tmpDirPtr);
  213. } else if (type == "File") {
  214. succ = _findDocWithImgPath(
  215. Pointer<Utf8>.fromAddress(msg["imgPathPtr"]),
  216. Pointer<Utf8>.fromAddress(msg["outPathPtr"]),
  217. docFinderPtr,
  218. coordsPtr,
  219. Pointer<NativeROI>.fromAddress(msg["roiPtr"]));
  220. }
  221. p.send({"succ": succ, "coordsPtr": coordsPtr.address});
  222. }
  223. // release res!!!
  224. _nativeDeleteDocFinder(docFinderPtr);
  225. malloc.free(tmpDirPtr);
  226. coordsPtr.release();
  227. // debugPrint('DocScan isolate has finished.');
  228. Isolate.exit();
  229. }
  230. static Future<int> _initNativeDocFinder(
  231. String docModelPath, String cornerModelPath, double precise) async {
  232. final nativeDocModelPath = docModelPath.toNativeUtf8();
  233. final nativeCornerModelPath = cornerModelPath.toNativeUtf8();
  234. final docFinderPtr =
  235. _nativeDocFinder(nativeDocModelPath, nativeCornerModelPath, precise);
  236. malloc.free(nativeDocModelPath);
  237. malloc.free(nativeCornerModelPath);
  238. return docFinderPtr;
  239. }
  240. }