|
@@ -34,6 +34,7 @@ class _PoseDetectorState extends State<PoseDetector> {
|
|
|
final List<StreamController> _streamControllers = [];
|
|
final List<StreamController> _streamControllers = [];
|
|
|
final StreamController<Pose> _poseController = StreamController.broadcast();
|
|
final StreamController<Pose> _poseController = StreamController.broadcast();
|
|
|
final StreamController<StepExercise> _stepExerciseController = StreamController.broadcast();
|
|
final StreamController<StepExercise> _stepExerciseController = StreamController.broadcast();
|
|
|
|
|
+ late final Stream<List<PoseLandmark>> _exerciseJointsStream;
|
|
|
Image? _cameraImage;
|
|
Image? _cameraImage;
|
|
|
Pose? _detectedPose;
|
|
Pose? _detectedPose;
|
|
|
Size _imageSize = Size.zero;
|
|
Size _imageSize = Size.zero;
|
|
@@ -46,34 +47,76 @@ class _PoseDetectorState extends State<PoseDetector> {
|
|
|
super.initState();
|
|
super.initState();
|
|
|
_streamControllers.add(_poseController);
|
|
_streamControllers.add(_poseController);
|
|
|
_streamControllers.add(_stepExerciseController);
|
|
_streamControllers.add(_stepExerciseController);
|
|
|
|
|
+ _exerciseJointsStream = _getExerciseJointsStream(_poseController.stream);
|
|
|
|
|
+ CombineLatestStream.combine2(
|
|
|
|
|
+ _exerciseJointsStream
|
|
|
|
|
+ .map((event) => event.where((e) => widget.exercise.jointsOnScreen.contains(e.type)).toList()),
|
|
|
|
|
+ _stepExerciseController.stream,
|
|
|
|
|
+ (a, b) => [a, b]).listen(_handleNotInPlacePosition);
|
|
|
|
|
+ CombineLatestStream.combine2(
|
|
|
|
|
+ _exerciseJointsStream
|
|
|
|
|
+ .map((event) => event.where((e) => widget.exercise.jointsOnScreen.contains(e.type)).toList())
|
|
|
|
|
+ .bufferCount(5, 1),
|
|
|
|
|
+ _stepExerciseController.stream,
|
|
|
|
|
+ (a, b) => [a, b]).listen(_handleReadyPosition);
|
|
|
_startCamera = _startCameraStream();
|
|
_startCamera = _startCameraStream();
|
|
|
- _meanFilterStream = _getMeanFilterStream(_poseController.stream);
|
|
|
|
|
|
|
+ _meanFilterStream = _getMeanFilterStream(_exerciseJointsStream);
|
|
|
_streamSubscriptions.add(
|
|
_streamSubscriptions.add(
|
|
|
- CombineLatestStream.combine2(_stepExerciseController.stream, _meanFilterStream, (a, b) => [a, b]).listen((value) {
|
|
|
|
|
- final stepExercise = value.first as StepExercise;
|
|
|
|
|
- final meanFilteredData = value.last as MeanFilteredData;
|
|
|
|
|
- final isStartOfExerciseMovement = widget.exercise.isAtStartMovement(meanFilteredData);
|
|
|
|
|
- final isEndOfExerciseMovement = widget.exercise.isAtEndMovement(meanFilteredData);
|
|
|
|
|
- if (stepExercise == StepExercise.notInPlace && isStartOfExerciseMovement) {
|
|
|
|
|
- _stepExerciseController.add(StepExercise.ready);
|
|
|
|
|
- }
|
|
|
|
|
- if ((stepExercise == StepExercise.ready || stepExercise == StepExercise.start) && isEndOfExerciseMovement) {
|
|
|
|
|
- _stepExerciseController.add(StepExercise.end);
|
|
|
|
|
- }
|
|
|
|
|
- if (stepExercise == StepExercise.end && isStartOfExerciseMovement) {
|
|
|
|
|
- _stepExerciseController.add(StepExercise.start);
|
|
|
|
|
- }
|
|
|
|
|
- }));
|
|
|
|
|
|
|
+ CombineLatestStream.combine2(_stepExerciseController.stream, _meanFilterStream, (a, b) => [a, b]).listen(
|
|
|
|
|
+ (value) {
|
|
|
|
|
+ final stepExercise = value.first as StepExercise;
|
|
|
|
|
+ if (stepExercise == StepExercise.notInPlace) return;
|
|
|
|
|
+ final meanFilteredData = value.last as MeanFilteredData;
|
|
|
|
|
+ final isStartOfExerciseMovement = widget.exercise.isAtStartMovement(meanFilteredData);
|
|
|
|
|
+ final isEndOfExerciseMovement = widget.exercise.isAtEndMovement(meanFilteredData);
|
|
|
|
|
+ if ((stepExercise == StepExercise.start) && isEndOfExerciseMovement) {
|
|
|
|
|
+ _stepExerciseController.add(StepExercise.end);
|
|
|
|
|
+ }
|
|
|
|
|
+ if ((stepExercise == StepExercise.end || stepExercise == StepExercise.ready) && isStartOfExerciseMovement) {
|
|
|
|
|
+ _stepExerciseController.add(StepExercise.start);
|
|
|
|
|
+ }
|
|
|
|
|
+ },
|
|
|
|
|
+ ),
|
|
|
|
|
+ );
|
|
|
_stepExerciseController.add(StepExercise.notInPlace);
|
|
_stepExerciseController.add(StepExercise.notInPlace);
|
|
|
_repCounter = _stepExerciseController.stream
|
|
_repCounter = _stepExerciseController.stream
|
|
|
- .where((event) => event == StepExercise.start)
|
|
|
|
|
|
|
+ .pairwise()
|
|
|
|
|
+ .where((event) => event.first == StepExercise.end && event.last == StepExercise.start)
|
|
|
.scan((int accumulated, value, index) => accumulated + 1, 0);
|
|
.scan((int accumulated, value, index) => accumulated + 1, 0);
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
- Stream<MeanFilteredData> _getMeanFilterStream(Stream<Pose> stream) {
|
|
|
|
|
|
|
+ void _handleNotInPlacePosition(List<Object?> event) {
|
|
|
|
|
+ final poseLandmarks = event.first as List<PoseLandmark>;
|
|
|
|
|
+ final stepExercise = event.last as StepExercise;
|
|
|
|
|
+ if (stepExercise == StepExercise.notInPlace) return;
|
|
|
|
|
+ for (final poseLandmark in poseLandmarks) {
|
|
|
|
|
+ if (poseLandmark.inFrameLikelihood < 0.8) {
|
|
|
|
|
+ _stepExerciseController.add(StepExercise.notInPlace);
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ void _handleReadyPosition(List<Object?> event) {
|
|
|
|
|
+ final poseLandmarksBuffered = event.first as List<List<PoseLandmark>>;
|
|
|
|
|
+ final stepExercise = event.last as StepExercise;
|
|
|
|
|
+ if (stepExercise != StepExercise.notInPlace) return;
|
|
|
|
|
+ for (final poseLandmarks in poseLandmarksBuffered) {
|
|
|
|
|
+ for (final poseLandmark in poseLandmarks) {
|
|
|
|
|
+ if (poseLandmark.inFrameLikelihood < 0.8) return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ _stepExerciseController.add(StepExercise.ready);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Stream<List<PoseLandmark>> _getExerciseJointsStream(Stream<Pose> stream) {
|
|
|
return stream
|
|
return stream
|
|
|
.where((pose) => pose.landmarks.isNotEmpty)
|
|
.where((pose) => pose.landmarks.isNotEmpty)
|
|
|
- .map((pose) => pose.landmarks.where((landmark) => Exercise.authorizedType.contains(landmark.type)).toList())
|
|
|
|
|
|
|
+ .map((pose) => pose.landmarks.where((landmark) => Exercise.authorizedType.contains(landmark.type)).toList());
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ Stream<MeanFilteredData> _getMeanFilterStream(Stream<List<PoseLandmark>> stream) {
|
|
|
|
|
+ return stream
|
|
|
// Get last [buffer] poses
|
|
// Get last [buffer] poses
|
|
|
.bufferCount(meanFilterBuffer, 1)
|
|
.bufferCount(meanFilterBuffer, 1)
|
|
|
// Swap matrix [buffer] * [authorizedType.length]
|
|
// Swap matrix [buffer] * [authorizedType.length]
|
|
@@ -143,7 +186,9 @@ class _PoseDetectorState extends State<PoseDetector> {
|
|
|
|
|
|
|
|
void _handlePose(Pose? pose) {
|
|
void _handlePose(Pose? pose) {
|
|
|
if (!mounted) return;
|
|
if (!mounted) return;
|
|
|
- if (pose != null) _poseController.add(pose);
|
|
|
|
|
|
|
+ if (pose != null) {
|
|
|
|
|
+ _poseController.add(pose);
|
|
|
|
|
+ }
|
|
|
setState(() {
|
|
setState(() {
|
|
|
_detectedPose = pose;
|
|
_detectedPose = pose;
|
|
|
});
|
|
});
|
|
@@ -174,10 +219,10 @@ class _PoseDetectorState extends State<PoseDetector> {
|
|
|
Center(
|
|
Center(
|
|
|
child: CustomPaint(
|
|
child: CustomPaint(
|
|
|
child: _cameraImage,
|
|
child: _cameraImage,
|
|
|
- foregroundPainter: PosePainter(
|
|
|
|
|
- pose: _detectedPose,
|
|
|
|
|
- imageSize: _imageSize,
|
|
|
|
|
- ),
|
|
|
|
|
|
|
+ // foregroundPainter: PosePainter(
|
|
|
|
|
+ // pose: _detectedPose,
|
|
|
|
|
+ // imageSize: _imageSize,
|
|
|
|
|
+ // ),
|
|
|
),
|
|
),
|
|
|
),
|
|
),
|
|
|
StreamBuilder<StepExercise>(
|
|
StreamBuilder<StepExercise>(
|
|
@@ -235,5 +280,4 @@ class _PoseDetectorState extends State<PoseDetector> {
|
|
|
},
|
|
},
|
|
|
);
|
|
);
|
|
|
}
|
|
}
|
|
|
-
|
|
|
|
|
}
|
|
}
|