简体   繁体   中英

How to make OpenCV tracking classes derive from a base class?

OpenCV tracking API has implementations in 3 different places.

  • video/tracking.hpp
  • tracking.hpp
  • tracking/tracking_legacy.hpp

I would like to be able to use any implementation by providing the name of the algorithm as input.

cv::Ptr<cv::Tracker> get_tracker(std::string trackerType);

A similar approach with Python seems to work.

if tracker_type == 'CSRT':
    tracker = cv2.TrackerCSRT_create()
elif tracker_type == 'MOSSE':
    tracker = cv.legacy.TrackerMOSSE_create()
...
ok = tracker.init(frame, bbox)
ok, bbox = tracker.update(frame)

The problem with C++ is that type of tracker should be known at compile time.

Having different implementations using templates is a solution for functions, but we are returning an object, so this is not applicable.

The proper solution would be using inheritance to derive cv::Tracker and cv::legacy::Tracker from a base class which has pure virtual functions init and update . Both of the classes have these methods, but they are seperately derived from cv::Algorithm which does not have any of these functions.

So far, I created seperate functions to get tracker object.

#include <opencv2/tracking.hpp>
#include <opencv2/tracking/tracking_legacy.hpp>
#include <string>
#include <stdexcept>

// https://docs.opencv.org/4.5.2/d0/d0a/classcv_1_1Tracker.html
cv::Ptr<cv::Tracker> get_tracker(std::string trackerType)
{
    if (trackerType == "MIL")
        return cv::TrackerMIL::create();
    if (trackerType == "GOTURN")
        return cv::TrackerGOTURN::create();
    if (trackerType == "CSRT")
        return cv::TrackerCSRT::create();
    if (trackerType == "KCF")
        return cv::TrackerKCF::create();
    throw std::runtime_error("Unknown tracker type.");
}

// https://docs.opencv.org/4.5.2/db/dfe/classcv_1_1legacy_1_1Tracker.html
cv::Ptr<cv::legacy::Tracker> get_legacy_tracker(std::string legacyTrackerType)
{
    if (legacyTrackerType == "MIL")
        return cv::legacy::TrackerMIL::create();
    if (legacyTrackerType == "BOOSTING")
        return cv::legacy::TrackerBoosting::create();
    if (legacyTrackerType == "MEDIANFLOW")
        return cv::legacy::TrackerMedianFlow::create();
    if (legacyTrackerType == "TLD")
        return cv::legacy::TrackerTLD::create();
    if (legacyTrackerType == "KCF")
        return cv::legacy::TrackerKCF::create();
    if (legacyTrackerType == "MOSSE")
        return cv::legacy::TrackerMOSSE::create();
    if (legacyTrackerType == "CSRT")
        return cv::legacy::TrackerCSRT::create();
    throw std::runtime_error("Unknown legacy tracker type.");
}

I am taking tracker type as a command line input. Any solution to make the following code work for all tracker types is appreciated. Tracker is preferred over legacy when available.

tracker->init(frame, bbox);
bool ok = tracker->update(frame, bbox);

Note: I am using OpenCV 4.5.2. legacy module is created in this commit .

Another note: Currently, pip package for opencv-python==4.5.2 is not available. The provided Python code is not tested, but written based on the documentation.

The solution lies in the example script of OpenCV. ( tracking/samples/samples_utility.hpp )

We need to convert cv::legacy::Tracker to the preferred type cv::Tracker . Just like the different input problem for a function, we need to implement these member functions separately.

class LegacyTrackerWrapper : public cv::Tracker
{
    const cv::Ptr<cv::legacy::Tracker> legacy_tracker_;

public:
    LegacyTrackerWrapper(const cv::Ptr<legacy::Tracker>& legacy_tracker)
        : legacy_tracker_(legacy_tracker) {}

    ~LegacyTrackerWrapper() override {}

    void init(cv::InputArray image, const cv::Rect& boundingBox) override
    {
        legacy_tracker_->init(image, static_cast<cv::Rect2d>(boundingBox));
    }

    bool update(cv::InputArray image, cv::Rect& boundingBox) override
    {
        cv::Rect2d boundingBox2d;
        bool res = legacy_tracker_->update(image, boundingBox2d);
        boundingBox = static_cast<cv::Rect>(boundingBox2d);  // the cast is not defined by default
        return res;
    }
};

We can use the wrapper class to obtain the desired behaviour.

cv::Ptr<cv::Tracker> upgradeTrackingAPI(const cv::Ptr<legacy::Tracker>& legacy_tracker)
{
    return cv::makePtr<LegacyTrackerWrapper>(legacy_tracker);
}

cv::Ptr<cv::Tracker> get_tracker(std::string trackerType)
{
    if (trackerType == "CSRT")
        return cv::TrackerCSRT::create();
    if (trackerType == "MOSSE")
        return upgradeTrackingAPI(cv::legacy::TrackerMOSSE::create());
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM