Repository for OpenCV's extra modules
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.
 
 
 
 
 
 

293 lines
10 KiB

#define CL_TARGET_OPENCL_VERSION 120
constexpr unsigned long WIDTH = 1920;
constexpr unsigned long HEIGHT = 1080;
constexpr unsigned int DOWN_SCALE = 2;
constexpr bool OFFSCREEN = false;
constexpr int VA_HW_DEVICE_INDEX = 0;
#include "../common/subsystems.hpp"
#include <csignal>
#include <list>
#include <vector>
#include <cstdint>
#include <string>
#include <opencv2/imgproc.hpp>
#include <opencv2/optflow.hpp>
using std::cerr;
using std::endl;
using std::vector;
using std::list;
using std::string;
int max_points = 2000;
static bool done = false;
static void finish(int ignore) {
std::cerr << endl;
done = true;
}
void overdefine(const vector<cv::Point2f>& srcPts, vector<cv::Point2f>& dstPts, size_t minPoints) {
assert(srcPts.size() > 1);
list<cv::Point2f> copy;
for(auto& pt : srcPts) {
copy.push_back(pt);
}
off_t diff = minPoints - copy.size();
while(diff > 0) {
auto it = copy.begin();
for(size_t i = 0; i < copy.size() && diff > 0; i+=2) {
const auto& first = *it;
++it;
if(it == copy.end())
it = copy.begin();
const auto& second = *it;
auto insertee = first;
auto vector = second - first;
vector.x /= 2.0;
vector.y /= 2.0;
insertee.x += vector.x;
insertee.y += vector.y;
--it;
copy.insert(it, std::move(insertee));
++it;
--diff;
}
}
for(auto& pt : copy) {
dstPts.push_back(pt);
}
assert(dstPts.size() >= minPoints);
}
void make_delaunay_mesh(const cv::Size &size, cv::Subdiv2D &subdiv, vector<cv::Point2f> &dstPoints) {
vector<cv::Vec6f> triangleList;
subdiv.getTriangleList(triangleList);
vector<cv::Point2f> pt(3);
cv::Rect rect(0, 0, size.width, size.height);
for (size_t i = 0; i < triangleList.size(); i++) {
cv::Vec6f t = triangleList[i];
pt[0] = cv::Point2f(t[0], t[1]);
pt[1] = cv::Point2f(t[2], t[3]);
pt[2] = cv::Point2f(t[4], t[5]);
if (rect.contains(pt[0]) && rect.contains(pt[1]) && rect.contains(pt[2])) {
dstPoints.push_back(pt[0]);
dstPoints.push_back(pt[1]);
dstPoints.push_back(pt[2]);
}
}
}
void collect_delaunay_mesh_points(const int width, const int height, const std::vector<cv::Point2f> &inPoints, std::vector<cv::Point2f> &outPoints) {
cv::Subdiv2D subdiv(cv::Rect(0, 0, width, height));
subdiv.insert(inPoints);
vector<cv::Point2f> triPoints;
make_delaunay_mesh( { width, height }, subdiv, triPoints);
for (size_t i = 0; i < triPoints.size(); i++) {
outPoints.push_back(triPoints[i]);
}
}
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;
uint64_t cnt = 1;
int64 start = cv::getTickCount();
double tickFreq = cv::getTickFrequency();
double lastFps = fps;
cv::UMat frameBuffer, videoFrame, downScaled, background;
cv::UMat foreground(HEIGHT, WIDTH, CV_8UC4, cv::Scalar::all(0));
cv::UMat downPrevGrey, downNextGrey, downMaskGrey;
vector<cv::Point2f> contourPoints, downNewPoints, downPrevPoints, downNextPoints, meshPoints, upPrevPoints, upNextPoints;
cv::Ptr<cv::BackgroundSubtractor> bgSubtractor = cv::createBackgroundSubtractorMOG2(100, 32.0, false);
std::vector<uchar> status;
std::vector<float> err;
vector<cv::Point2f> downHull, downOverdefined;
vector<vector<cv::Point> > contours;
vector<cv::Vec4i> hierarchy;
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);
cv::resize(videoFrame, downScaled, cv::Size(0, 0), 1.0 / DOWN_SCALE, 1.0 / DOWN_SCALE);
cv::boxFilter(downScaled, downScaled, -1, cv::Size(5, 5), cv::Point(-1, -1), true, cv::BORDER_REPLICATE);
cvtColor(downScaled, downNextGrey, cv::COLOR_RGB2GRAY);
bgSubtractor->apply(downScaled, downMaskGrey);
if (cv::countNonZero(downMaskGrey) < (WIDTH * HEIGHT * 0.1)) {
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(downMaskGrey, downMaskGrey, cv::MORPH_OPEN, element, cv::Point(-1, -1), 1);
findContours(downMaskGrey, contours, hierarchy, cv::RETR_TREE, cv::CHAIN_APPROX_SIMPLE);
meshPoints.clear();
for (const auto &c : contours) {
contourPoints.clear();
for (const auto &pt : c) {
contourPoints.push_back(pt);
}
collect_delaunay_mesh_points(downMaskGrey.cols, downMaskGrey.rows, contourPoints, meshPoints);
}
gl::bind();
nvg::begin();
nvg::clear();
if (meshPoints.size() > 12) {
cv::convexHull(meshPoints, downHull);
float area = cv::contourArea(downHull);
float density = (meshPoints.size() / area);
float max_points = density * 25000.0;
size_t copyn = std::min(meshPoints.size(), (size_t(std::ceil(max_points)) - downPrevPoints.size()));
std::random_shuffle(meshPoints.begin(), meshPoints.end());
if (downPrevPoints.size() < max_points) {
std::copy(meshPoints.begin(), meshPoints.begin() + copyn, std::back_inserter(downPrevPoints));
}
if (downPrevGrey.empty()) {
downPrevGrey = downNextGrey.clone();
}
cv::TermCriteria criteria = cv::TermCriteria((cv::TermCriteria::COUNT) + (cv::TermCriteria::EPS), 10, 0.03);
cv::calcOpticalFlowPyrLK(downPrevGrey, downNextGrey, downPrevPoints, downNextPoints, status, err, cv::Size(15, 15), 2, criteria, cv::OPTFLOW_LK_GET_MIN_EIGENVALS);
downNewPoints.clear();
if (downPrevPoints.size() > 1 && downNextPoints.size() > 1) {
upNextPoints.clear();
upPrevPoints.clear();
for (cv::Point2f pt : downPrevPoints) {
upPrevPoints.push_back(pt *= float(DOWN_SCALE));
}
for (cv::Point2f pt : downNextPoints) {
upNextPoints.push_back(pt *= float(DOWN_SCALE));
}
overdefine(downHull, downOverdefined, 5);
cv::RotatedRect ellipse = cv::fitEllipse(downOverdefined);
float shortAxis = std::min(ellipse.size.width, ellipse.size.height) * float(DOWN_SCALE);
float wholeArea = WIDTH * HEIGHT;
float stroke = 20.0 * sqrt(area / wholeArea);
using kb::nvg::vg;
nvgBeginPath(vg);
nvgStrokeWidth(vg, stroke);
nvgStrokeColor(vg, nvgHSLA(0.1, 1, 0.5, 48));
for (size_t i = 0; i < downPrevPoints.size(); i++) {
if (status[i] == 1 && err[i] < 0.005 && upNextPoints[i].y >= 0 && upNextPoints[i].x >= 0 && upNextPoints[i].y < HEIGHT && upNextPoints[i].x < WIDTH) {
downNewPoints.push_back(downNextPoints[i]);
float diffX = fabs(upNextPoints[i].x - upPrevPoints[i].x);
float diffY = fabs(upNextPoints[i].y - upPrevPoints[i].y);
float len = hypot(diffX, diffY);
if (len > 0 && len < shortAxis / 3.0) {
nvgMoveTo(vg, upNextPoints[i].x, upNextPoints[i].y);
nvgLineTo(vg, upPrevPoints[i].x, upPrevPoints[i].y);
}
}
}
nvgStroke(vg);
}
downPrevPoints = downNewPoints;
}
nvg::end();
} else {
gl::bind();
nvg::begin();
nvg::clear();
nvg::end();
foreground = cv::Scalar::all(0);
}
downPrevGrey = downNextGrey.clone();
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;
}