20 #include <google/protobuf/util/time_util.h>
24 #include "trackerdata.pb.h"
28 using google::protobuf::util::TimeUtil;
31 static inline void clampRect(cv::Rect2d &r,
int width,
int height)
33 r.x = std::clamp(r.x, 0.0,
double(width - 1));
34 r.y = std::clamp(r.y, 0.0,
double(height - 1));
35 r.width = std::clamp(r.width, 1.0,
double(width - r.x));
36 r.height = std::clamp(r.height, 1.0,
double(height - r.y));
41 : processingController(&processingController), json_interval(false){
51 if (trackerType ==
"BOOSTING")
52 return OPENCV_TRACKER_NS::TrackerBoosting::create();
53 if (trackerType ==
"MIL")
54 return OPENCV_TRACKER_NS::TrackerMIL::create();
55 if (trackerType ==
"KCF")
56 return OPENCV_TRACKER_NS::TrackerKCF::create();
57 if (trackerType ==
"TLD")
58 return OPENCV_TRACKER_NS::TrackerTLD::create();
59 if (trackerType ==
"MEDIANFLOW")
60 return OPENCV_TRACKER_NS::TrackerMedianFlow::create();
61 if (trackerType ==
"MOSSE")
62 return OPENCV_TRACKER_NS::TrackerMOSSE::create();
63 if (trackerType ==
"CSRT")
64 return OPENCV_TRACKER_NS::TrackerCSRT::create();
73 bool process_interval)
77 start = _start; end = _end;
78 if (!process_interval || end <= 1 || end - start == 0) {
79 start = int(video.
Start() * video.
Reader()->info.fps.ToFloat()) + 1;
80 end = int(video.
End() * video.
Reader()->info.fps.ToFloat()) + 1;
83 start = int(start + video.
Start() * video.
Reader()->info.fps.ToFloat()) + 1;
84 end = int(video.
End() * video.
Reader()->info.fps.ToFloat()) + 1;
87 processingController->
SetError(
false,
"");
89 bool trackerInit =
false;
92 for (
size_t frame = start; frame <= end; ++frame) {
93 if (processingController->
ShouldStop())
return;
96 cv::Mat img = f->GetImageCV();
100 int(bbox.x * img.cols),
101 int(bbox.y * img.rows),
102 int(bbox.width * img.cols),
103 int(bbox.height * img.rows)
121 uint(100 * (frame - start) / (end - start))
133 if (bbox.width < 0) {
134 bbox.x -= bbox.width;
135 bbox.width = -bbox.width;
137 if (bbox.height < 0) {
138 bbox.y -= bbox.height;
139 bbox.height = -bbox.height;
143 clampRect(bbox, frame.cols, frame.rows);
146 tracker->init(frame, bbox);
148 float fw = float(frame.cols), fh = float(frame.rows);
151 origWidth = bbox.width;
152 origHeight = bbox.height;
155 smoothC_x = bbox.x + bbox.width * 0.5;
156 smoothC_y = bbox.y + bbox.height * 0.5;
163 (bbox.x + bbox.width) / fw,
164 (bbox.y + bbox.height) / fh
174 const int W = frame.cols, H = frame.rows;
175 const auto& prev = trackedDataById[frameId - 1];
179 prev.x1 * W, prev.y1 * H,
180 (prev.x2 - prev.x1) * W,
181 (prev.y2 - prev.y1) * H
186 cv::cvtColor(frame, gray, cv::COLOR_BGR2GRAY);
188 const bool prevGrayMatches =
190 prevGray.size() == gray.size() &&
191 prevGray.type() == gray.type();
192 const bool fullPrevGrayMatches =
193 !fullPrevGray.empty() &&
194 fullPrevGray.size() == gray.size() &&
195 fullPrevGray.type() == gray.type();
197 if (!prevGray.empty() && !prevGrayMatches) {
206 if (prevGrayMatches && !prevPts.empty()) {
207 std::vector<cv::Point2f> currPts;
208 std::vector<uchar> status;
209 std::vector<float> err;
210 cv::calcOpticalFlowPyrLK(
215 cv::TermCriteria{cv::TermCriteria::COUNT|cv::TermCriteria::EPS,30,0.01},
216 cv::OPTFLOW_LK_GET_MIN_EIGENVALS, 1e-4
220 std::vector<double> dx, dy;
221 for (
size_t i = 0; i < status.size(); ++i) {
222 if (status[i] && err[i] < 12.0) {
223 dx.push_back(currPts[i].x - prevPts[i].x);
224 dy.push_back(currPts[i].y - prevPts[i].y);
228 if ((
int)dx.size() >= minKltPts) {
229 auto median = [&](
auto &v){
230 std::nth_element(v.begin(), v.begin()+v.size()/2, v.end());
231 return v[v.size()/2];
233 double mdx = median(dx), mdy = median(dy);
238 cand.width = origWidth;
239 cand.height = origHeight;
250 if (fullPrevGrayMatches) {
252 cv::calcOpticalFlowFarneback(
253 fullPrevGray, gray, flow,
256 cv::Scalar avg = cv::mean(flow);
260 cand.width = origWidth;
261 cand.height = origHeight;
263 if (lostCount >= 10) {
272 constexpr
double JITTER_THRESH = 1.0;
273 double measCx = cand.x + cand.width * 0.5;
274 double measCy = cand.y + cand.height * 0.5;
275 double dx = measCx - smoothC_x;
276 double dy = measCy - smoothC_y;
278 if (std::abs(dx) > JITTER_THRESH || std::abs(dy) > JITTER_THRESH) {
283 cand.x = smoothC_x - cand.width * 0.5;
284 cand.y = smoothC_y - cand.height * 0.5;
292 int roiX = int(std::clamp(cand.x, 0.0,
double(W - 1)));
293 int roiY = int(std::clamp(cand.y, 0.0,
double(H - 1)));
294 int roiW = int(std::min(cand.width,
double(W - roiX)));
295 int roiH = int(std::min(cand.height,
double(H - roiY)));
296 roiW = std::max(0, roiW);
297 roiH = std::max(0, roiH);
299 if (roiW > 0 && roiH > 0) {
300 cv::Rect roi(roiX, roiY, roiW, roiH);
301 cv::goodFeaturesToTrack(
303 kltMaxCorners, kltQualityLevel,
304 kltMinDist, cv::Mat(), kltBlockSize
306 for (
auto &pt : prevPts)
307 pt += cv::Point2f(
float(roi.x),
float(roi.y));
314 fullPrevGray = gray.clone();
315 prevGray = gray.clone();
317 float fw = float(W), fh = float(H);
322 (cand.x + cand.width) / fw,
323 (cand.y + cand.height) / fh
333 pb_tracker::Tracker trackerMessage;
336 for(std::map<size_t,FrameData>::iterator it=trackedDataById.begin(); it!=trackedDataById.end(); ++it){
338 pb_tracker::Frame* pbFrameData;
343 *trackerMessage.mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL));
347 std::fstream output(protobuf_data_path, ios::out | ios::trunc | ios::binary);
348 if (!trackerMessage.SerializeToOstream(&output)) {
349 std::cerr <<
"Failed to write protobuf message." << std::endl;
355 google::protobuf::ShutdownProtobufLibrary();
365 pbFrameData->set_id(fData.
frame_id);
366 pbFrameData->set_rotation(0);
368 pb_tracker::Frame::Box* box = pbFrameData->mutable_bounding_box();
370 box->set_x1(fData.
x1);
371 box->set_y1(fData.
y1);
372 box->set_x2(fData.
x2);
373 box->set_y2(fData.
y2);
380 if ( trackedDataById.find(frameId) == trackedDataById.end() ) {
385 return trackedDataById[frameId];
400 catch (
const std::exception& e)
411 if (!root[
"protobuf_data_path"].isNull()){
412 protobuf_data_path = (root[
"protobuf_data_path"].asString());
414 if (!root[
"tracker-type"].isNull()){
415 trackerType = (root[
"tracker-type"].asString());
418 if (!root[
"region"].isNull()){
419 double x = root[
"region"][
"normalized_x"].asDouble();
420 double y = root[
"region"][
"normalized_y"].asDouble();
421 double w = root[
"region"][
"normalized_width"].asDouble();
422 double h = root[
"region"][
"normalized_height"].asDouble();
423 cv::Rect2d prev_bbox(x,y,w,h);
426 if (!root[
"region"][
"first-frame"].isNull()){
427 start = root[
"region"][
"first-frame"].asInt64();
428 json_interval =
true;
431 processingController->
SetError(
true,
"No first-frame");
437 processingController->
SetError(
true,
"No initial bounding box selected");
454 pb_tracker::Tracker trackerMessage;
458 std::fstream input(protobuf_data_path, ios::in | ios::binary);
459 if (!trackerMessage.ParseFromIstream(&input)) {
460 std::cerr <<
"Failed to parse protobuf message." << std::endl;
466 trackedDataById.clear();
469 for (
size_t i = 0; i < trackerMessage.frame_size(); i++) {
470 const pb_tracker::Frame& pbFrameData = trackerMessage.frame(i);
473 size_t id = pbFrameData.id();
474 float rotation = pbFrameData.rotation();
477 const pb_tracker::Frame::Box& box = pbFrameData.bounding_box();
484 trackedDataById[id] =
FrameData(
id, rotation, x1, y1, x2, y2);
488 google::protobuf::ShutdownProtobufLibrary();
Track an object selected by the user.
Header file for all Exception classes.
Header file for OpenCVUtilities (set some common macros)
void SetError(bool err, std::string message)
void trackClip(openshot::Clip &video, size_t _start=0, size_t _end=0, bool process_interval=false)
CVTracker(std::string processInfoJson, ProcessingController &processingController)
bool SaveTrackedData()
Save protobuf file.
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
void SetJson(const std::string value)
Load JSON string into this object.
bool trackFrame(cv::Mat &frame, size_t frameId)
void AddFrameDataToProto(pb_tracker::Frame *pbFrameData, FrameData &fData)
Add frame tracked data into protobuf message.
FrameData GetTrackedData(size_t frameId)
Get tracked data for a given frame.
cv::Ptr< OPENCV_TRACKER_TYPE > selectTracker(std::string trackerType)
bool initTracker(cv::Mat &frame, size_t frameId)
float Start() const
Get start position (in seconds) of clip (trim start of video)
This class represents a clip (used to arrange readers on the timeline)
void Open() override
Open the internal reader.
float End() const override
Get end position (in seconds) of clip (trim end of video), which can be affected by the time curve.
std::shared_ptr< openshot::Frame > GetFrame(int64_t clip_frame_number) override
Get an openshot::Frame object for a specific frame number of this clip. The image size and number of ...
void Reader(openshot::ReaderBase *new_reader)
Set the current reader.
Exception for invalid JSON.
This namespace is the default namespace for all code in the openshot library.
const Json::Value stringToJson(const std::string value)