Skip to content

Commit 4fdd325

Browse files
committed
Added rotation transform to the tracker bounding-box.
Changed the bounding-box struct point convention to (cx, cy, width, height, angle). The GetFrame member function from Tracker class now uses the cv::RotatedRect object to represent the bounding-box and draw it on screen. The JSON and Protobuf communication between the backend and frontend still uses the (x1,y1)(x2,y2) point convention, the backend performs the adequate transformations.
1 parent 60cec47 commit 4fdd325

File tree

4 files changed

+119
-106
lines changed

4 files changed

+119
-106
lines changed

src/KeyFrameBBox.cpp

Lines changed: 47 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -142,14 +142,14 @@ KeyFrameBBox::KeyFrameBBox() : delta_x(0.0), delta_y(0.0), scale_x(1.0), scale_y
142142
}
143143

144144
// Add a BBox to the BoxVec map
145-
void KeyFrameBBox::AddBox(int64_t _frame_num, float _x1, float _y1, float _width, float _height)
145+
void KeyFrameBBox::AddBox(int64_t _frame_num, float _cx, float _cy, float _width, float _height, float _angle)
146146
{
147147
// Check if the given frame number is valid
148148
if (_frame_num < 0)
149149
return;
150150

151151
// Instantiate a new bounding-box
152-
BBox newBBox = BBox(_x1, _y1, _width, _height);
152+
BBox newBBox = BBox(_cx, _cy, _width, _height, _angle);
153153

154154
// Get the time of given frame
155155
double time = this->FrameNToTime(_frame_num, 1.0);
@@ -231,25 +231,12 @@ BBox KeyFrameBBox::GetValue(int64_t frame_number)
231231
BBox currentBBox = currentBBoxIterator->second;
232232

233233
// Adjust the BBox properties by the Keyframes values
234-
currentBBox.x1 += this->delta_x.GetValue(frame_number);
235-
currentBBox.y1 += this->delta_y.GetValue(frame_number);
234+
currentBBox.cx += this->delta_x.GetValue(frame_number);
235+
currentBBox.cy += this->delta_y.GetValue(frame_number);
236236
currentBBox.width *= this->scale_x.GetValue(frame_number);
237237
currentBBox.height *= this->scale_y.GetValue(frame_number);
238+
currentBBox.angle += this->rotation.GetValue(frame_number);
238239

239-
/* TODO - Add rotation
240-
(x1,y1) -> current point
241-
(x1',y1') -> transformed point
242-
(xc, yc) -> center of the BBox
243-
(xc, yc) = (x1 + w/2, y1 + h/2)
244-
rot -> rotation angle [radians]
245-
x1' = xc + (x1 - xc)*cos(rot) - (y1-yc)*sin(rot)
246-
y1' = yc + (x1 - xc)*sin(rot) + (y1-yc)*cos(rot)
247-
***
248-
x1' = (x1 + w/2) + (-w/2)*cos(rot) - (-h/2)*sin(rot)
249-
y1' = (y1 + h/2) + (-w/2)*sin(rot) + (-h/2)*cos(rot)
250-
currentBBox.x1 += currentBBox.width/2 - (currentBBox.width/2)*cos(rot*PI/180) + (currentBBox.height)*sin(rot*PI/180);
251-
currentBBox.y1 += currentBBox.height/2 - (currentBBox.width/2)*sin(rot*PI/180) - (currentBBox.height)*cos(rot*PI/180);
252-
*/
253240
return currentBBox;
254241
}
255242

@@ -263,37 +250,47 @@ BBox KeyFrameBBox::GetValue(int64_t frame_number)
263250
previousBBox, currentBBox, time);
264251

265252
// Adjust the BBox properties by the Keyframes values
266-
interpolatedBBox.x1 += this->delta_x.GetValue(frame_number);
267-
interpolatedBBox.y1 += this->delta_y.GetValue(frame_number);
253+
interpolatedBBox.cx += this->delta_x.GetValue(frame_number);
254+
interpolatedBBox.cy += this->delta_y.GetValue(frame_number);
268255
interpolatedBBox.width *= this->scale_x.GetValue(frame_number);
269256
interpolatedBBox.height *= this->scale_y.GetValue(frame_number);
257+
interpolatedBBox.angle += this->rotation.GetValue(frame_number);
270258

271259
return interpolatedBBox;
272260
}
273261

274262
// Interpolate the bouding-boxes properties
275263
BBox KeyFrameBBox::InterpolateBoxes(double t1, double t2, BBox left, BBox right, double target)
276264
{
277-
278-
Point p1_left(t1, left.x1, openshot::InterpolationType::LINEAR);
279-
Point p1_right(t2, right.x1, openshot::InterpolationType::LINEAR);
280-
Point p1 = InterpolateBetween(p1_left, p1_right, target, 0.01);
281-
282-
Point p2_left(t1, left.y1, openshot::InterpolationType::LINEAR);
283-
Point p2_right(t2, right.y1, openshot::InterpolationType::LINEAR);
284-
Point p2 = InterpolateBetween(p2_left, p2_right, target, 0.01);
285-
286-
Point p3_left(t1, left.height, openshot::InterpolationType::LINEAR);
287-
Point p3_right(t2, right.height, openshot::InterpolationType::LINEAR);
288-
Point p3 = InterpolateBetween(p3_left, p3_right, target, 0.01);
289-
290-
Point p4_left(t1, left.width, openshot::InterpolationType::LINEAR);
291-
Point p4_right(t2, right.width, openshot::InterpolationType::LINEAR);
292-
Point p4 = InterpolateBetween(p4_left, p4_right, target, 0.01);
293-
294-
BBox ans(p1.co.Y, p2.co.Y, p4.co.Y, p3.co.Y);
295-
296-
return ans;
265+
// Interpolate the x-coordinate of the center point
266+
Point cx_left(t1, left.cx, openshot::InterpolationType::LINEAR);
267+
Point cx_right(t2, right.cx, openshot::InterpolationType::LINEAR);
268+
Point cx = InterpolateBetween(cx_left, cx_right, target, 0.01);
269+
270+
// Interpolate de y-coordinate of the center point
271+
Point cy_left(t1, left.cy, openshot::InterpolationType::LINEAR);
272+
Point cy_right(t2, right.cy, openshot::InterpolationType::LINEAR);
273+
Point cy = InterpolateBetween(cy_left, cy_right, target, 0.01);
274+
275+
// Interpolate the width
276+
Point width_left(t1, left.width, openshot::InterpolationType::LINEAR);
277+
Point width_right(t2, right.width, openshot::InterpolationType::LINEAR);
278+
Point width = InterpolateBetween(width_left, width_right, target, 0.01);
279+
280+
// Interpolate the height
281+
Point height_left(t1, left.height, openshot::InterpolationType::LINEAR);
282+
Point height_right(t2, right.height, openshot::InterpolationType::LINEAR);
283+
Point height = InterpolateBetween(height_left, height_right, target, 0.01);
284+
285+
// Interpolate the rotation angle
286+
Point angle_left(t1, left.angle, openshot::InterpolationType::LINEAR);
287+
Point angle_right(t1, right.angle, openshot::InterpolationType::LINEAR);
288+
Point angle = InterpolateBetween(angle_left, angle_right, target, 0.01);
289+
290+
// Create a bounding box with the interpolated points
291+
BBox interpolatedBox(cx.co.Y, cy.co.Y, width.co.Y, height.co.Y, angle.co.Y);
292+
293+
return interpolatedBox;
297294
}
298295

299296
// Update object's BaseFps
@@ -348,16 +345,18 @@ bool KeyFrameBBox::LoadBoxData(std::string inputFilePath)
348345

349346
// Get bounding box data from current frame
350347
const libopenshottracker::Frame::Box &box = pbFrameData.bounding_box();
351-
352-
float x1 = box.x1();
353-
float y1 = box.y1();
354-
float x2 = box.x2();
355-
float y2 = box.y2();
356348

357-
if ( (x1 >= 0.0) && (y1 >= 0.0) && (x2 >= 0.0) && (y2 >= 0.0) )
349+
float width = box.x2() - box.x1();
350+
float height = box.y2() - box.y1();
351+
float cx = box.x1() + width/2;
352+
float cy = box.y1() + height/2;
353+
float angle = 0.0;
354+
355+
356+
if ( (cx >= 0.0) && (cy >= 0.0) && (width >= 0.0) && (height >= 0.0) )
358357
{
359358
// The bounding-box properties are valid, so add it to the BoxVec map
360-
this->AddBox(frame_number, x1, y1, (x2 - x1), (y2 - y1));
359+
this->AddBox(frame_number, cx, cy, width, height, angle);
361360
}
362361
}
363362

@@ -464,7 +463,6 @@ void KeyFrameBBox::SetJsonValue(const Json::Value root)
464463
// Insert BBox into the BoxVec map
465464
BBox box;
466465
box.SetJsonValue(existing_point["data"]);
467-
//BoxVec.insert({existing_point["time"].asDouble(), box});
468466
BoxVec[existing_point["time"].asDouble()] = box;
469467
}
470468
}
@@ -482,4 +480,4 @@ void KeyFrameBBox::SetJsonValue(const Json::Value root)
482480
rotation.SetJsonValue(root["rotation"]);
483481

484482
return;
485-
}
483+
}

src/KeyFrameBBox.h

Lines changed: 27 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -60,10 +60,11 @@ namespace openshot
6060

6161
struct BBox
6262
{
63-
float x1 = -1; ///< x-coordinate of the top left corner
64-
float y1 = -1; ///< y-coordinate of the top left corner
63+
float cx = -1; ///< x-coordinate of the bounding box center
64+
float cy = -1; ///< y-coordinate of the bounding box center
6565
float width = -1; ///< bounding box width
6666
float height = -1; ///< bounding box height
67+
float angle = -1; ///< bounding box rotation angle [degrees]
6768

6869
/// Blank constructor
6970
BBox()
@@ -72,18 +73,21 @@ namespace openshot
7273
}
7374

7475
/// Default constructor, which takes the bounding box top-left corner coordinates, width and height.
75-
/// @param _x1 X-coordinate of the top left corner
76-
/// @param _y1 Y-coordinate of the top left corner
76+
/// @param _cx X-coordinate of the bounding box center
77+
/// @param _cy Y-coordinate of the bounding box center
7778
/// @param _width Bounding box width
78-
/// @param _height Bouding box height
79-
BBox(float _x1, float _y1, float _width, float _height)
79+
/// @param _height Bounding box height
80+
/// @param _angle Bounding box rotation angle [degrees]
81+
BBox(float _cx, float _cy, float _width, float _height, float _angle)
8082
{
81-
x1 = _x1;
82-
y1 = _y1;
83+
cx = _cx;
84+
cy = _cy;
8385
width = _width;
8486
height = _height;
87+
angle = _angle;
8588
}
8689

90+
8791
/// Generate JSON string of this object
8892
std::string Json() const
8993
{
@@ -94,10 +98,11 @@ namespace openshot
9498
Json::Value JsonValue() const
9599
{
96100
Json::Value root;
97-
root["x1"] = x1;
98-
root["y1"] = y1;
99-
root["height"] = height;
101+
root["cx"] = cx;
102+
root["cy"] = cy;
100103
root["width"] = width;
104+
root["height"] = height;
105+
root["angle"] = angle;
101106

102107
return root;
103108
}
@@ -124,15 +129,17 @@ namespace openshot
124129
{
125130

126131
// Set data from Json (if key is found)
127-
if (!root["x1"].isNull())
128-
x1 = root["x1"].asDouble();
129-
if (!root["y1"].isNull())
130-
y1 = root["y1"].asDouble();
131-
if (!root["height"].isNull())
132-
height = root["height"].asDouble();
132+
if (!root["cx"].isNull())
133+
cx = root["cx"].asDouble();
134+
if (!root["cy"].isNull())
135+
cy = root["cy"].asDouble();
133136
if (!root["width"].isNull())
134137
width = root["width"].asDouble();
135-
}
138+
if (!root["height"].isNull())
139+
height = root["height"].asDouble();
140+
if (!root["angle"].isNull())
141+
angle = root["angle"].asDouble();
142+
}
136143
};
137144

138145
/**
@@ -166,7 +173,7 @@ namespace openshot
166173
KeyFrameBBox();
167174

168175
/// Add a BBox to the BoxVec map
169-
void AddBox(int64_t _frame_num, float _x1, float _y1, float _width, float _height);
176+
void AddBox(int64_t _frame_num, float _cx, float _cy, float _width, float _height, float _angle);
170177

171178
/// Update object's BaseFps
172179
void SetBaseFPS(Fraction fps);
@@ -214,4 +221,4 @@ namespace openshot
214221

215222
} // namespace openshot
216223

217-
#endif
224+
#endif

src/effects/Tracker.cpp

Lines changed: 21 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -73,24 +73,30 @@ std::shared_ptr<Frame> Tracker::GetFrame(std::shared_ptr<Frame> frame, int64_t f
7373
cv::Mat frame_image = frame->GetImageCV();
7474

7575
// Check if frame isn't NULL
76-
if(!frame_image.empty()){
77-
76+
if(!frame_image.empty())
77+
{
7878
// Check if track data exists for the requested frame
79-
if (trackedData.Contains(frame_number)) {
80-
79+
if (trackedData.Contains(frame_number))
80+
{
8181
// Get the width and height of the image
8282
float fw = frame_image.size().width;
8383
float fh = frame_image.size().height;
8484

8585
// Get the bounding-box of given frame
8686
BBox fd = this->trackedData.GetValue(frame_number);
8787

88-
// Draw the bounding-box on the image
89-
cv::Rect2d box((int)( (fd.x1 ) * fw ),
90-
(int)( (fd.y1 ) * fh ),
91-
(int)( (fd.width) * fw),
92-
(int)( (fd.height) * fh) );
93-
cv::rectangle(frame_image, box, cv::Scalar( 255, 0, 0 ), 2, 1 );
88+
// Create a rotated rectangle object that holds the bounding box
89+
cv::RotatedRect box ( cv::Point2f( (int)(fd.cx*fw), (int)(fd.cy*fh) ),
90+
cv::Size2f( (int)(fd.width*fw), (int)(fd.height*fh) ),
91+
(int) (fd.angle) );
92+
// Get the bouding box vertices
93+
cv::Point2f vertices[4];
94+
box.points(vertices);
95+
// Draw the bounding-box on the image
96+
for (int i = 0; i < 4; i++)
97+
{
98+
cv::line(frame_image, vertices[i], vertices[(i+1)%4], cv::Scalar(255,0,0), 2);
99+
}
94100
}
95101
}
96102

@@ -194,10 +200,10 @@ std::string Tracker::PropertiesJSON(int64_t requested_frame) const {
194200
// Get the bounding-box for the given-frame
195201
BBox fd = trackedData.GetValue(requested_frame);
196202
// Add the data of given frame bounding-box to the JSON object
197-
root["x1"] = add_property_json("X1", fd.x1, "float", "", NULL, 0.0, 1.0, false, requested_frame);
198-
root["y1"] = add_property_json("Y1", fd.y1, "float", "", NULL, 0.0, 1.0, false, requested_frame);
199-
root["x2"] = add_property_json("X2", fd.x1+fd.width, "float", "", NULL, 0.0, 1.0, false, requested_frame);
200-
root["y2"] = add_property_json("Y2", fd.y1+fd.height, "float", "", NULL, 0.0, 1.0, false, requested_frame);
203+
root["x1"] = add_property_json("X1", fd.cx-(fd.width/2), "float", "", NULL, 0.0, 1.0, false, requested_frame);
204+
root["y1"] = add_property_json("Y1", fd.cy-(fd.height/2), "float", "", NULL, 0.0, 1.0, false, requested_frame);
205+
root["x2"] = add_property_json("X2", fd.cx+(fd.width/2), "float", "", NULL, 0.0, 1.0, false, requested_frame);
206+
root["y2"] = add_property_json("Y2", fd.cy+(fd.height/2), "float", "", NULL, 0.0, 1.0, false, requested_frame);
201207

202208
// Add the bounding-box Keyframes to the JSON object
203209
root["delta_x"] = add_property_json("Displacement X-axis", trackedData.delta_x.GetValue(requested_frame), "float", "", &trackedData.delta_x, -1.0, 1.0, false, requested_frame);
@@ -260,4 +266,4 @@ void Tracker::SetJson(int64_t requested_frame, const std::string value)
260266
throw InvalidJSON("JSON is invalid (missing keys or invalid data types)");
261267
}
262268
return;
263-
}
269+
}

0 commit comments

Comments
 (0)