-
Notifications
You must be signed in to change notification settings - Fork 6
/
irview.cpp
313 lines (220 loc) · 7.97 KB
/
irview.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
// irview : simple viewer for adding false colour to IR or thermal images
// / video / camera feed
// usage: prog {image/video file}
// Author : Toby Breckon, toby.breckon@durham.ac.uk
// Copyright (c) 2008 School of Engineering, Cranfield University
// Copyright (c) 2017 School of Engineering and Computing Sciences, Durham University
// Copyright (c) 2019-2022 Dept. Computer Science, Durham University
// License : GPL - http://www.gnu.org/licenses/gpl.html
/******************************************************************************/
#include <opencv2/videoio.hpp>
#include <opencv2/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include <iostream> // standard C++ I/O
#include <string> // standard C++ I/O
#include <algorithm> // includes max()
using namespace cv; // OpenCV API is in the C++ "cv" namespace
using namespace std;
/******************************************************************************/
// setup the camera index properly based on OS platform
// 0 in linux gives first camera for v4l
//-1 in windows gives first device or user dialog selection
#ifdef linux
#define CAMERA_INDEX 0
#define CAMERA_API_TO_USE CAP_V4L2
#else
#define CAMERA_INDEX -1
#define CAMERA_API_TO_USE CAP_ANY
#endif
/******************************************************************************/
#define PROG_ID_STRING "IrView v0.3 - (c) Toby Breckon, 2008-2022+"
#define LICENSE_STRING "GPL - http://www.gnu.org/licenses/gpl.html"
static void print_help(char **name){
printf("\n%s\n", PROG_ID_STRING);
printf ("\t with OpenCV version %s (%d.%d.%d)\n",
CV_VERSION,
CV_MAJOR_VERSION, CV_MINOR_VERSION, CV_SUBMINOR_VERSION);
printf("%s\n\n", LICENSE_STRING);
printf("Usage :%s [image/video file]\n", name[0]);
printf("Camera interface: run with no file argument for direct camera use (default: camera 0)\n");
printf("\nKeyboard commands\n");
printf("\t a - automatic scaling (default: on)\n");
printf("\t b - show both false colour and original (default: off)\n");
printf("\t c - toggle false colour (default: on)\n");
printf("\t e - exit (as per x or ESC)\n");
printf("\t f - toggle full screen (default: off)\n");
printf("\t x - exit\n\n");
}
/******************************************************************************/
// concatenate 2 OpenCV Mat Objects side-by-side (in general)
Mat concatImages(Mat img1, Mat img2)
{
Mat out = Mat(img1.rows, img1.cols + img2.cols, img1.type());
Mat roi = out(Rect(0, 0, img1.cols, img1.rows));
Mat roi2 = out(Rect(img1.cols, 0, img2.cols, img2.rows));
img1.copyTo(roi);
// depth of img1 is master, depth of img2 is slave
// so convert if needed
if (img1.depth() != img2.depth())
{
// e.g. if img2 is 8-bit and img1 32-bit - scale to range 0->1 (32-bit)
// otherwise img2 is 32-bit and img1 is 8-bit - scale to 0->255 (8-bit)
img2.convertTo(roi2, img1.depth(), (img2.depth() < img1.depth()) ? 1.0 / 255.0 : 255);
} else {
img2.copyTo(roi2);
}
return out;
}
/******************************************************************************/
// set all mat values at given channel to given value
// from: https://stackoverflow.com/questions/23510571/how-to-set-given-channel-of-a-cvmat-to-a-given-value-efficiently-without-chang/23518786
void setChannel(Mat &mat, int channel, unsigned char value)
{
// make sure have enough channels
if (mat.channels() < channel+1)
return;
// check mat is continuous or not
if (mat.isContinuous())
mat.reshape(1, mat.rows*mat.cols).col(channel).setTo(Scalar(value));
else{
for (int i = 0; i < mat.rows; i++)
mat.row(i).reshape(1, mat.cols).col(channel).setTo(Scalar(value));
}
}
/******************************************************************************/
int main( int argc, char** argv )
{
Mat img; // image object
VideoCapture capture; // capture object
const char* windowNameHSV = PROG_ID_STRING; // window name
Mat HSV; // HSV image
bool keepProcessing = true; // loop control flag
unsigned char key; // user input
int EVENT_LOOP_DELAY = 40; // 40ms equates to 1000ms/25fps =
// 40ms per frame
bool useFalseColour = true; // process flag - false colour
bool useNormalisation = true; // process flag - normalisation
bool useConcatImage = false; // process flag - show concatenated images
bool useFullScreen = false; // process flag - run full screen
// if command line arguments are provided try to read image/video_name
// otherwise default to capture from attached H/W camera
if(
( argc == 2 && (!(img = imread( argv[1], IMREAD_COLOR)).empty()))||
( argc == 2 && (capture.open(argv[1]) == true )) ||
( argc != 2 && (capture.open(CAMERA_INDEX, CAMERA_API_TO_USE) == true))
)
{
// print help
print_help(argv);
// create window object (use flag=0 to allow resize, 1 to auto fix size)
namedWindow(windowNameHSV, WINDOW_NORMAL);
if (capture.isOpened()) {
// if capture object in use (i.e. video/camera)
// get initial image from capture object
capture >> img;
if(img.empty()){
if (argc == 2){
printf("End of video file reached\n");
} else {
printf("ERROR: cannot get next frame from camera\n");
}
exit(0);
}
}
resizeWindow(windowNameHSV, img.cols, img.rows);
// setup output image in HSV
HSV = img.clone();
// set channels up for Saturation / Variance
Mat HSV_channels[3];
// start main loop
while (keepProcessing)
{
// if capture object in use (i.e. video/camera)
// get image from capture object
if (capture.isOpened()) {
// if capture object in use (i.e. video/camera)
// get initial image from capture object
capture >> img;
if(img.empty()){
if (argc == 2){
printf("End of video file reached\n");
} else {
printf("ERROR: cannot get next frame from camera\n");
}
exit(0);
}
}
// extract H, S and V channels
split(img,HSV_channels);
// do colour normalisation (makes it look more impressive)
if (useNormalisation){
normalize(HSV_channels[0], HSV_channels[0], 0, 255, NORM_MINMAX);
normalize(HSV_channels[2], HSV_channels[2], 0, 255, NORM_MINMAX);
}
// set S channel to max values
setChannel(HSV_channels[1], 0, 255);
// do scaling to avoid Hue space wrap around (i.e. dark == bright!)
// N.B. changing the scaling factor and addition will vary the colour
// effect - OpenCV 8-bit Hue in range 0->120 => 0.5 * Hue + 90 maps
// all values to (wrap-around) 180->60 range in Hue.
HSV_channels[0].convertTo(HSV_channels[0], -1 , 0.5, 90);
merge(HSV_channels, 3, HSV);
// put it all back together in RGB
cvtColor(HSV, HSV, COLOR_HSV2BGR);
// display image in window
if (useConcatImage){
imshow(windowNameHSV, concatImages(img, HSV));
} else {
if (useFalseColour){
imshow(windowNameHSV, HSV);
} else {
imshow(windowNameHSV, img);
}
}
// start event processing loop
key = waitKey(EVENT_LOOP_DELAY);
// process any keyboard input
switch (tolower(key))
{
case 'x':
case 'e':
case char(27): // ESC key
// if user presses "x" then exit
keepProcessing = false;
;
break;
case 'a':
case '7':
// toggle automatic scaling
useNormalisation = (!useNormalisation);
;
break;
case 'b':
case '8':
// toggle concatenated images
useConcatImage = (!useConcatImage);
;
break;
case 'c':
case '9':
// toggle false colour
useFalseColour = (!useFalseColour);
;
break;
case 'f':
// toggle full screen
useFullScreen = (!useFullScreen);
// set or unset the CV_WINDOW_FULLSCREEN flag via logical AND with toggle boolean
setWindowProperty(windowNameHSV, WND_PROP_FULLSCREEN, (WINDOW_FULLSCREEN & useFullScreen));
;
break;
}
}
// all OK : main returns 0
return 0;
}
// not OK : main returns -1
print_help(argv);
return -1;
}
/******************************************************************************/