21#include "stabilizedata.pb.h"
22#include <google/protobuf/util/time_util.h>
24#if CV_VERSION_MAJOR >= 5
25#define int64 opencv_broken_int
26#define uint64 opencv_broken_uint
27#include <opencv2/geometry/3d.hpp>
31#include <opencv2/calib3d.hpp>
36using google::protobuf::util::TimeUtil;
40: processingController(&processingController){
52 processingController->
SetError(
false,
"");
54 start = _start; end = _end;
56 avr_dx=0; avr_dy=0; avr_da=0; max_dx=0; max_dy=0; max_da=0;
60 cv::Size readerDims(video.
Reader()->info.width, video.
Reader()->info.height);
63 if(!process_interval || end <= 1 || end-start == 0){
65 start = (int)(video.
Start() * video.
Reader()->info.fps.ToFloat()) + 1;
66 end = (int)(video.
End() * video.
Reader()->info.fps.ToFloat()) + 1;
70 for (frame_number = start; frame_number <= end; frame_number++)
77 std::shared_ptr<openshot::Frame> f = video.
GetFrame(frame_number);
80 cv::Mat cvimage = f->GetImageCV();
82 if(cvimage.size().width != readerDims.width || cvimage.size().height != readerDims.height)
83 cv::resize(cvimage, cvimage, cv::Size(readerDims.width, readerDims.height));
84 cv::cvtColor(cvimage, cvimage, cv::COLOR_RGB2GRAY);
86 if(!TrackFrameFeatures(cvimage, frame_number)){
91 processingController->
SetProgress(uint(100*(frame_number-start)/(end-start)));
95 std::vector <CamTrajectory> trajectory = ComputeFramesTrajectory();
105 dataToNormalize.second.x/=readerDims.width;
106 dataToNormalize.second.y/=readerDims.height;
110 dataToNormalize.second.dx/=readerDims.width;
111 dataToNormalize.second.dy/=readerDims.height;
116bool CVStabilization::TrackFrameFeatures(cv::Mat frame,
size_t frameNum){
118 if(cv::countNonZero(frame) < 1){
123 if(prev_grey.empty()){
129 std::vector <cv::Point2f> prev_corner, cur_corner;
130 std::vector <cv::Point2f> prev_corner2, cur_corner2;
131 std::vector <uchar> status;
132 std::vector <float> err;
134 cv::goodFeaturesToTrack(prev_grey, prev_corner, 200, 0.01, 30);
136 cv::calcOpticalFlowPyrLK(prev_grey, frame, prev_corner, cur_corner, status, err);
138 for(
size_t i=0; i < status.size(); i++) {
140 prev_corner2.push_back(prev_corner[i]);
141 cur_corner2.push_back(cur_corner[i]);
145 if(prev_corner2.empty() || cur_corner2.empty()){
152 cv::Mat T = cv::estimateAffinePartial2D(prev_corner2, cur_corner2);
156 if(T.size().width == 0 || T.size().height == 0){
168 dx = T.at<
double>(0,2);
169 dy = T.at<
double>(1,2);
170 da = atan2(T.at<
double>(1,0), T.at<
double>(0,0));
174 if(dx > 200 || dy > 200 || da > 0.1){
182 if(fabs(dx) > max_dx)
184 if(fabs(dy) > max_dy)
186 if(fabs(da) > max_da)
192 frame.copyTo(prev_grey);
197std::vector<CamTrajectory> CVStabilization::ComputeFramesTrajectory(){
204 vector <CamTrajectory> trajectory;
207 for(
size_t i=0; i < prev_to_cur_transform.size(); i++) {
208 x += prev_to_cur_transform[i].dx;
209 y += prev_to_cur_transform[i].dy;
210 a += prev_to_cur_transform[i].da;
218std::map<size_t,CamTrajectory> CVStabilization::SmoothTrajectory(std::vector <CamTrajectory> &trajectory){
220 std::map <size_t,CamTrajectory> smoothed_trajectory;
222 for(
size_t i=0; i < trajectory.size(); i++) {
228 for(
int j=-smoothingWindow; j <= smoothingWindow; j++) {
229 if(i+j < trajectory.size()) {
230 sum_x += trajectory[i+j].x;
231 sum_y += trajectory[i+j].y;
232 sum_a += trajectory[i+j].a;
238 double avg_a = sum_a / count;
239 double avg_x = sum_x / count;
240 double avg_y = sum_y / count;
243 smoothed_trajectory[i + start] =
CamTrajectory(avg_x, avg_y, avg_a);
245 return smoothed_trajectory;
249std::map<size_t,TransformParam> CVStabilization::GenNewCamPosition(std::map <size_t,CamTrajectory> &smoothed_trajectory){
250 std::map <size_t,TransformParam> new_prev_to_cur_transform;
257 for(
size_t i=0; i < prev_to_cur_transform.size(); i++) {
258 x += prev_to_cur_transform[i].dx;
259 y += prev_to_cur_transform[i].dy;
260 a += prev_to_cur_transform[i].da;
263 double diff_x = smoothed_trajectory[i + start].x - x;
264 double diff_y = smoothed_trajectory[i + start].y - y;
265 double diff_a = smoothed_trajectory[i + start].a - a;
267 double dx = prev_to_cur_transform[i].dx + diff_x;
268 double dy = prev_to_cur_transform[i].dy + diff_y;
269 double da = prev_to_cur_transform[i].da + diff_a;
272 new_prev_to_cur_transform[i + start] =
TransformParam(dx, dy, da);
274 return new_prev_to_cur_transform;
282 pb_stabilize::Stabilization stabilizationMessage;
284 std::map<size_t,CamTrajectory>::iterator trajData =
trajectoryData.begin();
289 AddFrameDataToProto(stabilizationMessage.add_frame(), trajData->second, transData->second, trajData->first);
292 *stabilizationMessage.mutable_last_updated() = TimeUtil::SecondsToTimestamp(time(NULL));
295 std::fstream output(protobuf_data_path, ios::out | ios::trunc | ios::binary);
296 if (!stabilizationMessage.SerializeToOstream(&output)) {
297 std::cerr <<
"Failed to write protobuf message." << std::endl;
302 google::protobuf::ShutdownProtobufLibrary();
311 pbFrameData->set_id(frame_number);
314 pbFrameData->set_a(trajData.
a);
315 pbFrameData->set_x(trajData.
x);
316 pbFrameData->set_y(trajData.
y);
319 pbFrameData->set_da(transData.
da);
320 pbFrameData->set_dx(transData.
dx);
321 pbFrameData->set_dy(transData.
dy);
358 catch (
const std::exception& e)
369 if (!root[
"protobuf_data_path"].isNull()){
370 protobuf_data_path = (root[
"protobuf_data_path"].asString());
372 if (!root[
"smoothing-window"].isNull()){
373 smoothingWindow = (root[
"smoothing-window"].asInt());
387 pb_stabilize::Stabilization stabilizationMessage;
389 std::fstream input(protobuf_data_path, ios::in | ios::binary);
390 if (!stabilizationMessage.ParseFromIstream(&input)) {
391 std::cerr <<
"Failed to parse protobuf message." << std::endl;
400 for (
size_t i = 0; i < stabilizationMessage.frame_size(); i++) {
401 const pb_stabilize::Frame& pbFrameData = stabilizationMessage.frame(i);
404 size_t id = pbFrameData.id();
407 float x = pbFrameData.x();
408 float y = pbFrameData.y();
409 float a = pbFrameData.a();
415 float dx = pbFrameData.dx();
416 float dy = pbFrameData.dy();
417 float da = pbFrameData.da();
424 google::protobuf::ShutdownProtobufLibrary();
Header file for CVStabilization class.
Header file for all Exception classes.
TransformParam GetTransformParamData(size_t frameId)
bool _LoadStabilizedData()
void SetJsonValue(const Json::Value root)
Load Json::Value into this object.
CVStabilization(std::string processInfoJson, ProcessingController &processingController)
Set default smoothing window value to compute stabilization.
void AddFrameDataToProto(pb_stabilize::Frame *pbFrameData, CamTrajectory &trajData, TransformParam &transData, size_t frame_number)
Add frame stabilization data into protobuf message.
std::map< size_t, TransformParam > transformationData
void stabilizeClip(openshot::Clip &video, size_t _start=0, size_t _end=0, bool process_interval=false)
Process clip and store necessary stabilization data.
std::map< size_t, CamTrajectory > trajectoryData
CamTrajectory GetCamTrajectoryTrackedData(size_t frameId)
bool SaveStabilizedData()
void SetJson(const std::string value)
Load JSON string into this object.
void SetError(bool err, std::string message)
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)