pose_detector.dart 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163
  1. import 'dart:async';
  2. import 'dart:io';
  3. import 'package:body_detection/models/point3d.dart';
  4. import 'package:body_detection/models/pose_landmark.dart';
  5. import 'package:body_detection/models/pose_landmark_type.dart';
  6. import 'package:rxdart/rxdart.dart';
  7. import 'package:body_detection/body_detection.dart';
  8. import 'package:body_detection/models/image_result.dart';
  9. import 'package:body_detection/models/pose.dart';
  10. import 'package:flutter/material.dart';
  11. import 'package:path_provider/path_provider.dart';
  12. import 'pose_painter.dart';
  13. class PoseDetector extends StatefulWidget {
  14. const PoseDetector({Key? key}) : super(key: key);
  15. @override
  16. State<PoseDetector> createState() => _PoseDetectorState();
  17. }
  18. class _PoseDetectorState extends State<PoseDetector> {
  19. Image? _cameraImage;
  20. Pose? _detectedPose;
  21. Size _imageSize = Size.zero;
  22. late Future<void> _startCamera;
  23. final StreamController<Pose> _streamController = StreamController.broadcast();
  24. final Directory appDir = Directory('/storage/emulated/0/Android/data/com.example.physigo/files');
  25. @override
  26. initState() {
  27. super.initState();
  28. _startCamera = _startCameraStream();
  29. _writeDataToFile();
  30. }
  31. void _writeDataToFile() {
  32. const buffer = 10;
  33. File meanFilteredData = File("${appDir.path}/meanFilteredData.csv");
  34. if (meanFilteredData.existsSync()) meanFilteredData.deleteSync();
  35. // TODO: get positions variation
  36. // TODO: detect excentric/concentric part of the movement (derivative negative or positive)
  37. _streamController.stream
  38. .where((pose) => pose.landmarks.isNotEmpty)
  39. .map((pose) => pose.landmarks.where((landmark) => authorizedType.contains(landmark.type)).toList())
  40. .bufferCount(buffer, 1)
  41. .listen((filteredLandmarks) {
  42. // inverse height and width of the matrix
  43. List<List<PoseLandmark>> bufferedPositions = [];
  44. for (int j = 0; j < filteredLandmarks[0].length; j++) {
  45. List<PoseLandmark> positions = [];
  46. for (int i = 0; i < filteredLandmarks.length; i++) {
  47. positions.add(filteredLandmarks[i][j]);
  48. }
  49. bufferedPositions.add(positions);
  50. }
  51. // mean filters of the buffer points
  52. final meanPositions = bufferedPositions.map((landmarks) {
  53. return landmarks
  54. .map((landmark) => landmark.position)
  55. .map((position) => [
  56. position.x / buffer,
  57. position.y / buffer,
  58. position.z / buffer,
  59. ])
  60. .reduce((value, element) => [
  61. value[0] + element[0],
  62. value[1] + element[1],
  63. value[2] + element[2],
  64. ]);
  65. });
  66. for (var position in meanPositions) {
  67. final str = "${position[0]}, ${position[1]}, ${position[2]};";
  68. meanFilteredData.writeAsStringSync(str, mode: FileMode.append);
  69. }
  70. meanFilteredData.writeAsStringSync("\n", mode: FileMode.append);
  71. });
  72. }
  73. Future<void> _startCameraStream() async {
  74. await BodyDetection.startCameraStream(onFrameAvailable: _handleCameraImage, onPoseAvailable: _handlePose);
  75. await BodyDetection.enablePoseDetection();
  76. }
  77. Future<void> _stopCameraStream() async {
  78. await BodyDetection.disablePoseDetection();
  79. await BodyDetection.stopCameraStream();
  80. }
  81. void _handleCameraImage(ImageResult result) {
  82. if (!mounted) return;
  83. // To avoid a memory leak issue.
  84. // https://github.com/flutter/flutter/issues/60160
  85. PaintingBinding.instance?.imageCache?.clear();
  86. PaintingBinding.instance?.imageCache?.clearLiveImages();
  87. final image = Image.memory(
  88. result.bytes,
  89. gaplessPlayback: true,
  90. fit: BoxFit.contain,
  91. );
  92. setState(() {
  93. _cameraImage = image;
  94. _imageSize = result.size;
  95. });
  96. }
  97. void _handlePose(Pose? pose) {
  98. if (!mounted) return;
  99. if (pose != null) _streamController.add(pose);
  100. setState(() {
  101. _detectedPose = pose;
  102. });
  103. }
  104. @override
  105. void dispose() {
  106. _stopCameraStream();
  107. _streamController.close();
  108. super.dispose();
  109. }
  110. @override
  111. Widget build(BuildContext context) {
  112. return FutureBuilder<void>(
  113. future: _startCamera,
  114. builder: (context, snapshot) {
  115. if (snapshot.connectionState == ConnectionState.waiting) {
  116. return const Center(child: CircularProgressIndicator());
  117. }
  118. return Center(
  119. child: CustomPaint(
  120. size: _imageSize,
  121. child: _cameraImage,
  122. foregroundPainter: PosePainter(
  123. pose: _detectedPose,
  124. imageSize: _imageSize,
  125. ),
  126. ),
  127. );
  128. },
  129. );
  130. }
  131. static const authorizedType = [
  132. PoseLandmarkType.nose,
  133. PoseLandmarkType.leftShoulder,
  134. PoseLandmarkType.rightShoulder,
  135. PoseLandmarkType.leftElbow,
  136. PoseLandmarkType.rightElbow,
  137. PoseLandmarkType.leftWrist,
  138. PoseLandmarkType.rightWrist,
  139. PoseLandmarkType.leftHip,
  140. PoseLandmarkType.rightHip,
  141. PoseLandmarkType.leftKnee,
  142. PoseLandmarkType.rightKnee,
  143. PoseLandmarkType.leftAnkle,
  144. PoseLandmarkType.rightAnkle,
  145. ];
  146. }