提交 56ac91f2 编写于 作者: G Gines Hidalgo Martinez

3D reconstruction demo with hands & faces

上级 6fd758c2
......@@ -49,8 +49,8 @@ The pose estimation work is based on the C++ code from [the ECCV 2016 demo](http
## Operating Systems
1. **Ubuntu** 14 and 16.
2. **Windows** 10.
3. Other people have been able to install it on **Windows 7 and 8**, **Mac**, **CentOS**, and **Nvidia Jetson (TK1 and TX1)** embedded systems. However, we do not officially support them at the moment.
2. **Windows** 8 and 10.
3. OpenPose has also been used on **Windows 7**, **Mac**, **CentOS**, and **Nvidia Jetson (TK1 and TX1)** embedded systems. However, we do not officially support them at the moment.
......
......@@ -131,10 +131,7 @@ You just need to remove the OpenPose or portable demo folder.
## OpenPose 3D Demo
This is a beta version that makes body pose 3-D reconstruction and rendering. We will not keep updating it nor solving questions/issues about it at the moment. It requires the user to be familiar with camera calibration, i.e. extraction of intrinsic and extrinsic parameters. The Windows steps were tested and worked in the OpenPose 1.0.1 version from the first GitHub commit on July 17th, 2017 in the [oficial repository](https://github.com/CMU-Perceptual-Computing-Lab/openpose).
If you still wanna try our OpenPose 3-D reconstruction demo, see [doc/openpose_3d_reconstruction_demo.md](./openpose_3d_reconstruction_demo.md).
If you want to try our OpenPose 3-D reconstruction demo, see [doc/openpose_3d_reconstruction_demo.md](./openpose_3d_reconstruction_demo.md).
......@@ -205,6 +202,23 @@ windows\x64\Release\OpenPoseDemo.exe --image_dir examples\media\ --face --hand
**4. Maximum Accuracy Configuration**
This command provides the most accurate results we have been able to achieve for body, hand and face keypoint detection. However, this command will need around 8 GB of GPU memory and runs around 1 FPS on a Titan X.
```
# Ubuntu
./build/examples/openpose/openpose.bin --net_resolution "1312x736" --scale_number 4 --scale_gap 0.25 --hand --hand_scale_number 6 --hand_scale_range 0.4 --face
```
```
:: Windows - Demo
bin\OpenPoseDemo.exe --net_resolution "1312x736" --scale_number 4 --scale_gap 0.25 --hand --hand_scale_number 6 --hand_scale_range 0.4 --face
```
:: Windows - Library
windows\x64\Release\OpenPoseDemo.exe --net_resolution "1312x736" --scale_number 4 --scale_gap 0.25 --hand --hand_scale_number 6 --hand_scale_range 0.4 --face
```
## Expected Visual Results
......
# Running OpenPose 3-D Reconstruction Demo - Windows
This is a beta version that makes body pose 3-D reconstruction and rendering. We will not keep updating it nor solving questions/issues about it at the moment. It requires the user to be familiar with computer vision, in particular with camera calibration, i.e. extraction of intrinsic and extrinsic parameters.
Running OpenPose 3-D Reconstruction Demo
====================================
The Windows steps were tested and worked in the OpenPose 1.0.1 version from the last GitHub commit on July 7th, 2017 in the [oficial repository](https://github.com/CMU-Perceptual-Computing-Lab/openpose).
This is a beta version that makes body + face + hand keypoint 3-D reconstruction and rendering for 1 person. We will not keep updating it nor solving questions/issues about it at the moment. It requires the user to be familiar with computer vision, in particular with camera calibration, i.e. extraction of intrinsic and extrinsic parameters.
The Windows steps were tested and worked in the OpenPose 1.0.1 version from the last GitHub commit on July 18th, 2017 in the [official repository](https://github.com/CMU-Perceptual-Computing-Lab/openpose).
### Description of this demo:
- Auto detection of all the FLIR cameras, and extraction of images from each one of them.
- Hardware trigger and buffer `NewestFirstOverwrite` modes enabled. Hence, the algorithm will always get the last synchronized frame from each camera, deleting the rest.
- 3-D reconstruction of body, face and hands for 1 person.
- If more than 1 person is detected per camera, the algorithm will just try to match person 0 on each camera, which will potentially correspond to different people in the scene. Thus, the 3-D reconstruction will completely fail.
- Only points with high threshold with respect to each one of the cameras are reprojected (and later rendered). An alternative for > 4 cameras could be to do 3-D reprojection and render all points with good views in more than N different cameras (not implemented here).
- Only Direct linear transformation (DLT) applied. Non-linear optimization methods (e.g. from Ceres Solver) will potentially improve results (not implemented).
- Basic OpenGL rendering with the `freeglut` library.
### Hardware
This demo assumes 3 stereo cameras, FLIR company (former Point Grey). Ideally any USB-3 FLIR model should work, but we have only used the following specific specifications:
This demo assumes n arbitrary stereo cameras, FLIR company (former Point Grey). Ideally any USB-3 FLIR model should work, but we have only used the following specific specifications:
1. Camera details:
- Blackfly S Color 1.3 MP USB3 Vision (ON Semi PYTHON 1300)
......@@ -26,7 +39,7 @@ This demo assumes 3 stereo cameras, FLIR company (former Point Grey). Ideally an
### Calibrate Cameras
You must manually get the intrinsic and extrinsic parameters of your cameras and introduce them on: `openpose3d\cameraParameters.hpp`. We used the 8-distortion-parameter version of OpenCV.
You must manually get the intrinsic and extrinsic parameters of your cameras and introduce them on: `openpose3d\cameraParameters.hpp`. We used the 8-distortion-parameter version of OpenCV. By default, the program uses 3 cameras, but you can add or remove cameras from `cameraParameters.hpp` by adding or removing elements to `INTRINSICS`, `DISTORTIONS` and `M_EACH_CAMERA`. `INTRINSICS` corresponds to the intrinsic parameters, `DISTORTIONS` to the distortion coefficients, and `M_EACH_CAMERA` corresponds to the extrinsic parameters of the cameras with respect to camera 1 as origin.
......@@ -49,13 +62,51 @@ You must manually get the intrinsic and extrinsic parameters of your cameras and
### Ubuntu
We do not support Ubuntu at all for this demo. We did an original and very initial version long ago, but it was highly changed later. In case you need Ubuntu, these are the steps we used for our that original version in Ubuntu 16. Note that they might be several differences to make it work in the current version. Feel free to send us or make a pull request with the updated steps and we will update them, but we will not answer any kind of questions about it.
1. `sudo apt-get install freeglut3-dev`.
2. Compile OpenPose by your own [from https://github.com/CMU-Perceptual-Computing-Lab/openpose](from https://github.com/CMU-Perceptual-Computing-Lab/openpose).
3. Perform `make distribute` on OpenPose, and copy the `include` and `lib` files in [3rdparty/openpose/](3rdparty/openpose/).
4. Copy the `include` and `lib` folders from {OpenPose path}/3rdparty/caffe/distribute/ to [3rdparty/caffe/](3rdparty/caffe/).
5. Copy your Spinnaker desired version `include` and `lib` folders on [3rdparty/spinnaker/](3rdparty/spinnaker/).
6. Open the [rtstereo.pro](rtstereo.pro) file with Qt to have the project ready-to-compile-and-go. If you prefer using your own Makefile file, you can take a look to this Qt file to know which files (basically [src/](src/) and [include/](include/)) and compiler flags used.
1. If using Qt, you will have to manually copy the {OpenPose path}/models folder inside the generated build folder.
7. You must copy the contents of [add_to_bin_file/](add_to_bin_file/) where the final binary file is generated.
We did not create an Ubuntu version. We did an very first version for Ubuntu 16 long ago, but it was highly changed later. These are the steps we used for that one. Note that there might be needed some changes to make it work. Feel free to send us or make a pull request with any updated steps.
1. Install the OpenGL rendering library: `sudo apt-get install freeglut3-dev`.
2. Compile the standard OpenPose [from https://github.com/CMU-Perceptual-Computing-Lab/openpose](from https://github.com/CMU-Perceptual-Computing-Lab/openpose).
3. Perform `make distribute` on OpenPose, and copy the `include` and `lib` files from `distribute` into your custom `3rdparty/openpose/`.
4. Copy the `include` and `lib` folders from {OpenPose path}/3rdparty/caffe/distribute/ into your custom `3rdparty/caffe/`.
5. Copy your Spinnaker desired version `include` and `lib` folders in your custom `3rdparty/spinnaker/`.
7. From the Spinnaker `bin` folder, copy all the *.xml files to the generated build folder of your project.
8. Get the required files from the `include` and `src` folders placed in `{OpenPose path}/windows/OpenPose3DReconstruction/`. Check the Windows VS solution for more details.
9. Create a proper Makefile or CMake file to run it. The following code is part of an old QMake (Qt) file generated for the old version, you can ideally get all the flags and includes from it:
```
DEFINES += USE_CAFFE USE_CUDNN
INCLUDEPATH += \
$$PWD/include \
$$PWD/3rdparty/caffe/include \
$$PWD/3rdparty/openpose/include \
$$PWD/3rdparty/spinnaker/include \
/usr/include \
/usr/local/include \
/usr/local/cuda-8.0/include
}
# Generic
LIBS += -L/usr/lib/ -L/usr/local/lib/ -L/usr/lib/x86_64-linux-gnu
# OpenPose
LIBS += -Wl,-rpath=$$PWD/3rdparty/openpose/lib
LIBS += -Wl,-Bdynamic -L$$PWD/3rdparty/openpose/lib/ -lopenpose
# Caffe
LIBS += -Wl,-rpath=$$PWD/3rdparty/caffe/lib
LIBS += -Wl,-Bdynamic -L$$PWD/3rdparty/caffe/lib/ -lcaffe
# Spinnaker
LIBS += -Wl,-rpath=$$PWD/3rdparty/spinnaker/lib
LIBS += -Wl,-Bdynamic -L$$PWD/3rdparty/spinnaker/lib/ -lSpinnaker
# OpenCV
LIBS += -lopencv_core -lopencv_highgui -lopencv_imgproc -lopencv_contrib -lopencv_calib3d
# CUDA
LIBS += -I/usr/local/cuda-8.0/include/
LIBS += -L/usr/local/cuda-8.0/lib64 -lcudart -lcublas -lcurand
# OpenGL
LIBS += -lGLU -lGL -lglut
# Other 3rdparty
LIBS += -lcudnn -lglog -lgflags -lboost_system -lboost_filesystem -lm -lboost_thread
LIBS += -pthread -fPIC -std=c++11 -fopenmp
# Optimization flags
LIBS += -DNDEBUG -O3 -march=native
# Debug flags
LIBS += -Wpedantic -Wall -Wextra -Wfatal-errors
```
10. If you find any error/difference, feel free to add a pull request to help other users.
......@@ -13,7 +13,8 @@ namespace op
41,36, 42,43, 43,44, 44,45, 45,46, 46,47, 47,42, 48,49, 49,50, 50,51, 51,52, 52,53, 53,54, 54,55, 55,56, 56,57, 57,58, \
58,59, 59,48, 60,61, 61,62, 62,63, 63,64, 64,65, 65,66, 66,67, 67,60}
const std::vector<unsigned int> FACE_PAIRS_RENDER {FACE_PAIRS_RENDER_GPU};
#define FACE_COLORS_RENDER 255.f, 255.f, 255.f
#define FACE_COLORS_RENDER_GPU 255.f, 255.f, 255.f
const std::vector<float> FACE_COLORS_RENDER{FACE_COLORS_RENDER_GPU};
// Constant parameters
const auto FACE_CCN_DECREASE_FACTOR = 8.f;
......
......@@ -10,7 +10,7 @@ namespace op
const auto HAND_NUMBER_PARTS = 21u;
#define HAND_PAIRS_RENDER_GPU {0,1, 1,2, 2,3, 3,4, 0,5, 5,6, 6,7, 7,8, 0,9, 9,10, 10,11, 11,12, 0,13, 13,14, 14,15, 15,16, 0,17, 17,18, 18,19, 19,20}
const std::vector<unsigned int> HAND_PAIRS_RENDER {HAND_PAIRS_RENDER_GPU};
#define HAND_COLORS_RENDER \
#define HAND_COLORS_RENDER_GPU \
100.f, 100.f, 100.f, \
100.f, 0.f, 0.f, \
150.f, 0.f, 0.f, \
......@@ -32,6 +32,8 @@ namespace op
150.f, 0.f, 150.f, \
200.f, 0.f, 200.f, \
255.f, 0.f, 255.f
const std::vector<float> HAND_COLORS_RENDER{HAND_COLORS_RENDER_GPU};
// Constant parameters
const auto HAND_CCN_DECREASE_FACTOR = 8.f;
......
......@@ -40,7 +40,7 @@ namespace op
#define POSE_COCO_PAIRS_RENDER_GPU {1,2, 1,5, 2,3, 3,4, 5,6, 6,7, 1,8, 8,9, 9,10, 1,11, 11,12, 12,13, 1,0, 0,14, 14,16, 0,15, 15,17}
const std::vector<unsigned int> POSE_COCO_PAIRS_RENDER {POSE_COCO_PAIRS_RENDER_GPU};
const std::vector<unsigned int> POSE_COCO_PAIRS {1,2, 1,5, 2,3, 3,4, 5,6, 6,7, 1,8, 8,9, 9,10, 1,11, 11,12, 12,13, 1,0, 0,14, 14,16, 0,15, 15,17, 2,16, 5,17};
#define POSE_COCO_COLORS_RENDER \
#define POSE_COCO_COLORS_RENDER_GPU \
255.f, 0.f, 85.f, \
255.f, 0.f, 0.f, \
255.f, 85.f, 0.f, \
......@@ -59,6 +59,7 @@ namespace op
170.f, 0.f, 255.f, \
255.f, 0.f, 255.f, \
85.f, 0.f, 255.f
const std::vector<float> POSE_COCO_COLORS_RENDER{POSE_COCO_COLORS_RENDER_GPU};
// MPI
const std::map<unsigned int, std::string> POSE_MPI_BODY_PARTS{
{0, "Head"},
......@@ -83,7 +84,7 @@ namespace op
#define POSE_MPI_PAIRS_RENDER_GPU { 0,1, 1,2, 2,3, 3,4, 1,5, 5,6, 6,7, 1,14, 14,8, 8,9, 9,10, 14,11, 11,12, 12,13}
const std::vector<unsigned int> POSE_MPI_PAIRS POSE_MPI_PAIRS_RENDER_GPU;
// MPI colors chosen such that they are closed to COCO colors
#define POSE_MPI_COLORS_RENDER \
#define POSE_MPI_COLORS_RENDER_GPU \
255.f, 0.f, 85.f, \
255.f, 0.f, 0.f, \
255.f, 85.f, 0.f, \
......@@ -99,6 +100,7 @@ namespace op
0.f, 170.f, 255.f, \
0.f, 85.f, 255.f, \
0.f, 0.f, 255.f
const std::vector<float> POSE_MPI_COLORS_RENDER{POSE_MPI_COLORS_RENDER_GPU};
// BODY_22 (experimental, do not use)
const std::map<unsigned int, std::string> POSE_BODY_22_BODY_PARTS {
{0, "Nose"},
......@@ -130,7 +132,7 @@ namespace op
#define POSE_BODY_22_PAIRS_RENDER_GPU {1,2, 1,5, 2,3, 3,4, 5,6, 6,7, 1,8, 8,9, 9,10, 1,11, 11,12}
const std::vector<unsigned int> POSE_BODY_22_PAIRS_RENDER {POSE_BODY_22_PAIRS_RENDER_GPU};
const std::vector<unsigned int> POSE_BODY_22_PAIRS {1,2, 1,5, 2,3, 3,4, 5,6, 6,7, 1,8, 8,9, 9,10, 1,11, 11,12, 12,13, 1,0};
#define POSE_BODY_22_COLORS_RENDER \
#define POSE_BODY_22_COLORS_RENDER_GPU \
255.f, 0.f, 0.f, \
255.f, 85.f, 0.f, \
255.f, 170.f, 0.f, \
......@@ -149,6 +151,7 @@ namespace op
255.f, 0.f, 255.f, \
255.f, 0.f, 170.f, \
255.f, 0.f, 85.f
const std::vector<float> POSE_BODY_22_COLORS_RENDER{POSE_BODY_22_COLORS_RENDER_GPU};
// Constant Array Parameters
const std::array<float, (int)PoseModel::Size> POSE_CCN_DECREASE_FACTOR{
......@@ -169,6 +172,9 @@ namespace op
const std::array<std::vector<unsigned int>, (int)PoseModel::Size> POSE_MAP_IDX{
POSE_COCO_MAP_IDX, POSE_MPI_MAP_IDX, POSE_MPI_MAP_IDX, POSE_BODY_22_MAP_IDX
};
const std::array<std::vector<float>, (int)PoseModel::Size> POSE_COLORS{
POSE_COCO_COLORS_RENDER,POSE_MPI_COLORS_RENDER, POSE_MPI_COLORS_RENDER, POSE_BODY_22_COLORS_RENDER
};
const std::array<std::string, (int)PoseModel::Size> POSE_PROTOTXT{
"pose/coco/pose_deploy_linevec.prototxt",
"pose/mpi/pose_deploy_linevec.prototxt",
......
......@@ -5,8 +5,6 @@
namespace op
{
const std::vector<float> COLORS{FACE_COLORS_RENDER};
void renderFaceKeypointsCpu(Array<float>& frameArray, const Array<float>& faceKeypoints, const float renderThreshold)
{
try
......@@ -19,7 +17,7 @@ namespace op
const auto& pairs = FACE_PAIRS_RENDER;
// Render keypoints
renderKeypointsCpu(frameArray, faceKeypoints, pairs, COLORS, thicknessCircleRatio, thicknessLineRatioWRTCircle, renderThreshold);
renderKeypointsCpu(frameArray, faceKeypoints, pairs, FACE_COLORS_RENDER, thicknessCircleRatio, thicknessLineRatioWRTCircle, renderThreshold);
}
}
catch (const std::exception& e)
......
......@@ -8,7 +8,7 @@ namespace op
{
const dim3 THREADS_PER_BLOCK{128, 128, 1};
__constant__ const unsigned int PART_PAIRS_GPU[] = FACE_PAIRS_RENDER_GPU;
__constant__ const float COLORS[] = {FACE_COLORS_RENDER};
__constant__ const float COLORS[] = {FACE_COLORS_RENDER_GPU};
__global__ void renderFaceParts(float* targetPtr, const int targetWidth, const int targetHeight,
const float* const facePtr, const int numberPeople, const float threshold,
......
......@@ -5,8 +5,6 @@
namespace op
{
const std::vector<float> COLORS{HAND_COLORS_RENDER};
void renderHandKeypointsCpu(Array<float>& frameArray, const std::array<Array<float>, 2>& handKeypoints,
const float renderThreshold)
{
......@@ -18,10 +16,10 @@ namespace op
const auto& pairs = HAND_PAIRS_RENDER;
// Render keypoints
if (!frameArray.empty())
renderKeypointsCpu(frameArray, handKeypoints[0], pairs, COLORS, thicknessCircleRatio,
renderKeypointsCpu(frameArray, handKeypoints[0], pairs, HAND_COLORS_RENDER, thicknessCircleRatio,
thicknessLineRatioWRTCircle, renderThreshold);
if (!frameArray.empty())
renderKeypointsCpu(frameArray, handKeypoints[1], pairs, COLORS, thicknessCircleRatio,
renderKeypointsCpu(frameArray, handKeypoints[1], pairs, HAND_COLORS_RENDER, thicknessCircleRatio,
thicknessLineRatioWRTCircle, renderThreshold);
}
catch (const std::exception& e)
......
......@@ -7,7 +7,7 @@
namespace op
{
__constant__ const unsigned int PART_PAIRS_GPU[] = HAND_PAIRS_RENDER_GPU;
__constant__ const float COLORS[] = {HAND_COLORS_RENDER};
__constant__ const float COLORS[] = {HAND_COLORS_RENDER_GPU};
......
......@@ -5,9 +5,6 @@
namespace op
{
const std::vector<float> COCO_COLORS{POSE_COCO_COLORS_RENDER};
const std::vector<float> MPI_COLORS{POSE_MPI_COLORS_RENDER};
void renderPoseKeypointsCpu(Array<float>& frameArray, const Array<float>& poseKeypoints, const PoseModel poseModel,
const float renderThreshold, const bool blendOriginalFrame)
{
......@@ -26,10 +23,9 @@ namespace op
const auto thicknessCircleRatio = 1.f/75.f;
const auto thicknessLineRatioWRTCircle = 0.75f;
const auto& pairs = POSE_BODY_PART_PAIRS_RENDER[(int)poseModel];
const auto& colors = (poseModel == PoseModel::COCO_18 ? COCO_COLORS : MPI_COLORS);
// Render keypoints
renderKeypointsCpu(frameArray, poseKeypoints, pairs, colors, thicknessCircleRatio,
renderKeypointsCpu(frameArray, poseKeypoints, pairs, POSE_COLORS[(int)poseModel], thicknessCircleRatio,
thicknessLineRatioWRTCircle, renderThreshold);
}
}
......
......@@ -11,9 +11,9 @@ namespace op
__constant__ const unsigned int COCO_PAIRS_GPU[] = POSE_COCO_PAIRS_RENDER_GPU;
__constant__ const unsigned int BODY_22_PAIRS_GPU[] = POSE_BODY_22_PAIRS_RENDER_GPU;
__constant__ const unsigned int MPI_PAIRS_GPU[] = POSE_MPI_PAIRS_RENDER_GPU;
__constant__ const float COCO_COLORS[] = {POSE_COCO_COLORS_RENDER};
__constant__ const float BODY_22_COLORS[] = {POSE_BODY_22_COLORS_RENDER};
__constant__ const float MPI_COLORS[] = {POSE_MPI_COLORS_RENDER};
__constant__ const float COCO_COLORS[] = {POSE_COCO_COLORS_RENDER_GPU};
__constant__ const float BODY_22_COLORS[] = {POSE_BODY_22_COLORS_RENDER_GPU};
__constant__ const float MPI_COLORS[] = {POSE_MPI_COLORS_RENDER_GPU};
......
......@@ -7,36 +7,36 @@
// Intrinsic and distortion parameters
// Camera 1 parameters
const cv::Mat INTRINSIC_1 = (cv::Mat_<double>(3, 3) << 817.93481631740565, 0, 600.70689997785121,
0, 816.51774059837908, 517.84529566329593,
0, 0, 1);
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);
const cv::Mat INTRINSIC_2 = (cv::Mat_<double>(3, 3) << 816.20921132436638, 0, 612.67087968681585,
0, 816.18292222910486, 530.47901782670431,
0, 0, 1);
0, 816.18292222910486, 530.47901782670431,
0, 0, 1);
// Camera 2 parameters
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);
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
// From camera 1 to 2
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);
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,
0.00165046455210419, 0.999916727562850, -0.0127989806923977, 6.22648121362088,
-0.0914375794917412, 0.0128962828696210, 0.995727299487585, 63.4911132860733);
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,
0.00201646110733780, 0.999994431880356, 0.00265896462686206, 9.52238656728889,
0.0266003079592876, -0.00271166755609303, 0.999642471324391, -4.23534963077479);
0.00201646110733780, 0.999994431880356, 0.00265896462686206, 9.52238656728889,
0.0266003079592876, -0.00271166755609303, 0.999642471324391, -4.23534963077479);
......
......@@ -7,10 +7,10 @@
// Following OpenPose `tutorial_wrapper/` examples, we create our own class inherited from WorkerProducer.
// This worker:
// 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>>.
// 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>>.
// 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.
......
......@@ -10,16 +10,16 @@
class WRender3D : public op::WorkerConsumer<std::shared_ptr<std::vector<Datum3D>>>
{
public:
WRender3D();
WRender3D(const op::PoseModel poseModel = op::PoseModel::COCO_18);
void initializationOnThread() {}
void workConsumer(const std::shared_ptr<std::vector<Datum3D>>& datumsPtr);
void workConsumer(const std::shared_ptr<std::vector<Datum3D>>& datumsPtr);
private:
std::thread mRenderThread;
std::thread mRenderThread;
void visualizationThread();
void visualizationThread();
};
#endif // OPENPOSE3D_RENDERER_HPP
......@@ -145,6 +145,10 @@ std::vector<cv::Mat> acquireImages(Spinnaker::CameraList &cameraList)
{
try
{
// Security checks
if (cameraList.GetSize() != INTRINSICS.size())
op::checkE(cameraList.GetSize(), INTRINSICS.size(), "The number of cameras must be the same as the INTRINSICS vector size.", __LINE__, __FUNCTION__, __FILE__);
std::vector<cv::Mat> cvMats;
// Retrieve, convert, and return an image for each camera
......@@ -284,6 +288,18 @@ int printDeviceInfo(Spinnaker::GenApi::INodeMap &iNodeMap, const unsigned int ca
WPointGrey::WPointGrey() :
initialized{false}
{
try
{
// Security checks
if (INTRINSICS.size() != DISTORTIONS.size())
op::checkE(INTRINSICS.size(), DISTORTIONS.size(), "The INTRINSICS and DISTORTIONS vector should have the same size.", __LINE__, __FUNCTION__, __FILE__);
if (INTRINSICS.size() != M_EACH_CAMERA.size())
op::checkE(INTRINSICS.size(), M_EACH_CAMERA.size(), "The INTRINSICS and M_EACH_CAMERA vector should have the same size.", __LINE__, __FUNCTION__, __FILE__);
}
catch (const std::exception& e)
{
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
}
}
WPointGrey::~WPointGrey()
......
......@@ -54,61 +54,79 @@ double triangulateWithOptimization(cv::Mat& X, const std::vector<cv::Mat>& matri
void reconstructArray(op::Array<float>& keypoints3D, const std::vector<op::Array<float>>& keypointsVector)
{
const auto& keypoints0 = keypointsVector.at(0);
const auto& keypoints1 = keypointsVector.at(1);
const auto& keypoints2 = keypointsVector.at(2);
if (keypoints0.getSize(0) > 0 && keypoints1.getSize(0) > 0 && keypoints2.getSize(0) > 0)
{
// Count #correspondences
const auto threshold = 0.1f;
const auto numberBodyParts = keypoints0.getSize(1);
std::vector<int> indexesUsed;
std::vector<std::vector<cv::Point2d>> xyPoints;
for (auto part = 0; part < numberBodyParts; part++)
{
if (keypoints0[{0, part, 2}] > threshold && keypoints1[{0, part, 2}] > threshold && keypoints2[{0, part, 2}] > threshold)
{
indexesUsed.emplace_back(part);
xyPoints.emplace_back(std::vector<cv::Point2d>{
cv::Point2d{ keypoints0[{0, part, 0}], keypoints0[{0, part, 1}] },
cv::Point2d{ keypoints1[{0, part, 0}], keypoints1[{0, part, 1}] },
cv::Point2d{ keypoints2[{0, part, 0}], keypoints2[{0, part, 1}] }
});
}
}
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, M_EACH_CAMERA, xyPoints[i]);
xyzPoints[i] = cv::Point3d{ X.at<double>(0), X.at<double>(1), X.at<double>(2) };
}
// 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, M_EACH_CAMERA, 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 reconstructedcv::Points{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 = op::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;
}
}
}
}
// 3D points to pose
// OpenCV alternative:
// // http://docs.opencv.org/2.4/modules/calib3d/doc/camera_calibration_and_3d_reconstruction.html#triangulatepoints
// cv::Mat reconstructedcv::Points{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 = op::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)
......@@ -122,23 +140,25 @@ void WReconstruction3D::work(std::shared_ptr<std::vector<Datum3D>>& datumsPtr)
const auto profilerKey = op::Profiler::timerInit(__LINE__, __FUNCTION__, __FILE__);
if (datumsPtr != nullptr && /*!datumsPtr->empty() &&*/ datumsPtr->size() == 3)
{
// Pose
reconstructArray(datumsPtr->at(0).poseKeypoints3D, std::vector<op::Array<float>>{
datumsPtr->at(0).poseKeypoints, datumsPtr->at(1).poseKeypoints, datumsPtr->at(2).poseKeypoints
});
// Face
reconstructArray(datumsPtr->at(0).faceKeypoints3D, std::vector<op::Array<float>>{
datumsPtr->at(0).faceKeypoints, datumsPtr->at(1).faceKeypoints, datumsPtr->at(2).faceKeypoints
});
// Left hand
reconstructArray(datumsPtr->at(0).leftHandKeypoints3D, std::vector<op::Array<float>>{
datumsPtr->at(0).handKeypoints[0], datumsPtr->at(1).handKeypoints[0], datumsPtr->at(2).handKeypoints[0]
});
// Right hand
reconstructArray(datumsPtr->at(0).rightHandKeypoints3D, std::vector<op::Array<float>>{
datumsPtr->at(0).handKeypoints[1], datumsPtr->at(1).handKeypoints[1], datumsPtr->at(2).handKeypoints[1]
});
std::vector<op::Array<float>> poseKeypointVector;
std::vector<op::Array<float>> faceKeypointVector;
std::vector<op::Array<float>> leftHandKeypointVector;
std::vector<op::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]);
}
// Pose 3-D reconstruction
reconstructArray(datumsPtr->at(0).poseKeypoints3D, poseKeypointVector);
// Face 3-D reconstruction
reconstructArray(datumsPtr->at(0).faceKeypoints3D, faceKeypointVector);
// Left hand 3-D reconstruction
reconstructArray(datumsPtr->at(0).leftHandKeypoints3D, leftHandKeypointVector);
// Right hand 3-D reconstruction
reconstructArray(datumsPtr->at(0).rightHandKeypoints3D, rightHandKeypointVector);
// Profiling speed
op::Profiler::timerEnd(profilerKey);
op::Profiler::printAveragedTimeMsOnIterationX(profilerKey, __LINE__, __FUNCTION__, __FILE__, 100);
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册