-
Notifications
You must be signed in to change notification settings - Fork 226
/
Copy pathvp_utils.h
executable file
·193 lines (163 loc) · 9.17 KB
/
vp_utils.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <vector>
#include <regex>
#include <opencv2/imgproc.hpp>
// https://github.com/HowardHinnant/date/blob/master/include/date/date.h
#include "date.h"
using namespace std;
using namespace std::chrono;
namespace vp_utils {
// string format in C++17
template<typename ... Args>
inline string string_format(const string& format, Args ... args){
size_t size = 1 + snprintf(nullptr, 0, format.c_str(), args ...);
// unique_ptr<char[]> buf(new char[size]);
char bytes[size];
snprintf(bytes, size, format.c_str(), args ...);
return string(bytes);
}
// get optimal font scale depend on screen width and height
inline double get_optimal_font_scale(string text, int screen_width, int screen_height, cv::Size & text_size, int fontface = cv::FONT_HERSHEY_PLAIN, int thickness = 1) {
int baseline = 0;
for (int i = 20; i > 0; i--) {
auto size = cv::getTextSize(text, fontface, i / 20.0, thickness, &baseline);
if (size.width < screen_width && size.height < screen_height) {
text_size = size;
return i / 20.0;
}
}
return 1;
}
// draw rounded rectangle, fill the backgound.
inline void draw_rounded_rectangle(cv::Mat& src, cv::Point top_left, cv::Point bottom_right, int corner_radius, cv::Scalar color = cv::Scalar(), int thickness = 1, cv::Scalar fill_color = cv::Scalar(255, 255, 255), int line_type = cv::LINE_4)
{
/* corners:
* p1 - p2
* | |
* p4 - p3
*/
if (corner_radius < 1) {
corner_radius = 1;
}
cv::Point p1 = top_left;
cv::Point p2 = cv::Point (bottom_right.x, top_left.y);
cv::Point p3 = bottom_right;
cv::Point p4 = cv::Point (top_left.x, bottom_right.y);
// fill rounded rectangle
if (thickness < 0) {
cv::rectangle(src, cv::Rect(cv::Point (p1.x + corner_radius, p1.y), cv::Point (p3.x - corner_radius, p3.y)), fill_color, thickness);
cv::rectangle(src, cv::Rect(cv::Point (p1.x, p1.y + corner_radius), cv::Point (p3.x, p3.y - corner_radius)), fill_color, thickness);
cv::ellipse( src, p1 + cv::Point(corner_radius, corner_radius), cv::Size( corner_radius, corner_radius ), 180.0, 0, 90, fill_color, thickness, line_type );
cv::ellipse( src, p2 + cv::Point(-corner_radius, corner_radius), cv::Size( corner_radius, corner_radius ), 270.0, 0, 90, fill_color, thickness, line_type );
cv::ellipse( src, p3 + cv::Point(-corner_radius, -corner_radius), cv::Size( corner_radius, corner_radius ), 0.0, 0, 90, fill_color, thickness, line_type );
cv::ellipse( src, p4 + cv::Point(corner_radius, -corner_radius), cv::Size( corner_radius, corner_radius ), 90.0, 0, 90, fill_color, thickness, line_type );
thickness = 1;
}
// draw straight lines
cv::line(src, cv::Point (p1.x + corner_radius, p1.y), cv::Point (p2.x - corner_radius, p2.y), color, thickness, line_type);
cv::line(src, cv::Point (p2.x, p2.y + corner_radius), cv::Point (p3.x, p3.y - corner_radius), color, thickness, line_type);
cv::line(src, cv::Point (p4.x + corner_radius, p4.y), cv::Point (p3.x - corner_radius, p3.y), color, thickness, line_type);
cv::line(src, cv::Point (p1.x, p1.y + corner_radius), cv::Point (p4.x, p4.y - corner_radius), color, thickness, line_type);
// draw arcs
cv::ellipse( src, p1 + cv::Point(corner_radius, corner_radius), cv::Size( corner_radius, corner_radius ), 180.0, 0, 90, color, thickness, line_type );
cv::ellipse( src, p2 + cv::Point(-corner_radius, corner_radius), cv::Size( corner_radius, corner_radius ), 270.0, 0, 90, color, thickness, line_type );
cv::ellipse( src, p3 + cv::Point(-corner_radius, -corner_radius), cv::Size( corner_radius, corner_radius ), 0.0, 0, 90, color, thickness, line_type );
cv::ellipse( src, p4 + cv::Point(corner_radius, -corner_radius), cv::Size( corner_radius, corner_radius ), 90.0, 0, 90, color, thickness, line_type );
}
// put text at the center of specific rect on canvas
inline void put_text_at_center_of_rect(cv::Mat& canvas, string text, cv::Rect rect, bool fill = false, int font_face = cv::FONT_HERSHEY_PLAIN, int thickness = 1, cv::Scalar color = cv::Scalar(), cv::Scalar border_color = cv::Scalar(201, 201, 205), cv::Scalar fill_color = cv::Scalar(201, 201, 205), int padding = 1) {
cv::Size text_size;
auto font_scale = get_optimal_font_scale(text, rect.width - padding * 2, rect.height - padding * 2, text_size, font_face, thickness);
if (fill) {
draw_rounded_rectangle(canvas, cv::Point(rect.x, rect.y), cv::Point(rect.x + rect.width, rect.y + rect.height), 3, border_color, -1, fill_color);
}
cv::putText(canvas,
text,
cv::Point(rect.x + (rect.width - text_size.width) / 2, rect.y + rect.height - (rect.height - text_size.height) / 2),
font_face, font_scale, color);
}
// use std::string::end_with(...) in standard library directly for C++20
inline bool ends_with(std::string const & value, std::string const & ending) {
if (ending.size() > value.size()) return false;
return std::equal(ending.rbegin(), ending.rend(), value.rbegin());
}
// round double data using specific precision, return string.
inline std::string round_any(double input, int precision) {
input = input * std::pow(10, precision) + 0.5;
auto output = std::to_string(std::floor(input) / std::pow(10, precision));
output.erase(std::find_if(output.rbegin(), output.rend(), [](unsigned char ch) {
return ch != '0';
}).base(), output.end());
// remove the last point `.` if possible
if (ends_with(output, ".")) {
output.pop_back();
}
return output;
}
inline std::vector<std::string> string_split(const std::string& s, char delimiter) {
std::vector<std::string> tokens;
std::string token;
std::istringstream tokenStream(s);
while (std::getline(tokenStream, token, delimiter)) {
tokens.push_back(token);
}
return tokens;
}
// split time point to 7 parts, include year, month, day ... in order
inline void time_split(system_clock::time_point tp, std::vector<int>& time_parts, int time_zone = 8) {
// right time zone
tp = tp + std::chrono::hours{time_zone};
auto dp = date::floor<date::days>(tp);
auto ymd = date::year_month_day{dp};
auto time = date::make_time(std::chrono::duration_cast<std::chrono::milliseconds>(tp - dp));
time_parts.clear();
// push year/month/day
time_parts.push_back(static_cast<int>(ymd.year()));
time_parts.push_back(static_cast<unsigned>(ymd.month()));
time_parts.push_back(static_cast<unsigned>(ymd.day()));
// push hour/minute/second/milisecond
time_parts.push_back(time.hours().count());
time_parts.push_back(time.minutes().count());
time_parts.push_back(time.seconds().count());
time_parts.push_back(time.subseconds().count());
}
// set width and fill, like ostream.fill() and ostream.width()
inline std::string set_width_and_fill(std::string str,
int width,
char fill = '0',
bool fill_left = true) {
while (str.size() < width) {
if (fill_left) {
str = fill + str;
}
else {
str = str + fill;
}
}
return str;
}
// format time point to string, chars in <> are keywords.
// in: <year>-<mon>-<day> <hour>:<min>:<sec>.<mili>
// out: 2022-10-08 13:53:07.230
// or
// in: [<day>/<mon>/<year> <hour>:<min>:<sec>.<mili>]
// out: [08/10/2022 13:53:07.230]
inline std::string time_format(system_clock::time_point tp, std::string template_str = "<year>-<mon>-<day> <hour>:<min>:<sec>.<mili>", int time_zone = 8) {
// right time zone
tp = tp + std::chrono::hours{time_zone};
// 7 parts
std::vector<int> time_parts;
time_split(tp, time_parts, 0);
auto time_str = std::regex_replace(template_str, std::regex("<year>"), std::to_string(time_parts[0]));
time_str = std::regex_replace(time_str, std::regex("<mon>"), set_width_and_fill(std::to_string(time_parts[1]), 2));
time_str = std::regex_replace(time_str, std::regex("<day>"), set_width_and_fill(std::to_string(time_parts[2]), 2));
time_str = std::regex_replace(time_str, std::regex("<hour>"), set_width_and_fill(std::to_string(time_parts[3]), 2));
time_str = std::regex_replace(time_str, std::regex("<min>"), set_width_and_fill(std::to_string(time_parts[4]), 2));
time_str = std::regex_replace(time_str, std::regex("<sec>"), set_width_and_fill(std::to_string(time_parts[5]), 2));
time_str = std::regex_replace(time_str, std::regex("<mili>"), set_width_and_fill(std::to_string(time_parts[6]), 3));
return time_str;
}
}