提交 6f39dca6 编写于 作者: G gineshidalgo99

Added floating heatmap saving

上级 aeca5cad
......@@ -78,7 +78,7 @@ See [doc/installation.md](doc/installation.md) for instructions on how to build
## Quick Start
Most users do not need the [OpenPose C++ API](#openpose-c-api), but they can simply use the basic [Demo](#demo) and/or [OpenPose Wrapper](#openpose-wrapper).
- **Demo**: To easily process images/video/webcam and display/save the results. See [doc/demo_overview.md](doc/demo_overview.md). E.g. run it in a video with:
- **Demo**: To easily process images/video/webcam and display/save the results. See [doc/demo_overview.md](doc/demo_overview.md). E.g. run OpenPose in a video with:
```
# Ubuntu
./build/examples/openpose/openpose.bin --video examples/media/video.avi
......
......@@ -22,6 +22,15 @@ See [doc/quick_start.md#quick-start](./quick_start.md#quick-start).
## Reducing Latency/Lag
In general, there are 3 ways to reduce the latency (with some drawbacks each one):
- Reducing `--output_resolution`: It will slightly reduce the latency and increase the FPS. But the quality of the displayed image will deteriorate.
- Reducing `--net_resolution` and/or `--face_net_resolution` and/or `--hand_net_resolution`: It will increase the FPS and reduce the latency. But the accuracy will drop, specially for small people in the image.
- Enabling `--disable_multi_thread`: The latency should be reduced. But the speed will drop to 1-GPU speed (as it will only use 1 GPU). Note that it's practical only for body, if hands and face are also extracted, it's usually not worth it.
## Kinect 2.0 as Webcam on Windows 10
Since the Windows 10 Anniversary, Kinect 2.0 can be read as a normal webcam. All you need to do is go to `device manager`, expand the `kinect sensor devices` tab, right click and update driver of `WDF kinectSensor Interface`. If you already have another webcam, disconnect it or use `--camera 2`.
......@@ -152,44 +161,47 @@ Each flag is divided into flag name, default value, and description.
- DEFINE_string(net_resolution, "-1x368", "Multiples of 16. If it is increased, the accuracy potentially increases. If it is decreased, the speed increases. For maximum speed-accuracy balance, it should keep the closest aspect ratio possible to the images or videos to be processed. Using `-1` in any of the dimensions, OP will choose the optimal resolution depending on the other value introduced by the user. E.g. the default `-1x368` is equivalent to `656x368` in 16:9 videos, e.g. full HD (1980x1080) and HD (1280x720) resolutions.");
- DEFINE_int32(scale_number, 1, "Number of scales to average.");
- DEFINE_double(scale_gap, 0.3, "Scale gap between scales. No effect unless scale_number > 1. Initial scale is always 1. If you want to change the initial scale, you actually want to multiply the `net_resolution` by your desired initial scale.");
5. OpenPose Body Pose Heatmaps
- DEFINE_bool(heatmaps_add_parts, false, "If true, it will add the body part heatmaps to the final op::Datum::poseHeatMaps array, and analogously face & hand heatmaps to op::Datum::faceHeatMaps & op::Datum::handHeatMaps (program speed will decrease). Not required for our library, enable it only if you intend to process this information later. If more than one `add_heatmaps_X` flag is enabled, it will place then in sequential memory order: body parts + bkg + PAFs. It will follow the order on POSE_BODY_PART_MAPPING in `include/openpose/pose/poseParameters.hpp`.");
- DEFINE_bool(heatmaps_add_bkg, false, "Same functionality as `add_heatmaps_parts`, but adding the heatmap corresponding to background.");
- DEFINE_bool(heatmaps_add_PAFs, false, "Same functionality as `add_heatmaps_parts`, but adding the PAFs.");
- DEFINE_int32(heatmaps_scale, 2, "Set 0 to scale op::Datum::poseHeatMaps in the range [0,1], 1 for [-1,1]; and 2 for integer rounded [0,255].");
5. OpenPose Face
6. OpenPose Face
- DEFINE_bool(face, false, "Enables face keypoint detection. It will share some parameters from the body pose, e.g. `model_folder`. Note that this will considerable slow down the performance and increse the required GPU memory. In addition, the greater number of people on the image, the slower OpenPose will be.");
- DEFINE_string(face_net_resolution, "368x368", "Multiples of 16 and squared. Analogous to `net_resolution` but applied to the face keypoint detector. 320x320 usually works fine while giving a substantial speed up when multiple faces on the image.");
6. OpenPose Hand
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_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");
7. OpenPose Rendering
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_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`.");
8. OpenPose Rendering Pose
9. OpenPose Rendering Pose
- DEFINE_double(render_threshold, 0.05, "Only estimated keypoints whose score confidences are higher than this threshold will be rendered. Generally, a high threshold (> 0.5) will only render very clear body parts; while small thresholds (~0.1) will also output guessed and occluded keypoints, but also more false positives (i.e. wrong detections).");
- DEFINE_int32(render_pose, 2, "Set to 0 for no rendering, 1 for CPU rendering (slightly faster), and 2 for GPU rendering (slower but greater functionality, e.g. `alpha_X` flags). If rendering is enabled, it will render both `outputData` and `cvOutputData` with the original image and desired body part to be shown (i.e. keypoints, heat maps or PAFs).");
- DEFINE_double(alpha_pose, 0.6, "Blending factor (range 0-1) for the body part rendering. 1 will show it completely, 0 will hide it. Only valid for GPU rendering.");
- DEFINE_double(alpha_heatmap, 0.7, "Blending factor (range 0-1) between heatmap and original frame. 1 will only show the heatmap, 0 will only show the frame. Only valid for GPU rendering.");
9. OpenPose Rendering Face
10. OpenPose Rendering Face
- DEFINE_double(face_render_threshold, 0.4, "Analogous to `render_threshold`, but applied to the face keypoints.");
- DEFINE_int32(face_render, -1, "Analogous to `render_pose` but applied to the face. Extra option: -1 to use the same configuration that `render_pose` is using.");
- DEFINE_double(face_alpha_pose, 0.6, "Analogous to `alpha_pose` but applied to face.");
- DEFINE_double(face_alpha_heatmap, 0.7, "Analogous to `alpha_heatmap` but applied to face.");
10. OpenPose Rendering Hand
11. OpenPose Rendering Hand
- DEFINE_double(hand_render_threshold, 0.2, "Analogous to `render_threshold`, but applied to the hand keypoints.");
- DEFINE_int32(hand_render, -1, "Analogous to `render_pose` but applied to the hand. Extra option: -1 to use the same configuration that `render_pose` is using.");
- DEFINE_double(hand_alpha_pose, 0.6, "Analogous to `alpha_pose` but applied to hand.");
- DEFINE_double(hand_alpha_heatmap, 0.7, "Analogous to `alpha_heatmap` but applied to hand.");
11. Display
12. Display
- DEFINE_bool(fullscreen, false, "Run in full-screen mode (press f during runtime to toggle).");
- DEFINE_bool(no_gui_verbose, false, "Do not write text on output images on GUI (e.g. number of current frame and people). It does not affect the pose rendering.");
- DEFINE_bool(no_display, false, "Do not open a display window. Useful if there is no X server and/or to slightly speed up the processing if visual output is not required.");
......@@ -202,4 +214,4 @@ Each flag is divided into flag name, default value, and description.
- DEFINE_string(write_keypoint_json, "", "Directory to write people pose data in *.json format, compatible with any OpenCV version.");
- DEFINE_string(write_coco_json, "", "Full file path to write people pose data with *.json COCO validation format.");
- DEFINE_string(write_heatmaps, "", "Directory to write body pose heatmaps in *.png format. At least 1 `add_heatmaps_X` flag must be enabled.");
- DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`. Recommended `png` or any compressed and lossless format.");
- DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`. For lossless compression, recommended `png` for integer `heatmaps_scale` and `float` for floating values.");
......@@ -26,7 +26,7 @@ In order to use and/or slightly extend the OpenPose library, we try to explain t
## Basic Module: `core`
### Array<T> - The OpenPose Basic Raw Data Container
This template class implements a multidimensional data array. It is our basic data container, analogous to `cv::Mat` in OpenCV, Tensor in Torch and TensorFlow or Blob in Caffe. It wraps a `cv::Mat` and a `boost::shared_ptr`, both of them pointing to the same raw data. I.e. they both share the same memory, so we can read this data in both formats, while there is no performance impact. For instance, `op::Datum` has several `op::Array<float>`, for instance the `op::Datum<float> pose` with the pose data.
This template class implements a multidimensional data array. It is our basic data container, analogous to `cv::Mat` in OpenCV, Tensor in Torch and TensorFlow or Blob in Caffe. It wraps a `cv::Mat` and a `std::shared_ptr`, both of them pointing to the same raw data. I.e. they both share the same memory, so we can read this data in both formats, while there is no performance impact. For instance, `op::Datum` has several `op::Array<float>`, for instance the `op::Datum<float> pose` with the pose data.
#### Construction And Data allocation
There are 4 different ways to allocate the memory:
......@@ -35,7 +35,7 @@ There are 4 different ways to allocate the memory:
2. The constructor `Array(const int size)`, which calls `reset(size)`.
3. The `reset(const std::vector<int>& size)` function: It allocates the memory indicated for size. The allocated memory equals the product of all elements in the size vector. Internally, it is saved as a 1-D boost::shared_ptr<T[]>.
3. The `reset(const std::vector<int>& size)` function: It allocates the memory indicated for size. The allocated memory equals the product of all elements in the size vector. Internally, it is saved as a 1-D std::shared_ptr<T[]>.
4. The `reset(const int size)` function: equivalent for 1-dimension data (i.e. vector).
......@@ -48,7 +48,7 @@ The data can be access as a raw pointer, shared pointer or `cv::Mat`. So given y
2. As `const cv::Mat`: `array.getConstCvMat()`. We do not allow to directly modify the `cv::Mat`, since some operations might change the dimensional size of the data. If you want to do so, you can clone this `cv::Mat`, perform any desired operation, and copy it back to the array class with `setFrom()`.
3. As raw pointer: `T* getPtr()` and `const T* const getConstPtr()`. Similar to std:: and boost::shared_ptr::get(). For instance, CUDA code usually requires raw pointers to access its data.
3. As raw pointer: `T* getPtr()` and `const T* const getConstPtr()`. Similar to std:: and std::shared_ptr::get(). For instance, CUDA code usually requires raw pointers to access its data.
#### Dimensionality Information
There are several functions to get information about the allocated data:
......
......@@ -152,5 +152,7 @@ OpenPose Library - Release Notes
## Current version (future OpenPose 1.2.1)
3. Main bugs fixed:
1. Main improvements:
1. Heatmaps can be saved in floating format.
2. Main bugs fixed:
1. Render working on images > 4K (#324).
......@@ -87,6 +87,7 @@ DEFINE_int32(scale_number, 1, "Number of scales to ave
DEFINE_double(scale_gap, 0.3, "Scale gap between scales. No effect unless scale_number > 1. Initial scale is always 1."
" If you want to change the initial scale, you actually want to multiply the"
" `net_resolution` by your desired initial scale.");
// OpenPose Body Pose Heatmaps
DEFINE_bool(heatmaps_add_parts, false, "If true, it will add the body part heatmaps to the final op::Datum::poseHeatMaps array,"
" and analogously face & hand heatmaps to op::Datum::faceHeatMaps & op::Datum::handHeatMaps"
" (program speed will decrease). Not required for our library, enable it only if you intend"
......@@ -96,6 +97,8 @@ DEFINE_bool(heatmaps_add_parts, false, "If true, it will add th
DEFINE_bool(heatmaps_add_bkg, false, "Same functionality as `add_heatmaps_parts`, but adding the heatmap corresponding to"
" background.");
DEFINE_bool(heatmaps_add_PAFs, false, "Same functionality as `add_heatmaps_parts`, but adding the PAFs.");
DEFINE_int32(heatmaps_scale, 2, "Set 0 to scale op::Datum::poseHeatMaps in the range [0,1], 1 for [-1,1]; and 2 for integer"
" rounded [0,255].");
// OpenPose Face
DEFINE_bool(face, false, "Enables face keypoint detection. It will share some parameters from the body pose, e.g."
" `model_folder`. Note that this will considerable slow down the performance and increse"
......@@ -171,7 +174,8 @@ DEFINE_string(write_coco_json, "", "Full file path to write
DEFINE_string(write_heatmaps, "", "Directory to write body pose heatmaps in *.png format. At least 1 `add_heatmaps_X` flag"
" must be enabled.");
DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`."
" Recommended `png` or any compressed and lossless format.");
" For lossless compression, recommended `png` for integer `heatmaps_scale` and `float` for"
" floating values.");
int openPoseDemo()
{
......@@ -203,6 +207,7 @@ int openPoseDemo()
// heatmaps to add
const auto heatMapTypes = op::flagsToHeatMaps(FLAGS_heatmaps_add_parts, FLAGS_heatmaps_add_bkg,
FLAGS_heatmaps_add_PAFs);
const auto heatMapScale = op::flagsToHeatMapScaleMode(FLAGS_heatmaps_scale);
// Enabling Google Logging
const bool enableGoogleLogging = true;
// Logging
......@@ -217,9 +222,8 @@ int openPoseDemo()
(float)FLAGS_scale_gap, op::flagsToRenderMode(FLAGS_render_pose),
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, op::ScaleMode::UnsignedChar,
(float)FLAGS_render_threshold, enableGoogleLogging,
FLAGS_identification};
heatMapTypes, heatMapScale, (float)FLAGS_render_threshold,
enableGoogleLogging, 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),
......
......@@ -96,6 +96,7 @@ DEFINE_int32(scale_number, 1, "Number of scales to ave
DEFINE_double(scale_gap, 0.3, "Scale gap between scales. No effect unless scale_number > 1. Initial scale is always 1."
" If you want to change the initial scale, you actually want to multiply the"
" `net_resolution` by your desired initial scale.");
// OpenPose Body Pose Heatmaps
DEFINE_bool(heatmaps_add_parts, false, "If true, it will add the body part heatmaps to the final op::Datum::poseHeatMaps array,"
" and analogously face & hand heatmaps to op::Datum::faceHeatMaps & op::Datum::handHeatMaps"
" (program speed will decrease). Not required for our library, enable it only if you intend"
......@@ -182,7 +183,8 @@ DEFINE_string(write_coco_json, "", "Full file path to write
DEFINE_string(write_heatmaps, "", "Directory to write body pose heatmaps in *.png format. At least 1 `add_heatmaps_X` flag"
" must be enabled.");
DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`."
" Recommended `png` or any compressed and lossless format.");
" For lossless compression, recommended `png` for integer `heatmaps_scale` and `float` for"
" floating values.");
int openPoseTutorialWrapper4()
{
......@@ -214,6 +216,7 @@ int openPoseTutorialWrapper4()
// heatmaps to add
const auto heatMapTypes = op::flagsToHeatMaps(FLAGS_heatmaps_add_parts, FLAGS_heatmaps_add_bkg,
FLAGS_heatmaps_add_PAFs);
const auto heatMapScale = op::flagsToHeatMapScaleMode(FLAGS_heatmaps_scale);
// Enabling Google Logging
const bool enableGoogleLogging = true;
// Logging
......@@ -228,8 +231,8 @@ int openPoseTutorialWrapper4()
(float)FLAGS_scale_gap, op::flagsToRenderMode(FLAGS_render_pose),
poseModel, !FLAGS_disable_blending, (float)FLAGS_alpha_pose,
(float)FLAGS_alpha_heatmap, FLAGS_part_to_show, FLAGS_model_folder,
heatMapTypes, op::ScaleMode::UnsignedChar,
(float)FLAGS_render_threshold, enableGoogleLogging};
heatMapTypes, heatMapScale, (float)FLAGS_render_threshold,
enableGoogleLogging};
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{FLAGS_face, faceNetInputSize,
op::flagsToRenderMode(FLAGS_face_render, FLAGS_render_pose),
......
......@@ -86,6 +86,7 @@ DEFINE_int32(scale_number, 1, "Number of scales to ave
DEFINE_double(scale_gap, 0.3, "Scale gap between scales. No effect unless scale_number > 1. Initial scale is always 1."
" If you want to change the initial scale, you actually want to multiply the"
" `net_resolution` by your desired initial scale.");
// OpenPose Body Pose Heatmaps
DEFINE_bool(heatmaps_add_parts, false, "If true, it will add the body part heatmaps to the final op::Datum::poseHeatMaps array,"
" and analogously face & hand heatmaps to op::Datum::faceHeatMaps & op::Datum::handHeatMaps"
" (program speed will decrease). Not required for our library, enable it only if you intend"
......@@ -166,7 +167,8 @@ DEFINE_string(write_coco_json, "", "Full file path to write
DEFINE_string(write_heatmaps, "", "Directory to write body pose heatmaps in *.png format. At least 1 `add_heatmaps_X` flag"
" must be enabled.");
DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`."
" Recommended `png` or any compressed and lossless format.");
" For lossless compression, recommended `png` for integer `heatmaps_scale` and `float` for"
" floating values.");
// If the user needs his own variables, he can inherit the op::Datum struct and add them
......@@ -290,10 +292,7 @@ int openPoseTutorialWrapper1()
// heatmaps to add
const auto heatMapTypes = op::flagsToHeatMaps(FLAGS_heatmaps_add_parts, FLAGS_heatmaps_add_bkg,
FLAGS_heatmaps_add_PAFs);
op::check(FLAGS_heatmaps_scale >= 0 && FLAGS_heatmaps_scale <= 2, "Non valid `heatmaps_scale`.",
__LINE__, __FUNCTION__, __FILE__);
const auto heatMapScale = (FLAGS_heatmaps_scale == 0 ? op::ScaleMode::PlusMinusOne
: (FLAGS_heatmaps_scale == 1 ? op::ScaleMode::ZeroToOne : op::ScaleMode::UnsignedChar ));
const auto heatMapScale = op::flagsToHeatMapScaleMode(FLAGS_heatmaps_scale);
// Enabling Google Logging
const bool enableGoogleLogging = true;
// Logging
......
......@@ -69,6 +69,7 @@ DEFINE_int32(scale_number, 1, "Number of scales to ave
DEFINE_double(scale_gap, 0.3, "Scale gap between scales. No effect unless scale_number > 1. Initial scale is always 1."
" If you want to change the initial scale, you actually want to multiply the"
" `net_resolution` by your desired initial scale.");
// OpenPose Body Pose Heatmaps
DEFINE_bool(heatmaps_add_parts, false, "If true, it will add the body part heatmaps to the final op::Datum::poseHeatMaps array,"
" and analogously face & hand heatmaps to op::Datum::faceHeatMaps & op::Datum::handHeatMaps"
" (program speed will decrease). Not required for our library, enable it only if you intend"
......@@ -149,7 +150,8 @@ DEFINE_string(write_coco_json, "", "Full file path to write
DEFINE_string(write_heatmaps, "", "Directory to write body pose heatmaps in *.png format. At least 1 `add_heatmaps_X` flag"
" must be enabled.");
DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`."
" Recommended `png` or any compressed and lossless format.");
" For lossless compression, recommended `png` for integer `heatmaps_scale` and `float` for"
" floating values.");
// If the user needs his own variables, he can inherit the op::Datum struct and add them
......@@ -370,10 +372,7 @@ int openPoseTutorialWrapper2()
// heatmaps to add
const auto heatMapTypes = op::flagsToHeatMaps(FLAGS_heatmaps_add_parts, FLAGS_heatmaps_add_bkg,
FLAGS_heatmaps_add_PAFs);
op::check(FLAGS_heatmaps_scale >= 0 && FLAGS_heatmaps_scale <= 2, "Non valid `heatmaps_scale`.",
__LINE__, __FUNCTION__, __FILE__);
const auto heatMapScale = (FLAGS_heatmaps_scale == 0 ? op::ScaleMode::PlusMinusOne
: (FLAGS_heatmaps_scale == 1 ? op::ScaleMode::ZeroToOne : op::ScaleMode::UnsignedChar ));
const auto heatMapScale = op::flagsToHeatMapScaleMode(FLAGS_heatmaps_scale);
// Enabling Google Logging
const bool enableGoogleLogging = true;
// Logging
......
......@@ -69,6 +69,7 @@ DEFINE_int32(scale_number, 1, "Number of scales to ave
DEFINE_double(scale_gap, 0.3, "Scale gap between scales. No effect unless scale_number > 1. Initial scale is always 1."
" If you want to change the initial scale, you actually want to multiply the"
" `net_resolution` by your desired initial scale.");
// OpenPose Body Pose Heatmaps
DEFINE_bool(heatmaps_add_parts, false, "If true, it will add the body part heatmaps to the final op::Datum::poseHeatMaps array,"
" and analogously face & hand heatmaps to op::Datum::faceHeatMaps & op::Datum::handHeatMaps"
" (program speed will decrease). Not required for our library, enable it only if you intend"
......@@ -149,7 +150,8 @@ DEFINE_string(write_coco_json, "", "Full file path to write
DEFINE_string(write_heatmaps, "", "Directory to write body pose heatmaps in *.png format. At least 1 `add_heatmaps_X` flag"
" must be enabled.");
DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`."
" Recommended `png` or any compressed and lossless format.");
" For lossless compression, recommended `png` for integer `heatmaps_scale` and `float` for"
" floating values.");
// If the user needs his own variables, he can inherit the op::Datum struct and add them
......@@ -328,10 +330,7 @@ int openPoseTutorialWrapper3()
// heatmaps to add
const auto heatMapTypes = op::flagsToHeatMaps(FLAGS_heatmaps_add_parts, FLAGS_heatmaps_add_bkg,
FLAGS_heatmaps_add_PAFs);
op::check(FLAGS_heatmaps_scale >= 0 && FLAGS_heatmaps_scale <= 2, "Non valid `heatmaps_scale`.",
__LINE__, __FUNCTION__, __FILE__);
const auto heatMapScale = (FLAGS_heatmaps_scale == 0 ? op::ScaleMode::PlusMinusOne
: (FLAGS_heatmaps_scale == 1 ? op::ScaleMode::ZeroToOne : op::ScaleMode::UnsignedChar ));
const auto heatMapScale = op::flagsToHeatMapScaleMode(FLAGS_heatmaps_scale);
// Enabling Google Logging
const bool enableGoogleLogging = true;
// Logging
......
......@@ -69,6 +69,7 @@ DEFINE_int32(scale_number, 1, "Number of scales to ave
DEFINE_double(scale_gap, 0.3, "Scale gap between scales. No effect unless scale_number > 1. Initial scale is always 1."
" If you want to change the initial scale, you actually want to multiply the"
" `net_resolution` by your desired initial scale.");
// OpenPose Body Pose Heatmaps
DEFINE_bool(heatmaps_add_parts, false, "If true, it will add the body part heatmaps to the final op::Datum::poseHeatMaps array,"
" and analogously face & hand heatmaps to op::Datum::faceHeatMaps & op::Datum::handHeatMaps"
" (program speed will decrease). Not required for our library, enable it only if you intend"
......@@ -150,7 +151,8 @@ DEFINE_string(write_coco_json, "", "Full file path to write
DEFINE_string(write_heatmaps, "", "Directory to write body pose heatmaps in *.png format. At least 1 `add_heatmaps_X` flag"
" must be enabled.");
DEFINE_string(write_heatmaps_format, "png", "File extension and format for `write_heatmaps`, analogous to `write_images_format`."
" Recommended `png` or any compressed and lossless format.");
" For lossless compression, recommended `png` for integer `heatmaps_scale` and `float` for"
" floating values.");
int openpose3d()
{
......@@ -179,10 +181,7 @@ int openpose3d()
// heatmaps to add
const auto heatMapTypes = op::flagsToHeatMaps(FLAGS_heatmaps_add_parts, FLAGS_heatmaps_add_bkg,
FLAGS_heatmaps_add_PAFs);
op::check(FLAGS_heatmaps_scale >= 0 && FLAGS_heatmaps_scale <= 2, "Non valid `heatmaps_scale`.",
__LINE__, __FUNCTION__, __FILE__);
const auto heatMapScale = (FLAGS_heatmaps_scale == 0 ? op::ScaleMode::PlusMinusOne
: (FLAGS_heatmaps_scale == 1 ? op::ScaleMode::ZeroToOne : op::ScaleMode::UnsignedChar ));
const auto heatMapScale = op::flagsToHeatMapScaleMode(FLAGS_heatmaps_scale);
// Enabling Google Logging
const bool enableGoogleLogging = true;
// Logging
......
......@@ -11,7 +11,7 @@ namespace op
* Array<T>: The OpenPose Basic Raw Data Container
* This template class implements a multidimensional data array. It is our basic data container, analogous to cv::Mat in OpenCV, Tensor in
* Torch/TensorFlow or Blob in Caffe.
* It wraps a cv::Mat and a boost::shared_ptr, both of them pointing to the same raw data. I.e. they both share the same memory, so we can read
* It wraps a cv::Mat and a std::shared_ptr, both of them pointing to the same raw data. I.e. they both share the same memory, so we can read
* and modify this data in both formats with no performance impact.
* Hence, it keeps high performance while adding high-level functions.
*/
......@@ -192,7 +192,7 @@ namespace op
// -------------------------------------------------- Data Access Functions And Operators -------------------------------------------------- //
/**
* Return a raw pointer to the data. Similar to: boost::shared_ptr::get().
* Return a raw pointer to the data. Similar to: std::shared_ptr::get().
* Note: if you modify the pointer data, you will directly modify it in the Array<T> instance too.
* If you know you do not want to modify the data, then use getConstPtr() instead.
* @return A raw pointer to the data.
......@@ -373,7 +373,7 @@ namespace op
T& commonAt(const int index) const;
/**
* Private auxiliar function that sets the cv::Mat wrapper and makes it point to the same data than boost::shared_ptr points to.
* Private auxiliar function that sets the cv::Mat wrapper and makes it point to the same data than std::shared_ptr points to.
*/
void setCvMatFromSharedPtr();
};
......
......@@ -62,7 +62,7 @@ namespace caffe
}
namespace boost
{
template <typename T> class shared_ptr;
template <typename T> class shared_ptr; // E.g., boost::shared_ptr<caffe::Blob<float>>
}
// Includes at the end, since this macros class does not need them, but the files that call this
......
......@@ -10,23 +10,43 @@ namespace op
{
OP_API DataFormat stringToDataFormat(const std::string& dataFormat);
// Save custom float format
// Example to read it in Python, assuming a (18 x 300 x 500) size Array
// x = np.fromfile(heatMapFullPath, dtype=np.float32)
// assert x[0] == 3 # First parameter saves the number of dimensions (18x300x500 = 3 dimensions)
// shape_x = x[1:x[0]]
// assert len(shape_x[0]) == 3 # Number of dimensions
// assert shape_x[0] == 18 # Size of the first dimension
// assert shape_x[1] == 300 # Size of the second dimension
// assert shape_x[2] == 500 # Size of the third dimension
// arrayData = x[1+int(round(x[0])):]
OP_API void saveFloatArray(const Array<float>& array, const std::string& fullFilePath);
// Save/load json, xml, yaml, yml
OP_API void saveData(const std::vector<cv::Mat>& cvMats, const std::vector<std::string>& cvMatNames, const std::string& fileNameNoExtension, const DataFormat format);
OP_API void saveData(const std::vector<cv::Mat>& cvMats, const std::vector<std::string>& cvMatNames,
const std::string& fileNameNoExtension, const DataFormat format);
OP_API void saveData(const cv::Mat& cvMat, const std::string cvMatName, const std::string& fileNameNoExtension, const DataFormat format);
OP_API void saveData(const cv::Mat& cvMat, const std::string cvMatName, const std::string& fileNameNoExtension,
const DataFormat format);
OP_API std::vector<cv::Mat> loadData(const std::vector<std::string>& cvMatNames, const std::string& fileNameNoExtension, const DataFormat format);
OP_API std::vector<cv::Mat> loadData(const std::vector<std::string>& cvMatNames,
const std::string& fileNameNoExtension, const DataFormat format);
OP_API cv::Mat loadData(const std::string& cvMatName, const std::string& fileNameNoExtension, const DataFormat format);
OP_API cv::Mat loadData(const std::string& cvMatName, const std::string& fileNameNoExtension,
const DataFormat format);
// Json - Saving as *.json not available in OpenCV verions < 3.0, this function is a quick fix
OP_API void saveKeypointsJson(const Array<float>& keypoints, const std::string& keypointName, const std::string& fileName, const bool humanReadable);
OP_API void saveKeypointsJson(const Array<float>& keypoints, const std::string& keypointName,
const std::string& fileName, const bool humanReadable);
// It will save a bunch of Array<float> elements
OP_API void saveKeypointsJson(const std::vector<std::pair<Array<float>, std::string>>& keypointVector, const std::string& fileName, const bool humanReadable);
OP_API void saveKeypointsJson(const std::vector<std::pair<Array<float>, std::string>>& keypointVector,
const std::string& fileName, const bool humanReadable);
// Save/load image
OP_API void saveImage(const cv::Mat& cvMat, const std::string& fullFilePath, const std::vector<int>& openCvCompressionParams = {CV_IMWRITE_JPEG_QUALITY, 100, CV_IMWRITE_PNG_COMPRESSION, 9});
OP_API void saveImage(const cv::Mat& cvMat, const std::string& fullFilePath,
const std::vector<int>& openCvCompressionParams
= {CV_IMWRITE_JPEG_QUALITY, 100, CV_IMWRITE_PNG_COMPRESSION, 9});
OP_API cv::Mat loadImage(const std::string& fullFilePath, const int openCvFlags = CV_LOAD_IMAGE_ANYDEPTH);
......
......@@ -49,16 +49,18 @@ namespace op
* @param extensions std::vector<std::string> with the extensions of the desired files.
* @return std::vector<std::string> with the existing file names.
*/
OP_API std::vector<std::string> getFilesOnDirectory(const std::string& directoryPath, const std::vector<std::string>& extensions = {});
OP_API std::vector<std::string> getFilesOnDirectory(const std::string& directoryPath,
const std::vector<std::string>& extensions = {});
/**
* Analogous to getFilesOnDirectory(const std::string& directoryPath, const std::vector<std::string>& extensions) for 1 specific
* extension.
* Analogous to getFilesOnDirectory(const std::string& directoryPath, const std::vector<std::string>& extensions)
* for 1 specific extension.
* @param directoryPath std::string with the directory path.
* @param extension std::string with the extension of the desired files.
* @return std::vector<std::string> with the existing file names.
*/
OP_API std::vector<std::string> getFilesOnDirectory(const std::string& directoryPath, const std::string& extension);
OP_API std::vector<std::string> getFilesOnDirectory(const std::string& directoryPath,
const std::string& extension);
}
#endif // OPENPOSE_UTILITIES_FILE_SYSTEM_HPP
......@@ -12,6 +12,8 @@ namespace op
OP_API ScaleMode flagsToScaleMode(const int keypointScale);
OP_API ScaleMode flagsToHeatMapScaleMode(const int heatMapScale);
// Determine type of frame source
OP_API ProducerType flagsToProducerType(const std::string& imageDirectory, const std::string& videoPath,
const std::string& ipCameraPath, const int webcamIndex);
......
......@@ -26,6 +26,32 @@ namespace op
return false;
}
}
/**
* std::vector<T> concatenator.
* Auxiliary function that concatenate std::vectors of any class type T.
* It assumes basic copy (ideal for smart pointers, pointers, etc.), so note that the copy still shares the same
* internal data. It will not work for element that cannot be copied.
* @param vectorA First std::shared_ptr<T> element to be concatenated.
* @param vectorB Second std::shared_ptr<T> element to be concatenated.
* @return Concatenated std::vector<T> of both vectorA and vectorB.
*/
template <typename T>
std::vector<T> mergeVectors(const std::vector<T>& vectorA, const std::vector<T>& vectorB)
{
try
{
auto vectorToReturn(vectorA);
for (auto& tElement : vectorB)
vectorToReturn.emplace_back(tElement);
return vectorToReturn;
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return std::vector<T>{};
}
}
}
#endif // OPENPOSE_UTILITIES_STANDARD_HPP
......@@ -249,17 +249,6 @@ namespace op
*/
unsigned long long threadIdPP();
/**
* TWorker concatenator (private internal function).
* Auxiliary function that concatenate std::vectors of TWorker. Since TWorker is some kind of smart pointer
* (usually std::shared_ptr), its copy still shares the same internal data. It will not work for TWorker
* classes that do not share the data when moved.
* @param workersA First std::shared_ptr<TDatums> element to be concatenated.
* @param workersB Second std::shared_ptr<TDatums> element to be concatenated.
* @return Concatenated std::vector<TWorker> of both workersA and workersB.
*/
std::vector<TWorker> mergeWorkers(const std::vector<TWorker>& workersA, const std::vector<TWorker>& workersB);
DELETE_COPY(Wrapper);
};
}
......@@ -279,6 +268,7 @@ namespace op
#include <openpose/experimental/tracking/headers.hpp>
#include <openpose/utilities/cuda.hpp>
#include <openpose/utilities/fileSystem.hpp>
#include <openpose/utilities/standard.hpp>
namespace op
{
template<typename TDatums, typename TWorker, typename TQueue>
......@@ -476,10 +466,13 @@ namespace op
error(message, __LINE__, __FUNCTION__, __FILE__);
}
if (!wrapperStructOutput.writeHeatMaps.empty()
&& wrapperStructPose.heatMapScale != ScaleMode::UnsignedChar)
&& (wrapperStructPose.heatMapScale != ScaleMode::UnsignedChar &&
wrapperStructOutput.writeHeatMapsFormat != "float"))
{
const auto message = "In order to save the heatmaps, you must set wrapperStructPose.heatMapScale to"
" ScaleMode::UnsignedChar, i.e. range [0, 255].";
const auto message = "In order to save the heatmaps, you must either set"
" wrapperStructPose.heatMapScale to ScaleMode::UnsignedChar (i.e. range [0, 255])"
" or `write_heatmaps_format` to `float` to storage floating numbers in binary"
" mode.";
error(message, __LINE__, __FUNCTION__, __FILE__);
}
if (mUserOutputWs.empty() && mThreadManagerMode != ThreadManagerMode::Asynchronous
......@@ -828,7 +821,7 @@ namespace op
// Frames processor (OpenPose format -> cv::Mat format)
if (renderOutput)
{
mPostProcessingWs = mergeWorkers(mPostProcessingWs, cpuRenderers);
mPostProcessingWs = mergeVectors(mPostProcessingWs, cpuRenderers);
const auto opOutputToCvMat = std::make_shared<OpOutputToCvMat>();
mPostProcessingWs.emplace_back(std::make_shared<WOpOutputToCvMat<TDatumsPtr>>(opOutputToCvMat));
}
......@@ -1175,20 +1168,20 @@ namespace op
std::vector<TWorker> workersAux;
// Custom user Worker
if (!mUserInputWs.empty())
workersAux = mergeWorkers(workersAux, mUserInputWs);
workersAux = mergeVectors(workersAux, mUserInputWs);
// OpenPose producer
else if (wDatumProducer != nullptr)
workersAux = mergeWorkers(workersAux, {wDatumProducer});
workersAux = mergeVectors(workersAux, {wDatumProducer});
// Otherwise
else if (mThreadManagerMode != ThreadManagerMode::Asynchronous
&& mThreadManagerMode != ThreadManagerMode::AsynchronousIn)
error("No input selected.", __LINE__, __FUNCTION__, __FILE__);
if (spWCvMatToOpOutput == nullptr)
workersAux = mergeWorkers(workersAux, {spWIdGenerator, spWScaleAndSizeExtractor,
workersAux = mergeVectors(workersAux, {spWIdGenerator, spWScaleAndSizeExtractor,
spWCvMatToOpInput});
else
workersAux = mergeWorkers(workersAux, {spWIdGenerator, spWScaleAndSizeExtractor,
workersAux = mergeVectors(workersAux, {spWIdGenerator, spWScaleAndSizeExtractor,
spWCvMatToOpInput, spWCvMatToOpOutput});
// Thread 0 or 1, queues 0 -> 1
mThreadManager.add(mThreadId, workersAux, queueIn++, queueOut++);
......@@ -1243,8 +1236,8 @@ namespace op
else
{
// Post processing workers + User post processing workers + Output workers
auto workersAux = mergeWorkers(mPostProcessingWs, mUserPostProcessingWs);
workersAux = mergeWorkers(workersAux, mOutputWs);
auto workersAux = mergeVectors(mPostProcessingWs, mUserPostProcessingWs);
workersAux = mergeVectors(workersAux, mOutputWs);
if (!workersAux.empty())
{
// Thread 2 or 3, queues 2 -> 3
......@@ -1295,24 +1288,6 @@ namespace op
}
}
template<typename TDatums, typename TWorker, typename TQueue>
std::vector<TWorker> Wrapper<TDatums, TWorker, TQueue>::mergeWorkers(const std::vector<TWorker>& workersA,
const std::vector<TWorker>& workersB)
{
try
{
auto workersToReturn(workersA);
for (auto& worker : workersB)
workersToReturn.emplace_back(worker);
return workersToReturn;
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return std::vector<TWorker>{};
}
}
extern template class Wrapper<DATUM_BASE_NO_PTR>;
}
......
......@@ -371,10 +371,10 @@ namespace op
// Initial value
std::string string{"Array<T>::toString():\n"};
// Add each element
auto* dataPtr = spData.get();
const auto* dataPtr = spData.get();
for (auto i = 0u ; i < mVolume ; i++)
{
// Adding element sepearted by an space
// Adding element separated by a space
string += std::to_string(dataPtr[i]) + " ";
// Introduce an enter for each dimension change
// If comented, all values will be printed in the same line
......
#include <fstream> // std::ifstream
#include <fstream> // std::ifstream, std::ofstream
#include <opencv2/highgui/highgui.hpp> // cv::imread
#include <openpose/utilities/fastMath.hpp>
#include <openpose/utilities/fileSystem.hpp>
......@@ -71,6 +71,33 @@ namespace op
}
}
void saveFloatArray(const Array<float>& array, const std::string& fullFilePath)
{
try
{
// Open file
std::ofstream outputFile;
outputFile.open(fullFilePath, std::ios::binary);
// Save #dimensions
const auto numberDimensions = (float)(array.getNumberDimensions());
outputFile.write((char*)&numberDimensions, sizeof(float));
// Save dimensions
for (const auto& sizeI : array.getSize())
{
const float sizeIFloat = (float) sizeI;
outputFile.write((char*)&sizeIFloat, sizeof(float));
}
// Save each value
outputFile.write((char*)&array[0], array.getVolume() * sizeof(float));
// Close file
outputFile.close();
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
void saveData(const std::vector<cv::Mat>& cvMats, const std::vector<std::string>& cvMatNames, const std::string& fileNameNoExtension, const DataFormat format)
{
try
......@@ -217,7 +244,8 @@ namespace op
}
}
void saveImage(const cv::Mat& cvMat, const std::string& fullFilePath, const std::vector<int>& openCvCompressionParams)
void saveImage(const cv::Mat& cvMat, const std::string& fullFilePath,
const std::vector<int>& openCvCompressionParams)
{
try
{
......
......@@ -34,14 +34,28 @@ namespace op
for (auto i = 0u; i < fileNames.size(); i++)
fileNames[i] = {fileNameNoExtension + (i != 0 ? "_" + std::to_string(i) : "") + "." + mImageFormat};
// heatMaps -> cvOutputDatas
std::vector<cv::Mat> cvOutputDatas(heatMaps.size());
for (auto i = 0u; i < cvOutputDatas.size(); i++)
unrollArrayToUCharCvMat(cvOutputDatas[i], heatMaps[i]);
// Save each heatMap
for (auto i = 0u; i < cvOutputDatas.size(); i++)
saveImage(cvOutputDatas[i], fileNames[i]);
// Saving on custom floating type "float". Format it:
// First, the number of dimensions of the array.
// Next elements: the size of each dimension.
// Next: all the elements.
if (mImageFormat == "float")
{
if (heatMaps.size() > 1)
error("Float only implemented for heatMaps.size() == 1.", __LINE__, __FUNCTION__, __FILE__);
for (auto i = 0u; i < heatMaps.size(); i++)
saveFloatArray(heatMaps[i], fileNames[i]);
}
// Saving on integer type (jpg, png, etc.)
else
{
// heatMaps -> cvOutputDatas
std::vector<cv::Mat> cvOutputDatas(heatMaps.size());
for (auto i = 0u; i < cvOutputDatas.size(); i++)
unrollArrayToUCharCvMat(cvOutputDatas[i], heatMaps[i]);
// Save each heatMap
for (auto i = 0u; i < cvOutputDatas.size(); i++)
saveImage(cvOutputDatas[i], fileNames[i]);
}
}
}
catch (const std::exception& e)
......
......@@ -169,7 +169,8 @@ namespace op
}
}
std::vector<std::string> getFilesOnDirectory(const std::string& directoryPath, const std::vector<std::string>& extensions)
std::vector<std::string> getFilesOnDirectory(const std::string& directoryPath,
const std::vector<std::string>& extensions)
{
try
{
......
......@@ -53,7 +53,7 @@ namespace op
else if (keypointScale == 4)
return ScaleMode::PlusMinusOne;
// else
const std::string message = "String does not correspond to any scale mode: (0, 1, 2, 3, 4) for"
const std::string message = "Integer does not correspond to any scale mode: (0, 1, 2, 3, 4) for"
" (InputResolution, NetOutputResolution, OutputResolution, ZeroToOne,"
" PlusMinusOne).";
error(message, __LINE__, __FUNCTION__, __FILE__);
......@@ -66,6 +66,30 @@ namespace op
}
}
ScaleMode flagsToHeatMapScaleMode(const int heatMapScale)
{
try
{
log("", Priority::Low, __LINE__, __FUNCTION__, __FILE__);
if (heatMapScale == 0)
return ScaleMode::PlusMinusOne;
else if (heatMapScale == 1)
return ScaleMode::ZeroToOne;
else if (heatMapScale == 2)
return ScaleMode::UnsignedChar;
// else
const std::string message = "Integer does not correspond to any scale mode: (0, 1, 2) for"
" (PlusMinusOne, ZeroToOne, UnsignedChar).";
error(message, __LINE__, __FUNCTION__, __FILE__);
return ScaleMode::PlusMinusOne;
}
catch (const std::exception& e)
{
error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return ScaleMode::PlusMinusOne;
}
}
ProducerType flagsToProducerType(const std::string& imageDirectory, const std::string& videoPath,
const std::string& ipCameraPath, const int webcamIndex)
{
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册