first commit
This commit is contained in:
249
utils/analysis_board/vp_analysis_board.cpp
Executable file
249
utils/analysis_board/vp_analysis_board.cpp
Executable file
@@ -0,0 +1,249 @@
|
||||
|
||||
|
||||
#include "vp_analysis_board.h"
|
||||
#include "vp_version.h"
|
||||
#include "../vp_pipe_checker.h"
|
||||
|
||||
namespace vp_utils {
|
||||
vp_analysis_board::vp_analysis_board(std::vector<std::shared_ptr<vp_nodes::vp_node>> src_nodes_in_pipe):
|
||||
src_nodes_in_pipe(src_nodes_in_pipe) {
|
||||
init();
|
||||
}
|
||||
|
||||
vp_analysis_board::~vp_analysis_board() {
|
||||
// set alive to false and wait threads exits
|
||||
alive = false;
|
||||
if (display_th.joinable()) {
|
||||
display_th.join();
|
||||
}
|
||||
if (rtmp_th.joinable()) {
|
||||
rtmp_th.join();
|
||||
}
|
||||
}
|
||||
|
||||
void vp_analysis_board::init() {
|
||||
src_nodes_on_screen.clear();
|
||||
des_nodes_on_screen.clear();
|
||||
|
||||
// check pipe
|
||||
vp_pipe_checker pipe_checker;
|
||||
pipe_checker(src_nodes_in_pipe);
|
||||
|
||||
// layers number and max nodes number of all layers
|
||||
pipe_width = pipe_checker.pipe_width();
|
||||
pipe_height = pipe_checker.pipe_height();
|
||||
|
||||
// calculate the w and h of canvas
|
||||
canvas_width = pipe_width * node_width + (pipe_width - 1) * node_gap_horizontal + 2 * canvas_gap_horizontal;
|
||||
canvas_height = pipe_height * node_height + (pipe_height - 1) * node_gap_vertical + 2 * canvas_gap_vertical;
|
||||
|
||||
// create canvas Mat and initialize it with white
|
||||
bg_canvas.create(canvas_height, canvas_width, CV_8UC3);
|
||||
bg_canvas = cv::Scalar(255, 255, 255);
|
||||
|
||||
// map recursively
|
||||
map_nodes(src_nodes_on_screen, 1);
|
||||
|
||||
// render static parts starting with 1st layer
|
||||
render_layer(src_nodes_on_screen, bg_canvas);
|
||||
|
||||
// save to local by default
|
||||
save(board_title + ".png");
|
||||
}
|
||||
void vp_analysis_board::reload(std::vector<std::shared_ptr<vp_nodes::vp_node>> new_src_nodes_in_pipe) {
|
||||
std::lock_guard<std::mutex> guard(reload_lock);
|
||||
if (new_src_nodes_in_pipe.size() != 0) {
|
||||
this->src_nodes_in_pipe = new_src_nodes_in_pipe;
|
||||
}
|
||||
init();
|
||||
}
|
||||
|
||||
void vp_analysis_board::save(std::string path) {
|
||||
cv::imwrite(path, bg_canvas);
|
||||
}
|
||||
|
||||
void vp_analysis_board::display(int interval, bool block) {
|
||||
assert(interval > 0);
|
||||
if (displaying) {
|
||||
return;
|
||||
}
|
||||
|
||||
auto display_func = [&, interval](){
|
||||
while (alive) {
|
||||
auto loop_start = std::chrono::system_clock::now();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(reload_lock); // in case it reloading
|
||||
// deep copy the static background
|
||||
cv::Mat mat_to_display = bg_canvas.clone();
|
||||
// render dynamic parts starting with 1 st layer
|
||||
render_layer(src_nodes_on_screen, mat_to_display, false);
|
||||
cv::imshow(board_title, mat_to_display);
|
||||
}
|
||||
|
||||
// calculate the time need wait for
|
||||
auto loop_cost = std::chrono::system_clock::now() - loop_start;
|
||||
auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::seconds(interval) - loop_cost);
|
||||
|
||||
cv::waitKey(wait_time.count());
|
||||
}};
|
||||
displaying = true;
|
||||
display_th = std::thread(display_func);
|
||||
if (block) {
|
||||
display_th.join();
|
||||
}
|
||||
}
|
||||
|
||||
void vp_analysis_board::push_rtmp(std::string rtmp, int bitrate) {
|
||||
if (displaying) {
|
||||
return;
|
||||
}
|
||||
auto fps = 10;
|
||||
auto rtmp_url = vp_utils::string_format(gst_template, bitrate, rtmp.c_str());
|
||||
// 10 fps by default
|
||||
assert(rtmp_writer.open(rtmp_url, cv::CAP_GSTREAMER, fps, {bg_canvas.cols, bg_canvas.rows}));
|
||||
|
||||
auto display_func = [&, fps](){
|
||||
while (alive) {
|
||||
auto loop_start = std::chrono::system_clock::now();
|
||||
{
|
||||
std::lock_guard<std::mutex> guard(reload_lock); // in case it reloading
|
||||
// deep copy the static background
|
||||
cv::Mat mat_to_display = bg_canvas.clone();
|
||||
// render dynamic parts starting with 1 st layer
|
||||
render_layer(src_nodes_on_screen, mat_to_display, false);
|
||||
rtmp_writer.write(mat_to_display);
|
||||
}
|
||||
|
||||
// calculate the time need wait for
|
||||
auto loop_cost = std::chrono::system_clock::now() - loop_start;
|
||||
auto wait_time = std::chrono::duration_cast<std::chrono::milliseconds>(std::chrono::milliseconds(1000 / fps) - loop_cost);
|
||||
|
||||
std::this_thread::sleep_for(wait_time);
|
||||
}};
|
||||
displaying = true;
|
||||
rtmp_th = std::thread(display_func);
|
||||
}
|
||||
|
||||
void vp_analysis_board::render_layer(std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_layer, cv::Mat& canvas, bool static_parts) {
|
||||
std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_next_layer;
|
||||
for(auto& i : nodes_in_layer) {
|
||||
if (static_parts) {
|
||||
i->render_static_parts(canvas);
|
||||
}
|
||||
else {
|
||||
i->render_dynamic_parts(canvas);
|
||||
}
|
||||
auto n = i->get_next_nodes_on_screen();
|
||||
nodes_in_next_layer.insert(nodes_in_next_layer.end(), n.begin(), n.end());
|
||||
}
|
||||
|
||||
if (nodes_in_next_layer.size() > 0) {
|
||||
bool all_the_same = true;
|
||||
for(auto & i: nodes_in_next_layer) {
|
||||
if (i != nodes_in_next_layer[0]) {
|
||||
all_the_same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// just keep the first one if all the next nodes are the same
|
||||
if (all_the_same) {
|
||||
nodes_in_next_layer.erase(nodes_in_next_layer.begin() + 1, nodes_in_next_layer.end());
|
||||
}
|
||||
|
||||
render_layer(nodes_in_next_layer, canvas, static_parts);
|
||||
}
|
||||
else { // recursion end
|
||||
|
||||
/* global drawing */
|
||||
|
||||
// draw layer index at the bottom of canvas
|
||||
if (static_parts) {
|
||||
for (int i = 0; i < pipe_width; i++) {
|
||||
/* code */
|
||||
cv::putText(canvas, "layer_" + std::to_string(i + 1), cv::Point(canvas_gap_horizontal + (node_width + node_gap_horizontal) * i, canvas_height - int(canvas_gap_vertical / 3)), 1, 1, cv::Scalar(255, 0, 0));
|
||||
}
|
||||
}
|
||||
|
||||
// refresh time & version info at the top left of canvas
|
||||
if (!static_parts) {
|
||||
auto time = vp_utils::time_format(NOW, "<hour>:<min>:<sec>");
|
||||
cv::putText(canvas, time, cv::Point(20, 20), 1, 1, cv::Scalar(255, 0, 0));
|
||||
|
||||
auto version_info = APP_VERSION;
|
||||
cv::putText(canvas, version_info, cv::Point(canvas.cols - 240, 20), 1, 0.8, cv::Scalar(255, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void vp_analysis_board::map_nodes(std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_layer, int layer) {
|
||||
if (layer == 1) {
|
||||
// here nodes_in_layer is empty
|
||||
auto num_src_nodes_in_pipe = src_nodes_in_pipe.size();
|
||||
|
||||
auto base_left = layer_base_left_cal(layer - 1);
|
||||
auto base_top = layer_base_top_cal(num_src_nodes_in_pipe);
|
||||
|
||||
// map nodes at 1st layer in memory to screen
|
||||
for (int i = 0; i < num_src_nodes_in_pipe; i++) {
|
||||
auto node_left = base_left;
|
||||
auto node_top = base_top + i * (node_height + node_gap_vertical);
|
||||
|
||||
auto node_on_screen = std::make_shared<vp_node_on_screen>(src_nodes_in_pipe[i], vp_objects::vp_rect(node_left, node_top, node_width, node_height), layer);
|
||||
src_nodes_on_screen.push_back(node_on_screen);
|
||||
}
|
||||
|
||||
map_nodes(src_nodes_on_screen, layer + 1);
|
||||
}
|
||||
else {
|
||||
std::vector<std::shared_ptr<vp_nodes::vp_node>> all_nodes_in_next_layer;
|
||||
for(auto &i: nodes_in_layer) {
|
||||
auto next_nodes = i->get_orginal_node()->next_nodes();
|
||||
all_nodes_in_next_layer.insert(all_nodes_in_next_layer.end(), next_nodes.begin(), next_nodes.end());
|
||||
}
|
||||
if (all_nodes_in_next_layer.size() > 0) {
|
||||
bool all_the_same = true;
|
||||
for(auto & i: all_nodes_in_next_layer) {
|
||||
if (i != all_nodes_in_next_layer[0]) {
|
||||
all_the_same = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// just keep the first one if all the next nodes are the same
|
||||
if (all_the_same) {
|
||||
all_nodes_in_next_layer.erase(all_nodes_in_next_layer.begin() + 1, all_nodes_in_next_layer.end());
|
||||
}
|
||||
|
||||
auto num_all_nodes_in_next_layer = all_nodes_in_next_layer.size();
|
||||
auto base_left = layer_base_left_cal(layer - 1);
|
||||
auto base_top = layer_base_top_cal(num_all_nodes_in_next_layer);
|
||||
|
||||
auto index = 0;
|
||||
std::shared_ptr<vp_node_on_screen> node_on_screen = nullptr;
|
||||
std::vector<std::shared_ptr<vp_node_on_screen>> nodes_in_next_layer;
|
||||
for(int i = 0; i < nodes_in_layer.size(); i++) {
|
||||
auto node_left = base_left;
|
||||
auto next_nodes_in_pipe = nodes_in_layer[i]->get_orginal_node()->next_nodes();
|
||||
for (int j = 0; j < next_nodes_in_pipe.size(); j++)
|
||||
{
|
||||
auto node_top = base_top + index * (node_height + node_gap_vertical);
|
||||
if (!all_the_same || node_on_screen == nullptr) {
|
||||
node_on_screen = std::make_shared<vp_node_on_screen>(next_nodes_in_pipe[j], vp_objects::vp_rect(node_left, node_top, node_width, node_height), layer);
|
||||
}
|
||||
nodes_in_layer[i]->get_next_nodes_on_screen().push_back(node_on_screen);
|
||||
|
||||
if (!all_the_same || nodes_in_next_layer.empty()) {
|
||||
nodes_in_next_layer.push_back(node_on_screen);
|
||||
}
|
||||
index++;
|
||||
}
|
||||
}
|
||||
// next layer
|
||||
map_nodes(nodes_in_next_layer, layer + 1);
|
||||
}
|
||||
else {
|
||||
// cache the last layer
|
||||
des_nodes_on_screen = nodes_in_layer;
|
||||
} // recursion end
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user