4_asynchronous_loop_custom_input_and_output.cpp 15.1 KB
Newer Older
1
// ------------------------- OpenPose C++ API Tutorial - Example 4 - Custom Input and Output -------------------------
G
gineshidalgo99 已提交
2 3 4 5 6 7 8 9 10 11 12 13 14
// Asynchronous mode: ideal for fast prototyping when performance is not an issue. The user emplaces/pushes and pops frames from the OpenPose wrapper
// when he desires to.

// This example shows the user how to use the OpenPose wrapper class:
    // 1. User reads images
    // 2. Extract and render keypoint / heatmap / PAF of that image
    // 3. Save the results on disk
    // 4. User displays the rendered pose
    // Everything in a multi-thread scenario
// In addition to the previous OpenPose modules, we also need to use:
    // 1. `core` module:
        // For the Array<float> class that the `pose` module needs
        // For the Datum struct that the `thread` module sends between the queues
G
gineshidalgo99 已提交
15
    // 2. `utilities` module: for the error & logging functions, i.e., op::error & op::log respectively
G
gineshidalgo99 已提交
16 17 18 19 20 21 22 23 24 25 26 27 28 29
// This file should only be used for the user to take specific examples.

// Command-line user intraface
#define OPENPOSE_FLAGS_DISABLE_PRODUCER
#define OPENPOSE_FLAGS_DISABLE_DISPLAY
#include <openpose/flags.hpp>
// OpenPose dependencies
#include <openpose/headers.hpp>

// Custom OpenPose flags
// Producer
DEFINE_string(image_dir, "examples/media/",
    "Process a directory of images. Read all standard formats (jpg, png, bmp, etc.).");

30 31
// If the user needs his own variables, he can inherit the op::Datum struct and add them in there.
// UserDatum can be directly used by the OpenPose wrapper because it inherits from op::Datum, just define
G
gineshidalgo99 已提交
32 33
// WrapperT<std::vector<std::shared_ptr<UserDatum>>> instead of Wrapper
// (or equivalently WrapperT<std::vector<std::shared_ptr<UserDatum>>>)
G
gineshidalgo99 已提交
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
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 UserInputClass
{
public:
    UserInputClass(const std::string& directoryPath) :
        mImageFiles{op::getFilesOnDirectory(directoryPath, "jpg")},
        // If we want "jpg" + "png" images
        // mImageFiles{op::getFilesOnDirectory(directoryPath, std::vector<std::string>{"jpg", "png"})},
        mCounter{0},
        mClosed{false}
    {
        if (mImageFiles.empty())
            op::error("No images found on: " + directoryPath, __LINE__, __FUNCTION__, __FILE__);
    }

G
gineshidalgo99 已提交
62
    std::shared_ptr<std::vector<std::shared_ptr<UserDatum>>> createDatum()
G
gineshidalgo99 已提交
63 64 65 66 67 68 69 70 71 72 73 74 75
    {
        // Close program when empty frame
        if (mClosed || mImageFiles.size() <= mCounter)
        {
            op::log("Last frame read and added to queue. Closing program after it is processed.", op::Priority::High);
            // This funtion stops this worker, which will eventually stop the whole thread system once all the frames
            // have been processed
            mClosed = true;
            return nullptr;
        }
        else // if (!mClosed)
        {
            // Create new datum
G
gineshidalgo99 已提交
76
            auto datumsPtr = std::make_shared<std::vector<std::shared_ptr<UserDatum>>>();
G
gineshidalgo99 已提交
77
            datumsPtr->emplace_back();
G
gineshidalgo99 已提交
78
            auto& datumPtr = datumsPtr->at(0);
79
            datumPtr = std::make_shared<UserDatum>();
G
gineshidalgo99 已提交
80 81

            // Fill datum
G
gineshidalgo99 已提交
82
            datumPtr->cvInputData = cv::imread(mImageFiles.at(mCounter++));
G
gineshidalgo99 已提交
83 84

            // If empty frame -> return nullptr
G
gineshidalgo99 已提交
85
            if (datumPtr->cvInputData.empty())
G
gineshidalgo99 已提交
86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111
            {
                op::log("Empty frame detected on path: " + mImageFiles.at(mCounter-1) + ". Closing program.",
                        op::Priority::High);
                mClosed = true;
                datumsPtr = nullptr;
            }

            return datumsPtr;
        }
    }

    bool isFinished() const
    {
        return mClosed;
    }

private:
    const std::vector<std::string> mImageFiles;
    unsigned long long mCounter;
    bool mClosed;
};

// This worker will just read and return all the jpg files in a directory
class UserOutputClass
{
public:
G
gineshidalgo99 已提交
112
    bool display(const std::shared_ptr<std::vector<std::shared_ptr<UserDatum>>>& datumsPtr)
G
gineshidalgo99 已提交
113 114
    {
        // User's displaying/saving/other processing here
G
gineshidalgo99 已提交
115 116
            // datumPtr->cvOutputData: rendered frame with pose or heatmaps
            // datumPtr->poseKeypoints: Array<float> with the estimated pose
G
gineshidalgo99 已提交
117 118 119
        char key = ' ';
        if (datumsPtr != nullptr && !datumsPtr->empty())
        {
G
gineshidalgo99 已提交
120
            cv::imshow("User worker GUI", datumsPtr->at(0)->cvOutputData);
G
gineshidalgo99 已提交
121 122 123 124 125 126 127
            // Display image and sleeps at least 1 ms (it usually sleeps ~5-10 msec to display the image)
            key = (char)cv::waitKey(1);
        }
        else
            op::log("Nullptr or empty datumsPtr found.", op::Priority::High, __LINE__, __FUNCTION__, __FILE__);
        return (key == 27);
    }
G
gineshidalgo99 已提交
128
    void printKeypoints(const std::shared_ptr<std::vector<std::shared_ptr<UserDatum>>>& datumsPtr)
G
gineshidalgo99 已提交
129 130 131 132 133 134
    {
        // Example: How to use the pose keypoints
        if (datumsPtr != nullptr && !datumsPtr->empty())
        {
            op::log("\nKeypoints:");
            // Accesing each element of the keypoints
G
gineshidalgo99 已提交
135
            const auto& poseKeypoints = datumsPtr->at(0)->poseKeypoints;
G
gineshidalgo99 已提交
136 137 138 139 140 141 142 143 144 145 146 147 148 149
            op::log("Person pose keypoints:");
            for (auto person = 0 ; person < poseKeypoints.getSize(0) ; person++)
            {
                op::log("Person " + std::to_string(person) + " (x, y, score):");
                for (auto bodyPart = 0 ; bodyPart < poseKeypoints.getSize(1) ; bodyPart++)
                {
                    std::string valueToPrint;
                    for (auto xyscore = 0 ; xyscore < poseKeypoints.getSize(2) ; xyscore++)
                        valueToPrint += std::to_string(   poseKeypoints[{person, bodyPart, xyscore}]   ) + " ";
                    op::log(valueToPrint);
                }
            }
            op::log(" ");
            // Alternative: just getting std::string equivalent
G
gineshidalgo99 已提交
150 151 152
            op::log("Face keypoints: " + datumsPtr->at(0)->faceKeypoints.toString());
            op::log("Left hand keypoints: " + datumsPtr->at(0)->handKeypoints[0].toString());
            op::log("Right hand keypoints: " + datumsPtr->at(0)->handKeypoints[1].toString());
G
gineshidalgo99 已提交
153
            // Heatmaps
G
gineshidalgo99 已提交
154
            const auto& poseHeatMaps = datumsPtr->at(0)->poseHeatMaps;
G
gineshidalgo99 已提交
155 156 157 158 159
            if (!poseHeatMaps.empty())
            {
                op::log("Pose heatmaps size: [" + std::to_string(poseHeatMaps.getSize(0)) + ", "
                        + std::to_string(poseHeatMaps.getSize(1)) + ", "
                        + std::to_string(poseHeatMaps.getSize(2)) + "]");
G
gineshidalgo99 已提交
160
                const auto& faceHeatMaps = datumsPtr->at(0)->faceHeatMaps;
G
gineshidalgo99 已提交
161 162 163 164
                op::log("Face heatmaps size: [" + std::to_string(faceHeatMaps.getSize(0)) + ", "
                        + std::to_string(faceHeatMaps.getSize(1)) + ", "
                        + std::to_string(faceHeatMaps.getSize(2)) + ", "
                        + std::to_string(faceHeatMaps.getSize(3)) + "]");
G
gineshidalgo99 已提交
165
                const auto& handHeatMaps = datumsPtr->at(0)->handHeatMaps;
G
gineshidalgo99 已提交
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180
                op::log("Left hand heatmaps size: [" + std::to_string(handHeatMaps[0].getSize(0)) + ", "
                        + std::to_string(handHeatMaps[0].getSize(1)) + ", "
                        + std::to_string(handHeatMaps[0].getSize(2)) + ", "
                        + std::to_string(handHeatMaps[0].getSize(3)) + "]");
                op::log("Right hand heatmaps size: [" + std::to_string(handHeatMaps[1].getSize(0)) + ", "
                        + std::to_string(handHeatMaps[1].getSize(1)) + ", "
                        + std::to_string(handHeatMaps[1].getSize(2)) + ", "
                        + std::to_string(handHeatMaps[1].getSize(3)) + "]");
            }
        }
        else
            op::log("Nullptr or empty datumsPtr found.", op::Priority::High, __LINE__, __FUNCTION__, __FILE__);
    }
};

G
gineshidalgo99 已提交
181
int tutorialApiCpp4()
G
gineshidalgo99 已提交
182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
{
    try
    {
        op::log("Starting OpenPose demo...", op::Priority::High);
        const auto timerBegin = std::chrono::high_resolution_clock::now();

        // 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::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
        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;

G
gineshidalgo99 已提交
220 221
        // Configuring OpenPose
        op::log("Configuring OpenPose...", op::Priority::High);
G
gineshidalgo99 已提交
222
        op::WrapperT<UserDatum> opWrapperT{op::ThreadManagerMode::Asynchronous};
G
gineshidalgo99 已提交
223 224 225 226 227 228
        // 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,
229
            (float)FLAGS_render_threshold, FLAGS_number_people_max, FLAGS_maximize_positives, FLAGS_fps_max,
230
            FLAGS_prototxt_path, FLAGS_caffemodel_path, enableGoogleLogging};
G
gineshidalgo99 已提交
231
        opWrapperT.configure(wrapperStructPose);
G
gineshidalgo99 已提交
232 233 234 235
        // 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};
G
gineshidalgo99 已提交
236
        opWrapperT.configure(wrapperStructFace);
G
gineshidalgo99 已提交
237 238 239 240 241
        // 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};
G
gineshidalgo99 已提交
242
        opWrapperT.configure(wrapperStructHand);
G
gineshidalgo99 已提交
243 244 245
        // 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};
G
gineshidalgo99 已提交
246
        opWrapperT.configure(wrapperStructExtra);
247
        // Output (comment or use default argument to disable any output)
G
gineshidalgo99 已提交
248
        const op::WrapperStructOutput wrapperStructOutput{
G
gineshidalgo99 已提交
249 250
            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,
251
            FLAGS_write_images, FLAGS_write_images_format, FLAGS_write_video, FLAGS_write_video_fps,
252 253
            FLAGS_write_heatmaps, FLAGS_write_heatmaps_format, FLAGS_write_video_3d, FLAGS_write_video_adam,
            FLAGS_write_bvh, FLAGS_udp_host, FLAGS_udp_port};
G
gineshidalgo99 已提交
254
        opWrapperT.configure(wrapperStructOutput);
255
        // No GUI. Equivalent to: opWrapper.configure(op::WrapperStructGui{});
G
gineshidalgo99 已提交
256 257
        // Set to single-thread (for sequential processing and/or debugging and/or reducing latency)
        if (FLAGS_disable_multi_thread)
G
gineshidalgo99 已提交
258
            opWrapperT.disableMultiThreading();
G
gineshidalgo99 已提交
259

G
gineshidalgo99 已提交
260
        // Start, run, and stop processing - exec() blocks this thread until OpenPose wrapper has finished
G
gineshidalgo99 已提交
261
        op::log("Starting thread(s)...", op::Priority::High);
262
        opWrapperT.start();
G
gineshidalgo99 已提交
263 264 265 266 267 268 269 270 271 272 273

        // User processing
        UserInputClass userInputClass(FLAGS_image_dir);
        UserOutputClass userOutputClass;
        bool userWantsToExit = false;
        while (!userWantsToExit && !userInputClass.isFinished())
        {
            // Push frame
            auto datumToProcess = userInputClass.createDatum();
            if (datumToProcess != nullptr)
            {
274
                auto successfullyEmplaced = opWrapperT.waitAndEmplace(datumToProcess);
G
gineshidalgo99 已提交
275
                // Pop frame
G
gineshidalgo99 已提交
276
                std::shared_ptr<std::vector<std::shared_ptr<UserDatum>>> datumProcessed;
277
                if (successfullyEmplaced && opWrapperT.waitAndPop(datumProcessed))
G
gineshidalgo99 已提交
278 279 280 281 282 283 284 285 286 287 288
                {
                    userWantsToExit = userOutputClass.display(datumProcessed);
                    userOutputClass.printKeypoints(datumProcessed);
                }
                else
                    op::log("Processed datum could not be emplaced.", op::Priority::High,
                            __LINE__, __FUNCTION__, __FILE__);
            }
        }

        op::log("Stopping thread(s)", op::Priority::High);
289
        opWrapperT.stop();
G
gineshidalgo99 已提交
290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312

        // Measuring total time
        const auto now = std::chrono::high_resolution_clock::now();
        const auto totalTimeSec = (double)std::chrono::duration_cast<std::chrono::nanoseconds>(now-timerBegin).count()
                                * 1e-9;
        const auto message = "OpenPose demo successfully finished. Total time: "
                           + std::to_string(totalTimeSec) + " seconds.";
        op::log(message, op::Priority::High);

        // Return successful message
        return 0;
    }
    catch (const std::exception& e)
    {
        return -1;
    }
}

int main(int argc, char *argv[])
{
    // Parsing command line flags
    gflags::ParseCommandLineFlags(&argc, &argv, true);

G
gineshidalgo99 已提交
313 314
    // Running tutorialApiCpp4
    return tutorialApiCpp4();
G
gineshidalgo99 已提交
315
}