Python Pybind11 wrapper (#1014)

This commit is contained in:
Raaj
2019-01-11 19:39:05 -05:00
committed by Gines
parent 6a6dd2e000
commit 29fc0039ff
20 changed files with 701 additions and 741 deletions
+5 -5
View File
@@ -3,12 +3,12 @@ set(PYTHON_FILES
__init__.py
_openpose.cpp)
add_library(_openpose SHARED ${PYTHON_FILES})
target_link_libraries(_openpose openpose ${OpenPose_3rdparty_libraries})
pybind11_add_module(_openpose _openpose.cpp)
target_link_libraries(_openpose PRIVATE pybind11::module openpose_src ${OpenPose_3rdparty_libraries})
SET_TARGET_PROPERTIES(_openpose PROPERTIES PREFIX "")
configure_file(openpose.py openpose.py)
configure_file(__init__.py __init__.py)
#install(TARGETS _openpose DESTINATION python)
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ DESTINATION python/openpose FILES_MATCHING PATTERN "*.so")
install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ DESTINATION python/openpose FILES_MATCHING PATTERN "*.py")
#install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ DESTINATION python/openpose FILES_MATCHING PATTERN "*.so")
#install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/ DESTINATION python/openpose FILES_MATCHING PATTERN "*.py")
+1 -1
View File
@@ -1 +1 @@
from openpose import *
from . import _openpose as openpose
+370 -316
View File
@@ -2,24 +2,15 @@
#define OPENPOSE_PYTHON_HPP
#define BOOST_DATE_TIME_NO_LIB
// OpenPose dependencies
#include <openpose/core/headers.hpp>
#include <openpose/filestream/headers.hpp>
#include <openpose/gui/headers.hpp>
#include <openpose/pose/headers.hpp>
#include <openpose/utilities/headers.hpp>
#include <caffe/caffe.hpp>
#include <stdlib.h>
#include <openpose/flags.hpp>
#include <openpose/headers.hpp>
#include <openpose/wrapper/headers.hpp>
#include <openpose/net/bodyPartConnectorCaffe.hpp>
#include <openpose/net/nmsCaffe.hpp>
#include <openpose/net/resizeAndMergeCaffe.hpp>
#include <openpose/pose/poseParameters.hpp>
#include <openpose/pose/enumClasses.hpp>
#include <openpose/pose/poseExtractor.hpp>
#include <openpose/gpu/cuda.hpp>
#include <openpose/gpu/opencl.hcl>
#include <openpose/core/macros.hpp>
#include <pybind11/pybind11.h>
#include <pybind11/stl.h>
#include <pybind11/numpy.h>
#include <opencv2/core/core.hpp>
#include <stdexcept>
#ifdef _WIN32
#define OP_EXPORT __declspec(dllexport)
@@ -27,328 +18,391 @@
#define OP_EXPORT
#endif
#define default_logging_level 3
#define default_output_resolution "-1x-1"
#define default_net_resolution "-1x368"
#define default_model_pose "COCO"
#define default_alpha_pose 0.6
#define default_scale_gap 0.25
#define default_scale_number 1
#define default_render_threshold 0.05
#define default_num_gpu_start 0
#define default_disable_blending false
#define default_model_folder "models/"
namespace op{
// Todo, have GPU Number, handle, OpenCL/CPU Cases
OP_API class OpenPose {
namespace py = pybind11;
void parse_gflags(const std::vector<std::string>& argv)
{
std::vector<char*> argv_vec;
for(auto& arg : argv) argv_vec.emplace_back((char*)arg.c_str());
char** cast = &argv_vec[0];
int size = argv_vec.size();
gflags::ParseCommandLineFlags(&size, &cast, true);
}
void init_int(py::dict d)
{
std::vector<std::string> argv;
argv.emplace_back("openpose.py");
for (auto item : d){
argv.emplace_back("--" + std::string(py::str(item.first)));
argv.emplace_back(py::str(item.second));
}
parse_gflags(argv);
}
void init_argv(std::vector<std::string> argv)
{
argv.insert(argv.begin(), "openpose.py");
parse_gflags(argv);
}
class WrapperPython{
public:
std::unique_ptr<op::PoseExtractorCaffe> poseExtractorCaffe;
std::unique_ptr<op::PoseCpuRenderer> poseRenderer;
std::unique_ptr<op::FrameDisplayer> frameDisplayer;
std::unique_ptr<op::ScaleAndSizeExtractor> scaleAndSizeExtractor;
std::unique_ptr<op::Wrapper> opWrapper;
std::unique_ptr<op::ResizeAndMergeCaffe<float>> resizeAndMergeCaffe;
std::unique_ptr<op::NmsCaffe<float>> nmsCaffe;
std::unique_ptr<op::BodyPartConnectorCaffe<float>> bodyPartConnectorCaffe;
std::shared_ptr<caffe::Blob<float>> heatMapsBlob;
std::shared_ptr<caffe::Blob<float>> peaksBlob;
op::Array<float> mPoseKeypoints;
op::Array<float> mPoseScores;
op::PoseModel poseModel;
int mGpuID;
WrapperPython(int mode = 0)
{
op::log("Starting OpenPose Python Wrapper...", op::Priority::High);
OpenPose(int FLAGS_logging_level = default_logging_level,
std::string FLAGS_output_resolution = default_output_resolution,
std::string FLAGS_net_resolution = default_net_resolution,
std::string FLAGS_model_pose = default_model_pose,
float FLAGS_alpha_pose = default_alpha_pose,
float FLAGS_scale_gap = default_scale_gap,
int FLAGS_scale_number = default_scale_number,
float FLAGS_render_threshold = default_render_threshold,
int FLAGS_num_gpu_start = default_num_gpu_start,
int FLAGS_disable_blending = default_disable_blending,
std::string FLAGS_model_folder = default_model_folder
) {
mGpuID = FLAGS_num_gpu_start;
#ifdef USE_CUDA
caffe::Caffe::set_mode(caffe::Caffe::GPU);
caffe::Caffe::SetDevice(mGpuID);
#elif defined USE_OPENCL
caffe::Caffe::set_mode(caffe::Caffe::GPU);
std::vector<int> devices;
const int maxNumberGpu = op::OpenCL::getTotalGPU();
for (auto i = 0; i < maxNumberGpu; i++)
devices.emplace_back(i);
caffe::Caffe::SetDevices(devices);
caffe::Caffe::SelectDevice(mGpuID, true);
op::OpenCL::getInstance(mGpuID, CL_DEVICE_TYPE_GPU, true);
#else
caffe::Caffe::set_mode(caffe::Caffe::CPU);
#endif
op::log("OpenPose Library Python Wrapper", op::Priority::High);
// ------------------------- INITIALIZATION -------------------------
// Step 1 - Set logging level
// - 0 will output all the logging messages
// - 255 will output nothing
// Construct opWrapper
opWrapper = std::unique_ptr<op::Wrapper>(new op::Wrapper(static_cast<op::ThreadManagerMode>(mode)));
}
void configure(py::dict params = py::dict())
{
if(params.size()) init_int(params);
// logging_level
op::check(0 <= FLAGS_logging_level && FLAGS_logging_level <= 255, "Wrong logging_level value.",
__LINE__, __FUNCTION__, __FILE__);
op::ConfigureLog::setPriorityThreshold((op::Priority)FLAGS_logging_level);
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
// Step 2 - Read GFlags (user defined configuration)
op::Profiler::setDefaultX(FLAGS_profile_speed);
// Applying user defined configuration - GFlags to program variables
// outputSize
const auto outputSize = op::flagsToPoint(FLAGS_output_resolution, "-1x-1");
// netInputSize
const auto netInputSize = op::flagsToPoint(FLAGS_net_resolution, "-1x368");
// faceNetInputSize
const auto faceNetInputSize = op::flagsToPoint(FLAGS_face_net_resolution, "368x368 (multiples of 16)");
// handNetInputSize
const auto handNetInputSize = op::flagsToPoint(FLAGS_hand_net_resolution, "368x368 (multiples of 16)");
// poseModel
poseModel = op::flagsToPoseModel(FLAGS_model_pose);
// Check no contradictory flags enabled
if (FLAGS_alpha_pose < 0. || FLAGS_alpha_pose > 1.)
op::error("Alpha value for blending must be in the range [0,1].", __LINE__, __FUNCTION__, __FILE__);
if (FLAGS_scale_gap <= 0. && FLAGS_scale_number > 1)
op::error("Incompatible flag configuration: scale_gap must be greater than 0 or scale_number = 1.",
__LINE__, __FUNCTION__, __FILE__);
// Step 3 - Initialize all required classes
scaleAndSizeExtractor = std::unique_ptr<op::ScaleAndSizeExtractor>(new op::ScaleAndSizeExtractor(netInputSize, outputSize, FLAGS_scale_number, FLAGS_scale_gap));
const auto poseModel = op::flagsToPoseModel(FLAGS_model_pose);
// JSON saving
if (!FLAGS_write_keypoint.empty())
op::log("Flag `write_keypoint` is deprecated and will eventually be removed."
" Please, use `write_json` instead.", op::Priority::Max);
// keypointScale
const auto keypointScale = op::flagsToScaleMode(FLAGS_keypoint_scale);
// 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);
// >1 camera view?
const auto multipleView = (FLAGS_3d || FLAGS_3d_views > 1);
// Enabling Google Logging
const bool enableGoogleLogging = true;
poseExtractorCaffe = std::unique_ptr<op::PoseExtractorCaffe>(new op::PoseExtractorCaffe{ poseModel, FLAGS_model_folder, FLAGS_num_gpu_start });
poseRenderer = std::unique_ptr<op::PoseCpuRenderer>(new op::PoseCpuRenderer{ poseModel, (float)FLAGS_render_threshold, !FLAGS_disable_blending,
(float)FLAGS_alpha_pose });
frameDisplayer = std::unique_ptr<op::FrameDisplayer>(new op::FrameDisplayer{ "OpenPose Tutorial - Example 1", outputSize });
// Custom
resizeAndMergeCaffe = std::unique_ptr<op::ResizeAndMergeCaffe<float>>(new op::ResizeAndMergeCaffe<float>{});
nmsCaffe = std::unique_ptr<op::NmsCaffe<float>>(new op::NmsCaffe<float>{});
bodyPartConnectorCaffe = std::unique_ptr<op::BodyPartConnectorCaffe<float>>(new op::BodyPartConnectorCaffe<float>{});
heatMapsBlob = { std::make_shared<caffe::Blob<float>>(1,1,1,1) };
peaksBlob = { std::make_shared<caffe::Blob<float>>(1,1,1,1) };
bodyPartConnectorCaffe->setPoseModel(poseModel);
// Step 4 - Initialize resources on desired thread (in this case single thread, i.e., we init resources here)
poseExtractorCaffe->initializationOnThread();
poseRenderer->initializationOnThread();
// Pose configuration (use WrapperStructPose{} for default and recommended configuration)
const op::WrapperStructPose wrapperStructPose{
!FLAGS_body_disable, netInputSize, outputSize, keypointScale, FLAGS_num_gpu, FLAGS_num_gpu_start,
FLAGS_scale_number, (float)FLAGS_scale_gap, op::flagsToRenderMode(FLAGS_render_pose, multipleView),
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, FLAGS_number_people_max, FLAGS_maximize_positives, FLAGS_fps_max,
FLAGS_prototxt_path, FLAGS_caffemodel_path, enableGoogleLogging};
opWrapper->configure(wrapperStructPose);
// Face configuration (use op::WrapperStructFace{} to disable it)
const op::WrapperStructFace wrapperStructFace{
FLAGS_face, faceNetInputSize, op::flagsToRenderMode(FLAGS_face_render, multipleView, FLAGS_render_pose),
(float)FLAGS_face_alpha_pose, (float)FLAGS_face_alpha_heatmap, (float)FLAGS_face_render_threshold};
opWrapper->configure(wrapperStructFace);
// Hand configuration (use op::WrapperStructHand{} to disable it)
const op::WrapperStructHand wrapperStructHand{
FLAGS_hand, handNetInputSize, FLAGS_hand_scale_number, (float)FLAGS_hand_scale_range, FLAGS_hand_tracking,
op::flagsToRenderMode(FLAGS_hand_render, multipleView, FLAGS_render_pose), (float)FLAGS_hand_alpha_pose,
(float)FLAGS_hand_alpha_heatmap, (float)FLAGS_hand_render_threshold};
opWrapper->configure(wrapperStructHand);
// Extra functionality configuration (use op::WrapperStructExtra{} to disable it)
const op::WrapperStructExtra wrapperStructExtra{
FLAGS_3d, FLAGS_3d_min_views, FLAGS_identification, FLAGS_tracking, FLAGS_ik_threads};
opWrapper->configure(wrapperStructExtra);
// Output (comment or use default argument to disable any output)
const op::WrapperStructOutput wrapperStructOutput{
FLAGS_cli_verbose, FLAGS_write_keypoint, op::stringToDataFormat(FLAGS_write_keypoint_format),
FLAGS_write_json, FLAGS_write_coco_json, FLAGS_write_coco_foot_json, FLAGS_write_coco_json_variant,
FLAGS_write_images, FLAGS_write_images_format, FLAGS_write_video, FLAGS_write_video_fps,
FLAGS_write_heatmaps, FLAGS_write_heatmaps_format, FLAGS_write_video_3d, FLAGS_write_video_adam,
FLAGS_write_bvh, FLAGS_udp_host, FLAGS_udp_port};
opWrapper->configure(wrapperStructOutput);
// No GUI. Equivalent to: opWrapper.configure(op::WrapperStructGui{});
// Set to single-thread (for sequential processing and/or debugging and/or reducing latency)
if (FLAGS_disable_multi_thread)
opWrapper->disableMultiThreading();
}
std::vector<caffe::Blob<float>*> caffeNetSharedToPtr(
std::vector<boost::shared_ptr<caffe::Blob<float>>>& caffeNetOutputBlob)
void start(){
opWrapper->start();
}
void stop(){
opWrapper->stop();
}
void exec(){
const auto cameraSize = op::flagsToPoint(FLAGS_camera_resolution, "-1x-1");
op::ProducerType producerType;
std::string producerString;
std::tie(producerType, producerString) = op::flagsToProducer(
FLAGS_image_dir, FLAGS_video, FLAGS_ip_camera, FLAGS_camera, FLAGS_flir_camera, FLAGS_flir_camera_index);
// Producer (use default to disable any input)
const op::WrapperStructInput wrapperStructInput{
producerType, producerString, FLAGS_frame_first, FLAGS_frame_step, FLAGS_frame_last,
FLAGS_process_real_time, FLAGS_frame_flip, FLAGS_frame_rotate, FLAGS_frames_repeat,
cameraSize, FLAGS_camera_parameter_path, FLAGS_frame_undistort, FLAGS_3d_views};
opWrapper->configure(wrapperStructInput);
// GUI (comment or use default argument to disable any visual output)
const op::WrapperStructGui wrapperStructGui{
op::flagsToDisplayMode(FLAGS_display, FLAGS_3d), !FLAGS_no_gui_verbose, FLAGS_fullscreen};
opWrapper->configure(wrapperStructGui);
opWrapper->exec();
}
void emplaceAndPop(std::vector<std::shared_ptr<op::Datum>>& l)
{
try
{
// Prepare spCaffeNetOutputBlobss
std::vector<caffe::Blob<float>*> caffeNetOutputBlobs(caffeNetOutputBlob.size());
for (auto i = 0u; i < caffeNetOutputBlobs.size(); i++)
caffeNetOutputBlobs[i] = caffeNetOutputBlob[i].get();
return caffeNetOutputBlobs;
}
catch (const std::exception& e)
{
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
return{};
}
}
void forward(const cv::Mat& inputImage, op::Array<float>& poseKeypoints, cv::Mat& displayImage, bool display = false) {
op::OpOutputToCvMat opOutputToCvMat;
op::CvMatToOpInput cvMatToOpInput;
op::CvMatToOpOutput cvMatToOpOutput;
if (inputImage.empty())
op::error("Could not open or find the image: ", __LINE__, __FUNCTION__, __FILE__);
const op::Point<int> imageSize{ inputImage.cols, inputImage.rows };
// Step 2 - Get desired scale sizes
std::vector<double> scaleInputToNetInputs;
std::vector<op::Point<int>> netInputSizes;
double scaleInputToOutput;
op::Point<int> outputResolution;
std::tie(scaleInputToNetInputs, netInputSizes, scaleInputToOutput, outputResolution)
= scaleAndSizeExtractor->extract(imageSize);
// Step 3 - Format input image to OpenPose input and output formats
const auto netInputArray = cvMatToOpInput.createArray(inputImage, scaleInputToNetInputs, netInputSizes);
// Step 4 - Estimate poseKeypoints
poseExtractorCaffe->forwardPass(netInputArray, imageSize, scaleInputToNetInputs);
poseKeypoints = poseExtractorCaffe->getPoseKeypoints();
if (display) {
auto outputArray = cvMatToOpOutput.createArray(inputImage, scaleInputToOutput, outputResolution);
// Step 5 - Render poseKeypoints
poseRenderer->renderPose(outputArray, poseKeypoints, scaleInputToOutput);
// Step 6 - OpenPose output format to cv::Mat
displayImage = opOutputToCvMat.formatToCvMat(outputArray);
}
}
void poseFromHeatmap(const cv::Mat& inputImage, std::vector<boost::shared_ptr<caffe::Blob<float>>>& caffeNetOutputBlob, op::Array<float>& poseKeypoints, cv::Mat& displayImage, std::vector<op::Point<int>>& imageSizes) {
// Get Scale
const op::Point<int> inputDataSize{ inputImage.cols, inputImage.rows };
// Convert to Ptr
//std::vector<boost::shared_ptr<caffe::Blob<float>>> a;
//caffeNetOutputBlob.emplace_back(caffeHmPtr);
const auto caffeNetOutputBlobs = caffeNetSharedToPtr(caffeNetOutputBlob);
// To be called once only
resizeAndMergeCaffe->Reshape(caffeNetOutputBlobs, { heatMapsBlob.get() },
op::getPoseNetDecreaseFactor(poseModel), 1.f / 1.f, true,
0);
nmsCaffe->Reshape({ heatMapsBlob.get() }, { peaksBlob.get() }, op::getPoseMaxPeaks(),
op::getPoseNumberBodyParts(poseModel), 0);
bodyPartConnectorCaffe->Reshape({ heatMapsBlob.get(), peaksBlob.get() });
// Normal
op::OpOutputToCvMat opOutputToCvMat;
op::CvMatToOpInput cvMatToOpInput;
op::CvMatToOpOutput cvMatToOpOutput;
if (inputImage.empty())
op::error("Could not open or find the image: ", __LINE__, __FUNCTION__, __FILE__);
const op::Point<int> imageSize{ inputImage.cols, inputImage.rows };
// Step 2 - Get desired scale sizes
std::vector<double> scaleInputToNetInputs;
std::vector<op::Point<int>> netInputSizes;
double scaleInputToOutput;
op::Point<int> outputResolution;
std::tie(scaleInputToNetInputs, netInputSizes, scaleInputToOutput, outputResolution)
= scaleAndSizeExtractor->extract(imageSize);
const auto netInputArray = cvMatToOpInput.createArray(inputImage, scaleInputToNetInputs, netInputSizes);
// Run the modes
const std::vector<float> floatScaleRatios(scaleInputToNetInputs.begin(), scaleInputToNetInputs.end());
resizeAndMergeCaffe->setScaleRatios(floatScaleRatios);
std::vector<caffe::Blob<float>*> heatMapsBlobs{ heatMapsBlob.get() };
std::vector<caffe::Blob<float>*> peaksBlobs{ peaksBlob.get() };
#ifdef USE_CUDA
resizeAndMergeCaffe->Forward_gpu(caffeNetOutputBlobs, heatMapsBlobs); // ~5ms
#elif defined USE_OPENCL
resizeAndMergeCaffe->Forward_ocl(caffeNetOutputBlobs, heatMapsBlobs); // ~5ms
#else
resizeAndMergeCaffe->Forward_cpu(caffeNetOutputBlobs, heatMapsBlobs); // ~5ms
#endif
nmsCaffe->setThreshold((float)poseExtractorCaffe->get(op::PoseProperty::NMSThreshold));
#ifdef USE_CUDA
nmsCaffe->Forward_gpu(heatMapsBlobs, peaksBlobs);// ~2ms
#elif defined USE_OPENCL
nmsCaffe->Forward_ocl(heatMapsBlobs, peaksBlobs);// ~2ms
#else
nmsCaffe->Forward_cpu(heatMapsBlobs, peaksBlobs);// ~2ms
#endif
op::cudaCheck(__LINE__, __FUNCTION__, __FILE__);
float mScaleNetToOutput = 1. / scaleInputToNetInputs[0];
bodyPartConnectorCaffe->setScaleNetToOutput(mScaleNetToOutput);
bodyPartConnectorCaffe->setInterMinAboveThreshold(
(float)poseExtractorCaffe->get(op::PoseProperty::ConnectInterMinAboveThreshold)
);
bodyPartConnectorCaffe->setInterThreshold((float)poseExtractorCaffe->get(op::PoseProperty::ConnectInterThreshold));
bodyPartConnectorCaffe->setMinSubsetCnt((int)poseExtractorCaffe->get(op::PoseProperty::ConnectMinSubsetCnt));
bodyPartConnectorCaffe->setMinSubsetScore((float)poseExtractorCaffe->get(op::PoseProperty::ConnectMinSubsetScore));
#ifdef USE_CUDA
bodyPartConnectorCaffe->Forward_gpu({ heatMapsBlob.get(),
peaksBlob.get() },
mPoseKeypoints, mPoseScores);
#else
bodyPartConnectorCaffe->Forward_cpu({ heatMapsBlob.get(),
peaksBlob.get() },
mPoseKeypoints, mPoseScores);
#endif
poseKeypoints = mPoseKeypoints;
auto outputArray = cvMatToOpOutput.createArray(inputImage, scaleInputToOutput, outputResolution);
// Step 5 - Render poseKeypoints
poseRenderer->renderPose(outputArray, mPoseKeypoints, scaleInputToOutput);
// Step 6 - OpenPose output format to cv::Mat
displayImage = opOutputToCvMat.formatToCvMat(outputArray);
auto datumsPtr = std::make_shared<std::vector<std::shared_ptr<op::Datum>>>(l);
opWrapper->emplaceAndPop(datumsPtr);
}
};
#ifdef __cplusplus
extern "C" {
#endif
PYBIND11_MODULE(_openpose, m) {
typedef void* c_OP;
op::Array<float> output;
// Functions for Init Params
m.def("init_int", &init_int, "Init Function");
m.def("init_argv", &init_argv, "Init Function");
OP_EXPORT c_OP newOP(int logging_level,
char* output_resolution,
char* net_resolution,
char* model_pose,
float alpha_pose,
float scale_gap,
int scale_number,
float render_threshold,
int num_gpu_start,
bool disable_blending,
char* model_folder
) {
return new OpenPose(logging_level, output_resolution, net_resolution, model_pose, alpha_pose,
scale_gap, scale_number, render_threshold, num_gpu_start, disable_blending, model_folder);
}
OP_EXPORT void delOP(c_OP op) {
delete (OpenPose *)op;
}
OP_EXPORT void forward(c_OP op, unsigned char* img, size_t rows, size_t cols, int* size, unsigned char* displayImg, bool display) {
OpenPose* openPose = (OpenPose*)op;
cv::Mat image(rows, cols, CV_8UC3, img);
cv::Mat displayImage(rows, cols, CV_8UC3, displayImg);
openPose->forward(image, output, displayImage, display);
if (output.getSize().size()) {
size[0] = output.getSize()[0];
size[1] = output.getSize()[1];
size[2] = output.getSize()[2];
}
else {
size[0] = 0; size[1] = 0; size[2] = 0;
}
if (display) memcpy(displayImg, displayImage.ptr(), sizeof(unsigned char)*rows*cols * 3);
}
OP_EXPORT void getOutputs(c_OP op, float* array) {
if (output.getSize().size())
memcpy(array, output.getPtr(), output.getSize()[0] * output.getSize()[1] * output.getSize()[2] * sizeof(float));
}
OP_EXPORT void poseFromHeatmap(c_OP op, unsigned char* img, size_t rows, size_t cols, unsigned char* displayImg, float* hm, int* size, float* ratios) {
OpenPose* openPose = (OpenPose*)op;
cv::Mat image(rows, cols, CV_8UC3, img);
cv::Mat displayImage(rows, cols, CV_8UC3, displayImg);
// OpenposePython
py::class_<WrapperPython>(m, "WrapperPython")
.def(py::init<>())
.def(py::init<int>())
.def("configure", &WrapperPython::configure)
.def("start", &WrapperPython::start)
.def("stop", &WrapperPython::stop)
.def("execute", &WrapperPython::exec)
.def("emplaceAndPop", &WrapperPython::emplaceAndPop)
;
std::vector<boost::shared_ptr<caffe::Blob<float>>> caffeNetOutputBlob;
// Datum Object
py::class_<op::Datum, std::shared_ptr<op::Datum>>(m, "Datum")
.def(py::init<>())
.def_readwrite("id", &op::Datum::id)
.def_readwrite("subId", &op::Datum::subId)
.def_readwrite("subIdMax", &op::Datum::subIdMax)
.def_readwrite("name", &op::Datum::name)
.def_readwrite("frameNumber", &op::Datum::frameNumber)
.def_readwrite("cvInputData", &op::Datum::cvInputData)
.def_readwrite("inputNetData", &op::Datum::inputNetData)
.def_readwrite("outputData", &op::Datum::outputData)
.def_readwrite("cvOutputData", &op::Datum::cvOutputData)
.def_readwrite("cvOutputData3D", &op::Datum::cvOutputData3D)
.def_readwrite("poseKeypoints", &op::Datum::poseKeypoints)
.def_readwrite("poseIds", &op::Datum::poseIds)
.def_readwrite("poseScores", &op::Datum::poseScores)
.def_readwrite("poseHeatMaps", &op::Datum::poseHeatMaps)
.def_readwrite("poseCandidates", &op::Datum::poseCandidates)
.def_readwrite("faceRectangles", &op::Datum::faceRectangles)
.def_readwrite("faceKeypoints", &op::Datum::faceKeypoints)
.def_readwrite("faceHeatMaps", &op::Datum::faceHeatMaps)
.def_readwrite("handRectangles", &op::Datum::handRectangles)
.def_readwrite("handKeypoints", &op::Datum::handKeypoints)
.def_readwrite("handHeatMaps", &op::Datum::handHeatMaps)
.def_readwrite("poseKeypoints3D", &op::Datum::poseKeypoints3D)
.def_readwrite("faceKeypoints3D", &op::Datum::faceKeypoints3D)
.def_readwrite("handKeypoints3D", &op::Datum::handKeypoints3D)
.def_readwrite("cameraMatrix", &op::Datum::cameraMatrix)
.def_readwrite("cameraExtrinsics", &op::Datum::cameraExtrinsics)
.def_readwrite("cameraIntrinsics", &op::Datum::cameraIntrinsics)
.def_readwrite("scaleInputToNetInputs", &op::Datum::scaleInputToNetInputs)
.def_readwrite("netInputSizes", &op::Datum::netInputSizes)
.def_readwrite("scaleInputToOutput", &op::Datum::scaleInputToOutput)
.def_readwrite("netOutputSize", &op::Datum::netOutputSize)
.def_readwrite("scaleNetToOutput", &op::Datum::scaleNetToOutput)
.def_readwrite("elementRendered", &op::Datum::elementRendered)
;
for (int i = 0; i<size[0]; i++) {
boost::shared_ptr<caffe::Blob<float>> caffeHmPtr(new caffe::Blob<float>());
caffeHmPtr->Reshape(1, size[1], size[2] * ((float)ratios[i] / (float)ratios[0]), size[3] * ((float)ratios[i] / (float)ratios[0]));
float* startIndex = &hm[i*size[1] * size[2] * size[3]];
for (int d = 0; d<caffeHmPtr->shape()[1]; d++) {
for (int r = 0; r<caffeHmPtr->shape()[2]; r++) {
for (int c = 0; c<caffeHmPtr->shape()[3]; c++) {
int toI = d*caffeHmPtr->shape()[2] * caffeHmPtr->shape()[3] + r*caffeHmPtr->shape()[3] + c;
int fromI = d*size[2] * size[3] + r*size[3] + c;
caffeHmPtr->mutable_cpu_data()[toI] = startIndex[fromI];
}
}
}
caffeNetOutputBlob.emplace_back(caffeHmPtr);
}
// Rectangle
py::class_<op::Rectangle<float>>(m, "Rectangle")
.def("__repr__", [](op::Rectangle<float> &a) { return a.toString(); })
.def(py::init<>())
.def(py::init<float, float, float, float>())
.def_readwrite("x", &op::Rectangle<float>::x)
.def_readwrite("y", &op::Rectangle<float>::y)
.def_readwrite("width", &op::Rectangle<float>::width)
.def_readwrite("height", &op::Rectangle<float>::height)
;
std::vector<op::Point<int>> imageSizes;
for (int i = 0; i<size[0]; i++) {
op::Point<int> point(cols*ratios[i], rows*ratios[i]);
imageSizes.emplace_back(point);
}
// Point
py::class_<op::Point<int>>(m, "Point")
.def("__repr__", [](op::Point<int> &a) { return a.toString(); })
.def(py::init<>())
.def(py::init<int, int>())
.def_readwrite("x", &op::Point<int>::x)
.def_readwrite("y", &op::Point<int>::y)
;
openPose->poseFromHeatmap(image, caffeNetOutputBlob, output, displayImage, imageSizes);
memcpy(displayImg, displayImage.ptr(), sizeof(unsigned char)*rows*cols * 3);
// Copy back kp size
if (output.getSize().size()) {
size[0] = output.getSize()[0];
size[1] = output.getSize()[1];
size[2] = output.getSize()[2];
}
else {
size[0] = 0; size[1] = 0; size[2] = 0;
}
}
#ifdef __cplusplus
#ifdef VERSION_INFO
m.attr("__version__") = VERSION_INFO;
#else
m.attr("__version__") = "dev";
#endif
}
#endif
}
// Numpy - op::Array<float> interop
namespace pybind11 { namespace detail {
template <> struct type_caster<op::Array<float>> {
public:
PYBIND11_TYPE_CASTER(op::Array<float>, _("numpy.ndarray"));
// Cast numpy to op::Array<float>
bool load(handle src, bool imp)
{
// array b(src, true);
array b = reinterpret_borrow<array>(src);
buffer_info info = b.request();
if (info.format != format_descriptor<float>::format())
throw std::runtime_error("op::Array only supports float32 now");
//std::vector<int> a(info.shape);
std::vector<int> shape(std::begin(info.shape), std::end(info.shape));
// No copy
value = op::Array<float>(shape, (float*)info.ptr);
// Copy
//value = op::Array<float>(shape);
//memcpy(value.getPtr(), info.ptr, value.getVolume()*sizeof(float));
return true;
}
// Cast op::Array<float> to numpy
static handle cast(const op::Array<float> &m, return_value_policy, handle defval)
{
std::string format = format_descriptor<float>::format();
return array(buffer_info(
m.getPseudoConstPtr(),/* Pointer to buffer */
sizeof(float), /* Size of one scalar */
format, /* Python struct-style format descriptor */
m.getSize().size(), /* Number of dimensions */
m.getSize(), /* Buffer dimensions */
m.getStride() /* Strides (in bytes) for each index */
)).release();
}
};
}} // namespace pybind11::detail
// Numpy - cv::Mat interop
namespace pybind11 { namespace detail {
template <> struct type_caster<cv::Mat> {
public:
PYBIND11_TYPE_CASTER(cv::Mat, _("numpy.ndarray"));
// Cast numpy to cv::Mat
bool load(handle src, bool)
{
/* Try a default converting into a Python */
//array b(src, true);
array b = reinterpret_borrow<array>(src);
buffer_info info = b.request();
int ndims = info.ndim;
decltype(CV_32F) dtype;
size_t elemsize;
if (info.format == format_descriptor<float>::format()) {
if (ndims == 3) {
dtype = CV_32FC3;
} else {
dtype = CV_32FC1;
}
elemsize = sizeof(float);
} else if (info.format == format_descriptor<double>::format()) {
if (ndims == 3) {
dtype = CV_64FC3;
} else {
dtype = CV_64FC1;
}
elemsize = sizeof(double);
} else if (info.format == format_descriptor<unsigned char>::format()) {
if (ndims == 3) {
dtype = CV_8UC3;
} else {
dtype = CV_8UC1;
}
elemsize = sizeof(unsigned char);
} else {
throw std::logic_error("Unsupported type");
return false;
}
std::vector<int> shape = {(int)info.shape[0], (int)info.shape[1]};
value = cv::Mat(cv::Size(shape[1], shape[0]), dtype, info.ptr, cv::Mat::AUTO_STEP);
return true;
}
// Cast cv::Mat to numpy
static handle cast(const cv::Mat &m, return_value_policy, handle defval)
{
std::string format = format_descriptor<unsigned char>::format();
size_t elemsize = sizeof(unsigned char);
int dim;
switch(m.type()) {
case CV_8U:
format = format_descriptor<unsigned char>::format();
elemsize = sizeof(unsigned char);
dim = 2;
break;
case CV_8UC3:
format = format_descriptor<unsigned char>::format();
elemsize = sizeof(unsigned char);
dim = 3;
break;
case CV_32F:
format = format_descriptor<float>::format();
elemsize = sizeof(float);
dim = 2;
break;
case CV_64F:
format = format_descriptor<double>::format();
elemsize = sizeof(double);
dim = 2;
break;
default:
throw std::logic_error("Unsupported type");
}
std::vector<size_t> bufferdim;
std::vector<size_t> strides;
if (dim == 2) {
bufferdim = {(size_t) m.rows, (size_t) m.cols};
strides = {elemsize * (size_t) m.cols, elemsize};
} else if (dim == 3) {
bufferdim = {(size_t) m.rows, (size_t) m.cols, (size_t) 3};
strides = {(size_t) elemsize * m.cols * 3, (size_t) elemsize * 3, (size_t) elemsize};
}
return array(buffer_info(
m.data, /* Pointer to buffer */
elemsize, /* Size of one scalar */
format, /* Python struct-style format descriptor */
dim, /* Number of dimensions */
bufferdim, /* Buffer dimensions */
strides /* Strides (in bytes) for each index */
)).release();
}
};
}} // namespace pybind11::detail
#endif
-243
View File
@@ -1,243 +0,0 @@
"""
Wrap the OpenPose library with Python.
To install run `make install` and library will be stored in /usr/local/python
"""
import numpy as np
import ctypes as ct
import cv2
import os
from sys import platform
dir_path = os.path.dirname(os.path.realpath(__file__))
if platform == "win32":
os.environ['PATH'] = dir_path + "/../../bin;" + os.environ['PATH']
os.environ['PATH'] = dir_path + "/../../x64/Debug;" + os.environ['PATH']
os.environ['PATH'] = dir_path + "/../../x64/Release;" + os.environ['PATH']
class OpenPose(object):
"""
Ctypes linkage
"""
if platform == "linux" or platform == "linux2":
_libop= np.ctypeslib.load_library('_openpose', dir_path+'/_openpose.so')
elif platform == "darwin":
_libop= np.ctypeslib.load_library('_openpose', dir_path+'/_openpose.dylib')
elif platform == "win32":
try:
_libop= np.ctypeslib.load_library('_openpose', dir_path+'/Release/_openpose.dll')
except OSError as e:
_libop= np.ctypeslib.load_library('_openpose', dir_path+'/Debug/_openpose.dll')
_libop.newOP.argtypes = [
ct.c_int, ct.c_char_p, ct.c_char_p, ct.c_char_p, ct.c_float, ct.c_float, ct.c_int, ct.c_float, ct.c_int, ct.c_bool, ct.c_char_p]
_libop.newOP.restype = ct.c_void_p
_libop.delOP.argtypes = [ct.c_void_p]
_libop.delOP.restype = None
_libop.forward.argtypes = [
ct.c_void_p, np.ctypeslib.ndpointer(dtype=np.uint8),
ct.c_size_t, ct.c_size_t,
np.ctypeslib.ndpointer(dtype=np.int32), np.ctypeslib.ndpointer(dtype=np.uint8), ct.c_bool]
_libop.forward.restype = None
_libop.getOutputs.argtypes = [
ct.c_void_p, np.ctypeslib.ndpointer(dtype=np.float32)]
_libop.getOutputs.restype = None
_libop.poseFromHeatmap.argtypes = [
ct.c_void_p, np.ctypeslib.ndpointer(dtype=np.uint8),
ct.c_size_t, ct.c_size_t,
np.ctypeslib.ndpointer(dtype=np.uint8),
np.ctypeslib.ndpointer(dtype=np.float32), np.ctypeslib.ndpointer(dtype=np.int32), np.ctypeslib.ndpointer(dtype=np.float32)]
_libop.poseFromHeatmap.restype = None
def encode(self, string):
return ct.c_char_p(string.encode('utf-8'))
def __init__(self, params):
"""
OpenPose Constructor: Prepares OpenPose object
Parameters
----------
params : dict of required parameters. refer to openpose example for more details
Returns
-------
outs: OpenPose object
"""
self.op = self._libop.newOP(params["logging_level"],
self.encode(params["output_resolution"]),
self.encode(params["net_resolution"]),
self.encode(params["model_pose"]),
params["alpha_pose"],
params["scale_gap"],
params["scale_number"],
params["render_threshold"],
params["num_gpu_start"],
params["disable_blending"],
self.encode(params["default_model_folder"]))
def __del__(self):
"""
OpenPose Destructor: Destroys OpenPose object
"""
self._libop.delOP(self.op)
def forward(self, image, display = False):
"""
Forward: Takes in an image and returns the human 2D poses, along with drawn image if required
Parameters
----------
image : color image of type ndarray
display : If set to true, we return both the pose and an annotated image for visualization
Returns
-------
array: ndarray of human 2D poses [People * BodyPart * XYConfidence]
displayImage : image for visualization
"""
shape = image.shape
displayImage = np.zeros(shape=(image.shape),dtype=np.uint8)
size = np.zeros(shape=(3),dtype=np.int32)
self._libop.forward(self.op, image, shape[0], shape[1], size, displayImage, display)
array = np.zeros(shape=(size),dtype=np.float32)
self._libop.getOutputs(self.op, array)
if display:
return array, displayImage
return array
def poseFromHM(self, image, hm, ratios=[1]):
"""
Pose From Heatmap: Takes in an image, computed heatmaps, and require scales and computes pose
Parameters
----------
image : color image of type ndarray
hm : heatmap of type ndarray with heatmaps and part affinity fields
ratios : scaling ration if needed to fuse multiple scales
Returns
-------
array: ndarray of human 2D poses [People * BodyPart * XYConfidence]
displayImage : image for visualization
"""
if len(ratios) != len(hm):
raise Exception("Ratio shape mismatch")
# Find largest
hm_combine = np.zeros(shape=(len(hm), hm[0].shape[1], hm[0].shape[2], hm[0].shape[3]),dtype=np.float32)
i=0
for h in hm:
hm_combine[i,:,0:h.shape[2],0:h.shape[3]] = h
i+=1
hm = hm_combine
ratios = np.array(ratios,dtype=np.float32)
shape = image.shape
displayImage = np.zeros(shape=(image.shape),dtype=np.uint8)
size = np.zeros(shape=(4),dtype=np.int32)
size[0] = hm.shape[0]
size[1] = hm.shape[1]
size[2] = hm.shape[2]
size[3] = hm.shape[3]
self._libop.poseFromHeatmap(self.op, image, shape[0], shape[1], displayImage, hm, size, ratios)
array = np.zeros(shape=(size[0],size[1],size[2]),dtype=np.float32)
self._libop.getOutputs(self.op, array)
return array, displayImage
@staticmethod
def process_frames(frame, boxsize = 368, scales = [1]):
base_net_res = None
imagesForNet = []
imagesOrig = []
for idx, scale in enumerate(scales):
# Calculate net resolution (width, height)
if idx == 0:
net_res = (16 * int((boxsize * frame.shape[1] / float(frame.shape[0]) / 16) + 0.5), boxsize)
base_net_res = net_res
else:
net_res = (int(min(base_net_res[0], max(1, int((base_net_res[0] * scale)+0.5)/16*16))),
int(min(base_net_res[1], max(1, int((base_net_res[1] * scale)+0.5)/16*16))))
input_res = [frame.shape[1], frame.shape[0]]
scale_factor = min((net_res[0] - 1) / float(input_res[0] - 1), (net_res[1] - 1) / float(input_res[1] - 1))
warp_matrix = np.array([[scale_factor,0,0],
[0,scale_factor,0]])
if scale_factor != 1:
imageForNet = cv2.warpAffine(frame, warp_matrix, net_res, flags=(cv2.INTER_AREA if scale_factor < 1. else cv2.INTER_CUBIC), borderMode=cv2.BORDER_CONSTANT, borderValue=(0,0,0))
else:
imageForNet = frame.copy()
imageOrig = imageForNet.copy()
imageForNet = imageForNet.astype(float)
imageForNet = imageForNet/256. - 0.5
imageForNet = np.transpose(imageForNet, (2,0,1))
imagesForNet.append(imageForNet)
imagesOrig.append(imageOrig)
return imagesForNet, imagesOrig
@staticmethod
def draw_all(imageForNet, heatmaps, currIndex, div=4., norm=False):
netDecreaseFactor = float(imageForNet.shape[0]) / float(heatmaps.shape[2]) # 8
resized_heatmaps = np.zeros(shape=(heatmaps.shape[0], heatmaps.shape[1], imageForNet.shape[0], imageForNet.shape[1]))
num_maps = heatmaps.shape[1]
combined = None
for i in range(0, num_maps):
heatmap = heatmaps[0,i,:,:]
resizedHeatmap = cv2.resize(heatmap, (0,0), fx=netDecreaseFactor, fy=netDecreaseFactor)
minVal, maxVal, minLoc, maxLoc = cv2.minMaxLoc(resizedHeatmap)
if i==currIndex and currIndex >=0:
resizedHeatmap = np.abs(resizedHeatmap)
resizedHeatmap = (resizedHeatmap*255.).astype(dtype='uint8')
im_color = cv2.applyColorMap(resizedHeatmap, cv2.COLORMAP_JET)
resizedHeatmap = cv2.addWeighted(imageForNet, 1, im_color, 0.3, 0)
cv2.circle(resizedHeatmap, (int(maxLoc[0]),int(maxLoc[1])), 5, (255,0,0), -1)
return resizedHeatmap
else:
resizedHeatmap = np.abs(resizedHeatmap)
if combined is None:
combined = np.copy(resizedHeatmap);
else:
if i <= num_maps-2:
combined += resizedHeatmap;
if norm:
combined = np.maximum(0, np.minimum(1, combined));
if currIndex < 0:
combined /= div
combined = (combined*255.).astype(dtype='uint8')
im_color = cv2.applyColorMap(combined, cv2.COLORMAP_JET)
combined = cv2.addWeighted(imageForNet, 0.5, im_color, 0.5, 0)
cv2.circle(combined, (int(maxLoc[0]),int(maxLoc[1])), 5, (255,0,0), -1)
return combined
if __name__ == "__main__":
params = dict()
params["logging_level"] = 3
params["output_resolution"] = "-1x-1"
params["net_resolution"] = "-1x368"
params["model_pose"] = "BODY_25"
params["alpha_pose"] = 0.6
params["scale_gap"] = 0.25
params["scale_number"] = 1
params["render_threshold"] = 0.05
params["num_gpu_start"] = 0
params["disable_blending"] = False
params["default_model_folder"] = "../../../models/"
openpose = OpenPose(params)
img = cv2.imread("../../../examples/media/COCO_val2014_000000000192.jpg")
arr, output_image = openpose.forward(img, True)
print(arr)
while 1:
cv2.imshow("output", output_image)
cv2.waitKey(15)