Skip to content

Instantly share code, notes, and snippets.

@eknight7
Last active February 8, 2023 06:24
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 2 You must be signed in to fork a gist
  • Save eknight7/d4a57504c8f866fc80c0eb2c61ff6b4f to your computer and use it in GitHub Desktop.
Save eknight7/d4a57504c8f866fc80c0eb2c61ff6b4f to your computer and use it in GitHub Desktop.
Multi-Hand Tracking via Live Webcam on CPU on Desktop: Shows how to extract landmarks on desktop.
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
cc_binary(
name = "multi_hand_tracking_cpu",
srcs = ["multi_hand_tracking_run_graph_cpu_main.cc"],
deps = [
"//mediapipe/framework:calculator_framework",
"//mediapipe/framework/formats:image_frame",
"//mediapipe/framework/formats:image_frame_opencv",
"//mediapipe/framework/port:commandlineflags",
"//mediapipe/framework/port:file_helpers",
"//mediapipe/framework/port:opencv_highgui",
"//mediapipe/framework/port:opencv_imgproc",
"//mediapipe/framework/port:opencv_video",
"//mediapipe/framework/port:parse_text_proto",
"//mediapipe/framework/port:status",
"//mediapipe/graphs/hand_tracking:multi_hand_desktop_tflite_calculators",
],
)
// Copyright 2019 The MediaPipe Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//
// An example of sending OpenCV webcam frames into a MediaPipe graph.
#include <cstdlib>
#include <memory>
#include <vector>
#include "mediapipe/framework/calculator_framework.h"
#include "mediapipe/framework/formats/image_frame.h"
#include "mediapipe/framework/formats/image_frame_opencv.h"
#include "mediapipe/framework/formats/landmark.pb.h"
#include "mediapipe/framework/port/commandlineflags.h"
#include "mediapipe/framework/port/file_helpers.h"
#include "mediapipe/framework/port/opencv_highgui_inc.h"
#include "mediapipe/framework/port/opencv_imgproc_inc.h"
#include "mediapipe/framework/port/opencv_video_inc.h"
#include "mediapipe/framework/port/parse_text_proto.h"
#include "mediapipe/framework/port/status.h"
constexpr char kWindowName[] = "MediaPipe";
constexpr char kCalculatorGraphConfigFile[] =
"mediapipe/graphs/hand_tracking/multi_hand_tracking_mobile.pbtxt";
// Input and output streams.
constexpr char kInputStream[] = "input_video";
constexpr char kOutputStream[] = "output_video";
constexpr char kMultiHandLandmarksOutputStream[] = "multi_hand_landmarks";
DEFINE_string(input_video_path, "",
"Full path of video to load. "
"If not provided, attempt to use a webcam.");
DEFINE_string(output_video_path, "",
"Full path of where to save result (.mp4 only). "
"If not provided, show result in a window.");
::mediapipe::Status RunMPPGraph(
std::unique_ptr<::mediapipe::CalculatorGraph> graph) {
LOG(INFO) << "Initialize the camera or load the video.";
cv::VideoCapture capture;
const bool load_video = !FLAGS_input_video_path.empty();
if (load_video) {
capture.open(FLAGS_input_video_path);
} else {
capture.open(0);
}
RET_CHECK(capture.isOpened());
cv::VideoWriter writer;
const bool save_video = !FLAGS_output_video_path.empty();
if (!save_video) {
cv::namedWindow(kWindowName, /*flags=WINDOW_AUTOSIZE*/ 1);
#if (CV_MAJOR_VERSION >= 3) && (CV_MINOR_VERSION >= 2)
capture.set(cv::CAP_PROP_FRAME_WIDTH, 640);
capture.set(cv::CAP_PROP_FRAME_HEIGHT, 480);
capture.set(cv::CAP_PROP_FPS, 30);
#endif
}
LOG(INFO) << "Start running the calculator graph.";
ASSIGN_OR_RETURN(::mediapipe::OutputStreamPoller poller,
graph->AddOutputStreamPoller(kOutputStream));
ASSIGN_OR_RETURN(::mediapipe::OutputStreamPoller multi_hand_landmarks_poller,
graph->AddOutputStreamPoller(kMultiHandLandmarksOutputStream));
MP_RETURN_IF_ERROR(graph->StartRun({}));
LOG(INFO) << "Start grabbing and processing frames.";
bool grab_frames = true;
while (grab_frames) {
// Capture opencv camera or video frame.
cv::Mat camera_frame_raw;
capture >> camera_frame_raw;
if (camera_frame_raw.empty()) break; // End of video.
cv::Mat camera_frame;
cv::cvtColor(camera_frame_raw, camera_frame, cv::COLOR_BGR2RGB);
if (!load_video) {
cv::flip(camera_frame, camera_frame, /*flipcode=HORIZONTAL*/ 1);
}
// Wrap Mat into an ImageFrame.
auto input_frame = absl::make_unique<::mediapipe::ImageFrame>(
::mediapipe::ImageFormat::SRGB, camera_frame.cols, camera_frame.rows,
::mediapipe::ImageFrame::kDefaultAlignmentBoundary);
cv::Mat input_frame_mat = ::mediapipe::formats::MatView(input_frame.get());
camera_frame.copyTo(input_frame_mat);
// Send image packet into the graph.
size_t frame_timestamp_us =
(double)cv::getTickCount() / (double)cv::getTickFrequency() * 1e6;
MP_RETURN_IF_ERROR(graph->AddPacketToInputStream(
kInputStream, ::mediapipe::Adopt(input_frame.release())
.At(::mediapipe::Timestamp(frame_timestamp_us))));
// Get the graph result packet, or stop if that fails.
::mediapipe::Packet packet;
if (!poller.Next(&packet)) break;
auto& output_frame = packet.Get<::mediapipe::ImageFrame>();
// Get the packet containing multi_hand_landmarks.
::mediapipe::Packet multi_hand_landmarks_packet;
if (!multi_hand_landmarks_poller.Next(&multi_hand_landmarks_packet)) break;
const auto& multi_hand_landmarks =
multi_hand_landmarks_packet.Get<
std::vector<::mediapipe::NormalizedLandmarkList>>();
LOG(INFO) << "#Multi Hand landmarks: " << multi_hand_landmarks.size();
int hand_id = 0;
for (const auto& single_hand_landmarks: multi_hand_landmarks) {
++hand_id;
LOG(INFO) << "Hand [" << hand_id << "]:";
for (int i = 0; i < single_hand_landmarks.landmark_size(); ++i) {
const auto& landmark = single_hand_landmarks.landmark(i);
LOG(INFO) << "\tLandmark [" << i << "]: ("
<< landmark.x() << ", "
<< landmark.y() << ", "
<< landmark.z() << ")";
}
}
// Convert back to opencv for display or saving.
cv::Mat output_frame_mat = ::mediapipe::formats::MatView(&output_frame);
cv::cvtColor(output_frame_mat, output_frame_mat, cv::COLOR_RGB2BGR);
if (save_video) {
if (!writer.isOpened()) {
LOG(INFO) << "Prepare video writer.";
writer.open(FLAGS_output_video_path,
::mediapipe::fourcc('a', 'v', 'c', '1'), // .mp4
capture.get(cv::CAP_PROP_FPS), output_frame_mat.size());
RET_CHECK(writer.isOpened());
}
writer.write(output_frame_mat);
} else {
cv::imshow(kWindowName, output_frame_mat);
// Press any key to exit.
const int pressed_key = cv::waitKey(5);
if (pressed_key >= 0 && pressed_key != 255) grab_frames = false;
}
}
LOG(INFO) << "Shutting down.";
if (writer.isOpened()) writer.release();
MP_RETURN_IF_ERROR(graph->CloseInputStream(kInputStream));
return graph->WaitUntilDone();
}
::mediapipe::Status InitializeAndRunMPPGraph() {
std::string calculator_graph_config_contents;
MP_RETURN_IF_ERROR(::mediapipe::file::GetContents(
kCalculatorGraphConfigFile, &calculator_graph_config_contents));
LOG(INFO) << "Get calculator graph config contents: "
<< calculator_graph_config_contents;
mediapipe::CalculatorGraphConfig config =
mediapipe::ParseTextProtoOrDie<mediapipe::CalculatorGraphConfig>(
calculator_graph_config_contents);
LOG(INFO) << "Initialize the calculator graph.";
std::unique_ptr<::mediapipe::CalculatorGraph> graph =
absl::make_unique<::mediapipe::CalculatorGraph>();
MP_RETURN_IF_ERROR(graph->Initialize(config));
return RunMPPGraph(std::move(graph));
}
int main(int argc, char** argv) {
google::InitGoogleLogging(argv[0]);
gflags::ParseCommandLineFlags(&argc, &argv, true);
::mediapipe::Status run_status = InitializeAndRunMPPGraph();
if (!run_status.ok()) {
LOG(ERROR) << "Failed to run the graph: " << run_status.message();
return EXIT_FAILURE;
} else {
LOG(INFO) << "Success!";
}
return EXIT_SUCCESS;
}
@eknight7
Copy link
Author

This is for answering the issue about how to extract multi-hand landmarks on desktop. For example: google-ai-edge/mediapipe#200 (comment).

@chillcloud-dev
Copy link

Thanks
it worked :)

MediaPipe Is so amazing

@snorlaxse
Copy link

bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/multi_hand_tracking:multi_hand_tracking_cpu

image

After build completed, run bazel-bin/mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_cpu as below:

GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_cpu --calculator_graph_config_file=mediapipe/graphs/hand_tracking/multi_hand_tracking_desktop_live.pbtxt --input_video_path=/Users/snorlaxse/Desktop/multi-hand-demo.mp4
ERROR: unknown command line flag 'calculator_graph_config_file'

image

Could you tell me how I can fix this issue ? @chillcloud-dev @eknight7

Thanks :)

@chillcloud-dev
Copy link

Hey @snorlaxse
your run command looks good
and i downloaded mediapipe just now and run both commands (run without input video) -> worked fine for me
u sure u didnt modify anything ?

try a fresh mediapipe and run your cmds again

@snorlaxse
Copy link

snorlaxse commented Jun 18, 2020 via email

@noirmist
Copy link

noirmist commented Jun 26, 2020

I guess it will stop when the hand is not showing.
Because I'm currently working on face one. It is also stopped.

@idrisrupt
Copy link

bazel build -c opt --define MEDIAPIPE_DISABLE_GPU=1 mediapipe/examples/desktop/multi_hand_tracking:multi_hand_tracking_cpu

image

After build completed, run bazel-bin/mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_cpu as below:

GLOG_logtostderr=1 bazel-bin/mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_cpu --calculator_graph_config_file=mediapipe/graphs/hand_tracking/multi_hand_tracking_desktop_live.pbtxt --input_video_path=/Users/snorlaxse/Desktop/multi-hand-demo.mp4
ERROR: unknown command line flag 'calculator_graph_config_file'

image

Could you tell me how I can fix this issue ? @chillcloud-dev @eknight7

Thanks :)

you have to run this command it'll work
bazel-bin\mediapipe\examples\desktop\multi_hand_tracking\multi_hand_tracking_cpu.exe

Good Luck

@tonywang531
Copy link

@eknight7

Sorry to bother you again. I tried to make the changes to the gpu version of the file but could not get it to work.

My code for the gpu file:
https://github.com/tonywang531/Temporary-code/blob/master/multi_hand_tracking_landmarks_gpu.cc

This is my build file.

cc_library(
name = "multi_hand_tracking_landmarks_gpu",
srcs = ["multi_hand_tracking_landmarks_gpu.cc"],
deps = [
	"//mediapipe/calculators/util:landmarks_to_render_data_calculator",
	"//mediapipe/framework:calculator_framework",
	"//mediapipe/framework/formats:image_frame",
	"//mediapipe/framework/formats:image_frame_opencv",
	"//mediapipe/framework/port:commandlineflags",
	"//mediapipe/framework/port:file_helpers",
	"//mediapipe/framework/port:opencv_highgui",
	"//mediapipe/framework/port:opencv_imgproc",
	"//mediapipe/framework/port:opencv_video",
	"//mediapipe/framework/port:parse_text_proto",
	"//mediapipe/framework/port:status",
	"//mediapipe/gpu:gl_calculator_helper",
	"//mediapipe/gpu:gpu_buffer",
	"//mediapipe/gpu:gpu_shared_data_internal",
        "//mediapipe/graphs/hand_tracking:multi_hand_mobile_calculators",
	],
)

The error message I got was:

tony@tony-desktop:~/mediapipe$ bazel build -c opt --copt -DMESA_EGL_NO_X11_HEADERS --copt -DEGL_NO_X11 mediapipe/examples/desktop/multi_hand_tracking:multi_hand_tracking_landmarks_gpu
INFO: Analyzed target //mediapipe/examples/desktop/multi_hand_tracking:multi_hand_tracking_landmarks_gpu (1 packages loaded, 2 targets configured).
INFO: Found 1 target...
ERROR: /home/tony/mediapipe/mediapipe/examples/desktop/multi_hand_tracking/BUILD:85:1: C++ compilation of rule '//mediapipe/examples/desktop/multi_hand_tracking:multi_hand_tracking_landmarks_gpu' failed (Exit 1) gcc failed: error executing command /usr/bin/gcc -U_FORTIFY_SOURCE -fstack-protector -Wall -Wunused-but-set-parameter -Wno-free-nonheap-object -fno-omit-frame-pointer -g0 -O2 '-D_FORTIFY_SOURCE=1' -DNDEBUG -ffunction-sections ... (remaining 199 argument(s) skipped)

Use --sandbox_debug to see verbose messages from the sandbox
In file included from ./mediapipe/framework/port/status_macros.h:18,
                 from ./mediapipe/framework/packet.h:38,
                 from ./mediapipe/framework/calculator_state.h:30,
                 from ./mediapipe/framework/calculator_context.h:23,
                 from ./mediapipe/framework/calculator_base.h:22,
                 from ./mediapipe/framework/calculator_framework.h:54,
                 from mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:21:
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc: In function 'mediapipe::Status RunMPPGraph()':
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:57:7: error: 'FLAGS_calculator_graph_config_file' was not declared in this scope
   57 |       FLAGS_calculator_graph_config_file, &calculator_graph_config_contents));
      |       ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./mediapipe/framework/deps/status_macros.h:87:45: note: in definition of macro 'MP_RETURN_IF_ERROR'
   87 |           status_macro_internal_adaptor = {(expr), __FILE__, __LINE__}) { \
      |                                             ^~~~
./mediapipe/framework/deps/status_macros.h:87:70: error: could not convert '{<expression error>, "mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc", 56}' from '<brace-enclosed initializer list>' to 'mediapipe::status_macro_internal::StatusAdaptorForMacros'
   87 |           status_macro_internal_adaptor = {(expr), __FILE__, __LINE__}) { \
      |                                                                      ^
      |                                                                      |
      |                                                                      <brace-enclosed initializer list>
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:56:3: note: in expansion of macro 'MP_RETURN_IF_ERROR'
   56 |   MP_RETURN_IF_ERROR(mediapipe::file::GetContents(
      |   ^~~~~~~~~~~~~~~~~~
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:97:25: error: base operand of '->' has non-pointer type 'mediapipe::CalculatorGraph'
   97 |                    graph->AddOutputStreamPoller(kOutputStream));
      |                         ^~
./mediapipe/framework/deps/status_macros.h:161:20: note: in definition of macro 'STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_'
  161 |   auto statusor = (rexpr);                                               \
      |                    ^~~~~
./mediapipe/framework/deps/status_macros.h:154:3: note: in expansion of macro 'STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_'
  154 |   STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_3_(lhs, rexpr, std::move(_))
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
./mediapipe/framework/deps/status_macros.h:149:72: note: in expansion of macro 'STATUS_MACROS_IMPL_ASSIGN_OR_RETURN_2_'
  149 | #define STATUS_MACROS_IMPL_GET_VARIADIC_HELPER_(_1, _2, _3, NAME, ...) NAME
      |                                                                        ^~~~
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:96:3: note: in expansion of macro 'ASSIGN_OR_RETURN'
   96 |   ASSIGN_OR_RETURN(::mediapipe::OutputStreamPoller poller,
      |   ^~~~~~~~~~~~~~~~
./mediapipe/framework/deps/status_macros.h:186:3: error: expected ',' or ';' before 'switch'
  186 |   switch (0)                             \
      |   ^~~~~~
./mediapipe/framework/deps/status_macros.h:85:3: note: in expansion of macro 'STATUS_MACROS_IMPL_ELSE_BLOCKER_'
   85 |   STATUS_MACROS_IMPL_ELSE_BLOCKER_                                        \
      |   ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:100:3: note: in expansion of macro 'MP_RETURN_IF_ERROR'
  100 |   MP_RETURN_IF_ERROR(graph->StartRun({}));
      |   ^~~~~~~~~~~~~~~~~~
./mediapipe/framework/deps/status_macros.h:87:71: error: expected primary-expression before ')' token
   87 |           status_macro_internal_adaptor = {(expr), __FILE__, __LINE__}) { \
      |                                                                       ^
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:100:3: note: in expansion of macro 'MP_RETURN_IF_ERROR'
  100 |   MP_RETURN_IF_ERROR(graph->StartRun({}));
      |   ^~~~~~~~~~~~~~~~~~
./mediapipe/framework/deps/status_macros.h:88:5: error: 'else' without a previous 'if'
   88 |   } else /* NOLINT */                                                     \
      |     ^~~~
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:100:3: note: in expansion of macro 'MP_RETURN_IF_ERROR'
  100 |   MP_RETURN_IF_ERROR(graph->StartRun({}));
      |   ^~~~~~~~~~~~~~~~~~
./mediapipe/framework/deps/status_macros.h:89:12: error: 'status_macro_internal_adaptor' was not declared in this scope
   89 |     return status_macro_internal_adaptor.Consume()
      |            ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
mediapipe/examples/desktop/multi_hand_tracking/multi_hand_tracking_landmarks_gpu.cc:100:3: note: in expansion of macro 'MP_RETURN_IF_ERROR'
  100 |   MP_RETURN_IF_ERROR(graph->StartRun({}));
      |   ^~~~~~~~~~~~~~~~~~
Target //mediapipe/examples/desktop/multi_hand_tracking:multi_hand_tracking_landmarks_gpu failed to build
Use --verbose_failures to see the command lines of failed build steps.
INFO: Elapsed time: 3.632s, Critical Path: 3.49s
INFO: 89 processes: 89 linux-sandbox.
FAILED: Build did NOT complete successfully

@dgrnd4
Copy link

dgrnd4 commented Sep 24, 2020

Hi @eknight7 ,
i use your files and everything is working well.

now i want to get even the position of the bounding boxes. Could you please tell me how?? Many thanks!!

@Gul11
Copy link

Gul11 commented Oct 12, 2020

Hi @eknight7 ,
I am trying to replicate the same to get Iris landmarks, I was able to complete the build, however, I'm not sure where exactly I can check the log info, I tried printing these landmarks but it didn't work either, I'm running this on jupyter terminal.
Is there any specific location for logs?

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment