porting to emscripten

pull/3471/head
kallaballa 2 years ago
parent 4db3c81e70
commit e50460ae72
  1. 62
      Makefile
  2. 9
      src/common/detail/clglcontext.cpp
  3. 5
      src/common/detail/nanovgcontext.hpp
  4. 5
      src/common/nvg.hpp
  5. 19
      src/common/viz2d.cpp
  6. 2
      src/font/Makefile
  7. 2
      src/font/font-demo.cpp
  8. 19
      src/optflow/Makefile
  9. 235
      src/optflow/optflow-demo.cpp

@ -17,9 +17,8 @@ endif
ifdef EMSDK
CXX := em++
EMCXXFLAGS += -flto -sDISABLE_EXCEPTION_CATCHING=0 -sDISABLE_EXCEPTION_THROWING=0 -fexceptions
EMLDFLAGS += -s USE_GLFW=3 -s WASM=1 -s -s WASM_BIGINT -s LLD_REPORT_UNDEFINED=1 -s ALLOW_MEMORY_GROWTH=1 -sDISABLE_EXCEPTION_CATCHING=0 -sDISABLE_EXCEPTION_THROWING=0 -sEXCEPTION_DEBUG=1 -fexceptions
#LIBS += -lzlib -lopencv_calib3d -lopencv_core -lopencv_dnn -lopencv_features2d -lopencv_flann -lopencv_imgproc -lopencv_objdetect -lopencv_photo -lopencv_video -lopencv_objdetect -lopencv_face
EMCXXFLAGS += -flto -sINITIAL_MEMORY=512MB -sTOTAL_MEMORY=512MB -s USE_PTHREADS=1 -pthread
EMLDFLAGS += -sUSE_GLFW=3 -sWASM=1 -sWASM_BIGINT -sINITIAL_MEMORY=512MB -sTOTAL_MEMORY=512MB -sALLOW_MEMORY_GROWTH=1 -sUSE_PTHREADS=1 -pthread -sPTHREAD_POOL_SIZE=navigator.hardwareConcurrency
EMCXXFLAGS += -msimd128
CXXFLAGS += $(EMCXXFLAGS) -c
LDFLAGS += $(EMLDFLAGS)
@ -27,24 +26,69 @@ endif
all: release
release: CXXFLAGS += -g0 -O3
ifneq ($(UNAME_S), Darwin)
release: LDFLAGS += -s
endif
ifdef EMSDK
release: CXXFLAGS += -DNDEBUG -g0 -O3
release: LDFLAGS += -s STACK_OVERFLOW_CHECK=0 -s ASSERTIONS=0 -s SAFE_HEAP=0
endif
release: CXXFLAGS += -g0 -O3 -c
release: dirs
shrink: CXXFLAGS += -Os -w
shrink: LDFLAGS += -s
shrink: dirs
info: CXXFLAGS += -g3 -O0
info: LDFLAGS += -Wl,--export-dynamic -rdynamic
info: dirs
debug: CXXFLAGS += -g3 -O0 -rdynamic
ifndef EMSDK
debug: CXXFLAGS += -rdynamic
debug: LDFLAGS += -rdynamic
else
debug: CXXFLAGS += -sDISABLE_EXCEPTION_CATCHING=0 -sDISABLE_EXCEPTION_THROWING=0 -fexceptions
debug: LDFLAGS += -s ASSERTIONS=2 -sLLD_REPORT_UNDEFINED=1 -sDISABLE_EXCEPTION_CATCHING=0 -sDISABLE_EXCEPTION_THROWING=0 -sEXCEPTION_DEBUG=1 -fexceptions
endif
debug: CXXFLAGS += -g3 -O0
debug: LDFLAGS += -Wl,--export-dynamic
debug: dirs
profile: CXXFLAGS += -g3 -O1
profile: CXXFLAGS += -g3 -O3
profile: LDFLAGS += -Wl,--export-dynamic
ifdef EMSDK
profile: LDFLAGS += --profiling
profile: CXXFLAGS += --profiling
endif
ifndef EMSDK
profile: CXXFLAGS += -rdynamic
endif
profile: dirs
unsafe: CXXFLAGS += -g0 -Ofast -DNDEBUG -ffast-math -ftree-vectorizer-verbose=1 -funroll-loops -ftree-vectorize -fno-signed-zeros -fno-trapping-math -frename-registers
ifdef EMSDK
unsafe: CXXFLAGS += -DNDEBUG -g0 -O3 --closure 1 -ffp-contract=fast -freciprocal-math -fno-signed-zeros
unsafe: LDFLAGS += -s STACK_OVERFLOW_CHECK=0 -s ASSERTIONS=0 -s SAFE_HEAP=0 --closure 1 -menable-unsafe-fp-math
else
unsafe: CXXFLAGS += -DNDEBUG -g0 -Ofast
endif
#ifeq ($(UNAME_S), Darwin)
unsafe: LDFLAGS += -s
#endif
unsafe: dirs
asan: CXXFLAGS += -g3 -O0 -fno-omit-frame-pointer -fsanitize=address
asan: LDFLAGS += -fsanitize=address
ifdef EMSDK
asan: CXXFLAGS += -fsanitize=address
asan: LDFLAGS += -s STACK_OVERFLOW_CHECK=2 -s ASSERTIONS=2 -s NO_DISABLE_EXCEPTION_CATCHING=1 -s EXCEPTION_DEBUG=1 -fsanitize=address
else
asan: CXXFLAGS += -rdynamic -fsanitize=address
asan: LDFLAGS += -rdynamic -fsanitize=address
endif
asan: CXXFLAGS += -g3 -O0 -fno-omit-frame-pointer
asan: LDFLAGS += -Wl,--export-dynamic -rdynamic
ifndef EMSDK
asan: LIBS+= -lbfd -ldw
endif
asan: dirs
clean: dirs

@ -100,24 +100,25 @@ void CLGLContext::end() {
void CLGLContext::download(cv::UMat& m) {
cv::Mat tmp = m.getMat(cv::ACCESS_RW);
assert(tmp.data != nullptr);
GL_CHECK(glReadPixels(0, 0, frameBufferSize_.width, frameBufferSize_.height, GL_RGBA, GL_UNSIGNED_BYTE, tmp.data));
GL_CHECK(glReadPixels(0, 0, tmp.cols, tmp.rows, GL_RGBA, GL_UNSIGNED_BYTE, tmp.data));
tmp.release();
}
void CLGLContext::upload(const cv::UMat& m) {
cv::Mat tmp = m.getMat(cv::ACCESS_RW);
assert(tmp.data != nullptr);
GL_CHECK(glTexSubImage2D(
GL_TEXTURE_2D,
0,
0,
0,
frameBufferSize_.width,
frameBufferSize_.height,
tmp.cols,
tmp.rows,
GL_RGBA,
GL_UNSIGNED_BYTE,
tmp.data)
);
tmp.release();
}
void CLGLContext::acquireFromGL(cv::UMat &m) {

@ -1,7 +1,12 @@
#ifndef SRC_COMMON_NANOVGCONTEXT_HPP_
#define SRC_COMMON_NANOVGCONTEXT_HPP_
#ifndef __EMSCRIPTEN__
#define NANOGUI_USE_OPENGL
#else
#define NANOGUI_USE_GLES
#define NANOGUI_GLES_VERSION 3
#endif
#include "clglcontext.hpp"
#include <nanogui/nanogui.h>
#include <nanogui/opengl.h>

@ -2,7 +2,12 @@
#define SRC_COMMON_NVG_HPP_
#include "viz2d.hpp"
#ifndef __EMSCRIPTEN__
#define NANOGUI_USE_OPENGL
#else
#define NANOGUI_USE_GLES
#define NANOGUI_GLES_VERSION 3
#endif
#include <nanogui/opengl.h>
namespace kb {

@ -177,7 +177,9 @@ Viz2D::~Viz2D() {
}
bool Viz2D::initializeWindowing() {
assert(glfwInit() == GLFW_TRUE);
if(glfwInit() != GLFW_TRUE)
return false;
glfwSetErrorCallback(kb::viz2d::error_callback);
if (debug_)
@ -186,13 +188,18 @@ bool Viz2D::initializeWindowing() {
if (offscreen_)
glfwWindowHint(GLFW_VISIBLE, GLFW_FALSE);
// glfwSetTime(0);
glfwSetTime(0);
#ifdef __APPLE__
glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
glfwWindowHint (GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint (GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint (GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint (GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
#elif __EMSCRIPTEN__
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0);
glfwWindowHint(GLFW_CONTEXT_CREATION_API, GLFW_EGL_CONTEXT_API);
glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API) ;
#else
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, major_);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, minor_);

@ -6,7 +6,7 @@ endif
SRCS := font-demo.cpp
#precompiled headers
HEADERS := ../common/subsystems.hpp
HEADERS :=
OBJS := ${SRCS:.cpp=.o}
DEPS := ${SRCS:.cpp=.dep}

@ -100,8 +100,8 @@ void setup_gui(cv::Ptr<kb::viz2d::Viz2D> v2d) {
void iteration() {
//BGRA
static cv::UMat stars, warped;
static size_t cnt = 0;
static cv::RNG rng(cv::getTickCount());
static size_t cnt = 0;
if(update_stars) {
v2d->nanovg([&](const cv::Size& sz) {

@ -1,15 +1,24 @@
ifdef EMSDK
TARGET := optflow-demo.js
else
TARGET := optflow-demo
endif
SRCS := optflow-demo.cpp
SRCS := optflow-demo.cpp
#precompiled headers
HEADERS :=
OBJS := ${SRCS:.cpp=.o}
DEPS := ${SRCS:.cpp=.dep}
OBJS := ${SRCS:.cpp=.o}
DEPS := ${SRCS:.cpp=.dep}
CXXFLAGS += -fpic
LDFLAGS +=
LIBS += -lm -lopencv_optflow -lOpenCL -lviz2d
LIBS += -lm -lopencv_optflow -lopencv_video -lopencv_features2d -lviz2d -lz
ifndef EMSDK
LIBS += -lOpenCL
else
LDFLAGS += -sFULL_ES3 -sUSE_ZLIB=1 --bind
endif
.PHONY: all release debug clean distclean
all: release

@ -9,9 +9,11 @@
#include <set>
#include <string>
#include <thread>
#include <random>
#include <opencv2/features2d.hpp>
#include <opencv2/imgproc.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/optflow.hpp>
#include <opencv2/core/ocl.hpp>
@ -38,17 +40,67 @@ enum PostProcModes {
constexpr unsigned int WIDTH = 1920;
constexpr unsigned int HEIGHT = 1080;
constexpr unsigned long DIAG = hypot(double(WIDTH), double(HEIGHT));
const unsigned long DIAG = hypot(double(WIDTH), double(HEIGHT));
constexpr const char* OUTPUT_FILENAME = "optflow-demo.mkv";
constexpr bool OFFSCREEN = false;
constexpr int VA_HW_DEVICE_INDEX = 0;
static cv::Ptr<kb::viz2d::Viz2D> v2d = new kb::viz2d::Viz2D(cv::Size(WIDTH, HEIGHT), cv::Size(WIDTH, HEIGHT), OFFSCREEN, "Sparse Optical Flow Demo");
#ifndef __EMSCRIPTEN__
static cv::Ptr<kb::viz2d::Viz2D> v2dMenu = new kb::viz2d::Viz2D(cv::Size(240, 360), cv::Size(240,360), false, "Display Settings");
#else
# include <emscripten.h>
# include <emscripten/bind.h>
# include <fstream>
using namespace emscripten;
unsigned char buffer[HEIGHT * WIDTH * 4];
std::string pushImage(std::string filename){
try {
std::ifstream fs(filename, std::fstream::in | std::fstream::binary);
fs.seekg (0, std::ios::end);
auto length = fs.tellg();
fs.seekg (0, std::ios::beg);
fs.read (reinterpret_cast<char*>(buffer),length);
cv::Mat tmp(HEIGHT, WIDTH, CV_8UC4, buffer);
v2d->capture([&](cv::UMat &videoFrame) {
tmp.copyTo(videoFrame);
});
tmp.release();
return "success";
} catch(std::exception& ex) {
return string(ex.what());
}
}
EMSCRIPTEN_BINDINGS(my_module)
{
function("push_image", &pushImage);
}
#endif
/** Visualization parameters **/
// Generate the foreground at this scale.
#ifndef __EMSCRIPTEN__
float fg_scale = 0.5f;
#else
float fg_scale = 0.5f;
#endif
// On every frame the foreground loses on brightness. specifies the loss in percent.
#ifndef __EMSCRIPTEN__
float fg_loss = 2.5;
#else
float fg_loss = 10.0;
#endif
//Convert the background to greyscale
BackgroundModes background_mode = GREY;
// Peak thresholds for the scene change detection. Lowering them makes the detection more sensitive but
@ -57,14 +109,31 @@ float scene_change_thresh = 0.29f;
float scene_change_thresh_diff = 0.1f;
// The theoretical maximum number of points to track which is scaled by the density of detected points
// and therefor is usually much smaller.
#ifndef __EMSCRIPTEN__
int max_points = 250000;
#else
int max_points = 10000;
#endif
// How many of the tracked points to lose intentionally, in percent.
#ifndef __EMSCRIPTEN__
float point_loss = 25;
#else
float point_loss = 10;
#endif
// The theoretical maximum size of the drawing stroke which is scaled by the area of the convex hull
// of tracked points and therefor is usually much smaller.
#ifndef __EMSCRIPTEN__
int max_stroke = 14;
#else
int max_stroke = 2;
#endif
// Keep alpha separate for the GUI
#ifndef __EMSCRIPTEN__
float alpha = 0.1f;
#else
float alpha = 1.0f;
#endif
// Red, green, blue and alpha. All from 0.0f to 1.0f
nanogui::Color effect_color(1.0f, 0.75f, 0.4f, 1.0f);
//display on-screen FPS
@ -74,7 +143,11 @@ bool stretch = false;
//Use OpenCL or not
bool use_acceleration = true;
//The post processing mode
#ifndef __EMSCRIPTEN__
PostProcModes post_proc_mode = GLOW;
#else
PostProcModes post_proc_mode = NONE;
#endif
// Intensity of glow or bloom defined by kernel size. The default scales with the image diagonal.
int kernel_size = std::max(int(DIAG / 100 % 2 == 0 ? DIAG / 100 + 1 : DIAG / 100), 1);
//The lightness selection threshold
@ -84,11 +157,10 @@ float bloom_gain = 3;
void prepare_motion_mask(const cv::UMat& srcGrey, cv::UMat& motionMaskGrey) {
static cv::Ptr<cv::BackgroundSubtractor> bg_subtrator = cv::createBackgroundSubtractorMOG2(100, 16.0, false);
static int morph_size = 1;
static cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2 * morph_size + 1, 2 * morph_size + 1), cv::Point(morph_size, morph_size));
bg_subtrator->apply(srcGrey, motionMaskGrey);
int morph_size = 1;
cv::Mat element = cv::getStructuringElement(cv::MORPH_RECT, cv::Size(2 * morph_size + 1, 2 * morph_size + 1), cv::Point(morph_size, morph_size));
cv::morphologyEx(motionMaskGrey, motionMaskGrey, cv::MORPH_OPEN, element, cv::Point(element.cols >> 1, element.rows >> 1), 2, cv::BORDER_CONSTANT, cv::morphologyDefaultBorderValue());
}
@ -124,6 +196,8 @@ void visualize_sparse_optical_flow(const cv::UMat &prevGrey, const cv::UMat &nex
static vector<cv::Point2f> upPrevPoints, upNextPoints;
static std::vector<uchar> status;
static std::vector<float> err;
static std::random_device rd;
static std::mt19937 g(rd());
if (detectedPoints.size() > 4) {
cv::convexHull(detectedPoints, hull);
@ -133,7 +207,7 @@ void visualize_sparse_optical_flow(const cv::UMat &prevGrey, const cv::UMat &nex
float strokeSize = maxStrokeSize * pow(area / (nextGrey.cols * nextGrey.rows), 0.33f);
size_t currentMaxPoints = ceil(density * maxPoints);
std::random_shuffle(prevPoints.begin(), prevPoints.end());
std::shuffle(prevPoints.begin(), prevPoints.end(), g);
prevPoints.resize(ceil(prevPoints.size() * (1.0f - (pointLossPercent / 100.0f))));
size_t copyn = std::min(detectedPoints.size(), (size_t(std::ceil(currentMaxPoints)) - prevPoints.size()));
@ -267,7 +341,6 @@ void composite_layers(cv::UMat& background, const cv::UMat& foreground, const cv
cv::add(background, post, dst);
}
void setup_gui(cv::Ptr<kb::viz2d::Viz2D> v2d, cv::Ptr<kb::viz2d::Viz2D> v2dMenu) {
v2d->makeWindow(5, 30, "Effects");
@ -344,30 +417,105 @@ void setup_gui(cv::Ptr<kb::viz2d::Viz2D> v2d, cv::Ptr<kb::viz2d::Viz2D> v2dMenu)
v2dMenu->makeFormVariable("Stetch", stretch, "Stretch the frame buffer to the window size")->set_callback([=](const bool &s) {
v2d->setStretching(s);
});
#ifndef __EMSCRIPTEN__
v2dMenu->makeButton("Fullscreen", [=]() {
v2d->setFullscreen(!v2d->isFullscreen());
});
v2dMenu->makeButton("Offscreen", [=]() {
v2d->setOffscreen(!v2d->isOffscreen());
});
#endif
}
void iteration() {
//BGRA
static cv::UMat background, down;
static cv::UMat foreground(v2d->getFrameBufferSize(), CV_8UC4, cv::Scalar::all(0));
//RGB
static cv::UMat menuFrame;
//GREY
static cv::UMat downPrevGrey, downNextGrey, downMotionMaskGrey;
static vector<cv::Point2f> detectedPoints;
if(v2d->isAccelerated() != use_acceleration)
v2d->setAccelerated(use_acceleration);
#ifndef __EMSCRIPTEN__
if(!v2d->capture())
exit(0);
#endif
v2d->opencl([&](cv::UMat& frameBuffer) {
cv::resize(frameBuffer, down, cv::Size(v2d->getFrameBufferSize().width * fg_scale, v2d->getFrameBufferSize().height * fg_scale));
frameBuffer.copyTo(background);
});
cv::cvtColor(down, downNextGrey, cv::COLOR_RGBA2GRAY);
//Subtract the background to create a motion mask
prepare_motion_mask(downNextGrey, downMotionMaskGrey);
//Detect trackable points in the motion mask
detect_points(downMotionMaskGrey, detectedPoints);
v2d->nanovg([&](const cv::Size& sz) {
v2d->clear();
if (!downPrevGrey.empty()) {
//We don't want the algorithm to get out of hand when there is a scene change, so we suppress it when we detect one.
if (!detect_scene_change(downMotionMaskGrey, scene_change_thresh, scene_change_thresh_diff)) {
//Visualize the sparse optical flow using nanovg
cv::Scalar color = cv::Scalar(effect_color.b() * 255.0f, effect_color.g() * 255.0f, effect_color.r() * 255.0f, alpha * 255.0f);
visualize_sparse_optical_flow(downPrevGrey, downNextGrey, detectedPoints, fg_scale, max_stroke, color, max_points, point_loss);
}
}
});
downPrevGrey = downNextGrey.clone();
v2d->opencl([&](cv::UMat& frameBuffer){
//Put it all together (OpenCL)
composite_layers(background, foreground, frameBuffer, frameBuffer, kernel_size, fg_loss, background_mode, post_proc_mode);
#ifndef __EMSCRIPTEN__
cvtColor(frameBuffer, menuFrame, cv::COLOR_BGRA2RGB);
#endif
});
update_fps(v2d, show_fps);
#ifndef __EMSCRIPTEN__
v2d->write();
v2dMenu->capture([&](cv::UMat& videoFrame) {
menuFrame.copyTo(videoFrame);
});
if(!v2dMenu->display())
exit(0);
#endif
//If onscreen rendering is enabled it displays the framebuffer in the native window. Returns false if the window was closed.
if(!v2d->display())
exit(0);
}
int main(int argc, char **argv) {
using namespace kb::viz2d;
#ifndef __EMSCRIPTEN__
if (argc != 2) {
std::cerr << "Usage: optflow <input-video-file>" << endl;
exit(1);
}
cv::Ptr<Viz2D> v2d = new Viz2D(cv::Size(WIDTH, HEIGHT), cv::Size(WIDTH, HEIGHT), OFFSCREEN, "Sparse Optical Flow Demo");
cv::Ptr<Viz2D> v2dMenu = new Viz2D(cv::Size(240, 360), cv::Size(240,360), false, "Display Settings");
#endif
print_system_info();
if(!v2d->isOffscreen()) {
#ifndef __EMSCRIPTEN__
setup_gui(v2d, v2dMenu);
v2d->setVisible(true);
v2dMenu->setVisible(true);
#else
setup_gui(v2d, v2d);
#endif
v2d->setVisible(true);
}
#ifndef __EMSCRIPTEN__
auto capture = v2d->makeVACapture(argv[1], VA_HW_DEVICE_INDEX);
if (!capture.isOpened()) {
@ -378,70 +526,15 @@ int main(int argc, char **argv) {
float fps = capture.get(cv::CAP_PROP_FPS);
float width = capture.get(cv::CAP_PROP_FRAME_WIDTH);
float height = capture.get(cv::CAP_PROP_FRAME_HEIGHT);
v2d->makeVAWriter(OUTPUT_FILENAME, cv::VideoWriter::fourcc('V', 'P', '9', '0'), fps, cv::Size(width, height), VA_HW_DEVICE_INDEX);
//BGRA
cv::UMat background, down;
cv::UMat foreground(v2d->getFrameBufferSize(), CV_8UC4, cv::Scalar::all(0));
//RGB
cv::UMat menuFrame;
//GREY
cv::UMat downPrevGrey, downNextGrey, downMotionMaskGrey;
vector<cv::Point2f> detectedPoints;
v2d->makeVAWriter(OUTPUT_FILENAME, cv::VideoWriter::fourcc('V', 'P', '9', '0'), fps, cv::Size(width, height), VA_HW_DEVICE_INDEX);
while (true) {
if(v2d->isAccelerated() != use_acceleration)
v2d->setAccelerated(use_acceleration);
if(!v2d->capture())
break;
v2d->opencl([&](cv::UMat& frameBuffer) {
cv::resize(frameBuffer, down, cv::Size(v2d->getFrameBufferSize().width * fg_scale, v2d->getFrameBufferSize().height * fg_scale));
frameBuffer.copyTo(background);
});
cv::cvtColor(down, downNextGrey, cv::COLOR_RGBA2GRAY);
//Subtract the background to create a motion mask
prepare_motion_mask(downNextGrey, downMotionMaskGrey);
//Detect trackable points in the motion mask
detect_points(downMotionMaskGrey, detectedPoints);
v2d->nanovg([&](const cv::Size& sz) {
v2d->clear();
if (!downPrevGrey.empty()) {
//We don't want the algorithm to get out of hand when there is a scene change, so we suppress it when we detect one.
if (!detect_scene_change(downMotionMaskGrey, scene_change_thresh, scene_change_thresh_diff)) {
//Visualize the sparse optical flow using nanovg
cv::Scalar color = cv::Scalar(effect_color.b() * 255.0f, effect_color.g() * 255.0f, effect_color.r() * 255.0f, alpha * 255.0f);
visualize_sparse_optical_flow(downPrevGrey, downNextGrey, detectedPoints, fg_scale, max_stroke, color, max_points, point_loss);
}
}
});
downPrevGrey = downNextGrey.clone();
v2d->opencl([&](cv::UMat& frameBuffer){
//Put it all together (OpenCL)
composite_layers(background, foreground, frameBuffer, frameBuffer, kernel_size, fg_loss, background_mode, post_proc_mode);
cvtColor(frameBuffer, menuFrame, cv::COLOR_BGRA2RGB);
});
update_fps(v2d, show_fps);
v2d->write();
v2dMenu->capture([&](cv::UMat& videoFrame) {
menuFrame.copyTo(videoFrame);
});
//If onscreen rendering is enabled it displays the framebuffer in the native window. Returns false if the window was closed.
if(!v2d->display())
break;
if(!v2dMenu->display())
break;
iteration();
}
#else
emscripten_set_main_loop(iteration, -1, false);
#endif
return 0;
}

Loading…
Cancel
Save