-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathhighlight.cpp
317 lines (254 loc) · 8.54 KB
/
highlight.cpp
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
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
// highlight.cpp : This file contains the 'main' function. Program execution begins and ends there.
// Austin Hester CS542o dec 2020
// g++.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 8.1.0
#include <opencv2/core/core.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <vector>
#include <iostream>
#include "./include/cla_parse.hpp"
#include "./include/dim_image.hpp"
#include "./include/dir_func.hpp"
#include "./include/hsv_convert.hpp"
#include "./include/img_struct.hpp"
const std::string WINDOW_NAME = "Highlight";
// CLA variables
std::string input_image;
bool grayscale;
// adjust dim_array values for some crazy backgrounds
const float dim_array[] = { 1, 0.9, 0.8, 0.7, 0.6, 0.5, 0.4, 0.3, 0.2, 0.1, -111.11, -22.22, -11.11, 111.11, 22.22, 11.11, 0 };
float dim_factor = 1;
int slider_dim_value = 0;
img_struct_t* og_image;
cv::Mat displayed_image;
cv::Mat hsv_image;
// SelectionStage
struct SelectionState {
cv::Point selection_top_left, selection_bottom_right, mouse_pos;
bool started = false, done = false;
uint step = 0;
cv::Rect
to_rect() {
return cv::Rect (
std::min(this->selection_top_left.x, this->mouse_pos.x),
std::min(this->selection_top_left.y, this->mouse_pos.y),
std::abs(this->selection_top_left.x - this->mouse_pos.x),
std::abs(this->selection_top_left.y - this->mouse_pos.y)
);
}
void
reset() {
this->selection_top_left = cv::Point(0, 0);
this->selection_bottom_right = cv::Point(0, 0);
this->mouse_pos = cv::Point(0, 0);
this->started = false;
this->done = false;
this->step = 0;
}
} state;
void on_rect_complete();
// 'event loop' for keypresses
int
wait_key()
{
char key_pressed = cv::waitKey(0) & 255;
if (cv::getWindowProperty(WINDOW_NAME, cv::WND_PROP_VISIBLE) < 1) {
// this ends the program if window is closed
return 0;
}
// 'o' or 'c' displays the original image
if (key_pressed == 'o' || key_pressed == 'c') {
// reset selection state
state.reset();
on_rect_complete();
cv::imshow(WINDOW_NAME, displayed_image);
}
// 's' saves the current image
if (key_pressed == 's') {
if (!displayed_image.empty()) {
// if there's an image, save it
std::string out_filename =
"output_" + std::string(grayscale ? "grayscale_" : "color_") +
std::to_string(state.selection_top_left.x) + "_" +
std::to_string(state.selection_bottom_right.y) +
"/" + input_image;
write_img_to_file(
displayed_image,
"./out",
out_filename
);
std::cout << "Image saved to ./out/" << out_filename << std::endl;
return 1;
}
}
// 'q' or <escape> quits out
if (key_pressed == 27 || key_pressed == 'q') {
cv::destroyAllWindows();
return 0;
}
return 1;
}
// save the ROI to a cv::Mat
cv::Mat
extract_roi(cv::Mat src, cv::Rect rect)
{
cv::Mat dst;
src(rect).copyTo(dst);
return dst;
}
// grayscale processing, return equalized ROI
cv::Mat
process_grayscale()
{
// deep copy original to displayed_image
og_image->image.copyTo(displayed_image); // comment this out to draw a bunch of rectangles!
// save the region of interest
cv::Mat roi = extract_roi(displayed_image, state.to_rect());
dim_grayscale_image(displayed_image, dim_factor);
// equalize region of interest
cv::Mat equalized_roi;
cv::equalizeHist(roi, equalized_roi);
roi.release();
return equalized_roi;
}
// color processing, return equalized ROI
cv::Mat
process_color()
{
// deep copy original HSV to displayed_image
hsv_image.copyTo(displayed_image);
// save the region of interest in HSV
cv::Mat roi = extract_roi(displayed_image, state.to_rect());
// HSV dimming value channel
dim_hsv_image(displayed_image, dim_factor);
// displayed image back to BGR
hsv_to_bgr(displayed_image, &displayed_image);
// [] HSV equalizer
cv::Mat equalized_roi;
// 1. split the original image into 3 hsv channels
cv::Mat hsv_values[3];
cv::split(roi, hsv_values);
std::vector<cv::Mat> channels = { hsv_values[0], hsv_values[1], hsv_values[2] };
// 2. equalize the image
cv::Mat value_equalized;
cv::equalizeHist(channels[2], value_equalized);
channels[2] = value_equalized;
// 3. merge channels back together
cv:merge(channels, equalized_roi);
// 4. convert equalized ROI to BGR
hsv_to_bgr(equalized_roi, &equalized_roi);
value_equalized.release();
roi.release();
return equalized_roi;
}
// post complete rectangle
void
on_rect_complete()
{
if (state.to_rect().area() == 0) {
if (grayscale) {
// deep copy original to displayed_image
og_image->image.copyTo(displayed_image);
dim_grayscale_image(displayed_image, dim_factor);
} else {
// deep copy original HSV to displayed_image
hsv_image.copyTo(displayed_image);
dim_hsv_image(displayed_image, dim_factor);
// displayed image back to BGR
hsv_to_bgr(displayed_image, &displayed_image);
}
cv::imshow(WINDOW_NAME, displayed_image);
return;
}
// equalize the ROI (in real_time)
cv::Mat equalized_roi;
if (grayscale) {
// grayscale processing
equalized_roi = process_grayscale();
} else {
// color processing (BGR -> HSV)
equalized_roi = process_color();
}
// insert ROI into displayed image
equalized_roi.copyTo(displayed_image(state.to_rect()));
equalized_roi.release();
// show the final product
cv::imshow(WINDOW_NAME, displayed_image);
}
// trackbar for dim level
static void
on_trackbar_dim_level(int, void*)
{
dim_factor = dim_array[slider_dim_value];
on_rect_complete();
}
void
mouse_callback(int event, int x, int y, int, void*)
{
// https://gist.github.com/guimeira/541e9056364b9491452b7027f12536cc (modified move)
switch (event) {
case cv::EVENT_LBUTTONDOWN:
// reset selection state
state.reset();
state.selection_top_left.x = x;
state.selection_top_left.y = y;
state.mouse_pos.x = x;
state.mouse_pos.y = y;
state.done = false;
state.started = true;
break;
case cv::EVENT_LBUTTONUP:
state.selection_bottom_right.x = x;
state.selection_bottom_right.y = y;
state.started = false;
on_rect_complete();
state.done = true;
break;
case cv::EVENT_MOUSEMOVE:
// don't listen for anything if not started
if (!state.started) break;
// listen only every X times ( increase X if your PC is slow )
if (++state.step % 2 == 0) break;
// only allow mouse_pos to handle changes inside display
if (x < 0) x = 0; else if (x >= displayed_image.cols) x = displayed_image.cols - 1;
state.mouse_pos.x = x;
if (y < 0) y = 0; else if (y >= displayed_image.rows) y = displayed_image.rows - 1;
state.mouse_pos.y = y;
// draw rectangle in real time
on_rect_complete();
break;
}
}
int
main(int argc, const char** argv)
{
// parse and save command line args
int parse_result = parse_arguments(
argc, argv,
&input_image,
&grayscale
);
if (parse_result != 1) return parse_result;
assert(input_image.length() > 0);
// open image, grayscale = true
og_image = open_image(input_image.c_str(), grayscale);
assert(og_image != NULL);
// deep keep to displayed_image
og_image->image.copyTo(displayed_image);
// initialize HSV image if using color
if (!grayscale) {
bgr_to_hsv(displayed_image, &hsv_image);
}
std::cout << "\nShortcuts:\n\ts\t- save image\n\tc\t- clear selection\n\tq\t- quit\n";
// display the original image
cv::imshow(WINDOW_NAME, displayed_image);
cv::createTrackbar("Dim Level", WINDOW_NAME, &slider_dim_value, sizeof(dim_array)/sizeof(float)-1, on_trackbar_dim_level);
cv::setMouseCallback(WINDOW_NAME, mouse_callback);
// 'event loop' for keypresses
while (wait_key());
og_image->image.release();
delete og_image;
hsv_image.release();
displayed_image.release();
return 0;
}