// This file is part of OpenCV project. // It is subject to the license terms in the LICENSE file found in the top-level directory // of this distribution and at http://opencv.org/license.html. #ifndef __OPENCV_TRACKING_TRACKING_BY_MATCHING_HPP__ #define __OPENCV_TRACKING_TRACKING_BY_MATCHING_HPP__ #include #include #include #include #include #include #include #include #include #include "opencv2/core.hpp" #include "opencv2/imgproc.hpp" namespace cv { namespace detail { inline namespace tracking { //! @addtogroup tracking_detail //! @{ namespace tbm { //Tracking-by-Matching /// /// \brief The TrackedObject struct defines properties of detected object. /// struct CV_EXPORTS TrackedObject { cv::Rect rect; ///< Detected object ROI (zero area if N/A). double confidence; ///< Detection confidence level (-1 if N/A). int frame_idx; ///< Frame index where object was detected (-1 if N/A). int object_id; ///< Unique object identifier (-1 if N/A). uint64_t timestamp; ///< Timestamp in milliseconds. /// /// \brief Default constructor. /// TrackedObject() : confidence(-1), frame_idx(-1), object_id(-1), timestamp(0) {} /// /// \brief Constructor with parameters. /// \param rect Bounding box of detected object. /// \param confidence Confidence of detection. /// \param frame_idx Index of frame. /// \param object_id Object ID. /// TrackedObject(const cv::Rect &rect, float confidence, int frame_idx, int object_id) : rect(rect), confidence(confidence), frame_idx(frame_idx), object_id(object_id), timestamp(0) {} }; using TrackedObjects = std::deque; bool operator==(const TrackedObject& first, const TrackedObject& second); bool operator!=(const TrackedObject& first, const TrackedObject& second); /// (object id, detected objects) pairs collection. using ObjectTracks = std::unordered_map; /// /// \brief The IImageDescriptor class declares base class for image /// descriptor. /// class CV_EXPORTS IImageDescriptor { public: /// /// \brief Descriptor size getter. /// \return Descriptor size. /// virtual cv::Size size() const = 0; /// /// \brief Computes image descriptor. /// \param[in] mat Color image. /// \param[out] descr Computed descriptor. /// virtual void compute(const cv::Mat &mat, CV_OUT cv::Mat& descr) = 0; /// /// \brief Computes image descriptors in batches. /// \param[in] mats Images of interest. /// \param[out] descrs Matrices to store the computed descriptors. /// virtual void compute(const std::vector &mats, CV_OUT std::vector& descrs) = 0; virtual ~IImageDescriptor() {} }; /// /// \brief Uses resized image as descriptor. /// class CV_EXPORTS ResizedImageDescriptor : public IImageDescriptor { public: /// /// \brief Constructor. /// \param[in] descr_size Size of the descriptor (resized image). /// \param[in] interpolation Interpolation algorithm. /// explicit ResizedImageDescriptor(const cv::Size &descr_size, const cv::InterpolationFlags interpolation) : descr_size_(descr_size), interpolation_(interpolation) { CV_Assert(descr_size.width > 0); CV_Assert(descr_size.height > 0); } /// /// \brief Returns descriptor size. /// \return Number of elements in the descriptor. /// cv::Size size() const override { return descr_size_; } /// /// \brief Computes image descriptor. /// \param[in] mat Frame containing the image of interest. /// \param[out] descr Matrix to store the computed descriptor. /// void compute(const cv::Mat &mat, CV_OUT cv::Mat& descr) override { CV_Assert(!mat.empty()); cv::resize(mat, descr, descr_size_, 0, 0, interpolation_); } /// /// \brief Computes images descriptors. /// \param[in] mats Frames containing images of interest. /// \param[out] descrs Matrices to store the computed descriptors. // void compute(const std::vector &mats, CV_OUT std::vector& descrs) override { descrs.resize(mats.size()); for (size_t i = 0; i < mats.size(); i++) { compute(mats[i], descrs[i]); } } private: cv::Size descr_size_; cv::InterpolationFlags interpolation_; }; /// /// \brief The IDescriptorDistance class declares an interface for distance /// computation between reidentification descriptors. /// class CV_EXPORTS IDescriptorDistance { public: /// /// \brief Computes distance between two descriptors. /// \param[in] descr1 First descriptor. /// \param[in] descr2 Second descriptor. /// \return Distance between two descriptors. /// virtual float compute(const cv::Mat &descr1, const cv::Mat &descr2) = 0; /// /// \brief Computes distances between two descriptors in batches. /// \param[in] descrs1 Batch of first descriptors. /// \param[in] descrs2 Batch of second descriptors. /// \return Distances between descriptors. /// virtual std::vector compute(const std::vector &descrs1, const std::vector &descrs2) = 0; virtual ~IDescriptorDistance() {} }; /// /// \brief The CosDistance class allows computing cosine distance between two /// reidentification descriptors. /// class CV_EXPORTS CosDistance : public IDescriptorDistance { public: /// /// \brief CosDistance constructor. /// \param[in] descriptor_size Descriptor size. /// explicit CosDistance(const cv::Size &descriptor_size); /// /// \brief Computes distance between two descriptors. /// \param descr1 First descriptor. /// \param descr2 Second descriptor. /// \return Distance between two descriptors. /// float compute(const cv::Mat &descr1, const cv::Mat &descr2) override; /// /// \brief Computes distances between two descriptors in batches. /// \param[in] descrs1 Batch of first descriptors. /// \param[in] descrs2 Batch of second descriptors. /// \return Distances between descriptors. /// std::vector compute( const std::vector &descrs1, const std::vector &descrs2) override; private: cv::Size descriptor_size_; }; /// /// \brief Computes distance between images /// using MatchTemplate function from OpenCV library /// and its cross-correlation computation method in particular. /// class CV_EXPORTS MatchTemplateDistance : public IDescriptorDistance { public: /// /// \brief Constructs the distance object. /// /// \param[in] type Method of MatchTemplate function computation. /// \param[in] scale Scale parameter for the distance. /// Final distance is computed as: /// scale * distance + offset. /// \param[in] offset Offset parameter for the distance. /// Final distance is computed as: /// scale * distance + offset. /// MatchTemplateDistance(int type = cv::TemplateMatchModes::TM_CCORR_NORMED, float scale = -1, float offset = 1) : type_(type), scale_(scale), offset_(offset) {} /// /// \brief Computes distance between image descriptors. /// \param[in] descr1 First image descriptor. /// \param[in] descr2 Second image descriptor. /// \return Distance between image descriptors. /// float compute(const cv::Mat &descr1, const cv::Mat &descr2) override; /// /// \brief Computes distances between two descriptors in batches. /// \param[in] descrs1 Batch of first descriptors. /// \param[in] descrs2 Batch of second descriptors. /// \return Distances between descriptors. /// std::vector compute(const std::vector &descrs1, const std::vector &descrs2) override; virtual ~MatchTemplateDistance() {} private: int type_; ///< Method of MatchTemplate function computation. float scale_; ///< Scale parameter for the distance. Final distance is /// computed as: scale * distance + offset. float offset_; ///< Offset parameter for the distance. Final distance is /// computed as: scale * distance + offset. }; /// /// \brief The TrackerParams struct stores parameters of TrackerByMatching /// struct CV_EXPORTS TrackerParams { size_t min_track_duration; ///< Min track duration in milliseconds. size_t forget_delay; ///< Forget about track if the last bounding box in /// track was detected more than specified number of /// frames ago. float aff_thr_fast; ///< Affinity threshold which is used to determine if /// tracklet and detection should be combined (fast /// descriptor is used). float aff_thr_strong; ///< Affinity threshold which is used to determine if /// tracklet and detection should be combined(strong /// descriptor is used). float shape_affinity_w; ///< Shape affinity weight. float motion_affinity_w; ///< Motion affinity weight. float time_affinity_w; ///< Time affinity weight. float min_det_conf; ///< Min confidence of detection. cv::Vec2f bbox_aspect_ratios_range; ///< Bounding box aspect ratios range. cv::Vec2f bbox_heights_range; ///< Bounding box heights range. int predict; ///< How many frames are used to predict bounding box in case /// of lost track. float strong_affinity_thr; ///< If 'fast' confidence is greater than this /// threshold then 'strong' Re-ID approach is /// used. float reid_thr; ///< Affinity threshold for re-identification. bool drop_forgotten_tracks; ///< Drop forgotten tracks. If it's enabled it /// disables an ability to get detection log. int max_num_objects_in_track; ///< The number of objects in track is /// restricted by this parameter. If it is negative or zero, the max number of /// objects in track is not restricted. /// /// Default constructor. /// TrackerParams(); }; /// /// \brief The Track class describes tracks. /// class CV_EXPORTS Track { public: /// /// \brief Track constructor. /// \param objs Detected objects sequence. /// \param last_image Image of last image in the detected object sequence. /// \param descriptor_fast Fast descriptor. /// \param descriptor_strong Strong descriptor (reid embedding). /// Track(const TrackedObjects &objs, const cv::Mat &last_image, const cv::Mat &descriptor_fast, const cv::Mat &descriptor_strong) : objects(objs), predicted_rect(!objs.empty() ? objs.back().rect : cv::Rect()), last_image(last_image), descriptor_fast(descriptor_fast), descriptor_strong(descriptor_strong), lost(0), length(1) { CV_Assert(!objs.empty()); first_object = objs[0]; } /// /// \brief empty returns if track does not contain objects. /// \return true if track does not contain objects. /// bool empty() const { return objects.empty(); } /// /// \brief size returns number of detected objects in a track. /// \return number of detected objects in a track. /// size_t size() const { return objects.size(); } /// /// \brief operator [] return const reference to detected object with /// specified index. /// \param i Index of object. /// \return const reference to detected object with specified index. /// const TrackedObject &operator[](size_t i) const { return objects[i]; } /// /// \brief operator [] return non-const reference to detected object with /// specified index. /// \param i Index of object. /// \return non-const reference to detected object with specified index. /// TrackedObject &operator[](size_t i) { return objects[i]; } /// /// \brief back returns const reference to last object in track. /// \return const reference to last object in track. /// const TrackedObject &back() const { CV_Assert(!empty()); return objects.back(); } /// /// \brief back returns non-const reference to last object in track. /// \return non-const reference to last object in track. /// TrackedObject &back() { CV_Assert(!empty()); return objects.back(); } TrackedObjects objects; ///< Detected objects; cv::Rect predicted_rect; ///< Rectangle that represents predicted position /// and size of bounding box if track has been lost. cv::Mat last_image; ///< Image of last detected object in track. cv::Mat descriptor_fast; ///< Fast descriptor. cv::Mat descriptor_strong; ///< Strong descriptor (reid embedding). size_t lost; ///< How many frames ago track has been lost. TrackedObject first_object; ///< First object in track. size_t length; ///< Length of a track including number of objects that were /// removed from track in order to avoid memory usage growth. }; /// /// \brief Tracker-by-Matching algorithm interface. /// /// This class is implementation of tracking-by-matching system. It uses two /// different appearance measures to compute affinity between bounding boxes: /// some fast descriptor and some strong descriptor. Each time the assignment /// problem is solved. The assignment problem in our case is how to establish /// correspondence between existing tracklets and recently detected objects. /// First step is to compute an affinity matrix between tracklets and /// detections. The affinity equals to /// appearance_affinity * motion_affinity * shape_affinity. /// Where appearance is 1 - distance(tracklet_fast_dscr, detection_fast_dscr). /// Second step is to solve the assignment problem using Kuhn-Munkres /// algorithm. If correspondence between some tracklet and detection is /// established with low confidence (affinity) then the strong descriptor is /// used to determine if there is correspondence between tracklet and detection. /// class CV_EXPORTS ITrackerByMatching { public: using Descriptor = std::shared_ptr; using Distance = std::shared_ptr; /// /// \brief Destructor for the tracker /// virtual ~ITrackerByMatching() {} /// /// \brief Process given frame. /// \param[in] frame Colored image (CV_8UC3). /// \param[in] detections Detected objects on the frame. /// \param[in] timestamp Timestamp must be positive and measured in /// milliseconds /// virtual void process(const cv::Mat &frame, const TrackedObjects &detections, uint64_t timestamp) = 0; /// /// \brief Pipeline parameters getter. /// \return Parameters of pipeline. /// virtual const TrackerParams ¶ms() const = 0; /// /// \brief Pipeline parameters setter. /// \param[in] params Parameters of pipeline. /// virtual void setParams(const TrackerParams ¶ms) = 0; /// /// \brief Fast descriptor getter. /// \return Fast descriptor used in pipeline. /// virtual const Descriptor &descriptorFast() const = 0; /// /// \brief Fast descriptor setter. /// \param[in] val Fast descriptor used in pipeline. /// virtual void setDescriptorFast(const Descriptor &val) = 0; /// /// \brief Strong descriptor getter. /// \return Strong descriptor used in pipeline. /// virtual const Descriptor &descriptorStrong() const = 0; /// /// \brief Strong descriptor setter. /// \param[in] val Strong descriptor used in pipeline. /// virtual void setDescriptorStrong(const Descriptor &val) = 0; /// /// \brief Fast distance getter. /// \return Fast distance used in pipeline. /// virtual const Distance &distanceFast() const = 0; /// /// \brief Fast distance setter. /// \param[in] val Fast distance used in pipeline. /// virtual void setDistanceFast(const Distance &val) = 0; /// /// \brief Strong distance getter. /// \return Strong distance used in pipeline. /// virtual const Distance &distanceStrong() const = 0; /// /// \brief Strong distance setter. /// \param[in] val Strong distance used in pipeline. /// virtual void setDistanceStrong(const Distance &val) = 0; /// /// \brief Returns number of counted people. /// \return a number of counted people. /// virtual size_t count() const = 0; /// /// \brief Get active tracks to draw /// \return Active tracks. /// virtual std::unordered_map > getActiveTracks() const = 0; /// /// \brief Get tracked detections. /// \return Tracked detections. /// virtual TrackedObjects trackedDetections() const = 0; /// /// \brief Draws active tracks on a given frame. /// \param[in] frame Colored image (CV_8UC3). /// \return Colored image with drawn active tracks. /// virtual cv::Mat drawActiveTracks(const cv::Mat &frame) = 0; /// /// \brief isTrackForgotten returns true if track is forgotten. /// \param id Track ID. /// \return true if track is forgotten. /// virtual bool isTrackForgotten(size_t id) const = 0; /// /// \brief tracks Returns all tracks including forgotten (lost too many frames /// ago). /// \return Set of tracks {id, track}. /// virtual const std::unordered_map &tracks() const = 0; /// /// \brief isTrackValid Checks whether track is valid (duration > threshold). /// \param track_id Index of checked track. /// \return True if track duration exceeds some predefined value. /// virtual bool isTrackValid(size_t track_id) const = 0; /// /// \brief dropForgottenTracks Removes tracks from memory that were lost too /// many frames ago. /// virtual void dropForgottenTracks() = 0; /// /// \brief dropForgottenTrack Check that the track was lost too many frames /// ago /// and removes it frm memory. /// virtual void dropForgottenTrack(size_t track_id) = 0; }; /// /// \brief The factory to create Tracker-by-Matching algorithm implementation. /// CV_EXPORTS cv::Ptr createTrackerByMatching(const TrackerParams ¶ms = TrackerParams()); } // namespace tbm //! @} }}} // namespace #endif // #ifndef __OPENCV_TRACKING_TRACKING_BY_MATCHING_HPP__