Skip to content

Commit e02d22b

Browse files
committed
Reduce time-consuming - use ffmpeg resize video.
1 parent 1c1e747 commit e02d22b

File tree

6 files changed

+150
-66
lines changed

6 files changed

+150
-66
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ brew install opencv3 --with-contrib --with-ffmpeg --c++11
2424

2525
# Boost
2626
brew install boost
27+
28+
# FFmpeg
29+
brew install ffmpeg
2730
```
2831

2932
##### Linux
@@ -57,6 +60,11 @@ sudo apt-get install libboost-all-dev
5760

5861
# OpenSSL
5962
sudo apt-get install libssl-dev
63+
64+
# FFmpeg
65+
# unofficial ppa
66+
sudo add-apt-repository ppa:jonathonf/ffmpeg-3
67+
sudo apt update && sudo apt install ffmpeg
6068
```
6169

6270
#### Compile & Build

animeloop-cli.xcodeproj/project.pbxproj

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -365,6 +365,7 @@
365365
88537EA11E4C501900385BCB /* Debug */ = {
366366
isa = XCBuildConfiguration;
367367
buildSettings = {
368+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
368369
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
369370
HEADER_SEARCH_PATHS = /usr/local/include;
370371
LIBRARY_SEARCH_PATHS = (
@@ -381,6 +382,7 @@
381382
88537EA21E4C501900385BCB /* Release */ = {
382383
isa = XCBuildConfiguration;
383384
buildSettings = {
385+
CLANG_CXX_LANGUAGE_STANDARD = "gnu++0x";
384386
CLANG_WARN_DOCUMENTATION_COMMENTS = NO;
385387
HEADER_SEARCH_PATHS = /usr/local/include;
386388
LIBRARY_SEARCH_PATHS = (

animeloop-cli/loop_video.cpp

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,7 @@
1212
#include "filter.hpp"
1313

1414
#include "json.h"
15-
#include <boost/filesystem.hpp>
16-
#include <locale>
17-
#include <numeric>
18-
#include <string>
15+
#include <sys/wait.h>
1916

2017
using namespace std;
2118
using namespace boost::filesystem;
@@ -47,13 +44,10 @@ al::LoopVideo::LoopVideo(std::string series, std::string episode, std::string in
4744
if (!exists(this->caches_path)) {
4845
create_directories(this->caches_path);
4946
}
50-
51-
// this->face_cascade_filename = "./lbpcascade_animeface.xml";
52-
// face_cascade.load(this->face_cascade_filename.string());
5347
}
5448

5549
void al::LoopVideo::init() {
56-
resize_video(this->input_path.string(), this->resized_video_filename.string(), cv::Size(this->kResizedWidth, this->kResizedHeight));
50+
resize_video(this->input_path, this->resized_video_filename, cv::Size(this->kResizedWidth, this->kResizedHeight));
5751
get_frames(this->resized_video_filename.string(), this->frames);
5852
get_hash_strings(this->resized_video_filename.string(), "dHash", this->dhash_strings, this->dhash_filename.string());
5953
get_hash_strings(this->resized_video_filename.string(), "pHash", this->phash_strings, this->phash_filename.string());
@@ -86,6 +80,18 @@ void al::LoopVideo::print(LoopDurations durations) {
8680
}
8781
}
8882

83+
int fork_gen_cover(string video_filepath, string cover_filepath) {
84+
int pid = fork();
85+
if(pid != 0) {
86+
/* We're in the parent process, return the child's pid. */
87+
return pid;
88+
}
89+
/* Otherwise, we're in the child process, so let's exec curl. */
90+
execlp("ffmpeg", "ffmpeg", "-loglevel", "panic", "-i", video_filepath.c_str(), "-vframes", "1", "-f", "image2", cover_filepath.c_str(), NULL);
91+
92+
exit(100);
93+
}
94+
8995
void al::LoopVideo::generate(const LoopDurations durations) {
9096
VideoInfo info = get_info(this->input_path.string());
9197

@@ -124,27 +130,37 @@ void al::LoopVideo::generate(const LoopDurations durations) {
124130
cv::Mat image;
125131
long frame = start_frame;
126132
while (capture.read(image)) {
127-
if (cover_enabled && frame == start_frame) {
128-
cv::imwrite(cover_filepath, image);
129-
} else if (frame > end_frame) {
133+
// Exclude the last frame.
134+
if (frame >= end_frame) {
130135
break;
131136
}
132-
137+
133138
writer.write(image);
134139
frame++;
135140
}
136141
writer.release();
137142
}
138-
143+
capture.release();
144+
139145
if (cover_enabled && !exists(cover_filepath)) {
140-
capture.set(CV_CAP_PROP_POS_FRAMES, start_frame);
141-
cv::Mat image;
142-
capture.read(image);
143-
cv::imwrite(cover_filepath, image);
146+
int cpid = fork_gen_cover(video_filepath, cover_filepath);
147+
if(cpid == -1) {
148+
/* Failed to fork */
149+
cerr << "Fork failed" << endl;
150+
throw;
151+
}
152+
153+
/* Optionally, wait for the child to exit and get
154+
the exit status. */
155+
int status;
156+
waitpid(cpid, &status, 0);
157+
if(! WIFEXITED(status)) {
158+
cerr << "The child was killed or segfaulted or something." << endl;
159+
}
160+
161+
status = WEXITSTATUS(status);
144162
}
145-
capture.release();
146163

147-
148164
// Save video info json file.
149165
Json::Value video_json;
150166

animeloop-cli/loop_video.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616
#include <boost/filesystem.hpp>
1717

1818
namespace al {
19-
const std::string kVersion = "1.3.1";
19+
const std::string kVersion = "1.3.2";
2020

2121
class LoopVideo {
2222
public:

animeloop-cli/utils.cpp

Lines changed: 93 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -12,11 +12,11 @@
1212
#include "json.h"
1313
#include <boost/filesystem.hpp>
1414
#include <boost/date_time/posix_time/posix_time.hpp>
15-
#include <boost/date_time/posix_time/posix_time_io.hpp>
16-
#include <locale>
15+
#include <boost/regex.hpp>
1716
#include <openssl/md5.h>
18-
#include <fstream>
1917
#include <numeric>
18+
#include <sys/wait.h>
19+
2020

2121
using namespace std;
2222
using namespace boost::filesystem;
@@ -42,37 +42,85 @@ VideoInfo al::get_info(std::string filename) {
4242
}
4343

4444

45+
int fork_resize_video(path input, path temp_filename, Size size) {
46+
int pid = fork();
47+
if(pid != 0) {
48+
/* We're in the parent process, return the child's pid. */
49+
return pid;
50+
}
51+
/* Otherwise, we're in the child process, so let's exec curl. */
52+
execlp("ffmpeg", "ffmpeg", "-loglevel", "panic", "-i", input.string().c_str(), "-s", (to_string(size.width) + "x" + to_string(size.height)).c_str(), "-an", temp_filename.string().c_str(), NULL);
53+
54+
exit(100);
55+
}
56+
57+
void al::resize_video(path input, path output, Size size) {
58+
path temp_output = output.parent_path().append("temp");
59+
path temp_filename = path(temp_output).append(output.filename().string());
60+
if (!exists(temp_output)) {
61+
create_directories(temp_output);
62+
} else {
63+
if (exists(temp_filename)) {
64+
remove(temp_filename);
65+
}
66+
}
4567

46-
void al::resize_video(string file, string output, Size size) {
4768
auto if_exists = exists(output);
4869

4970
if (!if_exists) {
50-
// Calculate hash string per frame.
51-
std::cout << "Resizing video... 0%" << std::endl;
52-
VideoCapture capture;
53-
capture.open(file);
54-
55-
VideoInfo info = get_info(capture);
71+
if (system("which ffmpeg") == 0) {
72+
cout << "Resizing video..." << endl;
73+
74+
int cpid = fork_resize_video(input, temp_filename, size);
75+
if(cpid == -1) {
76+
/* Failed to fork */
77+
cerr << "Fork failed" << endl;
78+
throw;
79+
}
5680

57-
VideoWriter writer(output, CV_FOURCC('H', '2', '6', '4'), info.fps, size);
81+
/* Optionally, wait for the child to exit and get
82+
the exit status. */
83+
int status;
84+
waitpid(cpid, &status, 0);
85+
if(! WIFEXITED(status)) {
86+
cerr << "The child was killed or segfaulted or something\n" << endl;
87+
}
5888

59-
60-
int percent = 0;
61-
int count = 0, total = info.frame_count;
62-
cv::Mat image;
63-
while (capture.read(image)) {
64-
if (count / double(total) * 100 > percent) {
65-
percent++;
66-
std::cout << "Resizing video... " << percent << "%" << std::endl;
89+
status = WEXITSTATUS(status);
90+
91+
92+
cout << "done." << endl;
93+
94+
} else {
95+
// Calculate hash string per frame.
96+
cout << "Resizing video... 0%" << endl;
97+
VideoCapture capture;
98+
capture.open(input.string());
99+
100+
VideoInfo info = get_info(capture);
101+
102+
VideoWriter writer(temp_filename.string(), CV_FOURCC('H', '2', '6', '4'), info.fps, size);
103+
104+
105+
int percent = 0;
106+
int count = 0, total = info.frame_count;
107+
cv::Mat image;
108+
while (capture.read(image)) {
109+
if (count / double(total) * 100 > percent) {
110+
percent++;
111+
std::cout << "Resizing video... " << percent << "%" << std::endl;
112+
}
113+
cv::resize(image, image, size);
114+
writer.write(image);
115+
image.release();
116+
count++;
67117
}
68-
cv::resize(image, image, size);
69-
writer.write(image);
70-
image.release();
71-
count++;
118+
119+
writer.release();
120+
capture.release();
72121
}
73-
74-
writer.release();
75-
capture.release();
122+
123+
system(("mv " + temp_filename.string() + " " + output.string()).c_str());
76124
}
77125
}
78126

@@ -91,19 +139,27 @@ bool al::get_frames(string file, FrameVector &frames) {
91139
return true;
92140
}
93141

94-
bool al::get_hash_strings(string file, string type, HashVector &hash_strings, string hash_file) {
95-
142+
void al::get_hash_strings(path filename, string type, HashVector &hash_strings, path hash_file) {
143+
path temp_path = hash_file.parent_path().append("temp");
144+
path temp_filename = path(temp_path).append(hash_file.filename().string());
145+
146+
if (!exists(temp_path)) {
147+
create_directories(temp_path);
148+
} else {
149+
if (exists(temp_filename)) {
150+
remove(temp_filename);
151+
}
152+
}
153+
96154
auto if_exists = exists(hash_file);
97155
VideoCapture capture;
98156
if (if_exists) {
99157
// Read video frames hash string from file.
100158
std::cout << "Restore " << type << " value from file..." << std::endl;
101159

102-
std::ifstream input_file(hash_file);
160+
std::ifstream input_file(hash_file.string());
103161
HashVector hashs;
104-
105-
int count;
106-
count = 0;
162+
107163
while (input_file) {
108164
string line;
109165
getline(input_file, line);
@@ -112,15 +168,13 @@ bool al::get_hash_strings(string file, string type, HashVector &hash_strings, st
112168
input_file.close();
113169

114170
hash_strings = hashs;
115-
return true;
116171
} else {
117172
// Calculate hash string per frame.
118173
VideoCapture capture;
119-
capture.open(file);
174+
capture.open(filename.string());
120175
VideoInfo info = get_info(capture);
121176
HashVector hashs;
122-
123-
177+
124178
cout << "Calculating " << type << " value... 0%" << endl;
125179
int percent = 0;
126180
int count = 0, total = info.frame_count;
@@ -139,13 +193,13 @@ bool al::get_hash_strings(string file, string type, HashVector &hash_strings, st
139193
hash_strings = hashs;
140194

141195
cout << "Save " << type << " value into file..." << endl;
142-
std::ofstream output_file(hash_file);
196+
std::ofstream output_file(temp_filename.string());
143197
ostream_iterator<string> output_iterator(output_file, "\n");
144198
std::copy(hashs.begin(), hashs.end(), output_iterator);
145199
output_file.close();
146-
return true;
200+
201+
system(("mv " + temp_filename.string() + " " + hash_file.string()).c_str());
147202
}
148-
return false;
149203
}
150204

151205

animeloop-cli/utils.hpp

Lines changed: 11 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,37 +14,41 @@
1414
#include <iostream>
1515
#include <string>
1616
#include <opencv2/opencv.hpp>
17+
#include <boost/filesystem/path.hpp>
1718

1819

1920
namespace al {
2021
VideoInfo get_info(std::string filename);
22+
2123
VideoInfo get_info(cv::VideoCapture &capture);
22-
24+
2325
/**
2426
Resize a video and write into a new file.
2527
2628
@param file intput path of origin video file
2729
@param output output path of resized video file
2830
@param size resizing size
2931
*/
30-
void resize_video(std::string file, std::string output, cv::Size size);
32+
void resize_video(boost::filesystem::path input, boost::filesystem::path output, cv::Size size);
3133

3234
bool get_frames(std::string file, FrameVector &frames);
3335

34-
bool get_hash_strings(std::string file, std::string type, al::HashVector &hash_strings, std::string hash_file);
35-
36+
void get_hash_strings(boost::filesystem::path filename, std::string type, al::HashVector &hash_strings,
37+
boost::filesystem::path hash_file);
38+
3639
double get_variance(std::vector<int> distances);
40+
3741
double get_mean(cv::Mat image);
38-
39-
42+
43+
4044
/**
4145
Convert seconds value to a time string.
4246
4347
@param seconds seconds value
4448
@return Time string
4549
*/
4650
std::string time_string(double seconds);
47-
51+
4852
/**
4953
Generate the MD5 checksum of a file.
5054

0 commit comments

Comments
 (0)