提交 8eab7d6b 编写于 作者: G gineshidalgo99

Added number_people_max flag

上级 9e1d6e41
......@@ -100,7 +100,7 @@ endif (WIN32)
# Set the acceleration library
set(GPU_MODE CUDA CACHE STRING "Select the acceleration GPU library or CPU otherwise.")
# set_property(CACHE GPU_MODE PROPERTY STRINGS CPU_ONLY)
# set_property(CACHE GPU_MODE PROPERTY STRINGS CUDA CPU_ONLY)
# # Test for CUDA or OpenCL
# find_package(CUDA)
......@@ -113,13 +113,19 @@ set_property(CACHE GPU_MODE PROPERTY STRINGS CUDA)
if (${GPU_MODE} MATCHES "CUDA")
# OpenPose flags
add_definitions(-DUSE_CUDA)
message(STATUS "Building with CUDA")
message(STATUS "Building with CUDA.")
elseif (${GPU_MODE} MATCHES "CPU_ONLY")
# OpenPose flag for Caffe
add_definitions(-DCPU_ONLY)
message(STATUS "Building CPU Only")
message(STATUS "Building CPU Only.")
endif ()
if (${USE_MKL})
# OpenPose flags
add_definitions(-DUSE_MKL)
message(STATUS "Building with MKL support.")
endif (${USE_MKL})
# Intel branch with MKL Support
if (${GPU_MODE} MATCHES "CPU_ONLY")
if (UNIX AND NOT APPLE)
......
......@@ -154,6 +154,7 @@ Each flag is divided into flag name, default value, and description.
- DEFINE_int32(num_gpu, -1, "The number of GPU devices to use. If negative, it will use all the available GPUs in your machine.");
- DEFINE_int32(num_gpu_start, 0, "GPU device start number.");
- DEFINE_int32(keypoint_scale, 0, "Scaling of the (x,y) coordinates of the final pose data array, i.e. the scale of the (x,y) coordinates that will be saved with the `write_keypoint` & `write_keypoint_json` flags. Select `0` to scale it to the original source resolution, `1`to scale it to the net output size (set with `net_resolution`), `2` to scale it to the final output size (set with `resolution`), `3` to scale it in the range [0,1], and 4 for range [-1,1]. Non related with `scale_number` and `scale_gap`.");
- DEFINE_int32(number_people_max, -1, "This parameter will limit the maximum number of people detected, by keeping the people with top scores. The score is based in person area over the image, body part score, as well as joint score (between each pair of connected body parts). Useful if you know the exact number of people in the scene, so it can remove false positives (if all the people have been detected. However, it might also include false negatives by removing very small or highly occluded people. -1 will keep them all.");
4. OpenPose Body Pose
- DEFINE_bool(body_disable, false, "Disable body keypoint detection. Option only possible for faster (but less accurate) face keypoint detection.");
......@@ -176,12 +177,12 @@ Each flag is divided into flag name, default value, and description.
7. OpenPose Hand
- DEFINE_bool(hand, false, "Enables hand keypoint detection. It will share some parameters from the body pose, e.g. `model_folder`. Analogously to `--face`, it will also slow down the performance, increase the required GPU memory and its speed depends on the number of people.");
- DEFINE_string(hand_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the hand keypoint detector.");
- DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4");
- DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4.");
- DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than `scale_gap` but applied to the hand keypoint detector. Total range between smallest and biggest scale. The scales will be centered in ratio 1. E.g. if scaleRange = 0.4 and scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.");
- DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it simply looks for hands in positions at which hands were located in previous frames, but it does not guarantee the same person ID among frames");
- DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it simply looks for hands in positions at which hands were located in previous frames, but it does not guarantee the same person ID among frames.");
8. OpenPose Rendering
- DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body part heat map, 19 for the background heat map, 20 for all the body part heat maps together, 21 for all the PAFs, 22-40 for each body part pair PAF");
- DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body part heat map, 19 for the background heat map, 20 for all the body part heat maps together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
- DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black background, instead of being rendered into the original image. Related: `part_to_show`, `alpha_pose`, and `alpha_pose`.");
9. OpenPose Rendering Pose
......
......@@ -206,7 +206,7 @@ You can include the 3D reconstruction module by:
- Copy `Spinnaker_v140.lib` and `Spinnakerd_v140.lib` from `{PointGreyParentDirectory}\Point Grey Research\Spinnaker\lib64\vs2015\` into `{OpenPoseDirectory}\3rdparty\windows\spinnaker\lib\`.
- (Optional) Spinnaker SDK overview: [https://www.ptgrey.com/spinnaker-sdk](https://www.ptgrey.com/spinnaker-sdk).
2. Install the 3D visualizer, FreeGLUT:
1. Ubuntu: run `sudo apt-get install freeglut3 freeglut3-dev libxmu-dev libxi-dev`.
1. Ubuntu: run `sudo apt-get update && sudo apt-get install build-essential freeglut3 freeglut3-dev libxmu-dev libxi-dev` and reboot your PC.
2. Windows:
1. It is automatically downloaded by the CMake installer.
2. Alternatively, if you prefer to download it yourself, you could either:
......
......@@ -8,8 +8,10 @@
4. [Camera Calibration](#camera-calibration)
5. [Camera Ordering](#camera-ordering)
6. [Installing the OpenPose 3-D Reconstruction Module](#installing-the-openpose-3-d-reconstruction-module)
7. [Using a Different Camera Brand](#using-a-different-camera-brand)
8. [Known Bug](#known-bug)
7. [Quick Start](#quick-start)
8. [Expected Visual Results](#expected-visual-results)
9. [Using a Different Camera Brand](#using-a-different-camera-brand)
10. [Known Bug](#known-bug)
......@@ -69,7 +71,12 @@ In order to verify that the camera parameters introduced by the user are sorted
## Installing the OpenPose 3-D Reconstruction Module
Check the [doc/installation.md#openpose-3d-reconstruction-module-and-demo](./installation.md#openpose-3d-reconstruction-module-and-demo) instructions.
Check the [doc/quick_start.md#3-d-reconstruction](./quick_start.md#3-d-reconstruction) for 3-D examples.
## Quick Start
You can copy and modify the OpenPose 3-D demo to use any camera brand, by modifying the frames producer. For that, you would need to provide your custom code to retrieve synchronized images from your cameras, as well as their intrinsic and extrinsic camera parameters.
......
......@@ -7,6 +7,7 @@ OpenPose - Quick Start
2. [Running on Webcam](#running-on-webcam)
3. [Running on Images](#running-on-images)
4. [Maximum Accuracy Configuration](#maximum-accuracy-configuration)
5. [3-D Reconstruction](#3-d-reconstruction)
2. [Expected Visual Results](#expected-visual-results)
......@@ -103,6 +104,28 @@ windows\x64\Release\OpenPoseDemo.exe --net_resolution "1312x736" --scale_number
### 3-D Reconstruction
```
# Ubuntu
./build/examples/openpose/openpose.bin --3d --number_people_max 1
# With face and hands
./build/examples/openpose/openpose.bin --3d --number_people_max 1 --face --hand
```
```
:: Windows - Portable Demo
bin\OpenPoseDemo.exe --3d --number_people_max 1
:: With face and hands
bin\OpenPoseDemo.exe --3d --number_people_max 1 --face --hand
```
```
:: Windows - Library
windows\x64\Release\OpenPoseDemo.exe --3d --number_people_max 1
:: With face and hands
windows\x64\Release\OpenPoseDemo.exe --3d --number_people_max 1 --face --hand
```
## Expected Visual Results
The visual GUI should show the original image with the poses blended on it, similarly to the pose of this gif:
<p align="center">
......
......@@ -179,8 +179,10 @@ OpenPose Library - Release Notes
7. Runtime verbose about average speed configurable by user with `PROFILER_ENABLED` option (CMake/Makefile.config) and `--profile_speed` flag.
8. Lighter Caffe version compiled by CMake in Ubuntu: disabled Caffe extra support (e.g., OpenCV, Python) and doc.
9. Renamed CMake binaries (Ubuntu) to match old Makefile format: `_bin` by `.bin`.
10. 3-D Reconstruction demo cleaned, implemented in Ubuntu too, and now defined as experimental module of OpenPose rather than just a demo.
10. 3-D reconstruction demo cleaned, implemented in Ubuntu too, and now defined as module of OpenPose rather than just a demo.
11. CMake as default installer in documentation.
12. Added flag: number_people_max to optionally select the maximum number of people to be detected.
13. 3-D reconstruction module forces the user to set `number_people_max 1` to avoid errors (as it assumes only 1 person per image).
2. Main bugs fixed:
1. Slight speed up (~1%) for performing the non-maximum suppression stage only in the body part heatmaps channels, and not also in the PAF channels.
2. Fixed core-dumped in PoseRenderer with GUI when changed element to be rendered to something else than skeleton.
......
......@@ -53,6 +53,12 @@ DEFINE_int32(keypoint_scale, 0, "Scaling of the (x,y) co
" size (set with `net_resolution`), `2` to scale it to the final output size (set with"
" `resolution`), `3` to scale it in the range [0,1], and 4 for range [-1,1]. Non related"
" with `scale_number` and `scale_gap`.");
DEFINE_int32(number_people_max, 1, "This parameter will limit the maximum number of people detected, by keeping the people with"
" top scores. The score is based in person area over the image, body part score, as well as"
" joint score (between each pair of connected body parts). Useful if you know the exact"
" number of people in the scene, so it can remove false positives (if all the people have"
" been detected. However, it might also include false negatives by removing very small or"
" highly occluded people. -1 will keep them all.");
// OpenPose Body Pose
DEFINE_bool(body_disable, false, "Disable body keypoint detection. Option only possible for faster (but less accurate) face"
" keypoint detection.");
......@@ -103,7 +109,7 @@ DEFINE_bool(hand, true, "Enables hand keypoint d
DEFINE_string(hand_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the hand keypoint"
" detector.");
DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results"
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4");
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4.");
DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than `scale_gap` but applied to the hand keypoint detector. Total range"
" between smallest and biggest scale. The scales will be centered in ratio 1. E.g. if"
" scaleRange = 0.4 and scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.");
......@@ -111,11 +117,16 @@ DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than
DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate"
" is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it"
" simply looks for hands in positions at which hands were located in previous frames, but"
" it does not guarantee the same person ID among frames");
" it does not guarantee the same person ID among frames.");
// OpenPose 3-D Reconstruction
DEFINE_bool(3d, true, "Running OpenPose 3-D reconstruction demo: 1) Reading from a stereo camera system."
" 2) Performing 3-D reconstruction from the multiple views. 3) Displaying 3-D reconstruction"
" results. Note that it will only display 1 person. If multiple people is present, it will"
" fail.");
// OpenPose Rendering
DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body"
" part heat map, 19 for the background heat map, 20 for all the body part heat maps"
" together, 21 for all the PAFs, 22-40 for each body part pair PAF");
" together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black"
" background, instead of being rendered into the original image. Related: `part_to_show`,"
" `alpha_pose`, and `alpha_pose`.");
......@@ -207,18 +218,13 @@ int openpose()
// Initializing the user custom classes
// Frames producer (e.g. video, webcam, ...)
auto wFlirReader = std::make_shared<op::WFlirReader>();
// Processing
auto wReconstruction3D = std::make_shared<op::WReconstruction3D>();
// GUI (Display)
auto wRender3D = std::make_shared<op::WRender3D>();
op::Wrapper<std::vector<op::Datum3D>> opWrapper;
op::Wrapper<std::vector<op::Datum>> opWrapper;
// Add custom input
const auto workerInputOnNewThread = true;
opWrapper.setWorkerInput(wFlirReader, workerInputOnNewThread);
// Add custom processing
const auto workerProcessingOnNewThread = true;
opWrapper.setWorkerPostProcessing(wReconstruction3D, workerProcessingOnNewThread);
// Add custom output
const auto workerOutputOnNewThread = true;
opWrapper.setWorkerOutput(wRender3D, workerOutputOnNewThread);
......@@ -229,7 +235,8 @@ int openpose()
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, heatMapScale, FLAGS_part_candidates,
(float)FLAGS_render_threshold, enableGoogleLogging};
(float)FLAGS_render_threshold, FLAGS_number_people_max,
enableGoogleLogging, FLAGS_3d};
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{FLAGS_face, faceNetInputSize,
op::flagsToRenderMode(FLAGS_face_render, FLAGS_render_pose),
......
// ------------------------- OpenPose Library Tutorial - Real Time Pose Estimation -------------------------
// If the user wants to learn to use the OpenPose library, we highly recommend to start with the `examples/tutorial_*/` folders.
// If the user wants to learn to use the OpenPose library, we highly recommend to start with the `examples/tutorial_*/`
// folders.
// This example summarizes all the funcitonality of the OpenPose library:
// 1. Read folder of images / video / webcam (`producer` module)
// 2. Extract and render body keypoint / heatmap / PAF of that image (`pose` module)
......@@ -73,7 +74,12 @@ DEFINE_int32(keypoint_scale, 0, "Scaling of the (x,y) co
" size (set with `net_resolution`), `2` to scale it to the final output size (set with"
" `resolution`), `3` to scale it in the range [0,1], and 4 for range [-1,1]. Non related"
" with `scale_number` and `scale_gap`.");
DEFINE_bool(identification, false, "Whether to enable people identification across frames. Not available yet, coming soon.");
DEFINE_int32(number_people_max, -1, "This parameter will limit the maximum number of people detected, by keeping the people with"
" top scores. The score is based in person area over the image, body part score, as well as"
" joint score (between each pair of connected body parts). Useful if you know the exact"
" number of people in the scene, so it can remove false positives (if all the people have"
" been detected. However, it might also include false negatives by removing very small or"
" highly occluded people. -1 will keep them all.");
// OpenPose Body Pose
DEFINE_bool(body_disable, false, "Disable body keypoint detection. Option only possible for faster (but less accurate) face"
" keypoint detection.");
......@@ -124,18 +130,25 @@ DEFINE_bool(hand, false, "Enables hand keypoint d
DEFINE_string(hand_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the hand keypoint"
" detector.");
DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results"
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4");
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4.");
DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than `scale_gap` but applied to the hand keypoint detector. Total range"
" between smallest and biggest scale. The scales will be centered in ratio 1. E.g. if"
" scaleRange = 0.4 and scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.");
DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate"
" is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it"
" simply looks for hands in positions at which hands were located in previous frames, but"
" it does not guarantee the same person ID among frames");
" it does not guarantee the same person ID among frames.");
// OpenPose 3-D Reconstruction
DEFINE_bool(3d, false, "Running OpenPose 3-D reconstruction demo: 1) Reading from a stereo camera system."
" 2) Performing 3-D reconstruction from the multiple views. 3) Displaying 3-D reconstruction"
" results. Note that it will only display 1 person. If multiple people is present, it will"
" fail.");
// OpenPose identification
DEFINE_bool(identification, false, "Whether to enable people identification across frames. Not available yet, coming soon.");
// OpenPose Rendering
DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body"
" part heat map, 19 for the background heat map, 20 for all the body part heat maps"
" together, 21 for all the PAFs, 22-40 for each body part pair PAF");
" together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black"
" background, instead of being rendered into the original image. Related: `part_to_show`,"
" `alpha_pose`, and `alpha_pose`.");
......@@ -248,8 +261,8 @@ int openPoseDemo()
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, heatMapScale, FLAGS_part_candidates,
(float)FLAGS_render_threshold, enableGoogleLogging,
FLAGS_identification};
(float)FLAGS_render_threshold, FLAGS_number_people_max,
enableGoogleLogging, FLAGS_3d, FLAGS_identification};
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{FLAGS_face, faceNetInputSize,
op::flagsToRenderMode(FLAGS_face_render, FLAGS_render_pose),
......
......@@ -56,9 +56,12 @@ int handFromJsonTest()
op::log("Configuring OpenPose wrapper.", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
op::WrapperHandFromJsonTest<std::vector<op::Datum>> opWrapper;
// Pose configuration (use WrapperStructPose{} for default and recommended configuration)
const bool identification = false;
op::WrapperStructPose wrapperStructPose{false, op::flagsToPoint("656x368"), op::flagsToPoint("1280x720"),
op::ScaleMode::InputResolution, FLAGS_num_gpu, FLAGS_num_gpu_start,
true, enableGoogleLogging};
1, 0.15f, op::RenderMode::None, op::PoseModel::COCO_18,
true, 0.f, 0.f, 0, "models/", {}, op::ScaleMode::ZeroToOne, false,
0.05f, -1, enableGoogleLogging, false, identification};
wrapperStructPose.modelFolder = FLAGS_model_folder;
// Hand configuration (use op::WrapperStructHand{} to disable it)
const op::WrapperStructHand wrapperStructHand{FLAGS_hand, handNetInputSize, FLAGS_hand_scale_number,
......
......@@ -83,6 +83,12 @@ DEFINE_int32(keypoint_scale, 0, "Scaling of the (x,y) co
" size (set with `net_resolution`), `2` to scale it to the final output size (set with"
" `resolution`), `3` to scale it in the range [0,1], and 4 for range [-1,1]. Non related"
" with `scale_number` and `scale_gap`.");
DEFINE_int32(number_people_max, -1, "This parameter will limit the maximum number of people detected, by keeping the people with"
" top scores. The score is based in person area over the image, body part score, as well as"
" joint score (between each pair of connected body parts). Useful if you know the exact"
" number of people in the scene, so it can remove false positives (if all the people have"
" been detected. However, it might also include false negatives by removing very small or"
" highly occluded people. -1 will keep them all.");
// OpenPose Body Pose
DEFINE_bool(body_disable, false, "Disable body keypoint detection. Option only possible for faster (but less accurate) face"
" keypoint detection.");
......@@ -133,18 +139,18 @@ DEFINE_bool(hand, false, "Enables hand keypoint d
DEFINE_string(hand_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the hand keypoint"
" detector.");
DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results"
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4");
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4.");
DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than `scale_gap` but applied to the hand keypoint detector. Total range"
" between smallest and biggest scale. The scales will be centered in ratio 1. E.g. if"
" scaleRange = 0.4 and scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.");
DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate"
" is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it"
" simply looks for hands in positions at which hands were located in previous frames, but"
" it does not guarantee the same person ID among frames");
" it does not guarantee the same person ID among frames.");
// OpenPose Rendering
DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body"
" part heat map, 19 for the background heat map, 20 for all the body part heat maps"
" together, 21 for all the PAFs, 22-40 for each body part pair PAF");
" together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black"
" background, instead of being rendered into the original image. Related: `part_to_show`,"
" `alpha_pose`, and `alpha_pose`.");
......@@ -252,7 +258,8 @@ int openPoseTutorialWrapper4()
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, heatMapScale, FLAGS_part_candidates,
(float)FLAGS_render_threshold, enableGoogleLogging};
(float)FLAGS_render_threshold, FLAGS_number_people_max,
enableGoogleLogging};
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{FLAGS_face, faceNetInputSize,
op::flagsToRenderMode(FLAGS_face_render, FLAGS_render_pose),
......
......@@ -51,7 +51,7 @@ DEFINE_int32(scale_number, 1, "Number of scales to ave
// OpenPose Rendering
DEFINE_int32(part_to_show, 19, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body"
" part heat map, 19 for the background heat map, 20 for all the body part heat maps"
" together, 21 for all the PAFs, 22-40 for each body part pair PAF");
" together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black"
" background, instead of being rendered into the original image. Related: `part_to_show`,"
" `alpha_pose`, and `alpha_pose`.");
......
......@@ -73,6 +73,12 @@ DEFINE_int32(keypoint_scale, 0, "Scaling of the (x,y) co
" size (set with `net_resolution`), `2` to scale it to the final output size (set with"
" `resolution`), `3` to scale it in the range [0,1], and 4 for range [-1,1]. Non related"
" with `scale_number` and `scale_gap`.");
DEFINE_int32(number_people_max, -1, "This parameter will limit the maximum number of people detected, by keeping the people with"
" top scores. The score is based in person area over the image, body part score, as well as"
" joint score (between each pair of connected body parts). Useful if you know the exact"
" number of people in the scene, so it can remove false positives (if all the people have"
" been detected. However, it might also include false negatives by removing very small or"
" highly occluded people. -1 will keep them all.");
// OpenPose Body Pose
DEFINE_bool(body_disable, false, "Disable body keypoint detection. Option only possible for faster (but less accurate) face"
" keypoint detection.");
......@@ -123,18 +129,18 @@ DEFINE_bool(hand, false, "Enables hand keypoint d
DEFINE_string(hand_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the hand keypoint"
" detector.");
DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results"
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4");
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4.");
DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than `scale_gap` but applied to the hand keypoint detector. Total range"
" between smallest and biggest scale. The scales will be centered in ratio 1. E.g. if"
" scaleRange = 0.4 and scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.");
DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate"
" is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it"
" simply looks for hands in positions at which hands were located in previous frames, but"
" it does not guarantee the same person ID among frames");
" it does not guarantee the same person ID among frames.");
// OpenPose Rendering
DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body"
" part heat map, 19 for the background heat map, 20 for all the body part heat maps"
" together, 21 for all the PAFs, 22-40 for each body part pair PAF");
" together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black"
" background, instead of being rendered into the original image. Related: `part_to_show`,"
" `alpha_pose`, and `alpha_pose`.");
......@@ -328,7 +334,8 @@ int openPoseTutorialWrapper1()
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, heatMapScale, FLAGS_part_candidates,
(float)FLAGS_render_threshold, enableGoogleLogging};
(float)FLAGS_render_threshold, FLAGS_number_people_max,
enableGoogleLogging};
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{FLAGS_face, faceNetInputSize,
op::flagsToRenderMode(FLAGS_face_render, FLAGS_render_pose),
......
......@@ -56,6 +56,12 @@ DEFINE_int32(keypoint_scale, 0, "Scaling of the (x,y) co
" size (set with `net_resolution`), `2` to scale it to the final output size (set with"
" `resolution`), `3` to scale it in the range [0,1], and 4 for range [-1,1]. Non related"
" with `scale_number` and `scale_gap`.");
DEFINE_int32(number_people_max, -1, "This parameter will limit the maximum number of people detected, by keeping the people with"
" top scores. The score is based in person area over the image, body part score, as well as"
" joint score (between each pair of connected body parts). Useful if you know the exact"
" number of people in the scene, so it can remove false positives (if all the people have"
" been detected. However, it might also include false negatives by removing very small or"
" highly occluded people. -1 will keep them all.");
// OpenPose Body Pose
DEFINE_bool(body_disable, false, "Disable body keypoint detection. Option only possible for faster (but less accurate) face"
" keypoint detection.");
......@@ -106,18 +112,18 @@ DEFINE_bool(hand, false, "Enables hand keypoint d
DEFINE_string(hand_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the hand keypoint"
" detector.");
DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results"
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4");
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4.");
DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than `scale_gap` but applied to the hand keypoint detector. Total range"
" between smallest and biggest scale. The scales will be centered in ratio 1. E.g. if"
" scaleRange = 0.4 and scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.");
DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate"
" is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it"
" simply looks for hands in positions at which hands were located in previous frames, but"
" it does not guarantee the same person ID among frames");
" it does not guarantee the same person ID among frames.");
// OpenPose Rendering
DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body"
" part heat map, 19 for the background heat map, 20 for all the body part heat maps"
" together, 21 for all the PAFs, 22-40 for each body part pair PAF");
" together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black"
" background, instead of being rendered into the original image. Related: `part_to_show`,"
" `alpha_pose`, and `alpha_pose`.");
......@@ -424,7 +430,8 @@ int openPoseTutorialWrapper2()
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, heatMapScale, FLAGS_part_candidates,
(float)FLAGS_render_threshold, enableGoogleLogging};
(float)FLAGS_render_threshold, FLAGS_number_people_max,
enableGoogleLogging};
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{FLAGS_face, faceNetInputSize,
op::flagsToRenderMode(FLAGS_face_render, FLAGS_render_pose),
......
......@@ -56,6 +56,12 @@ DEFINE_int32(keypoint_scale, 0, "Scaling of the (x,y) co
" size (set with `net_resolution`), `2` to scale it to the final output size (set with"
" `resolution`), `3` to scale it in the range [0,1], and 4 for range [-1,1]. Non related"
" with `scale_number` and `scale_gap`.");
DEFINE_int32(number_people_max, -1, "This parameter will limit the maximum number of people detected, by keeping the people with"
" top scores. The score is based in person area over the image, body part score, as well as"
" joint score (between each pair of connected body parts). Useful if you know the exact"
" number of people in the scene, so it can remove false positives (if all the people have"
" been detected. However, it might also include false negatives by removing very small or"
" highly occluded people. -1 will keep them all.");
// OpenPose Body Pose
DEFINE_bool(body_disable, false, "Disable body keypoint detection. Option only possible for faster (but less accurate) face"
" keypoint detection.");
......@@ -106,18 +112,18 @@ DEFINE_bool(hand, false, "Enables hand keypoint d
DEFINE_string(hand_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the hand keypoint"
" detector.");
DEFINE_int32(hand_scale_number, 1, "Analogous to `scale_number` but applied to the hand keypoint detector. Our best results"
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4");
" were found with `hand_scale_number` = 6 and `hand_scale_range` = 0.4.");
DEFINE_double(hand_scale_range, 0.4, "Analogous purpose than `scale_gap` but applied to the hand keypoint detector. Total range"
" between smallest and biggest scale. The scales will be centered in ratio 1. E.g. if"
" scaleRange = 0.4 and scalesNumber = 2, then there will be 2 scales, 0.8 and 1.2.");
DEFINE_bool(hand_tracking, false, "Adding hand tracking might improve hand keypoints detection for webcam (if the frame rate"
" is high enough, i.e. >7 FPS per GPU) and video. This is not person ID tracking, it"
" simply looks for hands in positions at which hands were located in previous frames, but"
" it does not guarantee the same person ID among frames");
" it does not guarantee the same person ID among frames.");
// OpenPose Rendering
DEFINE_int32(part_to_show, 0, "Prediction channel to visualize (default: 0). 0 for all the body parts, 1-18 for each body"
" part heat map, 19 for the background heat map, 20 for all the body part heat maps"
" together, 21 for all the PAFs, 22-40 for each body part pair PAF");
" together, 21 for all the PAFs, 22-40 for each body part pair PAF.");
DEFINE_bool(disable_blending, false, "If enabled, it will render the results (keypoint skeletons or heatmaps) on a black"
" background, instead of being rendered into the original image. Related: `part_to_show`,"
" `alpha_pose`, and `alpha_pose`.");
......@@ -365,7 +371,8 @@ int openPoseTutorialWrapper3()
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, heatMapScale, FLAGS_part_candidates,
(float)FLAGS_render_threshold, enableGoogleLogging};
(float)FLAGS_render_threshold, FLAGS_number_people_max,
enableGoogleLogging};
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{FLAGS_face, faceNetInputSize,
op::flagsToRenderMode(FLAGS_face_render, FLAGS_render_pose),
......
#ifndef OPENPOSE_3D_HEADERS_HPP
#define OPENPOSE_3D_HEADERS_HPP
// pose module
#include <openpose/3d/poseTriangulation.hpp>
#include <openpose/3d/wPoseTriangulation.hpp>
#endif // OPENPOSE_3D_HEADERS_HPP
#ifndef OPENPOSE_3D_POSE_TRIANGULATION_HPP
#define OPENPOSE_3D_POSE_TRIANGULATION_HPP
#include <opencv2/core/core.hpp>
#include <openpose/core/common.hpp>
namespace op
{
OP_API void reconstructArray(Array<float>& keypoints3D, const std::vector<Array<float>>& keypointsVector,
const std::vector<cv::Mat>& matrixEachCamera);
}
#endif // OPENPOSE_3D_POSE_TRIANGULATION_HPP
#ifndef OPENPOSE_3D_W_POSE_TRIANGULATION_HPP
#define OPENPOSE_3D_W_POSE_TRIANGULATION_HPP
#include <openpose/core/common.hpp>
#include <openpose/3d/poseTriangulation.hpp>
#include <openpose/thread/worker.hpp>
namespace op
{
template<typename TDatums>
class WPoseTriangulation : public Worker<TDatums>
{
public:
explicit WPoseTriangulation();
void initializationOnThread();
void work(TDatums& tDatums);
private:
DELETE_COPY(WPoseTriangulation);
};
}
// Implementation
#include <openpose/utilities/pointerContainer.hpp>
namespace op
{
template<typename TDatums>
WPoseTriangulation<TDatums>::WPoseTriangulation()
{
}
template<typename TDatums>
void WPoseTriangulation<TDatums>::initializationOnThread()
{
}
template<typename TDatums>
void WPoseTriangulation<TDatums>::work(TDatums& tDatums)
{
try
{
if (checkNoNullNorEmpty(tDatums))
{
// Debugging log
dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
// Profiling speed
const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__);
// 3-D triangulation and reconstruction
std::vector<cv::Mat> cameraParameterMatrices;
std::vector<Array<float>> poseKeypointVector;
std::vector<Array<float>> faceKeypointVector;
std::vector<Array<float>> leftHandKeypointVector;
std::vector<Array<float>> rightHandKeypointVector;
for (auto& datumsElement : *tDatums)
{
poseKeypointVector.emplace_back(datumsElement.poseKeypoints);
faceKeypointVector.emplace_back(datumsElement.faceKeypoints);
leftHandKeypointVector.emplace_back(datumsElement.handKeypoints[0]);
rightHandKeypointVector.emplace_back(datumsElement.handKeypoints[1]);
cameraParameterMatrices.emplace_back(datumsElement.cameraParameterMatrix);
}
// Pose 3-D reconstruction
reconstructArray(tDatums->at(0).poseKeypoints3D, poseKeypointVector, cameraParameterMatrices);
// Face 3-D reconstruction
reconstructArray(tDatums->at(0).faceKeypoints3D, faceKeypointVector, cameraParameterMatrices);
// Left hand 3-D reconstruction
reconstructArray(tDatums->at(0).handKeypoints3D[0], leftHandKeypointVector, cameraParameterMatrices);
// Right hand 3-D reconstruction
reconstructArray(tDatums->at(0).handKeypoints3D[1], rightHandKeypointVector, cameraParameterMatrices);
// Profiling speed
Profiler::timerEnd(profilerKey);
Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__);
// Debugging log
dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
}
}
catch (const std::exception& e)
{
this->stop();
tDatums = nullptr;
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
COMPILE_TEMPLATE_DATUM(WPoseTriangulation);
}
#endif // OPENPOSE_3D_W_POSE_TRIANGULATION_HPP
......@@ -145,12 +145,39 @@ namespace op
std::array<Array<float>, 2> handKeypoints;
/**
* Face pose heatmaps (hand parts and/or background) for the whole image.
* Hand pose heatmaps (hand parts and/or background) for the whole image.
* Analogous of faceHeatMaps applied to face.
* Size each Array: #people x #hand parts (21) x output_net_height x output_net_width
*/
std::array<Array<float>, 2> handHeatMaps;
// ---------------------------------------- 3-D Reconstruction parameters ---------------------------------------- //
/**
* Body pose (x,y,z,score) locations for each person in the image.
* Size: #people x #body parts (e.g. 18 for COCO or 15 for MPI) x 4 ((x,y,z) coordinates + score)
*/
Array<float> poseKeypoints3D;
/**
* Face keypoints (x,y,z,score) locations for each person in the image.
* It has been resized to the same resolution as `poseKeypoints3D`.
* Size: #people x #face parts (70) x 4 ((x,y,z) coordinates + score)
*/
Array<float> faceKeypoints3D;
/**
* Hand keypoints (x,y,z,score) locations for each person in the image.
* It has been resized to the same resolution as `poseKeypoints3D`.
* handKeypoints[0] corresponds to left hands, and handKeypoints[1] to right ones.
* Size each Array: #people x #hand parts (21) x 4 ((x,y,z) coordinates + score)
*/
std::array<Array<float>, 2> handKeypoints3D;
/**
* 4x3 extrinsic parameter matrix of the camera.
*/
cv::Mat cameraParameterMatrix;
// ---------------------------------------- Other (internal) parameters ---------------------------------------- //
/**
* Scale ratio between the input Datum::cvInputData and the net input size.
......
......@@ -9,6 +9,7 @@
#include <openpose/core/datum.hpp>
#include <openpose/core/enumClasses.hpp>
#include <openpose/core/gpuRenderer.hpp>
#include <openpose/core/keepTopNPeople.hpp>
#include <openpose/core/keypointScaler.hpp>
#include <openpose/core/macros.hpp>
#include <openpose/core/net.hpp>
......@@ -24,6 +25,7 @@
#include <openpose/core/scaleAndSizeExtractor.hpp>
#include <openpose/core/wCvMatToOpInput.hpp>
#include <openpose/core/wCvMatToOpOutput.hpp>
#include <openpose/core/wKeepTopNPeople.hpp>
#include <openpose/core/wKeypointScaler.hpp>
#include <openpose/core/wOpOutputToCvMat.hpp>
#include <openpose/core/wScaleAndSizeExtractor.hpp>
......
#ifndef OPENPOSE_CORE_KEEP_TOP_N_PEOPLE_HPP
#define OPENPOSE_CORE_KEEP_TOP_N_PEOPLE_HPP
#include <openpose/core/common.hpp>
namespace op
{
class OP_API KeepTopNPeople
{
public:
explicit KeepTopNPeople(const int numberPeopleMax);
Array<float> keepTopPeople(const Array<float>& peopleArrays, const Array<float>& poseScores) const;
private:
const int mNumberPeopleMax;
};
}
#endif // OPENPOSE_CORE_KEEP_TOP_N_PEOPLE_HPP
#ifndef OPENPOSE_CORE_W_KEEP_TOP_N_PEOPLE_HPP
#define OPENPOSE_CORE_W_KEEP_TOP_N_PEOPLE_HPP
#include <openpose/core/common.hpp>
#include <openpose/core/keepTopNPeople.hpp>
#include <openpose/thread/worker.hpp>
namespace op
{
template<typename TDatums>
class WKeepTopNPeople : public Worker<TDatums>
{
public:
explicit WKeepTopNPeople(const std::shared_ptr<KeepTopNPeople>& keepTopNPeople);
void initializationOnThread();
void work(TDatums& tDatums);
private:
std::shared_ptr<KeepTopNPeople> spKeepTopNPeople;
};
}
// Implementation
#include <openpose/utilities/pointerContainer.hpp>
namespace op
{
template<typename TDatums>
WKeepTopNPeople<TDatums>::WKeepTopNPeople(const std::shared_ptr<KeepTopNPeople>& keepTopNPeople) :
spKeepTopNPeople{keepTopNPeople}
{
}
template<typename TDatums>
void WKeepTopNPeople<TDatums>::initializationOnThread()
{
}
template<typename TDatums>
void WKeepTopNPeople<TDatums>::work(TDatums& tDatums)
{
try
{
if (checkNoNullNorEmpty(tDatums))
{
// Debugging log
dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
// Profiling speed
const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__);
// Rescale pose data
for (auto& tDatum : *tDatums)
{
tDatum.poseKeypoints = spKeepTopNPeople->keepTopPeople(tDatum.poseKeypoints, tDatum.poseScores);
tDatum.faceKeypoints = spKeepTopNPeople->keepTopPeople(tDatum.faceKeypoints, tDatum.poseScores);
tDatum.handKeypoints[0] = spKeepTopNPeople->keepTopPeople(tDatum.handKeypoints[0],
tDatum.poseScores);
tDatum.handKeypoints[1] = spKeepTopNPeople->keepTopPeople(tDatum.handKeypoints[1],
tDatum.poseScores);
}
// Profiling speed
Profiler::timerEnd(profilerKey);
Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__);
// Debugging log
dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
}
}
catch (const std::exception& e)
{
this->stop();
tDatums = nullptr;
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
COMPILE_TEMPLATE_DATUM(WKeepTopNPeople);
}
#endif // OPENPOSE_CORE_W_KEEP_TOP_N_PEOPLE_HPP
......@@ -55,8 +55,10 @@ namespace op
// Rescale pose data
for (auto& tDatum : *tDatums)
{
std::vector<Array<float>> arraysToScale{tDatum.poseKeypoints, tDatum.handKeypoints[0], tDatum.handKeypoints[1], tDatum.faceKeypoints};
spKeypointScaler->scale(arraysToScale, tDatum.scaleInputToOutput, tDatum.scaleNetToOutput, Point<int>{tDatum.cvInputData.cols, tDatum.cvInputData.rows});
std::vector<Array<float>> arraysToScale{tDatum.poseKeypoints, tDatum.handKeypoints[0],
tDatum.handKeypoints[1], tDatum.faceKeypoints};
spKeypointScaler->scale(arraysToScale, tDatum.scaleInputToOutput, tDatum.scaleNetToOutput,
Point<int>{tDatum.cvInputData.cols, tDatum.cvInputData.rows});
}
// Profiling speed
Profiler::timerEnd(profilerKey);
......
......@@ -33,7 +33,8 @@ namespace op
namespace op
{
template<typename TDatums>
WScaleAndSizeExtractor<TDatums>::WScaleAndSizeExtractor(const std::shared_ptr<ScaleAndSizeExtractor>& scaleAndSizeExtractor) :
WScaleAndSizeExtractor<TDatums>::WScaleAndSizeExtractor(
const std::shared_ptr<ScaleAndSizeExtractor>& scaleAndSizeExtractor) :
spScaleAndSizeExtractor{scaleAndSizeExtractor}
{
}
......
......@@ -10,9 +10,7 @@ namespace op
OP_API const cv::Mat getDistorsion(const int cameraIndex);
OP_API const cv::Mat getM(const int cameraIndex);
OP_API std::vector<cv::Mat> getMs();
OP_API cv::Mat getM(const int cameraIndex);
OP_API unsigned long long getNumberCameras();
}
......
#ifndef OPENPOSE_EXPERIMENTAL_3D_DATUM_3D_HPP
#define OPENPOSE_EXPERIMENTAL_3D_DATUM_3D_HPP
#include <openpose/core/array.hpp>
#include <openpose/core/datum.hpp>
namespace op
{
// Following OpenPose `tutorial_wrapper/` examples, we create our own class inherited from Datum
// See `examples/tutorial_wrapper/` for more details
struct OP_API Datum3D : public Datum
{
Array<float> poseKeypoints3D;
Array<float> faceKeypoints3D;
Array<float> leftHandKeypoints3D;
Array<float> rightHandKeypoints3D;
cv::Mat cameraParameterMatrix;
};
}
#endif // OPENPOSE_EXPERIMENTAL_3D_DATUM_3D_HPP
......@@ -2,7 +2,6 @@
#define OPENPOSE_EXPERIMENTAL_3D_POINT_GREY_HPP
#include <openpose/core/common.hpp>
#include <openpose/experimental/3d/datum3D.hpp>
#include <openpose/thread/workerProducer.hpp>
namespace op
......@@ -14,13 +13,13 @@ namespace op
* 1. Set hardware trigger and the buffer to get the latest obtained frame.
* 2. Read images from FLIR cameras.
* 3. Turn them into std::vector<cv::Mat>.
* 4. Return the resulting images wrapped into a std::shared_ptr<std::vector<Datum3D>>.
* 4. Return the resulting images wrapped into a std::shared_ptr<std::vector<Datum>>.
* The HW trigger + reading FLIR camera code is highly based on the Spinnaker SDK examples
* `AcquisitionMultipleCamera` and specially `Trigger`
* (located in `src/`). See them for more details about the cameras.
* See `examples/tutorial_wrapper/` for more details about inhering the WorkerProducer class.
*/
class OP_API WFlirReader : public WorkerProducer<std::shared_ptr<std::vector<Datum3D>>>
class OP_API WFlirReader : public WorkerProducer<std::shared_ptr<std::vector<Datum>>>
{
public:
WFlirReader();
......@@ -29,7 +28,7 @@ namespace op
void initializationOnThread();
std::shared_ptr<std::vector<Datum3D>> workProducer();
std::shared_ptr<std::vector<Datum>> workProducer();
private:
// PIMPL idiom
......
......@@ -3,12 +3,8 @@
// Camera intrinsic and extrinsic parameters
#include <openpose/experimental/3d/cameraParameters.hpp>
// Datum3D
#include <openpose/experimental/3d/datum3D.hpp>
// Flir (Point-Grey) cameras
#include <openpose/experimental/3d/flirReader.hpp>
// 3D reconstruction
#include <openpose/experimental/3d/reconstruction3D.hpp>
// OpenGL Renderer
#include <openpose/experimental/3d/renderer.hpp>
......
#ifndef OPENPOSE_EXPERIMENTAL_3D_RECONSTRUCTION_3D_HPP
#define OPENPOSE_EXPERIMENTAL_3D_RECONSTRUCTION_3D_HPP
#include <openpose/core/common.hpp>
#include <openpose/experimental/3d/datum3D.hpp>
#include <openpose/thread/worker.hpp>
namespace op
{
// Following OpenPose `tutorial_wrapper/` examples, we create our own class inherited from Worker.
// This worker will do 3-D reconstruction
// We apply the simple Direct linear transformation (DLT) algorithm, asumming each keypoint (e.g. right hip) is
// seen by all the cameras.
// No non-linear minimization used, and if some camera misses the point, it is not reconstructed.
// See `examples/tutorial_wrapper/` for more details about inhering the Worker class and using it for
// post-processing purposes.
class OP_API WReconstruction3D : public Worker<std::shared_ptr<std::vector<Datum3D>>>
{
public:
void initializationOnThread() {}
void work(std::shared_ptr<std::vector<Datum3D>>& datumsPtr);
};
}
#endif // OPENPOSE_EXPERIMENTAL_3D_RECONSTRUCTION_3D_HPP
......@@ -2,14 +2,13 @@
#define OPENPOSE_EXPERIMENTAL_3D_RENDERER_HPP
#include <openpose/core/common.hpp>
#include <openpose/experimental/3d/datum3D.hpp>
#include <openpose/pose/enumClasses.hpp>
#include <openpose/thread/workerConsumer.hpp>
namespace op
{
// This worker will do 3-D rendering
class OP_API WRender3D : public WorkerConsumer<std::shared_ptr<std::vector<Datum3D>>>
class OP_API WRender3D : public WorkerConsumer<std::shared_ptr<std::vector<Datum>>>
{
public:
WRender3D(const PoseModel poseModel = PoseModel::COCO_18);
......@@ -18,7 +17,7 @@ namespace op
void initializationOnThread();
void workConsumer(const std::shared_ptr<std::vector<Datum3D>>& datumsPtr);
void workConsumer(const std::shared_ptr<std::vector<Datum>>& datumsPtr);
};
}
......
......@@ -5,9 +5,9 @@ namespace op
{
enum class GpuMode : unsigned char
{
CUDA = 0,
OPEN_CL = 1,
NO_GPU = 2,
Cuda = 0,
OpenCL = 1,
NoGpu = 2,
Size,
};
}
......
#ifndef OPENPOSE_HEADERS_HPP
#define OPENPOSE_HEADERS_HPP
// 3d module
#include <openpose/3d/headers.hpp>
// core module
#include <openpose/core/headers.hpp>
......
......@@ -259,6 +259,7 @@ namespace op
// Implementation
#include <openpose/3d/headers.hpp>
#include <openpose/core/headers.hpp>
#include <openpose/experimental/tracking/headers.hpp>
#include <openpose/face/headers.hpp>
......@@ -453,18 +454,21 @@ namespace op
wrapperStructOutput, renderOutput, userOutputWsEmpty, mThreadManagerMode);
// Get number threads
auto numberThreads = wrapperStructPose.gpuNumber;
auto gpuNumberStart = wrapperStructPose.gpuNumberStart;
// CPU --> 1 thread or no pose extraction
#ifdef CPU_ONLY
const auto numberThreads = (wrapperStructPose.gpuNumber == 0 ? 0 : 1);
const auto gpuNumberStart = 0;
if (getGpuMode() == GpuMode::NoGpu)
{
numberThreads = (wrapperStructPose.gpuNumber == 0 ? 0 : 1);
gpuNumberStart = 0;
// Disabling multi-thread makes the code 400 ms faster (2.3 sec vs. 2.7 in i7-6850K)
// and fixes the bug that the screen was not properly displayed and only refreshed sometimes
// Note: The screen bug could be also fixed by using waitKey(30) rather than waitKey(1)
disableMultiThreading();
}
// GPU --> user picks (<= #GPUs)
#else
auto numberThreads = wrapperStructPose.gpuNumber;
const auto gpuNumberStart = wrapperStructPose.gpuNumberStart;
else
{
// If number GPU < 0 --> set it to all the available GPUs
if (numberThreads < 0)
{
......@@ -480,7 +484,7 @@ namespace op
+ " GPU(s), using " + std::to_string(numberThreads) + " of them starting at GPU "
+ std::to_string(gpuNumberStart) + ".", Priority::High);
}
#endif
}
// Proper format
const auto writeImagesCleaned = formatAsDirectory(wrapperStructOutput.writeImages);
......@@ -594,6 +598,17 @@ namespace op
spWPoses.resize(poseExtractors.size());
for (auto i = 0u; i < spWPoses.size(); i++)
spWPoses.at(i) = {std::make_shared<WPoseExtractor<TDatumsPtr>>(poseExtractors.at(i))};
// Added right after PoseExtractor to avoid:
// 1) Rendering people that are later deleted (wrong visualization).
// 2) Processing faces and hands on people that will be deleted (speed up).
if (wrapperStructPose.numberPeopleMax > 0)
{
// Add KeepTopNPeople for each PoseExtractor
const auto keepTopNPeople = std::make_shared<KeepTopNPeople>(wrapperStructPose.numberPeopleMax);
for (auto& wPose : spWPoses)
wPose.emplace_back(std::make_shared<WKeepTopNPeople<TDatumsPtr>>(keepTopNPeople));
}
}
......@@ -605,19 +620,19 @@ namespace op
if (wrapperStructPose.enable)
{
const auto faceDetector = std::make_shared<FaceDetector>(wrapperStructPose.poseModel);
for (auto gpu = 0u; gpu < spWPoses.size(); gpu++)
spWPoses.at(gpu).emplace_back(std::make_shared<WFaceDetector<TDatumsPtr>>(faceDetector));
for (auto& wPose : spWPoses)
wPose.emplace_back(std::make_shared<WFaceDetector<TDatumsPtr>>(faceDetector));
}
// OpenCV face detector
else
{
log("Body keypoint detection is disabled. Hence, using OpenCV face detector (much less"
" accurate but faster).", Priority::High);
for (auto gpu = 0u; gpu < spWPoses.size(); gpu++)
for (auto& wPose : spWPoses)
{
// 1 FaceDetectorOpenCV per thread, OpenCV face detector is not thread-safe
const auto faceDetectorOpenCV = std::make_shared<FaceDetectorOpenCV>(modelFolder);
spWPoses.at(gpu).emplace_back(
wPose.emplace_back(
std::make_shared<WFaceDetectorOpenCV<TDatumsPtr>>(faceDetectorOpenCV)
);
}
......@@ -772,6 +787,13 @@ namespace op
std::make_shared<WPersonIdExtractor<TDatumsPtr>>(personIdExtractor)
);
}
// 3-D reconstruction
if (wrapperStructPose.reconstruct3d)
{
mPostProcessingWs.emplace_back(
std::make_shared<WPoseTriangulation<TDatumsPtr>>()
);
}
// Frames processor (OpenPose format -> cv::Mat format)
if (renderOutput)
{
......
......@@ -148,6 +148,16 @@ namespace op
*/
float renderThreshold;
/**
* Maximum number of people to be detected.
* This parameter will limit the maximum number of people detected, by keeping the people with the
* `numberPeopleMax` top scores.
* Useful if you know the exact number of people in the scene, so it can remove false positives (if all the
* people have been detected.
* However, it might also include false negatives by removing very small or highly occluded people.
*/
int numberPeopleMax;
/**
* Whether to internally enable Google Logging.
* This option is only applicable if Caffe is used.
......@@ -157,6 +167,14 @@ namespace op
*/
bool enableGoogleLogging;
/**
* Whether to run the 3-D reconstruction demo, i.e.,
* 1) Reading from a stereo camera system.
* 2) Performing 3-D reconstruction from the multiple views.
* 3) Displaying 3-D reconstruction results.
*/
bool reconstruct3d;
/**
* Whether to return a person ID for each body skeleton, providing temporal consistency.
*/
......@@ -178,7 +196,8 @@ namespace op
const int defaultPartToRender = 0, const std::string& modelFolder = "models/",
const std::vector<HeatMapType>& heatMapTypes = {},
const ScaleMode heatMapScale = ScaleMode::ZeroToOne, const bool addPartCandidates = false,
const float renderThreshold = 0.05f, const bool enableGoogleLogging = true,
const float renderThreshold = 0.05f, const int numberPeopleMax = -1,
const bool enableGoogleLogging = true, const bool reconstruct3d = false,
const bool identification = false);
};
}
......
set(SOURCES_OP_3D
defineTemplates.cpp
poseTriangulation.cpp)
include(${CMAKE_SOURCE_DIR}/cmake/Utils.cmake)
prepend(SOURCES_OP_3D_WITH_CP ${CMAKE_CURRENT_SOURCE_DIR} ${SOURCES_OP_3D})
set(SOURCES_OP_3D_WITH_CP ${SOURCES_OP_3D_WITH_CP} PARENT_SCOPE)
set(SOURCES_OPENPOSE ${SOURCES_OPENPOSE} ${SOURCES_OP_3D_WITH_CP} PARENT_SCOPE)
if (UNIX AND NOT APPLE)
if (${GPU_MODE} MATCHES "CUDA")
cuda_add_library(openpose_core ${SOURCES_OP_3D})
else()
add_library(openpose_core ${SOURCES_OP_3D})
endif ()
add_library(caffe SHARED IMPORTED)
set_property(TARGET caffe PROPERTY IMPORTED_LOCATION ${Caffe_LIBS})
target_link_libraries(openpose_core caffe ${MKL_LIBS})
if (BUILD_CAFFE)
add_dependencies(openpose_core openpose_caffe)
endif (BUILD_CAFFE)
install(TARGETS openpose_core
EXPORT OpenPose
RUNTIME DESTINATION bin
LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib/openpose)
endif (UNIX AND NOT APPLE)
#include <openpose/3d/headers.hpp>
namespace op
{
DEFINE_TEMPLATE_DATUM(WPoseTriangulation);
}
#include <opencv2/calib3d/calib3d.hpp>
#include <openpose/3d/poseTriangulation.hpp>
namespace op
{
double calcReprojectionError(const cv::Mat& X, const std::vector<cv::Mat>& M,
const std::vector<cv::Point2d>& points2d)
{
try
{
auto averageError = 0.;
for (auto i = 0u ; i < M.size() ; i++)
{
cv::Mat imageX = M[i] * X;
imageX /= imageX.at<double>(2,0);
const auto error = std::sqrt(std::pow(imageX.at<double>(0,0) - points2d[i].x,2)
+ std::pow(imageX.at<double>(1,0) - points2d[i].y,2));
// log("Error: " + std::to_string(error));
averageError += error;
}
return averageError / M.size();
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return -1.;
}
}
void triangulate(cv::Mat& X, const std::vector<cv::Mat>& matrixEachCamera,
const std::vector<cv::Point2d>& pointsOnEachCamera)
{
try
{
// Security checks
if (matrixEachCamera.empty() || matrixEachCamera.size() != pointsOnEachCamera.size())
error("numberCameras.empty() || numberCameras.size() != pointsOnEachCamera.size()",
__LINE__, __FUNCTION__, __FILE__);
// Create and fill A
const auto numberCameras = (int)matrixEachCamera.size();
cv::Mat A = cv::Mat::zeros(numberCameras*2, 4, CV_64F);
for (auto i = 0 ; i < numberCameras ; i++)
{
cv::Mat temp = pointsOnEachCamera[i].x*matrixEachCamera[i].rowRange(2,3)
- matrixEachCamera[i].rowRange(0,1);
temp.copyTo(A.rowRange(i*2, i*2+1));
temp = pointsOnEachCamera[i].y*matrixEachCamera[i].rowRange(2,3) - matrixEachCamera[i].rowRange(1,2);
temp.copyTo(A.rowRange(i*2+1, i*2+2));
}
// SVD on A
cv::SVD svd{A};
svd.solveZ(A,X);
X /= X.at<double>(3);
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
// TODO: ask for the missing function: TriangulationOptimization
double triangulateWithOptimization(cv::Mat& X, const std::vector<cv::Mat>& matrixEachCamera,
const std::vector<cv::Point2d>& pointsOnEachCamera)
{
try
{
triangulate(X, matrixEachCamera, pointsOnEachCamera);
return 0.;
// return calcReprojectionError(X, matrixEachCamera, pointsOnEachCamera);
// //if (matrixEachCamera.size() >= 3)
// //double beforeError = calcReprojectionError(&matrixEachCamera, pointsOnEachCamera, X);
// double change = TriangulationOptimization(&matrixEachCamera, pointsOnEachCamera, X);
// //double afterError = calcReprojectionError(&matrixEachCamera,pointsOnEachCamera,X);
// //printfLog("!!Mine %.8f , inFunc %.8f \n",beforeError-afterError,change);
// return change;
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return -1.;
}
}
void reconstructArray(Array<float>& keypoints3D, const std::vector<Array<float>>& keypointsVector,
const std::vector<cv::Mat>& matrixEachCamera)
{
try
{
// Get number body parts
auto detectionMissed = false;
for (auto& keypoints : keypointsVector)
{
if (keypoints.empty())
{
detectionMissed = true;
break;
}
}
// If at least one keypoints element not empty
if (!detectionMissed)
{
const auto numberBodyParts = keypointsVector.at(0).getSize(1);
// Create x-y vector from high score results
const auto threshold = 0.2f;
std::vector<int> indexesUsed;
std::vector<std::vector<cv::Point2d>> xyPoints;
for (auto part = 0; part < numberBodyParts; part++)
{
// Create vector of points
auto missedPoint = false;
std::vector<cv::Point2d> xyPointsElement;
for (auto& keypoints : keypointsVector)
{
if (keypoints[{0, part, 2}] > threshold)
xyPointsElement.emplace_back(cv::Point2d{ keypoints[{0, part, 0}],
keypoints[{0, part, 1}]});
else
{
missedPoint = true;
break;
}
}
if (!missedPoint)
{
indexesUsed.emplace_back(part);
xyPoints.emplace_back(xyPointsElement);
}
}
// 3D reconstruction
if (!xyPoints.empty())
{
// Do 3D reconstruction
std::vector<cv::Point3f> xyzPoints(xyPoints.size());
for (auto i = 0u; i < xyPoints.size(); i++)
{
cv::Mat X;
triangulateWithOptimization(X, matrixEachCamera, xyPoints[i]);
xyzPoints[i] = cv::Point3d{ X.at<double>(0), X.at<double>(1), X.at<double>(2) };
}
// 3D points to pose
// OpenCV alternative:
// http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#triangulatepoints
// cv::Mat reconstructedPoints{4, firstcv::Points.size(), CV_64F};
// cv::triangulatecv::Points(cv::Mat::eye(3,4, CV_64F), M_3_1, firstcv::Points, secondcv::Points,
// reconstructedcv::Points);
keypoints3D = Array<float>{ { 1, numberBodyParts, 4 }, 0 };
for (auto index = 0u; index < indexesUsed.size(); index++)
{
auto& xValue = keypoints3D[{0, indexesUsed[index], 0}];
auto& yValue = keypoints3D[{0, indexesUsed[index], 1}];
auto& zValue = keypoints3D[{0, indexesUsed[index], 2}];
auto& scoreValue = keypoints3D[{0, indexesUsed[index], 3}];
if (std::isfinite(xyzPoints[index].x) && std::isfinite(xyzPoints[index].y)
&& std::isfinite(xyzPoints[index].z))
{
xValue = xyzPoints[index].x;
yValue = xyzPoints[index].y;
zValue = xyzPoints[index].z;
scoreValue = 1.f;
}
}
}
}
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
}
......@@ -5,6 +5,7 @@ set(SOURCES_OP_CORE
datum.cpp
defineTemplates.cpp
gpuRenderer.cpp
keepTopNPeople.cpp
keypointScaler.cpp
maximumBase.cpp
maximumBase.cu
......
......@@ -19,7 +19,7 @@ namespace op
inputNetData{datum.inputNetData},
outputData{datum.outputData},
cvOutputData{datum.cvOutputData},
// Resulting Array<float> data
// Resulting Array<float> data parameters
poseKeypoints{datum.poseKeypoints},
poseIds{datum.poseIds},
poseScores{datum.poseScores},
......@@ -27,8 +27,15 @@ namespace op
poseCandidates{datum.poseCandidates},
faceRectangles{datum.faceRectangles},
faceKeypoints{datum.faceKeypoints},
faceHeatMaps{datum.faceHeatMaps},
handRectangles{datum.handRectangles},
handKeypoints(datum.handKeypoints), // Parentheses instead of braces to avoid error in GCC 4.8
handHeatMaps(datum.handHeatMaps), // Parentheses instead of braces to avoid error in GCC 4.8
// 3-D Reconstruction parameters
poseKeypoints3D{datum.poseKeypoints3D},
faceKeypoints3D{datum.faceKeypoints3D},
handKeypoints3D(datum.handKeypoints3D), // Parentheses instead of braces to avoid error in GCC 4.8
cameraParameterMatrix{datum.cameraParameterMatrix},
// Other parameters
scaleInputToNetInputs{datum.scaleInputToNetInputs},
netInputSizes{datum.netInputSizes},
......@@ -51,7 +58,7 @@ namespace op
inputNetData = datum.inputNetData;
outputData = datum.outputData;
cvOutputData = datum.cvOutputData;
// Resulting Array<float> data
// Resulting Array<float> data parameters
poseKeypoints = datum.poseKeypoints;
poseIds = datum.poseIds,
poseScores = datum.poseScores,
......@@ -59,8 +66,15 @@ namespace op
poseCandidates = datum.poseCandidates,
faceRectangles = datum.faceRectangles,
faceKeypoints = datum.faceKeypoints,
faceHeatMaps = datum.faceHeatMaps,
handRectangles = datum.handRectangles,
handKeypoints = datum.handKeypoints,
handHeatMaps = datum.handHeatMaps,
// 3-D Reconstruction parameters
poseKeypoints3D = datum.poseKeypoints3D,
faceKeypoints3D = datum.faceKeypoints3D,
handKeypoints3D = datum.handKeypoints3D,
cameraParameterMatrix = datum.cameraParameterMatrix;
// Other parameters
scaleInputToNetInputs = datum.scaleInputToNetInputs;
netInputSizes = datum.netInputSizes;
......@@ -94,7 +108,7 @@ namespace op
std::swap(inputNetData, datum.inputNetData);
std::swap(outputData, datum.outputData);
std::swap(cvOutputData, datum.cvOutputData);
// Resulting Array<float> data
// Resulting Array<float> data parameters
std::swap(poseKeypoints, datum.poseKeypoints);
std::swap(poseIds, datum.poseIds);
std::swap(poseScores, datum.poseScores);
......@@ -102,8 +116,15 @@ namespace op
std::swap(poseCandidates, datum.poseCandidates);
std::swap(faceRectangles, datum.faceRectangles);
std::swap(faceKeypoints, datum.faceKeypoints);
std::swap(faceHeatMaps, datum.faceHeatMaps);
std::swap(handRectangles, datum.handRectangles);
std::swap(handKeypoints, datum.handKeypoints);
std::swap(handHeatMaps, datum.handHeatMaps);
// 3-D Reconstruction parameters
std::swap(poseKeypoints3D, datum.poseKeypoints3D);
std::swap(faceKeypoints3D, datum.faceKeypoints3D);
std::swap(handKeypoints3D, datum.handKeypoints3D);
std::swap(cameraParameterMatrix, datum.cameraParameterMatrix);
// Other parameters
std::swap(scaleInputToNetInputs, datum.scaleInputToNetInputs);
std::swap(netInputSizes, datum.netInputSizes);
......@@ -128,7 +149,7 @@ namespace op
std::swap(inputNetData, datum.inputNetData);
std::swap(outputData, datum.outputData);
std::swap(cvOutputData, datum.cvOutputData);
// Resulting Array<float> data
// Resulting Array<float> data parameters
std::swap(poseKeypoints, datum.poseKeypoints);
std::swap(poseIds, datum.poseIds);
std::swap(poseScores, datum.poseScores);
......@@ -136,8 +157,15 @@ namespace op
std::swap(poseCandidates, datum.poseCandidates);
std::swap(faceRectangles, datum.faceRectangles);
std::swap(faceKeypoints, datum.faceKeypoints);
std::swap(faceHeatMaps, datum.faceHeatMaps);
std::swap(handRectangles, datum.handRectangles);
std::swap(handKeypoints, datum.handKeypoints);
std::swap(handHeatMaps, datum.handHeatMaps);
// 3-D Reconstruction parameters
std::swap(poseKeypoints3D, datum.poseKeypoints3D);
std::swap(faceKeypoints3D, datum.faceKeypoints3D);
std::swap(handKeypoints3D, datum.handKeypoints3D);
std::swap(cameraParameterMatrix, datum.cameraParameterMatrix);
// Other parameters
std::swap(scaleInputToNetInputs, datum.scaleInputToNetInputs);
std::swap(netInputSizes, datum.netInputSizes);
......@@ -172,7 +200,7 @@ namespace op
datum.inputNetData[i] = inputNetData[i].clone();
datum.outputData = outputData.clone();
datum.cvOutputData = cvOutputData.clone();
// Resulting Array<float> data
// Resulting Array<float> data parameters
datum.poseKeypoints = poseKeypoints.clone();
datum.poseIds = poseIds.clone();
datum.poseScores = poseScores.clone();
......@@ -180,9 +208,18 @@ namespace op
datum.poseCandidates = poseCandidates;
datum.faceRectangles = faceRectangles;
datum.faceKeypoints = faceKeypoints.clone();
datum.faceHeatMaps = faceHeatMaps.clone();
datum.handRectangles = datum.handRectangles;
datum.handKeypoints[0] = handKeypoints[0].clone();
datum.handKeypoints[1] = handKeypoints[1].clone();
for (auto i = 0u ; i < datum.handKeypoints.size() ; i++)
datum.handKeypoints[i] = handKeypoints[i].clone();
for (auto i = 0u ; i < datum.handKeypoints.size() ; i++)
datum.handHeatMaps[i] = handHeatMaps[i].clone();
// 3-D Reconstruction parameters
datum.poseKeypoints3D = poseKeypoints3D.clone();
datum.faceKeypoints3D = faceKeypoints3D.clone();
for (auto i = 0u ; i < datum.handKeypoints.size() ; i++)
datum.handKeypoints3D[i] = handKeypoints3D[i].clone();
datum.cameraParameterMatrix = cameraParameterMatrix.clone();
// Other parameters
datum.scaleInputToNetInputs = scaleInputToNetInputs;
datum.netInputSizes = netInputSizes;
......
......@@ -4,6 +4,7 @@ namespace op
{
DEFINE_TEMPLATE_DATUM(WCvMatToOpInput);
DEFINE_TEMPLATE_DATUM(WCvMatToOpOutput);
DEFINE_TEMPLATE_DATUM(WKeepTopNPeople);
DEFINE_TEMPLATE_DATUM(WKeypointScaler);
DEFINE_TEMPLATE_DATUM(WOpOutputToCvMat);
DEFINE_TEMPLATE_DATUM(WScaleAndSizeExtractor);
......
#include <openpose/utilities/keypoint.hpp>
#include <openpose/core/keepTopNPeople.hpp>
namespace op
{
KeepTopNPeople::KeepTopNPeople(const int numberPeopleMax) :
mNumberPeopleMax{numberPeopleMax}
{
}
Array<float> KeepTopNPeople::keepTopPeople(const Array<float>& peopleArray, const Array<float>& poseScores) const
{
try
{
// Remove people if #people > mNumberPeopleMax
if (peopleArray.getVolume() > (unsigned int)mNumberPeopleMax && mNumberPeopleMax > 0)
{
// Security checks
if (poseScores.getVolume() != (unsigned int) poseScores.getSize(0))
error("The poseFinalScores variable should be a Nx1 vector, not a multidimensional array.",
__LINE__, __FUNCTION__, __FILE__);
if (peopleArray.getNumberDimensions() != 3)
error("The peopleArray variable should be a 3 dimensional array.",
__LINE__, __FUNCTION__, __FILE__);
// Get poseFinalScores
auto poseFinalScores = poseScores.clone();
for (auto person = 0 ; person < (int)poseFinalScores.getVolume() ; person ++)
poseFinalScores[person] *= std::sqrt(getKeypointsArea(peopleArray, person, 0.05f));
// Get threshold
auto poseScoresSorted = poseFinalScores.clone();
std::sort(poseScoresSorted.getPtr(), poseScoresSorted.getPtr() + poseScoresSorted.getSize(0),
std::greater<float>());
const auto threshold = poseScoresSorted[mNumberPeopleMax-1];
// Get number people in threshold
auto numberPeopleOnThreshold = 0;
for (auto person = 0 ; person < (int)poseFinalScores.getVolume() ; person ++)
if (poseFinalScores[person] == threshold)
numberPeopleOnThreshold++;
// Remove extra people - Fille topPeopleArray
// assignedPeopleOnThreshold avoids that people with repeated threshold remove higher elements.
// In our case, it will keep the first N people with score = threshold, while keeping all the
// people with higher scores.
// E.g., poseFinalScores = [0, 0.5, 0.5, 0.5, 1.0]; mNumberPeopleMax = 2
// Naively, we could accidentally keep the first 2x 0.5 and remove the 1.0 threshold.
// Our method keeps the first 0.5 and 1.0.
Array<float> topPeopleArray({mNumberPeopleMax, peopleArray.getSize(1), peopleArray.getSize(2)});
const auto personArea = peopleArray.getSize(1) * peopleArray.getSize(2);
auto assignedPeopleOnThreshold = 0;
auto nextPersonIndex = 0;
for (auto person = 0 ; person < (int)poseFinalScores.getVolume() ; person++)
{
if (poseFinalScores[person] >= threshold)
{
// Check we don't copy 2 values in the threshold
if (poseFinalScores[person] == threshold)
assignedPeopleOnThreshold++;
// Copy person into people array
if (poseFinalScores[person] > threshold
|| assignedPeopleOnThreshold <= numberPeopleOnThreshold)
{
const auto peopleArrayIndex = personArea*person;
const auto topArrayIndex = personArea*nextPersonIndex++;
std::copy(&peopleArray[peopleArrayIndex], &peopleArray[peopleArrayIndex]+personArea,
&topPeopleArray[topArrayIndex]);
}
}
}
return topPeopleArray;
}
// If no changes required
else
return peopleArray;
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return Array<float>{};
}
}
}
set(SOURCES_OP_3D
cameraParameters.cpp
flirReader.cpp
reconstruction3D.cpp
renderer.cpp)
include(${CMAKE_SOURCE_DIR}/cmake/Utils.cmake)
......
......@@ -11,19 +11,22 @@ namespace op
0, 816.51774059837908, 517.84529566329593,
0, 0, 1);
const cv::Mat DISTORTION_1 = (cv::Mat_<double>(8, 1) <<
-1.8102158829399091, 9.1966147162623262, -0.00044293900343777355, 0.0013638377686816653, 1.3303863414979364, -1.418905163635487, 8.4725535468475819, 4.7911023525901033);
-1.8102158829399091, 9.1966147162623262, -0.00044293900343777355, 0.0013638377686816653,
1.3303863414979364, -1.418905163635487, 8.4725535468475819, 4.7911023525901033);
// Camera 2 parameters
const cv::Mat INTRINSIC_2 = (cv::Mat_<double>(3, 3) << 816.20921132436638, 0, 612.67087968681585,
0, 816.18292222910486, 530.47901782670431,
0, 0, 1);
const cv::Mat DISTORTION_2 = (cv::Mat_<double>(8, 1) <<
-5.1088507540294881, 133.63995617304997, -0.0010048069080912836, 0.00018825291386406282, 20.688286893903879, -4.7604289550474768, 132.42412342224557, 70.01195364029752);
-5.1088507540294881, 133.63995617304997, -0.0010048069080912836, 0.00018825291386406282,
20.688286893903879, -4.7604289550474768, 132.42412342224557, 70.01195364029752);
const cv::Mat INTRINSIC_3 = (cv::Mat_<double>(3, 3) << 798.42980806905666, 0, 646.48130011561727,
0, 798.46535448393979, 523.91590563194586,
0, 0, 1);
// Camera 3
const cv::Mat DISTORTION_3 = (cv::Mat_<double>(8, 1) <<
-0.57530495294002304, -0.54721992620722582, -0.00037614702677289967, -0.00081995658363481598, -0.020321660897680775, -0.18040544059116842, -0.87724444571603022, -0.13136636671099691);
-0.57530495294002304, -0.54721992620722582, -0.00037614702677289967, -0.00081995658363481598,
-0.020321660897680775, -0.18040544059116842, -0.87724444571603022, -0.13136636671099691);
// Extrinsic parameters - rotation and pose orientation between cameras
// Camera 1
......@@ -33,15 +36,18 @@ namespace op
// Not working on Windows
// const cv::Mat M_1_1 = cv::Mat::eye(3, 4, CV_64F);
// From camera 1 to 2
const cv::Mat M_1_2 = (cv::Mat_<double>(3, 4) << 0.999962504862692, -0.00165862051503619, 0.00849928507093793, -238.301309354482,
const cv::Mat M_1_2 = (cv::Mat_<double>(3, 4)
<< 0.999962504862692, -0.00165862051503619, 0.00849928507093793, -238.301309354482,
0.00176155163779584, 0.999925029704659, -0.0121174215889211, 4.75863886121558,
-0.00847854967298925, 0.0121319391740716, 0.999890459124058, 15.9219925821916);
// From camera 1 to 3
const cv::Mat M_1_3 = (cv::Mat_<double>(3, 4) << 0.995809442124071, -0.000473104796892308, 0.0914512501193800, -461.301274485705,
const cv::Mat M_1_3 = (cv::Mat_<double>(3, 4)
<< 0.995809442124071, -0.000473104796892308, 0.0914512501193800, -461.301274485705,
0.00165046455210419, 0.999916727562850, -0.0127989806923977, 6.22648121362088,
-0.0914375794917412, 0.0128962828696210, 0.995727299487585, 63.4911132860733);
// From camera 2 to 3
const cv::Mat M_2_3 = (cv::Mat_<double>(3, 4) << 0.999644115423621, -0.00194501088674130, -0.0266056278177532, -235.236375502202,
const cv::Mat M_2_3 = (cv::Mat_<double>(3, 4)
<< 0.999644115423621, -0.00194501088674130, -0.0266056278177532, -235.236375502202,
0.00201646110733780, 0.999994431880356, 0.00265896462686206, 9.52238656728889,
0.0266003079592876, -0.00271166755609303, 0.999642471324391, -4.23534963077479);
......@@ -84,11 +90,12 @@ namespace op
}
}
const cv::Mat getM(const int cameraIndex)
cv::Mat getM(const int cameraIndex)
{
try
{
return INTRINSICS[cameraIndex] * M[cameraIndex];
return INTRINSICS.at(cameraIndex) * M.at(cameraIndex);
// Not working on Windows
// return M_EACH_CAMERA[cameraIndex];
}
catch (const std::exception& e)
......@@ -97,30 +104,7 @@ namespace op
return cv::Mat{};
}
}
std::vector<cv::Mat> getMs()
{
try
{
// Security checks
if (INTRINSICS.size() != DISTORTIONS.size())
error("The INTRINSICS and DISTORTIONS vector should have the same size.",
__LINE__, __FUNCTION__, __FILE__);
if (INTRINSICS.size() != M.size())
error("The INTRINSICS and M_EACH_CAMERA vector should have the same size.",
__LINE__, __FUNCTION__, __FILE__);
std::vector<cv::Mat> result(INTRINSICS.size());
for (auto i = 0u ; i < result.size(); i++)
result[i] = getM(i);
return result;
// return M_EACH_CAMERA;
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return{};
}
}
unsigned long long getNumberCameras()
{
try
......
......@@ -600,7 +600,7 @@ namespace op
#endif
}
std::shared_ptr<std::vector<Datum3D>> WFlirReader::workProducer()
std::shared_ptr<std::vector<Datum>> WFlirReader::workProducer()
{
try
{
......@@ -614,7 +614,7 @@ namespace op
// Get image from each camera
const auto cvMats = acquireImages(upImpl->mCameraList);
// Images to userDatum
auto datums3d = std::make_shared<std::vector<Datum3D>>(cvMats.size());
auto datums3d = std::make_shared<std::vector<Datum>>(cvMats.size());
for (auto i = 0u ; i < cvMats.size() ; i++)
{
datums3d->at(i).cvInputData = cvMats.at(i);
......
#include <opencv2/opencv.hpp>
#include <openpose/experimental/3d/reconstruction3D.hpp>
namespace op
{
double calcReprojectionError(const cv::Mat& X, const std::vector<cv::Mat>& M, const std::vector<cv::Point2d>& pt2D)
{
auto averageError = 0.;
for (auto i = 0u ; i < M.size() ; i++)
{
cv::Mat imageX = M[i] * X;
imageX /= imageX.at<double>(2,0);
const auto error = std::sqrt(std::pow(imageX.at<double>(0,0) - pt2D[i].x,2)
+ std::pow(imageX.at<double>(1,0) - pt2D[i].y,2));
//log("Error: " + std::to_string(error));
averageError += error;
}
return averageError / M.size();
}
void triangulate(cv::Mat& X, const std::vector<cv::Mat>& matrixEachCamera,
const std::vector<cv::Point2d>& pointOnEachCamera)
{
// Security checks
if (matrixEachCamera.empty() || matrixEachCamera.size() != pointOnEachCamera.size())
error("numberCameras.empty() || numberCameras.size() != pointOnEachCamera.size()",
__LINE__, __FUNCTION__, __FILE__);
// Create and fill A
const auto numberCameras = (int)matrixEachCamera.size();
cv::Mat A = cv::Mat::zeros(numberCameras*2, 4, CV_64F);
for (auto i = 0 ; i < numberCameras ; i++)
{
cv::Mat temp = pointOnEachCamera[i].x*matrixEachCamera[i].rowRange(2,3)
- matrixEachCamera[i].rowRange(0,1);
temp.copyTo(A.rowRange(i*2,i*2+1));
temp = pointOnEachCamera[i].y*matrixEachCamera[i].rowRange(2,3) - matrixEachCamera[i].rowRange(1,2);
temp.copyTo(A.rowRange(i*2+1,i*2+2));
}
// SVD on A
cv::SVD svd{A};
svd.solveZ(A,X);
X /= X.at<double>(3);
}
// TODO: ask for the missing function: TriangulationOptimization
double triangulateWithOptimization(cv::Mat& X, const std::vector<cv::Mat>& matrixEachCamera,
const std::vector<cv::Point2d>& pointOnEachCamera)
{
triangulate(X, matrixEachCamera, pointOnEachCamera);
// //if (matrixEachCamera.size() >= 3)
// //double beforeError = calcReprojectionError(&matrixEachCamera, pointOnEachCamera, X);
// double change = TriangulationOptimization(&matrixEachCamera, pointOnEachCamera, X);
// //double afterError = calcReprojectionError(&matrixEachCamera,pointOnEachCamera,X);
// //printfLog("!!Mine %.8f , inFunc %.8f \n",beforeError-afterError,change);
// return change;
return 0.;
}
void reconstructArray(Array<float>& keypoints3D, const std::vector<Array<float>>& keypointsVector,
const std::vector<cv::Mat>& matrixEachCamera)
{
// Get number body parts
auto detectionMissed = false;
for (auto& keypoints : keypointsVector)
{
if (keypoints.empty())
{
detectionMissed = true;
break;
}
}
// If at least one keypoints element not empty
if (!detectionMissed)
{
const auto numberBodyParts = keypointsVector.at(0).getSize(1);
// Create x-y vector from high score results
const auto threshold = 0.2f;
std::vector<int> indexesUsed;
std::vector<std::vector<cv::Point2d>> xyPoints;
for (auto part = 0; part < numberBodyParts; part++)
{
// Create vector of points
auto missedPoint = false;
std::vector<cv::Point2d> xyPointsElement;
for (auto& keypoints : keypointsVector)
{
if (keypoints[{0, part, 2}] > threshold)
xyPointsElement.emplace_back(cv::Point2d{ keypoints[{0, part, 0}], keypoints[{0, part, 1}]});
else
{
missedPoint = true;
break;
}
}
if (!missedPoint)
{
indexesUsed.emplace_back(part);
xyPoints.emplace_back(xyPointsElement);
}
}
// 3D reconstruction
if (!xyPoints.empty())
{
// Do 3D reconstruction
std::vector<cv::Point3f> xyzPoints(xyPoints.size());
for (auto i = 0u; i < xyPoints.size(); i++)
{
cv::Mat X;
triangulateWithOptimization(X, matrixEachCamera, xyPoints[i]);
xyzPoints[i] = cv::Point3d{ X.at<double>(0), X.at<double>(1), X.at<double>(2) };
}
// 3D points to pose
// OpenCV alternative:
// http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#triangulatepoints
// cv::Mat reconstructedPoints{4, firstcv::Points.size(), CV_64F};
// cv::triangulatecv::Points(cv::Mat::eye(3,4, CV_64F), M_3_1, firstcv::Points, secondcv::Points,
// reconstructedcv::Points);
keypoints3D = Array<float>{ { 1, numberBodyParts, 4 }, 0 };
for (auto index = 0u; index < indexesUsed.size(); index++)
{
auto& xValue = keypoints3D[{0, indexesUsed[index], 0}];
auto& yValue = keypoints3D[{0, indexesUsed[index], 1}];
auto& zValue = keypoints3D[{0, indexesUsed[index], 2}];
auto& scoreValue = keypoints3D[{0, indexesUsed[index], 3}];
if (std::isfinite(xyzPoints[index].x) && std::isfinite(xyzPoints[index].y)
&& std::isfinite(xyzPoints[index].z))
{
xValue = xyzPoints[index].x;
yValue = xyzPoints[index].y;
zValue = xyzPoints[index].z;
scoreValue = 1.f;
}
}
}
}
}
void WReconstruction3D::work(std::shared_ptr<std::vector<Datum3D>>& datumsPtr)
{
// User's post-processing (after OpenPose processing & before OpenPose outputs) here
// datum.cvOutputData: rendered frame with pose or heatmaps
// datum.poseKeypoints: Array<float> with the estimated pose
try
{
// Debugging log
dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
// Profiling speed
const auto profilerKey = Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__);
if (datumsPtr != nullptr && !datumsPtr->empty())
{
std::vector<cv::Mat> matrixEachCamera;
std::vector<Array<float>> poseKeypointVector;
std::vector<Array<float>> faceKeypointVector;
std::vector<Array<float>> leftHandKeypointVector;
std::vector<Array<float>> rightHandKeypointVector;
for (auto& datumsElement : *datumsPtr)
{
poseKeypointVector.emplace_back(datumsElement.poseKeypoints);
faceKeypointVector.emplace_back(datumsElement.faceKeypoints);
leftHandKeypointVector.emplace_back(datumsElement.handKeypoints[0]);
rightHandKeypointVector.emplace_back(datumsElement.handKeypoints[1]);
matrixEachCamera.emplace_back(datumsElement.cameraParameterMatrix);
}
// Pose 3-D reconstruction
reconstructArray(datumsPtr->at(0).poseKeypoints3D, poseKeypointVector, matrixEachCamera);
// Face 3-D reconstruction
reconstructArray(datumsPtr->at(0).faceKeypoints3D, faceKeypointVector, matrixEachCamera);
// Left hand 3-D reconstruction
reconstructArray(datumsPtr->at(0).leftHandKeypoints3D, leftHandKeypointVector, matrixEachCamera);
// Right hand 3-D reconstruction
reconstructArray(datumsPtr->at(0).rightHandKeypoints3D, rightHandKeypointVector, matrixEachCamera);
// Profiling speed
Profiler::timerEnd(profilerKey);
Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__);
// Debugging log
dLog("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
}
}
catch (const std::exception& e)
{
log("Some kind of unexpected error happened.");
this->stop();
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
}
......@@ -459,7 +459,7 @@ namespace op
#endif
}
void WRender3D::workConsumer(const std::shared_ptr<std::vector<Datum3D>>& datumsPtr)
void WRender3D::workConsumer(const std::shared_ptr<std::vector<Datum>>& datumsPtr)
{
#ifdef WITH_3D_RENDERER
try
......@@ -487,8 +487,8 @@ namespace op
std::unique_lock<std::mutex> lock{gKeypoints3D.mutex};
gKeypoints3D.mPoseKeypoints = datumsPtr->at(0).poseKeypoints3D;
gKeypoints3D.mFaceKeypoints = datumsPtr->at(0).faceKeypoints3D;
gKeypoints3D.mLeftHandKeypoints = datumsPtr->at(0).leftHandKeypoints3D;
gKeypoints3D.mRightHandKeypoints = datumsPtr->at(0).rightHandKeypoints3D;
gKeypoints3D.mLeftHandKeypoints = datumsPtr->at(0).handKeypoints3D[0];
gKeypoints3D.mRightHandKeypoints = datumsPtr->at(0).handKeypoints3D[1];
gKeypoints3D.validKeypoints = true;
// Esc pressed -> Close program
if (sLastKeyPressed == 27)
......
......@@ -34,17 +34,17 @@ namespace op
try
{
#ifdef USE_CUDA
return GpuMode::CUDA;
return GpuMode::Cuda;
#elif defined USE_OPENCL
return GpuMode::OPEN_CL;
return GpuMode::OpenCL;
#else
return GpuMode::NO_GPU;
return GpuMode::NoGpu;
#endif
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return GpuMode::NO_GPU;
return GpuMode::NoGpu;
}
}
}
#include <openpose/gpu/gpu.hpp>
#include <openpose/thread/enumClasses.hpp>
#include <openpose/wrapper/wrapperAuxiliary.hpp>
......@@ -92,7 +93,8 @@ namespace op
}
if (!wrapperStructOutput.writeVideo.empty() && wrapperStructInput.producerSharedPtr == nullptr)
error("Writting video is only available if the OpenPose producer is used (i.e."
" wrapperStructInput.producerSharedPtr cannot be a nullptr).", __LINE__, __FUNCTION__, __FILE__);
" wrapperStructInput.producerSharedPtr cannot be a nullptr).",
__LINE__, __FUNCTION__, __FILE__);
if (!wrapperStructPose.enable)
{
if (!wrapperStructFace.enable)
......@@ -101,9 +103,28 @@ namespace op
error("Body keypoint detection must be enabled in order to run hand keypoint detection.",
__LINE__, __FUNCTION__, __FILE__);
}
#ifdef CPU_ONLY
// If 3-D module, 1 person is the maximum
if (wrapperStructPose.reconstruct3d && wrapperStructPose.numberPeopleMax > 1)
{
error("Set `--number_people_max 1` when using `--3d`. The 3-D reconstruction demo assumes there is"
" at most 1 person on each image.", __LINE__, __FUNCTION__, __FILE__);
}
// If CPU mode, #GPU cannot be > 0
if (getGpuMode() == GpuMode::NoGpu)
if (wrapperStructPose.gpuNumber > 0)
error("GPU number must be negative or 0 if CPU_ONLY is enabled.", __LINE__, __FUNCTION__, __FILE__);
error("GPU number must be negative or 0 if CPU_ONLY is enabled.",
__LINE__, __FUNCTION__, __FILE__);
// Net input resolution cannot be reshaped for Caffe OpenCL and MKL versions, only for CUDA version
#if defined USE_MKL || defined CPU_ONLY
// If image_dir and netInputSize == -1 --> error
if ((wrapperStructInput.producerSharedPtr == nullptr
|| wrapperStructInput.producerSharedPtr->getType() == ProducerType::ImageDirectory)
// If netInputSize is -1
&& (wrapperStructPose.netInputSize.x == -1 || wrapperStructPose.netInputSize.y == -1))
error("Dynamic `--net_resolution` is not supported in MKL (CPU) and OpenCL Caffe versions. Please"
" remove `-1` from `net_resolution` or use the Caffe master branch when processing images"
" and when using your custom image reader.",
__LINE__, __FUNCTION__, __FILE__);
#endif
log("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
......
......@@ -12,7 +12,8 @@ namespace op
const std::string& modelFolder_,
const std::vector<HeatMapType>& heatMapTypes_,
const ScaleMode heatMapScale_, const bool addPartCandidates_,
const float renderThreshold_, const bool enableGoogleLogging_,
const float renderThreshold_, const int numberPeopleMax_,
const bool enableGoogleLogging_, const bool reconstruct3d_,
const bool identification_) :
enable{enable_},
netInputSize{netInputSize_},
......@@ -33,7 +34,9 @@ namespace op
heatMapScale{heatMapScale_},
addPartCandidates{addPartCandidates_},
renderThreshold{renderThreshold_},
numberPeopleMax{numberPeopleMax_},
enableGoogleLogging{enableGoogleLogging_},
reconstruct3d{reconstruct3d_},
identification{identification_}
{
}
......
......@@ -124,7 +124,6 @@
<ClInclude Include="..\..\include\openpose\core\wOpOutputToCvMat.hpp" />
<ClInclude Include="..\..\include\openpose\core\wScaleAndSizeExtractor.hpp" />
<ClInclude Include="..\..\include\openpose\experimental\3d\cameraParameters.hpp" />
<ClInclude Include="..\..\include\openpose\experimental\3d\datum3D.hpp" />
<ClInclude Include="..\..\include\openpose\experimental\3d\flirReader.hpp" />
<ClInclude Include="..\..\include\openpose\experimental\3d\headers.hpp" />
<ClInclude Include="..\..\include\openpose\experimental\3d\reconstruction3D.hpp" />
......
......@@ -557,9 +557,6 @@
<ClInclude Include="..\..\include\openpose\experimental\3d\cameraParameters.hpp">
<Filter>Header Files\experimental\3d</Filter>
</ClInclude>
<ClInclude Include="..\..\include\openpose\experimental\3d\datum3D.hpp">
<Filter>Header Files\experimental\3d</Filter>
</ClInclude>
<ClInclude Include="..\..\include\openpose\experimental\3d\flirReader.hpp">
<Filter>Header Files\experimental\3d</Filter>
</ClInclude>
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册