import 'package:body_detection/models/pose.dart'; import 'package:body_detection/models/pose_landmark.dart'; import 'package:body_detection/models/pose_landmark_type.dart'; import 'package:flutter/widgets.dart'; class PosePainter extends CustomPainter { final Pose? pose; final Size imageSize; PosePainter({required this.pose, required this.imageSize, Key? key}); final pointPaint = Paint()..color = const Color.fromRGBO(255, 255, 255, 0.8); final leftPointPaint = Paint()..color = const Color.fromRGBO(223, 157, 80, 1); final rightPointPaint = Paint()..color = const Color.fromRGBO(100, 208, 218, 1); final linePaint = Paint() ..color = const Color.fromARGB(228, 0, 0, 0) ..strokeWidth = 3; final maskPaint = Paint()..colorFilter = const ColorFilter.mode(Color.fromRGBO(0, 0, 255, 0.5), BlendMode.srcOut); @override void paint(Canvas canvas, Size size) { if (pose == null) return; final double hRatio = imageSize.width == 0 ? 1 : size.width / imageSize.width; final double vRatio = imageSize.height == 0 ? 1 : size.height / imageSize.height; offsetForPart(PoseLandmark part) => Offset(part.position.x * hRatio, part.position.y * vRatio); // Landmark connections final landmarksByType = {for (final it in pose!.landmarks) it.type: it}; for (final connection in _connections) { final point1 = offsetForPart(landmarksByType[connection[0]]!); final point2 = offsetForPart(landmarksByType[connection[1]]!); canvas.drawLine(point1, point2, linePaint); } for (final part in pose!.landmarks) { // Landmark points canvas.drawCircle(offsetForPart(part), 5, pointPaint); if (part.type.isLeftSide) { canvas.drawCircle(offsetForPart(part), 3, leftPointPaint); } else if (part.type.isRightSide) { canvas.drawCircle(offsetForPart(part), 3, rightPointPaint); } // Landmark labels TextSpan span = TextSpan( text: part.type.toString().substring(16), style: const TextStyle( color: Color.fromRGBO(0, 128, 255, 1), fontSize: 10, shadows: [ Shadow( color: Color.fromRGBO(255, 255, 255, 1), offset: Offset(1, 1), blurRadius: 1, ), ], ), ); TextPainter tp = TextPainter(text: span, textAlign: TextAlign.left); tp.textDirection = TextDirection.ltr; tp.layout(); tp.paint(canvas, offsetForPart(part)); } } @override bool shouldRepaint(PosePainter oldDelegate) { return oldDelegate.pose != pose || oldDelegate.imageSize != imageSize; } static const List> _connections = [ [PoseLandmarkType.leftEar, PoseLandmarkType.leftEyeOuter], [PoseLandmarkType.leftEyeOuter, PoseLandmarkType.leftEye], [PoseLandmarkType.leftEye, PoseLandmarkType.leftEyeInner], [PoseLandmarkType.leftEyeInner, PoseLandmarkType.nose], [PoseLandmarkType.nose, PoseLandmarkType.rightEyeInner], [PoseLandmarkType.rightEyeInner, PoseLandmarkType.rightEye], [PoseLandmarkType.rightEye, PoseLandmarkType.rightEyeOuter], [PoseLandmarkType.rightEyeOuter, PoseLandmarkType.rightEar], [PoseLandmarkType.mouthLeft, PoseLandmarkType.mouthRight], [PoseLandmarkType.leftShoulder, PoseLandmarkType.rightShoulder], [PoseLandmarkType.leftShoulder, PoseLandmarkType.leftHip], [PoseLandmarkType.rightShoulder, PoseLandmarkType.rightHip], [PoseLandmarkType.rightShoulder, PoseLandmarkType.rightElbow], [PoseLandmarkType.rightWrist, PoseLandmarkType.rightElbow], [PoseLandmarkType.rightWrist, PoseLandmarkType.rightThumb], [PoseLandmarkType.rightWrist, PoseLandmarkType.rightIndexFinger], [PoseLandmarkType.rightWrist, PoseLandmarkType.rightPinkyFinger], [PoseLandmarkType.leftHip, PoseLandmarkType.rightHip], [PoseLandmarkType.leftHip, PoseLandmarkType.leftKnee], [PoseLandmarkType.rightHip, PoseLandmarkType.rightKnee], [PoseLandmarkType.rightKnee, PoseLandmarkType.rightAnkle], [PoseLandmarkType.leftKnee, PoseLandmarkType.leftAnkle], [PoseLandmarkType.leftElbow, PoseLandmarkType.leftShoulder], [PoseLandmarkType.leftWrist, PoseLandmarkType.leftElbow], [PoseLandmarkType.leftWrist, PoseLandmarkType.leftThumb], [PoseLandmarkType.leftWrist, PoseLandmarkType.leftIndexFinger], [PoseLandmarkType.leftWrist, PoseLandmarkType.leftPinkyFinger], [PoseLandmarkType.leftAnkle, PoseLandmarkType.leftHeel], [PoseLandmarkType.leftAnkle, PoseLandmarkType.leftToe], [PoseLandmarkType.rightAnkle, PoseLandmarkType.rightHeel], [PoseLandmarkType.rightAnkle, PoseLandmarkType.rightToe], [PoseLandmarkType.rightHeel, PoseLandmarkType.rightToe], [PoseLandmarkType.leftHeel, PoseLandmarkType.leftToe], [PoseLandmarkType.rightIndexFinger, PoseLandmarkType.rightPinkyFinger], [PoseLandmarkType.leftIndexFinger, PoseLandmarkType.leftPinkyFinger], ]; }