Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Tetragramm
GitHub Repository: Tetragramm/opencv
Path: blob/master/apps/visualisation/opencv_visualisation.cpp
16337 views
1
////////////////////////////////////////////////////////////////////////////////////////
2
//
3
// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.
4
//
5
// By downloading, copying, installing or using the software you agree to this license.
6
// If you do not agree to this license, do not download, install,
7
// copy or use the software.
8
//
9
//
10
// License Agreement
11
// For Open Source Computer Vision Library
12
//
13
// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.
14
// Copyright (C) 2009, Willow Garage Inc., all rights reserved.
15
// Copyright (C) 2013, OpenCV Foundation, all rights reserved.
16
// Third party copyrights are property of their respective owners.
17
//
18
// Redistribution and use in source and binary forms, with or without modification,
19
// are permitted provided that the following conditions are met:
20
//
21
// * Redistribution's of source code must retain the above copyright notice,
22
// this list of conditions and the following disclaimer.
23
//
24
// * Redistribution's in binary form must reproduce the above copyright notice,
25
// this list of conditions and the following disclaimer in the documentation
26
// and/or other materials provided with the distribution.
27
//
28
// * The name of the copyright holders may not be used to endorse or promote products
29
// derived from this software without specific prior written permission.
30
//
31
// This software is provided by the copyright holders and contributors "as is" and
32
// any express or implied warranties, including, but not limited to, the implied
33
// warranties of merchantability and fitness for a particular purpose are disclaimed.
34
// In no event shall the Intel Corporation or contributors be liable for any direct,
35
// indirect, incidental, special, exemplary, or consequential damages
36
// (including, but not limited to, procurement of substitute goods or services;
37
// loss of use, data, or profits; or business interruption) however caused
38
// and on any theory of liability, whether in contract, strict liability,
39
// or tort (including negligence or otherwise) arising in any way out of
40
// the use of this software, even if advised of the possibility of such damage.
41
//
42
////////////////////////////////////////////////////////////////////////////////////////
43
44
/*****************************************************************************************************
45
46
Software for visualising cascade classifier models trained by OpenCV and to get a better
47
understanding of the used features.
48
49
USAGE:
50
./opencv_visualisation --model=<model.xml> --image=<ref.png> --data=<video output folder>
51
52
Created by: Puttemans Steven - April 2016
53
*****************************************************************************************************/
54
55
#include <opencv2/core.hpp>
56
#include <opencv2/highgui.hpp>
57
#include <opencv2/imgproc.hpp>
58
#include <opencv2/imgcodecs.hpp>
59
#include <opencv2/videoio.hpp>
60
61
#include <fstream>
62
#include <iostream>
63
64
using namespace std;
65
using namespace cv;
66
67
struct rect_data{
68
int x;
69
int y;
70
int w;
71
int h;
72
float weight;
73
};
74
75
static void printLimits(){
76
cerr << "Limits of the current interface:" << endl;
77
cerr << " - Only handles cascade classifier models, trained with the opencv_traincascade tool, containing stumps as decision trees [default settings]." << endl;
78
cerr << " - The image provided needs to be a sample window with the original model dimensions, passed to the --image parameter." << endl;
79
cerr << " - ONLY handles HAAR and LBP features." << endl;
80
}
81
82
int main( int argc, const char** argv )
83
{
84
CommandLineParser parser(argc, argv,
85
"{ help h usage ? | | show this message }"
86
"{ image i | | (required) path to reference image }"
87
"{ model m | | (required) path to cascade xml file }"
88
"{ data d | | (optional) path to video output folder }"
89
);
90
// Read in the input arguments
91
if (parser.has("help")){
92
parser.printMessage();
93
printLimits();
94
return 0;
95
}
96
string model(parser.get<string>("model"));
97
string output_folder(parser.get<string>("data"));
98
string image_ref = (parser.get<string>("image"));
99
if (model.empty() || image_ref.empty()){
100
parser.printMessage();
101
printLimits();
102
return -1;
103
}
104
105
// Value for timing
106
// You can increase this to have a better visualisation during the generation
107
int timing = 1;
108
109
// Value for cols of storing elements
110
int cols_prefered = 5;
111
112
// Open the XML model
113
FileStorage fs;
114
bool model_ok = fs.open(model, FileStorage::READ);
115
if (!model_ok){
116
cerr << "the cascade file '" << model << "' could not be loaded." << endl;
117
return -1;
118
}
119
// Get a the required information
120
// First decide which feature type we are using
121
FileNode cascade = fs["cascade"];
122
string feature_type = cascade["featureType"];
123
bool haar = false, lbp = false;
124
if (feature_type.compare("HAAR") == 0){
125
haar = true;
126
}
127
if (feature_type.compare("LBP") == 0){
128
lbp = true;
129
}
130
if ( feature_type.compare("HAAR") != 0 && feature_type.compare("LBP")){
131
cerr << "The model is not an HAAR or LBP feature based model!" << endl;
132
cerr << "Please select a model that can be visualized by the software." << endl;
133
return -1;
134
}
135
136
// We make a visualisation mask - which increases the window to make it at least a bit more visible
137
int resize_factor = 10;
138
int resize_storage_factor = 10;
139
Mat reference_image = imread(image_ref, IMREAD_GRAYSCALE );
140
if (reference_image.empty()){
141
cerr << "the reference image '" << image_ref << "'' could not be loaded." << endl;
142
return -1;
143
}
144
Mat visualization;
145
resize(reference_image, visualization, Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), 0, 0, INTER_LINEAR_EXACT);
146
147
// First recover for each stage the number of weak features and their index
148
// Important since it is NOT sequential when using LBP features
149
vector< vector<int> > stage_features;
150
FileNode stages = cascade["stages"];
151
FileNodeIterator it_stages = stages.begin(), it_stages_end = stages.end();
152
int idx = 0;
153
for( ; it_stages != it_stages_end; it_stages++, idx++ ){
154
vector<int> current_feature_indexes;
155
FileNode weak_classifiers = (*it_stages)["weakClassifiers"];
156
FileNodeIterator it_weak = weak_classifiers.begin(), it_weak_end = weak_classifiers.end();
157
vector<int> values;
158
for(int idy = 0; it_weak != it_weak_end; it_weak++, idy++ ){
159
(*it_weak)["internalNodes"] >> values;
160
current_feature_indexes.push_back( (int)values[2] );
161
}
162
stage_features.push_back(current_feature_indexes);
163
}
164
165
// If the output option has been chosen than we will store a combined image plane for
166
// each stage, containing all weak classifiers for that stage.
167
bool draw_planes = false;
168
stringstream output_video;
169
output_video << output_folder << "model_visualization.avi";
170
VideoWriter result_video;
171
if( output_folder.compare("") != 0 ){
172
draw_planes = true;
173
result_video.open(output_video.str(), VideoWriter::fourcc('X','V','I','D'), 15, Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), false);
174
}
175
176
if(haar){
177
// Grab the corresponding features dimensions and weights
178
FileNode features = cascade["features"];
179
vector< vector< rect_data > > feature_data;
180
FileNodeIterator it_features = features.begin(), it_features_end = features.end();
181
for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
182
vector< rect_data > current_feature_rectangles;
183
FileNode rectangles = (*it_features)["rects"];
184
int nrects = (int)rectangles.size();
185
for(int k = 0; k < nrects; k++){
186
rect_data current_data;
187
FileNode single_rect = rectangles[k];
188
current_data.x = (int)single_rect[0];
189
current_data.y = (int)single_rect[1];
190
current_data.w = (int)single_rect[2];
191
current_data.h = (int)single_rect[3];
192
current_data.weight = (float)single_rect[4];
193
current_feature_rectangles.push_back(current_data);
194
}
195
feature_data.push_back(current_feature_rectangles);
196
}
197
198
// Loop over each possible feature on its index, visualise on the mask and wait a bit,
199
// then continue to the next feature.
200
// If visualisations should be stored then do the in between calculations
201
Mat image_plane;
202
Mat metadata = Mat::zeros(150, 1000, CV_8UC1);
203
vector< rect_data > current_rects;
204
for(int sid = 0; sid < (int)stage_features.size(); sid ++){
205
if(draw_planes){
206
int features_nmbr = (int)stage_features[sid].size();
207
int cols = cols_prefered;
208
int rows = features_nmbr / cols;
209
if( (features_nmbr % cols) > 0){
210
rows++;
211
}
212
image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);
213
}
214
for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
215
stringstream meta1, meta2;
216
meta1 << "Stage " << sid << " / Feature " << fid;
217
meta2 << "Rectangles: ";
218
Mat temp_window = visualization.clone();
219
Mat temp_metadata = metadata.clone();
220
int current_feature_index = stage_features[sid][fid];
221
current_rects = feature_data[current_feature_index];
222
Mat single_feature = reference_image.clone();
223
resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);
224
for(int i = 0; i < (int)current_rects.size(); i++){
225
rect_data local = current_rects[i];
226
if(draw_planes){
227
if(local.weight >= 0){
228
rectangle(single_feature, Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), Scalar(0), FILLED);
229
}else{
230
rectangle(single_feature, Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), Scalar(255), FILLED);
231
}
232
}
233
Rect part(local.x * resize_factor, local.y * resize_factor, local.w * resize_factor, local.h * resize_factor);
234
meta2 << part << " (w " << local.weight << ") ";
235
if(local.weight >= 0){
236
rectangle(temp_window, part, Scalar(0), FILLED);
237
}else{
238
rectangle(temp_window, part, Scalar(255), FILLED);
239
}
240
}
241
imshow("features", temp_window);
242
putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
243
result_video.write(temp_window);
244
// Copy the feature image if needed
245
if(draw_planes){
246
single_feature.copyTo(image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
247
}
248
putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
249
putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
250
imshow("metadata", temp_metadata);
251
waitKey(timing);
252
}
253
//Store the stage image if needed
254
if(draw_planes){
255
stringstream save_location;
256
save_location << output_folder << "stage_" << sid << ".png";
257
imwrite(save_location.str(), image_plane);
258
}
259
}
260
}
261
262
if(lbp){
263
// Grab the corresponding features dimensions and weights
264
FileNode features = cascade["features"];
265
vector<Rect> feature_data;
266
FileNodeIterator it_features = features.begin(), it_features_end = features.end();
267
for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
268
FileNode rectangle = (*it_features)["rect"];
269
Rect current_feature ((int)rectangle[0], (int)rectangle[1], (int)rectangle[2], (int)rectangle[3]);
270
feature_data.push_back(current_feature);
271
}
272
273
// Loop over each possible feature on its index, visualise on the mask and wait a bit,
274
// then continue to the next feature.
275
Mat image_plane;
276
Mat metadata = Mat::zeros(150, 1000, CV_8UC1);
277
for(int sid = 0; sid < (int)stage_features.size(); sid ++){
278
if(draw_planes){
279
int features_nmbr = (int)stage_features[sid].size();
280
int cols = cols_prefered;
281
int rows = features_nmbr / cols;
282
if( (features_nmbr % cols) > 0){
283
rows++;
284
}
285
image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);
286
}
287
for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
288
stringstream meta1, meta2;
289
meta1 << "Stage " << sid << " / Feature " << fid;
290
meta2 << "Rectangle: ";
291
Mat temp_window = visualization.clone();
292
Mat temp_metadata = metadata.clone();
293
int current_feature_index = stage_features[sid][fid];
294
Rect current_rect = feature_data[current_feature_index];
295
Mat single_feature = reference_image.clone();
296
resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);
297
298
// VISUALISATION
299
// The rectangle is the top left one of a 3x3 block LBP constructor
300
Rect resized(current_rect.x * resize_factor, current_rect.y * resize_factor, current_rect.width * resize_factor, current_rect.height * resize_factor);
301
meta2 << resized;
302
// Top left
303
rectangle(temp_window, resized, Scalar(255), 1);
304
// Top middle
305
rectangle(temp_window, Rect(resized.x + resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);
306
// Top right
307
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);
308
// Middle left
309
rectangle(temp_window, Rect(resized.x, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);
310
// Middle middle
311
rectangle(temp_window, Rect(resized.x + resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), FILLED);
312
// Middle right
313
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);
314
// Bottom left
315
rectangle(temp_window, Rect(resized.x, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
316
// Bottom middle
317
rectangle(temp_window, Rect(resized.x + resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
318
// Bottom right
319
rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);
320
321
if(draw_planes){
322
Rect resized_inner(current_rect.x * resize_storage_factor, current_rect.y * resize_storage_factor, current_rect.width * resize_storage_factor, current_rect.height * resize_storage_factor);
323
// Top left
324
rectangle(single_feature, resized_inner, Scalar(255), 1);
325
// Top middle
326
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);
327
// Top right
328
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);
329
// Middle left
330
rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
331
// Middle middle
332
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), FILLED);
333
// Middle right
334
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
335
// Bottom left
336
rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
337
// Bottom middle
338
rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
339
// Bottom right
340
rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);
341
342
single_feature.copyTo(image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
343
}
344
345
putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
346
putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
347
imshow("metadata", temp_metadata);
348
imshow("features", temp_window);
349
putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));
350
result_video.write(temp_window);
351
352
waitKey(timing);
353
}
354
355
//Store the stage image if needed
356
if(draw_planes){
357
stringstream save_location;
358
save_location << output_folder << "stage_" << sid << ".png";
359
imwrite(save_location.str(), image_plane);
360
}
361
}
362
}
363
return 0;
364
}
365
366