mirror of
https://github.com/gosticks/openpose.git
synced 2026-08-12 12:20:22 +00:00
OpenPose - 1st commit
This commit is contained in:
@@ -0,0 +1,195 @@
|
||||
// ------------------------- OpenPose Library Tutorial - Thread - Example 1 - OpenPose Read And Display -------------------------
|
||||
// This third example shows the user how to:
|
||||
// 1. Read folder of images / video / webcam (`producer` module)
|
||||
// 2. Display the rendered pose (`gui` module)
|
||||
// Everything in a multi-thread scenario (`thread` module)
|
||||
// In addition to the previous OpenPose modules, we also need to use:
|
||||
// 1. `core` module: for the Datum struct that the `thread` module sends between the queues
|
||||
// 2. `utilities` module: for the error & logging functions, i.e. op::error & op::log respectively
|
||||
|
||||
// 3rdpary depencencies
|
||||
#include <gflags/gflags.h> // DEFINE_bool, DEFINE_int32, DEFINE_int64, DEFINE_uint64, DEFINE_double, DEFINE_string
|
||||
#include <glog/logging.h> // google::InitGoogleLogging, CHECK, CHECK_EQ, LOG, VLOG, ...
|
||||
// OpenPose dependencies
|
||||
#include <openpose/core/headers.hpp>
|
||||
#include <openpose/gui/headers.hpp>
|
||||
#include <openpose/producer/headers.hpp>
|
||||
#include <openpose/thread/headers.hpp>
|
||||
#include <openpose/utilities/headers.hpp>
|
||||
|
||||
// Gflags in the command line terminal. Check all the options by adding the flag `--help`, e.g. `rtpose.bin --help`.
|
||||
// Note: This command will show you flags for several files. Check only the flags for the file you are checking. E.g. for `rtpose`, look for `Flags from examples/openpose/rtpose.cpp:`.
|
||||
// Debugging
|
||||
DEFINE_int32(logging_level, 3, "The logging level. Integer in the range [0, 255]. 0 will output any log() message, while 255 will not output any."
|
||||
" Current OpenPose library messages are in the range 0-4: 1 for low priority messages and 4 for important ones.");
|
||||
// Producer
|
||||
DEFINE_int32(camera, 0, "The camera index for cv::VideoCapture. Integer in the range [0, 9].");
|
||||
DEFINE_string(camera_resolution, "1280x720", "Size of the camera frames to ask for.");
|
||||
DEFINE_string(video, "", "Use a video file instead of the camera. Use `examples/media/video.avi` for our default example video.");
|
||||
DEFINE_string(image_dir, "", "Process a directory of images. Use `examples/media/` for our default example folder with 20 images.");
|
||||
// OpenPose
|
||||
DEFINE_string(resolution, "1280x720", "The image resolution (display). Use \"-1x-1\" to force the program to use the default images resolution.");
|
||||
// Consumer
|
||||
DEFINE_bool(fullscreen, false, "Run in full-screen mode (press f during runtime to toggle).");
|
||||
DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is too long, it will skip frames. If it is"
|
||||
" too fast, it will slow it down.");
|
||||
|
||||
// Determine type of frame source
|
||||
op::ProducerType gflagsToProducerType(const std::string& imageDirectory, const std::string& videoPath, const int webcamIndex)
|
||||
{
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
// Avoid duplicates (e.g. selecting at the time camera & video)
|
||||
if (!imageDirectory.empty() && !videoPath.empty())
|
||||
op::error("Selected simultaneously image directory and video. Please, select only one.", __LINE__, __FUNCTION__, __FILE__);
|
||||
else if (!imageDirectory.empty() && webcamIndex != 0)
|
||||
op::error("Selected simultaneously image directory and webcam. Please, select only one.", __LINE__, __FUNCTION__, __FILE__);
|
||||
else if (!videoPath.empty() && webcamIndex != 0)
|
||||
op::error("Selected simultaneously video and webcam. Please, select only one.", __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// Get desired op::ProducerType
|
||||
if (!imageDirectory.empty())
|
||||
return op::ProducerType::ImageDirectory;
|
||||
else if (!videoPath.empty())
|
||||
return op::ProducerType::Video;
|
||||
else
|
||||
return op::ProducerType::Webcam;
|
||||
}
|
||||
|
||||
std::shared_ptr<op::Producer> gflagsToProducer(const std::string& imageDirectory, const std::string& videoPath, const int webcamIndex, const cv::Size webcamResolution)
|
||||
{
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
const auto type = gflagsToProducerType(imageDirectory, videoPath, webcamIndex);
|
||||
|
||||
if (type == op::ProducerType::ImageDirectory)
|
||||
return std::make_shared<op::ImageDirectoryReader>(imageDirectory);
|
||||
else if (type == op::ProducerType::Video)
|
||||
return std::make_shared<op::VideoReader>(videoPath);
|
||||
else if (type == op::ProducerType::Webcam)
|
||||
return std::make_shared<op::WebcamReader>(webcamIndex, webcamResolution);
|
||||
else
|
||||
{
|
||||
op::error("Undefined Producer selected.", __LINE__, __FUNCTION__, __FILE__);
|
||||
return std::shared_ptr<op::Producer>{};
|
||||
}
|
||||
}
|
||||
|
||||
// Google flags into program variables
|
||||
std::tuple<cv::Size, cv::Size, std::shared_ptr<op::Producer>> gflagsToOpParameters()
|
||||
{
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
// cameraFrameSize
|
||||
cv::Size cameraFrameSize;
|
||||
auto nRead = sscanf(FLAGS_camera_resolution.c_str(), "%dx%d", &cameraFrameSize.width, &cameraFrameSize.height);
|
||||
op::checkE(nRead, 2, "Error, camera resolution format (" + FLAGS_camera_resolution + ") invalid, should be e.g., 1280x720", __LINE__, __FUNCTION__, __FILE__);
|
||||
// outputSize
|
||||
cv::Size outputSize;
|
||||
nRead = sscanf(FLAGS_resolution.c_str(), "%dx%d", &outputSize.width, &outputSize.height);
|
||||
op::checkE(nRead, 2, "Error, resolution format (" + FLAGS_resolution + ") invalid, should be e.g., 960x540 ", __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// producerType
|
||||
const auto producerSharedPtr = gflagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_camera, cameraFrameSize);
|
||||
const auto displayProducerFpsMode = (FLAGS_process_real_time ? op::ProducerFpsMode::OriginalFps : op::ProducerFpsMode::RetrievalFps);
|
||||
producerSharedPtr->setProducerFpsMode(displayProducerFpsMode);
|
||||
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
return std::make_tuple(cameraFrameSize, outputSize, producerSharedPtr);
|
||||
}
|
||||
|
||||
int openPoseTutorialThread1()
|
||||
{
|
||||
op::log("OpenPose Library Tutorial - Example 3.", op::Priority::Max);
|
||||
// ------------------------- INITIALIZATION -------------------------
|
||||
// Step 1 - Set logging level
|
||||
// - 0 will output all the logging messages
|
||||
// - 255 will output nothing
|
||||
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);
|
||||
// Step 2 - Read Google flags (user defined configuration)
|
||||
cv::Size cameraFrameSize;
|
||||
cv::Size outputSize;
|
||||
std::shared_ptr<op::Producer> producerSharedPtr;
|
||||
std::tie(cameraFrameSize, outputSize, producerSharedPtr) = gflagsToOpParameters();
|
||||
// Step 3 - Setting producer
|
||||
auto videoSeekSharedPtr = std::make_shared<std::pair<std::atomic<bool>, std::atomic<int>>>();
|
||||
videoSeekSharedPtr->first = false;
|
||||
videoSeekSharedPtr->second = 0;
|
||||
const cv::Size producerSize{(int)producerSharedPtr->get(CV_CAP_PROP_FRAME_WIDTH),
|
||||
(int)producerSharedPtr->get(CV_CAP_PROP_FRAME_HEIGHT)};
|
||||
if (outputSize.width == -1 || outputSize.height == -1)
|
||||
{
|
||||
if (producerSize.area() > 0)
|
||||
outputSize = producerSize;
|
||||
else
|
||||
op::error("Output resolution = input resolution not valid for image reading (size might change between images).", __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
// Step 4 - Setting thread workers && manager
|
||||
typedef std::vector<op::Datum> TypedefDatumsNoPtr;
|
||||
typedef std::shared_ptr<TypedefDatumsNoPtr> TypedefDatums;
|
||||
op::ThreadManager<TypedefDatums> threadManager;
|
||||
// Step 5 - Initializing the worker classes
|
||||
// Frames producer (e.g. video, webcam, ...)
|
||||
auto DatumProducer = std::make_shared<op::DatumProducer<TypedefDatumsNoPtr>>(producerSharedPtr);
|
||||
auto wDatumProducer = std::make_shared<op::WDatumProducer<TypedefDatums, TypedefDatumsNoPtr>>(DatumProducer);
|
||||
// GUI (Display)
|
||||
auto gui = std::make_shared<op::Gui>(FLAGS_fullscreen, outputSize, threadManager.getIsRunningSharedPtr());
|
||||
auto wGui = std::make_shared<op::WGui<TypedefDatums>>(gui);
|
||||
|
||||
// ------------------------- CONFIGURING THREADING -------------------------
|
||||
// In this simple multi-thread example, we will do the following:
|
||||
// 3 (virtual) queues: 0, 1, 2
|
||||
// 1 real queue: 1. The first and last queue ids (in this case 0 and 2) are not actual queues, but the beginning and end of the processing sequence
|
||||
// 2 threads: 0, 1
|
||||
// wDatumProducer will generate frames (there is no real queue 0) and push them on queue 1
|
||||
// wGui will pop frames from queue 1 and process them (there is no real queue 2)
|
||||
auto threadId = 0ull;
|
||||
auto queueIn = 0ull;
|
||||
auto queueOut = 1ull;
|
||||
threadManager.add(threadId++, wDatumProducer, queueIn++, queueOut++); // Thread 0, queues 0 -> 1
|
||||
threadManager.add(threadId++, wGui, queueIn++, queueOut++); // Thread 1, queues 1 -> 2
|
||||
|
||||
// Equivalent single-thread version (option a)
|
||||
// const auto threadId = 0ull;
|
||||
// auto queueIn = 0ull;
|
||||
// auto queueOut = 1ull;
|
||||
// threadManager.add(threadId, wDatumProducer, queueIn++, queueOut++); // Thread 0, queues 0 -> 1
|
||||
// threadManager.add(threadId, wGui, queueIn++, queueOut++); // Thread 0, queues 1 -> 2
|
||||
|
||||
// Equivalent single-thread version (option b)
|
||||
// const auto threadId = 0ull;
|
||||
// const auto queueIn = 0ull;
|
||||
// const auto queueOut = 1ull;
|
||||
// threadManager.add(threadId, {wDatumProducer, wGui}, queueIn, queueOut); // Thread 0, queues 0 -> 1
|
||||
|
||||
// ------------------------- STARTING AND STOPPING THREADING -------------------------
|
||||
op::log("Starting thread(s)", op::Priority::Max);
|
||||
// Two different ways of running the program on multithread enviroment
|
||||
// Option a) Using the main thread (this thread) for processing (it saves 1 thread, recommended)
|
||||
// threadManager.exec(); // It blocks this thread until all threads have finished
|
||||
// Option b) Giving to the user the control of this thread
|
||||
// Start threads
|
||||
threadManager.start();
|
||||
// Keep program alive while running threads. Here the user could perform any other desired function
|
||||
while (threadManager.isRunning())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{33});
|
||||
// Stop and join threads
|
||||
op::log("Stopping thread(s)", op::Priority::Max);
|
||||
threadManager.stop();
|
||||
|
||||
// ------------------------- CLOSING -------------------------
|
||||
// Logging information message
|
||||
op::log("Example 3 successfully finished.", op::Priority::Max);
|
||||
// Return successful message
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Initializing google logging (Caffe uses it for logging)
|
||||
google::InitGoogleLogging("openPoseTutorialThread1");
|
||||
|
||||
// Parsing command line flags
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
// Running openPoseTutorialThread1
|
||||
return openPoseTutorialThread1();
|
||||
}
|
||||
@@ -0,0 +1,237 @@
|
||||
// ------------------------- OpenPose Library Tutorial - Thread - Example 2 - User Processing Function -------------------------
|
||||
// This fourth example shows the user how to:
|
||||
// 1. Read folder of images / video / webcam (`producer` module)
|
||||
// 2. Use the processing implemented by the user
|
||||
// 3. Display the rendered pose (`gui` module)
|
||||
// Everything in a multi-thread scenario (`thread` module)
|
||||
// In addition to the previous OpenPose modules, we also need to use:
|
||||
// 1. `core` module: for the Datum struct that the `thread` module sends between the queues
|
||||
// 2. `utilities` module: for the error & logging functions, i.e. op::error & op::log respectively
|
||||
|
||||
// 3rdpary depencencies
|
||||
#include <gflags/gflags.h> // DEFINE_bool, DEFINE_int32, DEFINE_int64, DEFINE_uint64, DEFINE_double, DEFINE_string
|
||||
#include <glog/logging.h> // google::InitGoogleLogging, CHECK, CHECK_EQ, LOG, VLOG, ...
|
||||
// OpenPose dependencies
|
||||
#include <openpose/core/headers.hpp>
|
||||
#include <openpose/gui/headers.hpp>
|
||||
#include <openpose/producer/headers.hpp>
|
||||
#include <openpose/thread/headers.hpp>
|
||||
#include <openpose/utilities/headers.hpp>
|
||||
|
||||
// Gflags in the command line terminal. Check all the options by adding the flag `--help`, e.g. `rtpose.bin --help`.
|
||||
// Note: This command will show you flags for several files. Check only the flags for the file you are checking. E.g. for `rtpose`, look for `Flags from examples/openpose/rtpose.cpp:`.
|
||||
// Debugging
|
||||
DEFINE_int32(logging_level, 3, "The logging level. Integer in the range [0, 255]. 0 will output any log() message, while 255 will not output any."
|
||||
" Current OpenPose library messages are in the range 0-4: 1 for low priority messages and 4 for important ones.");
|
||||
// Producer
|
||||
DEFINE_int32(camera, 0, "The camera index for cv::VideoCapture. Integer in the range [0, 9].");
|
||||
DEFINE_string(camera_resolution, "1280x720", "Size of the camera frames to ask for.");
|
||||
DEFINE_string(video, "", "Use a video file instead of the camera. Use `examples/media/video.avi` for our default example video.");
|
||||
DEFINE_string(image_dir, "", "Process a directory of images. Use `examples/media/` for our default example folder with 20 images.");
|
||||
// OpenPose
|
||||
DEFINE_string(resolution, "1280x720", "The image resolution (display). Use \"-1x-1\" to force the program to use the default images resolution.");
|
||||
// Consumer
|
||||
DEFINE_bool(fullscreen, false, "Run in full-screen mode (press f during runtime to toggle).");
|
||||
DEFINE_bool(process_real_time, false, "Enable to keep the original source frame rate (e.g. for video). If the processing time is too long, it will skip frames. If it is"
|
||||
" too fast, it will slow it down.");
|
||||
|
||||
// This class can be implemented either as a template or as a simple class given
|
||||
// that the user usually knows which kind of data he will move between the queues,
|
||||
// in this case we assume a std::shared_ptr of a std::vector of op::Datum
|
||||
class WUserClass : public op::Worker<std::shared_ptr<std::vector<op::Datum>>>
|
||||
{
|
||||
public:
|
||||
WUserClass()
|
||||
{
|
||||
// User's constructor here
|
||||
}
|
||||
|
||||
void initializationOnThread() {}
|
||||
|
||||
void work(std::shared_ptr<std::vector<op::Datum>>& datumsPtr)
|
||||
{
|
||||
try
|
||||
{
|
||||
// User's processing here
|
||||
// datum.cvInputData: initial cv::Mat obtained from the frames producer (video, webcam, etc.)
|
||||
// datum.cvOutputData: final cv::Mat to be displayed
|
||||
if (datumsPtr != nullptr)
|
||||
for (auto& datum : *datumsPtr)
|
||||
cv::bitwise_not(datum.cvInputData, datum.cvOutputData);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
op::log("Some kind of unexpected error happened.");
|
||||
this->stop();
|
||||
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Determine type of frame source
|
||||
op::ProducerType gflagsToProducerType(const std::string& imageDirectory, const std::string& videoPath, const int webcamIndex)
|
||||
{
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
// Avoid duplicates (e.g. selecting at the time camera & video)
|
||||
if (!imageDirectory.empty() && !videoPath.empty())
|
||||
op::error("Selected simultaneously image directory and video. Please, select only one.", __LINE__, __FUNCTION__, __FILE__);
|
||||
else if (!imageDirectory.empty() && webcamIndex != 0)
|
||||
op::error("Selected simultaneously image directory and webcam. Please, select only one.", __LINE__, __FUNCTION__, __FILE__);
|
||||
else if (!videoPath.empty() && webcamIndex != 0)
|
||||
op::error("Selected simultaneously video and webcam. Please, select only one.", __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// Get desired op::ProducerType
|
||||
if (!imageDirectory.empty())
|
||||
return op::ProducerType::ImageDirectory;
|
||||
else if (!videoPath.empty())
|
||||
return op::ProducerType::Video;
|
||||
else
|
||||
return op::ProducerType::Webcam;
|
||||
}
|
||||
|
||||
std::shared_ptr<op::Producer> gflagsToProducer(const std::string& imageDirectory, const std::string& videoPath, const int webcamIndex, const cv::Size webcamResolution)
|
||||
{
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
const auto type = gflagsToProducerType(imageDirectory, videoPath, webcamIndex);
|
||||
|
||||
if (type == op::ProducerType::ImageDirectory)
|
||||
return std::make_shared<op::ImageDirectoryReader>(imageDirectory);
|
||||
else if (type == op::ProducerType::Video)
|
||||
return std::make_shared<op::VideoReader>(videoPath);
|
||||
else if (type == op::ProducerType::Webcam)
|
||||
return std::make_shared<op::WebcamReader>(webcamIndex, webcamResolution);
|
||||
else
|
||||
{
|
||||
op::error("Undefined Producer selected.", __LINE__, __FUNCTION__, __FILE__);
|
||||
return std::shared_ptr<op::Producer>{};
|
||||
}
|
||||
}
|
||||
|
||||
// Google flags into program variables
|
||||
std::tuple<cv::Size, cv::Size, std::shared_ptr<op::Producer>> gflagsToOpParameters()
|
||||
{
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
// cameraFrameSize
|
||||
cv::Size cameraFrameSize;
|
||||
auto nRead = sscanf(FLAGS_camera_resolution.c_str(), "%dx%d", &cameraFrameSize.width, &cameraFrameSize.height);
|
||||
op::checkE(nRead, 2, "Error, camera resolution format (" + FLAGS_camera_resolution + ") invalid, should be e.g., 1280x720", __LINE__, __FUNCTION__, __FILE__);
|
||||
// outputSize
|
||||
cv::Size outputSize;
|
||||
nRead = sscanf(FLAGS_resolution.c_str(), "%dx%d", &outputSize.width, &outputSize.height);
|
||||
op::checkE(nRead, 2, "Error, resolution format (" + FLAGS_resolution + ") invalid, should be e.g., 960x540 ", __LINE__, __FUNCTION__, __FILE__);
|
||||
|
||||
// producerType
|
||||
const auto producerSharedPtr = gflagsToProducer(FLAGS_image_dir, FLAGS_video, FLAGS_camera, cameraFrameSize);
|
||||
const auto displayProducerFpsMode = (FLAGS_process_real_time ? op::ProducerFpsMode::OriginalFps : op::ProducerFpsMode::RetrievalFps);
|
||||
producerSharedPtr->setProducerFpsMode(displayProducerFpsMode);
|
||||
|
||||
op::log("", op::Priority::Low, __LINE__, __FUNCTION__, __FILE__);
|
||||
return std::make_tuple(cameraFrameSize, outputSize, producerSharedPtr);
|
||||
}
|
||||
|
||||
int openPoseTutorialThread2()
|
||||
{
|
||||
op::log("OpenPose Library Tutorial - Example 3.", op::Priority::Max);
|
||||
// ------------------------- INITIALIZATION -------------------------
|
||||
// Step 1 - Set logging level
|
||||
// - 0 will output all the logging messages
|
||||
// - 255 will output nothing
|
||||
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);
|
||||
// Step 2 - Read Google flags (user defined configuration)
|
||||
cv::Size cameraFrameSize;
|
||||
cv::Size outputSize;
|
||||
std::shared_ptr<op::Producer> producerSharedPtr;
|
||||
std::tie(cameraFrameSize, outputSize, producerSharedPtr) = gflagsToOpParameters();
|
||||
// Step 3 - Setting producer
|
||||
auto videoSeekSharedPtr = std::make_shared<std::pair<std::atomic<bool>, std::atomic<int>>>();
|
||||
videoSeekSharedPtr->first = false;
|
||||
videoSeekSharedPtr->second = 0;
|
||||
const cv::Size producerSize{(int)producerSharedPtr->get(CV_CAP_PROP_FRAME_WIDTH),
|
||||
(int)producerSharedPtr->get(CV_CAP_PROP_FRAME_HEIGHT)};
|
||||
if (outputSize.width == -1 || outputSize.height == -1)
|
||||
{
|
||||
if (producerSize.area() > 0)
|
||||
outputSize = producerSize;
|
||||
else
|
||||
op::error("Output resolution = input resolution not valid for image reading (size might change between images).", __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
// Step 4 - Setting thread workers && manager
|
||||
typedef std::vector<op::Datum> TypedefDatumsNoPtr;
|
||||
typedef std::shared_ptr<TypedefDatumsNoPtr> TypedefDatums;
|
||||
op::ThreadManager<TypedefDatums> threadManager;
|
||||
// Step 5 - Initializing the worker classes
|
||||
// Frames producer (e.g. video, webcam, ...)
|
||||
auto DatumProducer = std::make_shared<op::DatumProducer<TypedefDatumsNoPtr>>(producerSharedPtr);
|
||||
auto wDatumProducer = std::make_shared<op::WDatumProducer<TypedefDatums, TypedefDatumsNoPtr>>(DatumProducer);
|
||||
// Specific WUserClass
|
||||
auto wUserClass = std::make_shared<WUserClass>();
|
||||
// GUI (Display)
|
||||
auto gui = std::make_shared<op::Gui>(FLAGS_fullscreen, outputSize, threadManager.getIsRunningSharedPtr());
|
||||
auto wGui = std::make_shared<op::WGui<TypedefDatums>>(gui);
|
||||
|
||||
// ------------------------- CONFIGURING THREADING -------------------------
|
||||
// In this simple multi-thread example, we will do the following:
|
||||
// 3 (virtual) queues: 0, 1, 2
|
||||
// 1 real queue: 1. The first and last queue ids (in this case 0 and 2) are not actual queues, but the beginning and end of the processing sequence
|
||||
// 2 threads: 0, 1
|
||||
// wDatumProducer will generate frames (there is no real queue 0) and push them on queue 1
|
||||
// wGui will pop frames from queue 1 and process them (there is no real queue 2)
|
||||
auto threadId = 0ull;
|
||||
auto queueIn = 0ull;
|
||||
auto queueOut = 1ull;
|
||||
threadManager.add(threadId++, wDatumProducer, queueIn++, queueOut++); // Thread 0, queues 0 -> 1
|
||||
threadManager.add(threadId++, wUserClass, queueIn++, queueOut++); // Thread 1, queues 1 -> 2
|
||||
threadManager.add(threadId++, wGui, queueIn++, queueOut++); // Thread 2, queues 2 -> 3
|
||||
|
||||
// Equivalent single-thread version
|
||||
// const auto threadId = 0ull;
|
||||
// auto queueIn = 0ull;
|
||||
// auto queueOut = 1ull;
|
||||
// threadManager.add(threadId, wDatumProducer, queueIn++, queueOut++); // Thread 0, queues 0 -> 1
|
||||
// threadManager.add(threadId, wUserClass, queueIn++, queueOut++); // Thread 1, queues 1 -> 2
|
||||
// threadManager.add(threadId, wGui, queueIn++, queueOut++); // Thread 2, queues 2 -> 3
|
||||
|
||||
// Smart multi-thread version
|
||||
// Assume wUser is the slowest process, and that wDatumProducer + wGui is faster than wGui itself,
|
||||
// then, we can group the last 2 in the same thread and keep wGui in a different thread:
|
||||
// const auto threadId = 0ull;
|
||||
// auto queueIn = 0ull;
|
||||
// auto queueOut = 1ull;
|
||||
// threadManager.add(threadId, wDatumProducer, queueIn++, queueOut++); // Thread 0, queues 0 -> 1
|
||||
// threadManager.add(threadId+1, wUserClass, queueIn++, queueOut++); // Thread 1, queues 1 -> 2
|
||||
// threadManager.add(threadId, wGui, queueIn++, queueOut++); // Thread 0, queues 2 -> 3
|
||||
|
||||
// ------------------------- STARTING AND STOPPING THREADING -------------------------
|
||||
op::log("Starting thread(s)", op::Priority::Max);
|
||||
// Two different ways of running the program on multithread enviroment
|
||||
// Option a) Using the main thread (this thread) for processing (it saves 1 thread, recommended)
|
||||
// threadManager.exec(); // It blocks this thread until all threads have finished
|
||||
// Option b) Giving to the user the control of this thread
|
||||
// Start threads
|
||||
threadManager.start();
|
||||
// Keep program alive while running threads. Here the user could perform any other desired function
|
||||
while (threadManager.isRunning())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{33});
|
||||
// Stop and join threads
|
||||
op::log("Stopping thread(s)", op::Priority::Max);
|
||||
threadManager.stop();
|
||||
|
||||
// ------------------------- CLOSING -------------------------
|
||||
// Logging information message
|
||||
op::log("Example 3 successfully finished.", op::Priority::Max);
|
||||
// Return successful message
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Initializing google logging (Caffe uses it for logging)
|
||||
google::InitGoogleLogging("openPoseTutorialThread2");
|
||||
|
||||
// Parsing command line flags
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
// Running openPoseTutorialThread2
|
||||
return openPoseTutorialThread2();
|
||||
}
|
||||
@@ -0,0 +1,228 @@
|
||||
// ------------------------- OpenPose Library Tutorial - Thread - Example 3 - User Input Processing And Output -------------------------
|
||||
// This fourth example shows the user how to:
|
||||
// 1. Read folder of images / video / webcam (`producer` module)
|
||||
// 2. Use the processing implemented by the user
|
||||
// 3. Display the rendered pose (`gui` module)
|
||||
// Everything in a multi-thread scenario (`thread` module)
|
||||
// In addition to the previous OpenPose modules, we also need to use:
|
||||
// 1. `core` module: for the Datum struct that the `thread` module sends between the queues
|
||||
// 2. `utilities` module: for the error & logging functions, i.e. op::error & op::log respectively
|
||||
|
||||
// 3rdpary depencencies
|
||||
#include <gflags/gflags.h> // DEFINE_bool, DEFINE_int32, DEFINE_int64, DEFINE_uint64, DEFINE_double, DEFINE_string
|
||||
#include <glog/logging.h> // google::InitGoogleLogging, CHECK, CHECK_EQ, LOG, VLOG, ...
|
||||
// OpenPose dependencies
|
||||
// Option a) Importing all modules
|
||||
#include <openpose/headers.hpp>
|
||||
// Option b) Manually importing the desired modules. Recommended if you only intend to use a few modules.
|
||||
// #include <openpose/core/headers.hpp>
|
||||
// #include <openpose/filestream/headers.hpp>
|
||||
// #include <openpose/gui/headers.hpp>
|
||||
// #include <openpose/pose/headers.hpp>
|
||||
// #include <openpose/producer/headers.hpp>
|
||||
// #include <openpose/thread/headers.hpp>
|
||||
// #include <openpose/utilities/headers.hpp>
|
||||
|
||||
// Gflags in the command line terminal. Check all the options by adding the flag `--help`, e.g. `rtpose.bin --help`.
|
||||
// Note: This command will show you flags for several files. Check only the flags for the file you are checking. E.g. for `rtpose`, look for `Flags from examples/openpose/rtpose.cpp:`.
|
||||
// Debugging
|
||||
DEFINE_int32(logging_level, 3, "The logging level. Integer in the range [0, 255]. 0 will output any log() message, while 255 will not output any."
|
||||
" Current OpenPose library messages are in the range 0-4: 1 for low priority messages and 4 for important ones.");
|
||||
// Producer
|
||||
DEFINE_string(image_dir, "examples/media/", "Process a directory of images.");
|
||||
// Consumer
|
||||
DEFINE_bool(fullscreen, false, "Run in full-screen mode (press f during runtime to toggle).");
|
||||
|
||||
// The W-classes can be implemented either as a template or as simple classes given
|
||||
// that the user usually knows which kind of data he will move between the queues,
|
||||
// in this case we assume a std::shared_ptr of a std::vector of op::Datum
|
||||
|
||||
// This worker will just read and return all the jpg files in a directory
|
||||
class WUserInput : public op::WorkerProducer<std::shared_ptr<std::vector<op::Datum>>>
|
||||
{
|
||||
public:
|
||||
WUserInput(const std::string& directoryPath) :
|
||||
mImageFiles{op::getFilesOnDirectory(directoryPath, "jpg")},
|
||||
// mImageFiles{op::getFilesOnDirectory(directoryPath, std::vector<std::string>{"jpg", "png"})}, // If we want "jpg" + "png" images
|
||||
mCounter{0}
|
||||
{
|
||||
if (mImageFiles.empty())
|
||||
op::error("No images found on: " + directoryPath, __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
|
||||
void initializationOnThread() {}
|
||||
|
||||
std::shared_ptr<std::vector<op::Datum>> workProducer()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Close program when empty frame
|
||||
if (mImageFiles.size() <= mCounter)
|
||||
{
|
||||
op::log("Last frame read and added to queue. Closing program after it is processed.", op::Priority::Max);
|
||||
// This funtion stops this worker, which will eventually stop the whole thread system once all the frames have been processed
|
||||
this->stop();
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new datum
|
||||
auto datumsPtr = std::make_shared<std::vector<op::Datum>>();
|
||||
datumsPtr->emplace_back();
|
||||
auto& datum = datumsPtr->at(0);
|
||||
|
||||
// Fill datum
|
||||
datum.cvInputData = cv::imread(mImageFiles.at(mCounter++));
|
||||
|
||||
// If empty frame -> return nullptr
|
||||
if (datum.cvInputData.empty())
|
||||
{
|
||||
op::log("Empty frame detected on path: " + mImageFiles.at(mCounter-1) + ". Closing program.", op::Priority::Max);
|
||||
this->stop();
|
||||
datumsPtr = nullptr;
|
||||
}
|
||||
|
||||
return datumsPtr;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
op::log("Some kind of unexpected error happened.");
|
||||
this->stop();
|
||||
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<std::string> mImageFiles;
|
||||
unsigned long long mCounter;
|
||||
};
|
||||
|
||||
// This worker will just invert the image
|
||||
class WUserPostProcessing : public op::Worker<std::shared_ptr<std::vector<op::Datum>>>
|
||||
{
|
||||
public:
|
||||
WUserPostProcessing()
|
||||
{
|
||||
// User's constructor here
|
||||
}
|
||||
|
||||
void initializationOnThread() {}
|
||||
|
||||
void work(std::shared_ptr<std::vector<op::Datum>>& datumsPtr)
|
||||
{
|
||||
// User's post-processing (after OpenPose processing & before OpenPose outputs) here
|
||||
// datum.cvOutputData: rendered frame with pose or heatmaps
|
||||
// datum.pose: Array<float> with the estimated pose
|
||||
try
|
||||
{
|
||||
if (datumsPtr != nullptr && !datumsPtr->empty())
|
||||
for (auto& datum : *datumsPtr)
|
||||
cv::bitwise_not(datum.cvInputData, datum.cvOutputData);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
op::log("Some kind of unexpected error happened.");
|
||||
this->stop();
|
||||
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This worker will just read and return all the jpg files in a directory
|
||||
class WUserOutput : public op::WorkerConsumer<std::shared_ptr<std::vector<op::Datum>>>
|
||||
{
|
||||
public:
|
||||
void initializationOnThread() {}
|
||||
|
||||
void workConsumer(const std::shared_ptr<std::vector<op::Datum>>& datumsPtr)
|
||||
{
|
||||
try
|
||||
{
|
||||
// User's displaying/saving/other processing here
|
||||
// datum.cvOutputData: rendered frame with pose or heatmaps
|
||||
// datum.pose: Array<float> with the estimated pose
|
||||
if (datumsPtr != nullptr && !datumsPtr->empty())
|
||||
{
|
||||
cv::imshow("User worker GUI", datumsPtr->at(0).cvOutputData);
|
||||
cv::waitKey(500); // It sleeps 500 ms just to let the user see the output. Change to 33ms for normal 30 fps display
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
op::log("Some kind of unexpected error happened.");
|
||||
this->stop();
|
||||
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int openPoseTutorialThread3()
|
||||
{
|
||||
op::log("OpenPose Library Tutorial - Example 3.", op::Priority::Max);
|
||||
// ------------------------- INITIALIZATION -------------------------
|
||||
// Step 1 - Set logging level
|
||||
// - 0 will output all the logging messages
|
||||
// - 255 will output nothing
|
||||
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);
|
||||
// Step 2 - Setting thread workers && manager
|
||||
typedef std::shared_ptr<std::vector<op::Datum>> TypedefDatums;
|
||||
typedef std::shared_ptr<op::Worker<TypedefDatums>> TypedefWorker;
|
||||
op::ThreadManager<TypedefDatums> threadManager;
|
||||
// Step 3 - Initializing the worker classes
|
||||
// Frames producer (e.g. video, webcam, ...)
|
||||
TypedefWorker wUserInput = std::make_shared<WUserInput>(FLAGS_image_dir);
|
||||
// Processing
|
||||
TypedefWorker wUserProcessing = std::make_shared<WUserPostProcessing>();
|
||||
// GUI (Display)
|
||||
TypedefWorker wUserOutput = std::make_shared<WUserOutput>();
|
||||
|
||||
// ------------------------- CONFIGURING THREADING -------------------------
|
||||
// In this simple multi-thread example, we will do the following:
|
||||
// 3 (virtual) queues: 0, 1, 2
|
||||
// 1 real queue: 1. The first and last queue ids (in this case 0 and 2) are not actual queues, but the beginning and end of the processing sequence
|
||||
// 2 threads: 0, 1
|
||||
// wUserInput will generate frames (there is no real queue 0) and push them on queue 1
|
||||
// wGui will pop frames from queue 1 and process them (there is no real queue 2)
|
||||
auto threadId = 0ull;
|
||||
auto queueIn = 0ull;
|
||||
auto queueOut = 1ull;
|
||||
threadManager.add(threadId++, wUserInput, queueIn++, queueOut++); // Thread 0, queues 0 -> 1
|
||||
threadManager.add(threadId++, wUserProcessing, queueIn++, queueOut++); // Thread 1, queues 1 -> 2
|
||||
threadManager.add(threadId++, wUserOutput, queueIn++, queueOut++); // Thread 2, queues 2 -> 3
|
||||
|
||||
// ------------------------- STARTING AND STOPPING THREADING -------------------------
|
||||
op::log("Starting thread(s)", op::Priority::Max);
|
||||
// Two different ways of running the program on multithread enviroment
|
||||
// Option a) Using the main thread (this thread) for processing (it saves 1 thread, recommended)
|
||||
// threadManager.exec(); // It blocks this thread until all threads have finished
|
||||
// Option b) Giving to the user the control of this thread
|
||||
// Start threads
|
||||
threadManager.start();
|
||||
// Keep program alive while running threads. Here the user could perform any other desired function
|
||||
while (threadManager.isRunning())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{33});
|
||||
// Stop and join threads
|
||||
op::log("Stopping thread(s)", op::Priority::Max);
|
||||
threadManager.stop();
|
||||
|
||||
// ------------------------- CLOSING -------------------------
|
||||
// Logging information message
|
||||
op::log("Example 3 successfully finished.", op::Priority::Max);
|
||||
// Return successful message
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Initializing google logging (Caffe uses it for logging)
|
||||
google::InitGoogleLogging("openPoseTutorialThread3");
|
||||
|
||||
// Parsing command line flags
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
// Running openPoseTutorialThread3
|
||||
return openPoseTutorialThread3();
|
||||
}
|
||||
@@ -0,0 +1,240 @@
|
||||
// ------------------------- OpenPose Library Tutorial - Thread - Example 4 - User Input Processing And Output -------------------------
|
||||
// This fourth example shows the user how to:
|
||||
// 1. Read folder of images / video / webcam (`producer` module)
|
||||
// 2. Use the processing implemented by the user
|
||||
// 3. Display the rendered pose (`gui` module)
|
||||
// Everything in a multi-thread scenario (`thread` module)
|
||||
// In addition to the previous OpenPose modules, we also need to use:
|
||||
// 1. `core` module: for the Datum struct that the `thread` module sends between the queues
|
||||
// 2. `utilities` module: for the error & logging functions, i.e. op::error & op::log respectively
|
||||
|
||||
// 3rdpary depencencies
|
||||
#include <gflags/gflags.h> // DEFINE_bool, DEFINE_int32, DEFINE_int64, DEFINE_uint64, DEFINE_double, DEFINE_string
|
||||
#include <glog/logging.h> // google::InitGoogleLogging, CHECK, CHECK_EQ, LOG, VLOG, ...
|
||||
// OpenPose dependencies
|
||||
// Option a) Importing all modules
|
||||
#include <openpose/headers.hpp>
|
||||
// Option b) Manually importing the desired modules. Recommended if you only intend to use a few modules.
|
||||
// #include <openpose/core/headers.hpp>
|
||||
// #include <openpose/filestream/headers.hpp>
|
||||
// #include <openpose/gui/headers.hpp>
|
||||
// #include <openpose/pose/headers.hpp>
|
||||
// #include <openpose/producer/headers.hpp>
|
||||
// #include <openpose/thread/headers.hpp>
|
||||
// #include <openpose/utilities/headers.hpp>
|
||||
|
||||
// Gflags in the command line terminal. Check all the options by adding the flag `--help`, e.g. `rtpose.bin --help`.
|
||||
// Note: This command will show you flags for several files. Check only the flags for the file you are checking. E.g. for `rtpose`, look for `Flags from examples/openpose/rtpose.cpp:`.
|
||||
// Debugging
|
||||
DEFINE_int32(logging_level, 3, "The logging level. Integer in the range [0, 255]. 0 will output any log() message, while 255 will not output any."
|
||||
" Current OpenPose library messages are in the range 0-4: 1 for low priority messages and 4 for important ones.");
|
||||
// Producer
|
||||
DEFINE_string(image_dir, "examples/media/", "Process a directory of images.");
|
||||
// Consumer
|
||||
DEFINE_bool(fullscreen, false, "Run in full-screen mode (press f during runtime to toggle).");
|
||||
|
||||
|
||||
// If the user needs his own variables, he can inherit the op::Datum struct and add them
|
||||
// UserDatum can be directly used by the OpenPose wrapper because it inherits from op::Datum, just define Wrapper<UserDatum> instead of Wrapper<op::Datum>
|
||||
struct UserDatum : public op::Datum
|
||||
{
|
||||
bool boolThatUserNeedsForSomeReason;
|
||||
|
||||
UserDatum(const bool boolThatUserNeedsForSomeReason_ = false) :
|
||||
boolThatUserNeedsForSomeReason{boolThatUserNeedsForSomeReason_}
|
||||
{}
|
||||
};
|
||||
|
||||
// The W-classes can be implemented either as a template or as simple classes given
|
||||
// that the user usually knows which kind of data he will move between the queues,
|
||||
// in this case we assume a std::shared_ptr of a std::vector of UserDatum
|
||||
|
||||
// This worker will just read and return all the jpg files in a directory
|
||||
class WUserInput : public op::WorkerProducer<std::shared_ptr<std::vector<UserDatum>>>
|
||||
{
|
||||
public:
|
||||
WUserInput(const std::string& directoryPath) :
|
||||
mImageFiles{op::getFilesOnDirectory(directoryPath, "jpg")},
|
||||
// mImageFiles{op::getFilesOnDirectory(directoryPath, std::vector<std::string>{"jpg", "png"})}, // If we want "jpg" + "png" images
|
||||
mCounter{0}
|
||||
{
|
||||
if (mImageFiles.empty())
|
||||
op::error("No images found on: " + directoryPath, __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
|
||||
void initializationOnThread() {}
|
||||
|
||||
std::shared_ptr<std::vector<UserDatum>> workProducer()
|
||||
{
|
||||
try
|
||||
{
|
||||
// Close program when empty frame
|
||||
if (mImageFiles.size() <= mCounter)
|
||||
{
|
||||
op::log("Last frame read and added to queue. Closing program after it is processed.", op::Priority::Max);
|
||||
// This funtion stops this worker, which will eventually stop the whole thread system once all the frames have been processed
|
||||
this->stop();
|
||||
return nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Create new datum
|
||||
auto datumsPtr = std::make_shared<std::vector<UserDatum>>();
|
||||
datumsPtr->emplace_back();
|
||||
auto& datum = datumsPtr->at(0);
|
||||
|
||||
// Fill datum
|
||||
datum.cvInputData = cv::imread(mImageFiles.at(mCounter++));
|
||||
|
||||
// If empty frame -> return nullptr
|
||||
if (datum.cvInputData.empty())
|
||||
{
|
||||
op::log("Empty frame detected on path: " + mImageFiles.at(mCounter-1) + ". Closing program.", op::Priority::Max);
|
||||
this->stop();
|
||||
datumsPtr = nullptr;
|
||||
}
|
||||
|
||||
return datumsPtr;
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
op::log("Some kind of unexpected error happened.");
|
||||
this->stop();
|
||||
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const std::vector<std::string> mImageFiles;
|
||||
unsigned long long mCounter;
|
||||
};
|
||||
|
||||
// This worker will just invert the image
|
||||
class WUserPostProcessing : public op::Worker<std::shared_ptr<std::vector<UserDatum>>>
|
||||
{
|
||||
public:
|
||||
WUserPostProcessing()
|
||||
{
|
||||
// User's constructor here
|
||||
}
|
||||
|
||||
void initializationOnThread() {}
|
||||
|
||||
void work(std::shared_ptr<std::vector<UserDatum>>& datumsPtr)
|
||||
{
|
||||
// User's post-processing (after OpenPose processing & before OpenPose outputs) here
|
||||
// datum.cvOutputData: rendered frame with pose or heatmaps
|
||||
// datum.pose: Array<float> with the estimated pose
|
||||
try
|
||||
{
|
||||
if (datumsPtr != nullptr && !datumsPtr->empty())
|
||||
for (auto& datum : *datumsPtr)
|
||||
cv::bitwise_not(datum.cvInputData, datum.cvOutputData);
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
op::log("Some kind of unexpected error happened.");
|
||||
this->stop();
|
||||
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// This worker will just read and return all the jpg files in a directory
|
||||
class WUserOutput : public op::WorkerConsumer<std::shared_ptr<std::vector<UserDatum>>>
|
||||
{
|
||||
public:
|
||||
void initializationOnThread() {}
|
||||
|
||||
void workConsumer(const std::shared_ptr<std::vector<UserDatum>>& datumsPtr)
|
||||
{
|
||||
try
|
||||
{
|
||||
// User's displaying/saving/other processing here
|
||||
// datum.cvOutputData: rendered frame with pose or heatmaps
|
||||
// datum.pose: Array<float> with the estimated pose
|
||||
if (datumsPtr != nullptr && !datumsPtr->empty())
|
||||
{
|
||||
cv::imshow("User worker GUI", datumsPtr->at(0).cvOutputData);
|
||||
cv::waitKey(500); // It sleeps 500 ms just to let the user see the output. Change to 33ms for normal 30 fps display
|
||||
}
|
||||
}
|
||||
catch (const std::exception& e)
|
||||
{
|
||||
op::log("Some kind of unexpected error happened.");
|
||||
this->stop();
|
||||
op::error(e.what(), __LINE__, __FUNCTION__, __FILE__);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
int openPoseTutorialThread4()
|
||||
{
|
||||
op::log("OpenPose Library Tutorial - Example 3.", op::Priority::Max);
|
||||
// ------------------------- INITIALIZATION -------------------------
|
||||
// Step 1 - Set logging level
|
||||
// - 0 will output all the logging messages
|
||||
// - 255 will output nothing
|
||||
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);
|
||||
// Step 2 - Setting thread workers && manager
|
||||
typedef std::shared_ptr<std::vector<UserDatum>> TypedefDatums;
|
||||
typedef std::shared_ptr<op::Worker<TypedefDatums>> TypedefWorker;
|
||||
op::ThreadManager<TypedefDatums> threadManager;
|
||||
// Step 3 - Initializing the worker classes
|
||||
// Frames producer (e.g. video, webcam, ...)
|
||||
TypedefWorker wUserInput = std::make_shared<WUserInput>(FLAGS_image_dir);
|
||||
// Processing
|
||||
TypedefWorker wUserProcessing = std::make_shared<WUserPostProcessing>();
|
||||
// GUI (Display)
|
||||
TypedefWorker wUserOutput = std::make_shared<WUserOutput>();
|
||||
|
||||
// ------------------------- CONFIGURING THREADING -------------------------
|
||||
// In this simple multi-thread example, we will do the following:
|
||||
// 3 (virtual) queues: 0, 1, 2
|
||||
// 1 real queue: 1. The first and last queue ids (in this case 0 and 2) are not actual queues, but the beginning and end of the processing sequence
|
||||
// 2 threads: 0, 1
|
||||
// wUserInput will generate frames (there is no real queue 0) and push them on queue 1
|
||||
// wGui will pop frames from queue 1 and process them (there is no real queue 2)
|
||||
auto threadId = 0ull;
|
||||
auto queueIn = 0ull;
|
||||
auto queueOut = 1ull;
|
||||
threadManager.add(threadId++, wUserInput, queueIn++, queueOut++); // Thread 0, queues 0 -> 1
|
||||
threadManager.add(threadId++, wUserProcessing, queueIn++, queueOut++); // Thread 1, queues 1 -> 2
|
||||
threadManager.add(threadId++, wUserOutput, queueIn++, queueOut++); // Thread 2, queues 2 -> 3
|
||||
|
||||
// ------------------------- STARTING AND STOPPING THREADING -------------------------
|
||||
op::log("Starting thread(s)", op::Priority::Max);
|
||||
// Two different ways of running the program on multithread enviroment
|
||||
// Option a) Using the main thread (this thread) for processing (it saves 1 thread, recommended)
|
||||
// threadManager.exec(); // It blocks this thread until all threads have finished
|
||||
// Option b) Giving to the user the control of this thread
|
||||
// Start threads
|
||||
threadManager.start();
|
||||
// Keep program alive while running threads. Here the user could perform any other desired function
|
||||
while (threadManager.isRunning())
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds{33});
|
||||
// Stop and join threads
|
||||
op::log("Stopping thread(s)", op::Priority::Max);
|
||||
threadManager.stop();
|
||||
|
||||
// ------------------------- CLOSING -------------------------
|
||||
// Logging information message
|
||||
op::log("Example 3 successfully finished.", op::Priority::Max);
|
||||
// Return successful message
|
||||
return 0;
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
{
|
||||
// Initializing google logging (Caffe uses it for logging)
|
||||
google::InitGoogleLogging("openPoseTutorialThread4");
|
||||
|
||||
// Parsing command line flags
|
||||
gflags::ParseCommandLineFlags(&argc, &argv, true);
|
||||
|
||||
// Running openPoseTutorialThread4
|
||||
return openPoseTutorialThread4();
|
||||
}
|
||||
Reference in New Issue
Block a user