SLProject  4.2.000
A platform independent 3D computer graphics framework for desktop OS, Android, iOS and online in web browsers
CVTrackedMediaPipeHands.cpp
Go to the documentation of this file.
1 /**
2  * \file CVTrackedMediaPipeHands.cpp
3  * \date December 2022
4  * \authors Marino von Wattenwyl
5  * \copyright http://opensource.org/licenses/GPL-3.0
6  * \remarks Please use clangformat to format the code. See more code style on
7  * https://github.com/cpvrlab/SLProject4/wiki/SLProject-Coding-Style
8 */
9 
10 #ifdef SL_BUILD_WITH_MEDIAPIPE
11 # include <CVTrackedMediaPipeHands.h>
12 
13 //-----------------------------------------------------------------------------
14 # define CHECK_MP_RESULT(result) \
15  if (!result) \
16  { \
17  const char* error = mp_get_last_error(); \
18  std::cerr << error << std::endl; \
19  mp_free_error(error); \
20  SL_EXIT_MSG("Exiting due to MediaPipe error"); \
21  }
22 //-----------------------------------------------------------------------------
23 typedef std::vector<std::pair<mp_hand_landmark, mp_hand_landmark>> ConnectionList;
24 //-----------------------------------------------------------------------------
25 // Defines the connection list used for drawing the hand skeleton
26 static const ConnectionList CONNECTIONS = {
27  {mp_hand_landmark_wrist, mp_hand_landmark_thumb_cmc},
28  {mp_hand_landmark_thumb_cmc, mp_hand_landmark_thumb_mcp},
29  {mp_hand_landmark_thumb_mcp, mp_hand_landmark_thumb_ip},
30  {mp_hand_landmark_thumb_ip, mp_hand_landmark_thumb_tip},
31  {mp_hand_landmark_wrist, mp_hand_landmark_index_finger_mcp},
32  {mp_hand_landmark_index_finger_mcp, mp_hand_landmark_index_finger_pip},
33  {mp_hand_landmark_index_finger_pip, mp_hand_landmark_index_finger_dip},
34  {mp_hand_landmark_index_finger_dip, mp_hand_landmark_index_finger_tip},
35  {mp_hand_landmark_index_finger_mcp, mp_hand_landmark_middle_finger_mcp},
36  {mp_hand_landmark_middle_finger_mcp, mp_hand_landmark_middle_finger_pip},
37  {mp_hand_landmark_middle_finger_pip, mp_hand_landmark_middle_finger_dip},
38  {mp_hand_landmark_middle_finger_dip, mp_hand_landmark_middle_finger_tip},
39  {mp_hand_landmark_middle_finger_mcp, mp_hand_landmark_ring_finger_mcp},
40  {mp_hand_landmark_ring_finger_mcp, mp_hand_landmark_ring_finger_pip},
41  {mp_hand_landmark_ring_finger_pip, mp_hand_landmark_ring_finger_dip},
42  {mp_hand_landmark_ring_finger_dip, mp_hand_landmark_ring_finger_tip},
43  {mp_hand_landmark_ring_finger_mcp, mp_hand_landmark_pinky_mcp},
44  {mp_hand_landmark_wrist, mp_hand_landmark_pinky_mcp},
45  {mp_hand_landmark_pinky_mcp, mp_hand_landmark_pinky_pip},
46  {mp_hand_landmark_pinky_pip, mp_hand_landmark_pinky_dip},
47  {mp_hand_landmark_pinky_dip, mp_hand_landmark_pinky_tip}};
48 //-----------------------------------------------------------------------------
49 CVTrackedMediaPipeHands::CVTrackedMediaPipeHands(SLstring dataPath)
50 {
51  mp_set_resource_dir(dataPath.c_str());
52 
53  SLstring graphPath = dataPath + "mediapipe/modules/hand_landmark/hand_landmark_tracking_cpu.binarypb";
54  auto* builder = mp_create_instance_builder(graphPath.c_str(), "image");
55 
56  // The following lines set some parameters for the tracking.
57  // These are taken from MediaPipe's Python API:
58  // https://github.com/google/mediapipe/blob/master/mediapipe/python/solutions/hands.py
59 
60  // Enable using the tracking results from the last frame to guess where the hand is now.
61  // This speeds up tracking considerably.
62  // Corresponds to "not static_image_mode" in the Python API.
63  mp_add_side_packet(builder,
64  "use_prev_landmarks",
65  mp_create_packet_bool(true));
66 
67  // Maximum number of hands that can be detected.
68  // Corresponds to "max_num_hands" in the Python API.
69  mp_add_side_packet(builder,
70  "num_hands",
71  mp_create_packet_int(2));
72 
73  // Complexity of the hand landmark model, can be 0 or 1.
74  // 0: Low landmark accuracy, fast
75  // 1: High landmark accuracy, slow
76  // Corresponds to "model_complexity" in the Python API.
77  mp_add_side_packet(builder,
78  "model_complexity",
79  mp_create_packet_int(1));
80 
81  // Minimum confidence value for hand detection to be considered sucessful.
82  // Corresponds to "min_detection_confidence" in the Python API.
83  mp_add_option_float(builder,
84  "palmdetectioncpu__TensorsToDetectionsCalculator",
85  "min_score_thresh",
86  0.5);
87 
88  // Minimum confidence value for the hand landmarks to be considered tracked successfully.
89  // Corresponds to "min_tracking_confidence" in the Python API.
90  mp_add_option_double(builder,
91  "handlandmarkcpu__ThresholdingCalculator",
92  "threshold",
93  0.5);
94 
95  // Creates a MediaPipe instance with the graph and some extra info.
96  _instance = mp_create_instance(builder);
97  CHECK_MP_RESULT(_instance)
98 
99  // Creates a poller to read packets from an output stream.
100  _landmarksPoller = mp_create_poller(_instance,
101  "multi_hand_landmarks");
102  CHECK_MP_RESULT(_landmarksPoller)
103 
104  // Starts the MediaPipe graph.
105  CHECK_MP_RESULT(mp_start(_instance))
106 
107  // clang-format off
108  // We define a identity matrix for the object view matrix because we do
109  // not transform any object in the scenegraph so far.
110  _objectViewMat = CVMatx44f(1,0,0,0,
111  0,1,0,0,
112  0,0,1,0,
113  0,0,0,1);
114  // clang-format on
115 }
116 //-----------------------------------------------------------------------------
117 CVTrackedMediaPipeHands::~CVTrackedMediaPipeHands()
118 {
119  mp_destroy_poller(_landmarksPoller);
120  CHECK_MP_RESULT(mp_destroy_instance(_instance))
121 }
122 //-----------------------------------------------------------------------------
123 bool CVTrackedMediaPipeHands::track(CVMat imageGray,
124  CVMat imageBgr,
125  CVCalibration* calib)
126 {
127  processImageInMediaPipe(imageBgr);
128 
129  if (mp_get_queue_size(_landmarksPoller) > 0)
130  {
131  auto* landmarksPacket = mp_poll_packet(_landmarksPoller);
132  auto* landmarks = mp_get_norm_multi_face_landmarks(landmarksPacket);
133 
134  drawResults(landmarks, imageBgr);
135 
136  mp_destroy_multi_face_landmarks(landmarks);
137  mp_destroy_packet(landmarksPacket);
138  }
139 
140  return true;
141 }
142 //-----------------------------------------------------------------------------
143 void CVTrackedMediaPipeHands::processImageInMediaPipe(CVMat imageBgr)
144 {
145  cv::cvtColor(imageBgr, _imageRgb, cv::COLOR_BGR2RGB);
146 
147  mp_image inImage;
148  inImage.data = _imageRgb.data;
149  inImage.width = _imageRgb.cols;
150  inImage.height = _imageRgb.rows;
151  inImage.format = mp_image_format_srgb;
152  mp_packet* packet = mp_create_packet_image(inImage);
153 
154  // Insert the image into the MediaPipe pipeline to be processed.
155  CHECK_MP_RESULT(mp_process(_instance, packet))
156 
157  // Block until the results from MediaPipe are available.
158  CHECK_MP_RESULT(mp_wait_until_idle(_instance))
159 }
160 //-----------------------------------------------------------------------------
161 //! Draws the hand skeleton with connections and joints into the RGB image
162 void CVTrackedMediaPipeHands::drawResults(mp_multi_face_landmark_list* landmarks,
163  CVMat imageBgr)
164 {
165  for (int i = 0; i < landmarks->length; i++)
166  {
167  auto& hand = landmarks->elements[i];
168 
169  for (auto& connection : CONNECTIONS)
170  {
171  auto p1 = hand.elements[connection.first];
172  auto p2 = hand.elements[connection.second];
173  float x1 = (float)imageBgr.cols * p1.x;
174  float y1 = (float)imageBgr.rows * p1.y;
175  float x2 = (float)imageBgr.cols * p2.x;
176  float y2 = (float)imageBgr.rows * p2.y;
177 
178  cv::line(imageBgr,
179  {(int)x1, (int)y1},
180  {(int)x2, (int)y2},
181  CV_RGB(0, 255, 0),
182  2);
183  }
184 
185  for (int j = 0; j < hand.length; j++)
186  {
187  auto p = hand.elements[j];
188  float x = (float)imageBgr.cols * p.x;
189  float y = (float)imageBgr.rows * p.y;
190  float radius = std::max(3.0f + 25.0f * -p.z, 1.0f);
191 
192  cv::circle(imageBgr,
193  CVPoint((int)x, (int)y),
194  (int)radius,
195  CV_RGB(255, 0, 0),
196  -1);
197  }
198  }
199 }
200 //-----------------------------------------------------------------------------
201 #endif
cv::Matx44f CVMatx44f
Definition: CVTypedefs.h:59
cv::Mat CVMat
Definition: CVTypedefs.h:38
cv::Point CVPoint
Definition: CVTypedefs.h:41
string SLstring
Definition: SL.h:158
Live video camera calibration class with OpenCV an OpenCV calibration.
Definition: CVCalibration.h:71