You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
180 lines
5.9 KiB
180 lines
5.9 KiB
#define CL_TARGET_OPENCL_VERSION 120 |
|
|
|
constexpr unsigned long WIDTH = 1920; |
|
constexpr unsigned long HEIGHT = 1080; |
|
constexpr bool OFFSCREEN = false; |
|
constexpr const int VA_HW_DEVICE_INDEX = 0; |
|
|
|
#include "../common/subsystems.hpp" |
|
#include <stdio.h> |
|
#include <csignal> |
|
#include <cstdint> |
|
#include <iomanip> |
|
#include <string> |
|
|
|
#include <opencv2/optflow.hpp> |
|
|
|
using std::cerr; |
|
using std::endl; |
|
using std::vector; |
|
using std::string; |
|
|
|
static bool done = false; |
|
static void finish(int ignore) { |
|
std::cerr << endl; |
|
done = true; |
|
} |
|
|
|
int main(int argc, char **argv) { |
|
signal(SIGINT, finish); |
|
using namespace kb; |
|
|
|
if (argc != 2) { |
|
std::cerr << "Usage: optflow <input-video-file>" << endl; |
|
exit(1); |
|
} |
|
|
|
va::init(); |
|
cv::VideoCapture cap(argv[1], cv::CAP_FFMPEG, { cv::CAP_PROP_HW_DEVICE, VA_HW_DEVICE_INDEX, cv::CAP_PROP_HW_ACCELERATION, cv::VIDEO_ACCELERATION_VAAPI, cv::CAP_PROP_HW_ACCELERATION_USE_OPENCL, 1 }); |
|
if (!cap.isOpened()) { |
|
cerr << "ERROR! Unable to open camera" << endl; |
|
return -1; |
|
} |
|
|
|
double fps = cap.get(cv::CAP_PROP_FPS); |
|
|
|
cv::VideoWriter encoder("optflow.mkv", cv::CAP_FFMPEG, cv::VideoWriter::fourcc('V', 'P', '9', '0'), fps, cv::Size(WIDTH, HEIGHT), { cv::VIDEOWRITER_PROP_HW_ACCELERATION, cv::VIDEO_ACCELERATION_VAAPI, cv::VIDEOWRITER_PROP_HW_ACCELERATION_USE_OPENCL, 1 }); |
|
|
|
if (!OFFSCREEN) |
|
x11::init(); |
|
egl::init(); |
|
gl::init(); |
|
nvg::init(); |
|
|
|
cerr << "VA Version: " << va::get_info() << endl; |
|
cerr << "EGL Version: " << egl::get_info() << endl; |
|
cerr << "OpenGL Version: " << gl::get_info() << endl; |
|
cerr << "OpenCL Platforms: " << endl << cl::get_info() << endl; |
|
|
|
cv::UMat frameBuffer; |
|
cv::UMat background; |
|
cv::UMat foreground(HEIGHT, WIDTH, CV_8UC4, cv::Scalar::all(0)); |
|
cv::UMat videoFrame, nextVideoFrameGray, prevVideoFrameGray, foregroundMaskGrey; |
|
|
|
cv::Ptr<cv::BackgroundSubtractor> bgSubtractor = cv::createBackgroundSubtractorMOG2(100, 32.0, false); |
|
|
|
vector<cv::Point2f> allPoints, prevPoints, nextPoints, newPoints; |
|
vector<vector<cv::Point> > contours; |
|
vector<cv::Vec4i> hierarchy; |
|
|
|
uint64_t cnt = 1; |
|
int64 start = cv::getTickCount(); |
|
double tickFreq = cv::getTickFrequency(); |
|
double lastFps = fps; |
|
|
|
while (!done) { |
|
va::bind(); |
|
cap >> videoFrame; |
|
if (videoFrame.empty()) |
|
break; |
|
|
|
cv::resize(videoFrame, videoFrame, cv::Size(WIDTH, HEIGHT)); |
|
cv::cvtColor(videoFrame, background, cv::COLOR_RGB2BGRA); |
|
cvtColor(videoFrame, nextVideoFrameGray, cv::COLOR_RGB2GRAY); |
|
|
|
bgSubtractor->apply(videoFrame, foregroundMaskGrey); |
|
|
|
int morph_size = 1; |
|
cv::Mat element = cv::getStructuringElement(cv::MORPH_ELLIPSE, cv::Size(2 * morph_size + 1, 2 * morph_size + 1), cv::Point(morph_size, morph_size)); |
|
cv::morphologyEx(foregroundMaskGrey, foregroundMaskGrey, cv::MORPH_OPEN, element, cv::Point(-1, -1), 2); |
|
cv::morphologyEx(foregroundMaskGrey, foregroundMaskGrey, cv::MORPH_CLOSE, element, cv::Point(-1, -1), 2); |
|
findContours(foregroundMaskGrey, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE); |
|
|
|
allPoints.clear(); |
|
for (const auto &c : contours) { |
|
for (const auto &pt : c) { |
|
allPoints.push_back(pt); |
|
} |
|
} |
|
|
|
gl::bind(); |
|
|
|
if (allPoints.size() > 4) { |
|
prevPoints = allPoints; |
|
if (prevVideoFrameGray.empty()) { |
|
prevVideoFrameGray = nextVideoFrameGray.clone(); |
|
} |
|
|
|
std::vector<uchar> status; |
|
std::vector<float> err; |
|
cv::TermCriteria criteria = cv::TermCriteria((cv::TermCriteria::COUNT) + (cv::TermCriteria::EPS), 10, 0.03); |
|
cv::calcOpticalFlowPyrLK(prevVideoFrameGray, nextVideoFrameGray, prevPoints, nextPoints, status, err, cv::Size(15, 15), 2, criteria); |
|
|
|
nvg::begin(); |
|
nvg::clear(); |
|
newPoints.clear(); |
|
|
|
using kb::nvg::vg; |
|
nvgBeginPath(vg); |
|
nvgStrokeWidth(vg, std::fmax(2.0, WIDTH/960.0)); |
|
nvgStrokeColor(vg, nvgHSLA(0.1, 1, 0.5, 32)); |
|
for (size_t i = 0; i < prevPoints.size(); i++) { |
|
|
|
if (status[i] == 1 && nextPoints[i].y >= 0 && nextPoints[i].x >= 0 && nextPoints[i].y < foregroundMaskGrey.rows && nextPoints[i].x < foregroundMaskGrey.cols) { |
|
double len = hypot(fabs(nextPoints[i].x - prevPoints[i].x), fabs(nextPoints[i].y - prevPoints[i].y)); |
|
if (len > 0) { |
|
newPoints.push_back(nextPoints[i]); |
|
|
|
nvgMoveTo(vg, nextPoints[i].x, nextPoints[i].y); |
|
nvgLineTo(vg, prevPoints[i].x, prevPoints[i].y); |
|
} |
|
} |
|
} |
|
nvgStroke(vg); |
|
|
|
nvg::end(); |
|
|
|
prevVideoFrameGray = nextVideoFrameGray.clone(); |
|
prevPoints = nextPoints; |
|
} |
|
|
|
gl::acquire_from_gl(frameBuffer); |
|
|
|
cv::flip(frameBuffer, frameBuffer, 0); |
|
cv::addWeighted(foreground, 0.9, frameBuffer, 1.1, 0.0, foreground); |
|
cv::addWeighted(background, 1.0, foreground, 1.0, 0.0, frameBuffer); |
|
cv::flip(frameBuffer, frameBuffer, 0); |
|
cv::cvtColor(frameBuffer, videoFrame, cv::COLOR_BGRA2RGB); |
|
|
|
gl::release_to_gl(frameBuffer); |
|
|
|
va::bind(); |
|
cv::flip(videoFrame, videoFrame, 0); |
|
encoder.write(videoFrame); |
|
|
|
if (x11::is_initialized()) { |
|
gl::bind(); |
|
gl::blit_frame_buffer_to_screen(); |
|
|
|
if (x11::window_closed()) { |
|
finish(0); |
|
break; |
|
} |
|
|
|
gl::swap_buffers(); |
|
} |
|
|
|
//Measure FPS |
|
if (cnt % uint64(ceil(lastFps)) == 0) { |
|
int64 tick = cv::getTickCount(); |
|
lastFps = tickFreq / ((tick - start + 1) / cnt); |
|
cerr << "FPS : " << lastFps << '\r'; |
|
start = tick; |
|
cnt = 1; |
|
} |
|
|
|
++cnt; |
|
} |
|
|
|
return 0; |
|
}
|
|
|