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
46Software for visualising cascade classifier models trained by OpenCV and to get a better
47understanding of the used features.
48
49USAGE:
50./opencv_visualisation --model=<model.xml> --image=<ref.png> --data=<video output folder>
51
52Created 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#include <sstream>
64
65using namespace std;
66using namespace cv;
67
68struct rect_data{
69 int x;
70 int y;
71 int w;
72 int h;
73 float weight;
74};
75
76static void printLimits(){
77 cerr << "Limits of the current interface:" << endl;
78 cerr << " - Only handles cascade classifier models, trained with the opencv_traincascade tool, containing stumps as decision trees [default settings]." << endl;
79 cerr << " - The image provided needs to be a sample window with the original model dimensions, passed to the --image parameter." << endl;
80 cerr << " - ONLY handles HAAR and LBP features." << endl;
81}
82
83int main( int argc, const char** argv )
84{
85 CommandLineParser parser(argc, argv,
86 "{ help h usage ? | | show this message }"
87 "{ image i | | (required) path to reference image }"
88 "{ model m | | (required) path to cascade xml file }"
89 "{ data d | | (optional) path to video output folder }"
90 "{ ext | avi | (optional) output video file extension e.g. avi (default) or mp4 }"
91 "{ fourcc | XVID | (optional) output video file's 4-character codec e.g. XVID (default) or H264 }"
92 "{ fps | 15 | (optional) output video file's frames-per-second rate }"
93 );
94 // Read in the input arguments
95 if (parser.has(name: "help")){
96 parser.printMessage();
97 printLimits();
98 return 0;
99 }
100 string model(parser.get<string>(name: "model"));
101 string output_folder(parser.get<string>(name: "data"));
102 string image_ref = (parser.get<string>(name: "image"));
103 string fourcc = (parser.get<string>(name: "fourcc"));
104 int fps = parser.get<int>(name: "fps");
105 if (model.empty() || image_ref.empty() || fourcc.size()!=4 || fps<1){
106 parser.printMessage();
107 printLimits();
108 return -1;
109 }
110
111 // Value for timing
112 // You can increase this to have a better visualisation during the generation
113 int timing = 1;
114
115 // Value for cols of storing elements
116 int cols_prefered = 5;
117
118 // Open the XML model
119 FileStorage fs;
120 bool model_ok = fs.open(filename: model, flags: FileStorage::READ);
121 if (!model_ok){
122 cerr << "the cascade file '" << model << "' could not be loaded." << endl;
123 return -1;
124 }
125 // Get a the required information
126 // First decide which feature type we are using
127 FileNode cascade = fs["cascade"];
128 string feature_type = cascade["featureType"];
129 bool haar = false, lbp = false;
130 if (feature_type.compare(s: "HAAR") == 0){
131 haar = true;
132 }
133 if (feature_type.compare(s: "LBP") == 0){
134 lbp = true;
135 }
136 if ( feature_type.compare(s: "HAAR") != 0 && feature_type.compare(s: "LBP")){
137 cerr << "The model is not an HAAR or LBP feature based model!" << endl;
138 cerr << "Please select a model that can be visualized by the software." << endl;
139 return -1;
140 }
141
142 // We make a visualisation mask - which increases the window to make it at least a bit more visible
143 int resize_factor = 10;
144 int resize_storage_factor = 10;
145 Mat reference_image = imread(filename: image_ref, flags: IMREAD_GRAYSCALE );
146 if (reference_image.empty()){
147 cerr << "the reference image '" << image_ref << "'' could not be loaded." << endl;
148 return -1;
149 }
150 Mat visualization;
151 resize(src: reference_image, dst: visualization, dsize: Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), fx: 0, fy: 0, interpolation: INTER_LINEAR_EXACT);
152
153 // First recover for each stage the number of weak features and their index
154 // Important since it is NOT sequential when using LBP features
155 vector< vector<int> > stage_features;
156 FileNode stages = cascade["stages"];
157 FileNodeIterator it_stages = stages.begin(), it_stages_end = stages.end();
158 int idx = 0;
159 for( ; it_stages != it_stages_end; it_stages++, idx++ ){
160 vector<int> current_feature_indexes;
161 FileNode weak_classifiers = (*it_stages)["weakClassifiers"];
162 FileNodeIterator it_weak = weak_classifiers.begin(), it_weak_end = weak_classifiers.end();
163 vector<int> values;
164 for(int idy = 0; it_weak != it_weak_end; it_weak++, idy++ ){
165 (*it_weak)["internalNodes"] >> values;
166 current_feature_indexes.push_back( x: (int)values[2] );
167 }
168 stage_features.push_back(x: current_feature_indexes);
169 }
170
171 // If the output option has been chosen than we will store a combined image plane for
172 // each stage, containing all weak classifiers for that stage.
173 bool draw_planes = false;
174 stringstream output_video;
175 output_video << output_folder << "model_visualization." << parser.get<string>(name: "ext");
176 VideoWriter result_video;
177 if( output_folder.compare(s: "") != 0 ){
178 draw_planes = true;
179 result_video.open(filename: output_video.str(), fourcc: VideoWriter::fourcc(c1: fourcc[0],c2: fourcc[1],c3: fourcc[2],c4: fourcc[3]), fps, frameSize: visualization.size(), isColor: false);
180 if (!result_video.isOpened()){
181 cerr << "the output video '" << output_video.str() << "' could not be opened."
182 << " fourcc=" << fourcc
183 << " fps=" << fps
184 << " frameSize=" << visualization.size()
185 << endl;
186 return -1;
187 }
188 }
189
190 if(haar){
191 // Grab the corresponding features dimensions and weights
192 FileNode features = cascade["features"];
193 vector< vector< rect_data > > feature_data;
194 FileNodeIterator it_features = features.begin(), it_features_end = features.end();
195 for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
196 vector< rect_data > current_feature_rectangles;
197 FileNode rectangles = (*it_features)["rects"];
198 int nrects = (int)rectangles.size();
199 for(int k = 0; k < nrects; k++){
200 rect_data current_data;
201 FileNode single_rect = rectangles[k];
202 current_data.x = (int)single_rect[0];
203 current_data.y = (int)single_rect[1];
204 current_data.w = (int)single_rect[2];
205 current_data.h = (int)single_rect[3];
206 current_data.weight = (float)single_rect[4];
207 current_feature_rectangles.push_back(x: current_data);
208 }
209 feature_data.push_back(x: current_feature_rectangles);
210 }
211
212 // Loop over each possible feature on its index, visualise on the mask and wait a bit,
213 // then continue to the next feature.
214 // If visualisations should be stored then do the in between calculations
215 Mat image_plane;
216 Mat metadata = Mat::zeros(rows: 150, cols: 1000, CV_8UC1);
217 vector< rect_data > current_rects;
218 for(int sid = 0; sid < (int)stage_features.size(); sid ++){
219 if(draw_planes){
220 int features_nmbr = (int)stage_features[sid].size();
221 int cols = cols_prefered;
222 int rows = features_nmbr / cols;
223 if( (features_nmbr % cols) > 0){
224 rows++;
225 }
226 image_plane = Mat::zeros(rows: reference_image.rows * resize_storage_factor * rows, cols: reference_image.cols * resize_storage_factor * cols, CV_8UC1);
227 }
228 for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
229 stringstream meta1, meta2;
230 meta1 << "Stage " << sid << " / Feature " << fid;
231 meta2 << "Rectangles: ";
232 Mat temp_window = visualization.clone();
233 Mat temp_metadata = metadata.clone();
234 int current_feature_index = stage_features[sid][fid];
235 current_rects = feature_data[current_feature_index];
236 Mat single_feature = reference_image.clone();
237 resize(src: single_feature, dst: single_feature, dsize: Size(), fx: resize_storage_factor, fy: resize_storage_factor, interpolation: INTER_LINEAR_EXACT);
238 for(int i = 0; i < (int)current_rects.size(); i++){
239 rect_data local = current_rects[i];
240 if(draw_planes){
241 if(local.weight >= 0){
242 rectangle(img: single_feature, rec: Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), color: Scalar(0), thickness: FILLED);
243 }else{
244 rectangle(img: single_feature, rec: Rect(local.x * resize_storage_factor, local.y * resize_storage_factor, local.w * resize_storage_factor, local.h * resize_storage_factor), color: Scalar(255), thickness: FILLED);
245 }
246 }
247 Rect part(local.x * resize_factor, local.y * resize_factor, local.w * resize_factor, local.h * resize_factor);
248 meta2 << part << " (w " << local.weight << ") ";
249 if(local.weight >= 0){
250 rectangle(img: temp_window, rec: part, color: Scalar(0), thickness: FILLED);
251 }else{
252 rectangle(img: temp_window, rec: part, color: Scalar(255), thickness: FILLED);
253 }
254 }
255 imshow(winname: "features", mat: temp_window);
256 putText(img: temp_window, text: meta1.str(), org: Point(15,15), fontFace: FONT_HERSHEY_SIMPLEX, fontScale: 0.5, color: Scalar(255));
257 result_video.write(image: temp_window);
258 // Copy the feature image if needed
259 if(draw_planes){
260 single_feature.copyTo(m: image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
261 }
262 putText(img: temp_metadata, text: meta1.str(), org: Point(15,15), fontFace: FONT_HERSHEY_SIMPLEX, fontScale: 0.5, color: Scalar(255));
263 putText(img: temp_metadata, text: meta2.str(), org: Point(15,40), fontFace: FONT_HERSHEY_SIMPLEX, fontScale: 0.5, color: Scalar(255));
264 imshow(winname: "metadata", mat: temp_metadata);
265 waitKey(delay: timing);
266 }
267 //Store the stage image if needed
268 if(draw_planes){
269 stringstream save_location;
270 save_location << output_folder << "stage_" << sid << ".png";
271 imwrite(filename: save_location.str(), img: image_plane);
272 }
273 }
274 }
275
276 if(lbp){
277 // Grab the corresponding features dimensions and weights
278 FileNode features = cascade["features"];
279 vector<Rect> feature_data;
280 FileNodeIterator it_features = features.begin(), it_features_end = features.end();
281 for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){
282 FileNode rectangle = (*it_features)["rect"];
283 Rect current_feature ((int)rectangle[0], (int)rectangle[1], (int)rectangle[2], (int)rectangle[3]);
284 feature_data.push_back(x: current_feature);
285 }
286
287 // Loop over each possible feature on its index, visualise on the mask and wait a bit,
288 // then continue to the next feature.
289 Mat image_plane;
290 Mat metadata = Mat::zeros(rows: 150, cols: 1000, CV_8UC1);
291 for(int sid = 0; sid < (int)stage_features.size(); sid ++){
292 if(draw_planes){
293 int features_nmbr = (int)stage_features[sid].size();
294 int cols = cols_prefered;
295 int rows = features_nmbr / cols;
296 if( (features_nmbr % cols) > 0){
297 rows++;
298 }
299 image_plane = Mat::zeros(rows: reference_image.rows * resize_storage_factor * rows, cols: reference_image.cols * resize_storage_factor * cols, CV_8UC1);
300 }
301 for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){
302 stringstream meta1, meta2;
303 meta1 << "Stage " << sid << " / Feature " << fid;
304 meta2 << "Rectangle: ";
305 Mat temp_window = visualization.clone();
306 Mat temp_metadata = metadata.clone();
307 int current_feature_index = stage_features[sid][fid];
308 Rect current_rect = feature_data[current_feature_index];
309 Mat single_feature = reference_image.clone();
310 resize(src: single_feature, dst: single_feature, dsize: Size(), fx: resize_storage_factor, fy: resize_storage_factor, interpolation: INTER_LINEAR_EXACT);
311
312 // VISUALISATION
313 // The rectangle is the top left one of a 3x3 block LBP constructor
314 Rect resized(current_rect.x * resize_factor, current_rect.y * resize_factor, current_rect.width * resize_factor, current_rect.height * resize_factor);
315 meta2 << resized;
316 // Top left
317 rectangle(img: temp_window, rec: resized, color: Scalar(255), thickness: 1);
318 // Top middle
319 rectangle(img: temp_window, rec: Rect(resized.x + resized.width, resized.y, resized.width, resized.height), color: Scalar(255), thickness: 1);
320 // Top right
321 rectangle(img: temp_window, rec: Rect(resized.x + 2*resized.width, resized.y, resized.width, resized.height), color: Scalar(255), thickness: 1);
322 // Middle left
323 rectangle(img: temp_window, rec: Rect(resized.x, resized.y + resized.height, resized.width, resized.height), color: Scalar(255), thickness: 1);
324 // Middle middle
325 rectangle(img: temp_window, rec: Rect(resized.x + resized.width, resized.y + resized.height, resized.width, resized.height), color: Scalar(255), thickness: FILLED);
326 // Middle right
327 rectangle(img: temp_window, rec: Rect(resized.x + 2*resized.width, resized.y + resized.height, resized.width, resized.height), color: Scalar(255), thickness: 1);
328 // Bottom left
329 rectangle(img: temp_window, rec: Rect(resized.x, resized.y + 2*resized.height, resized.width, resized.height), color: Scalar(255), thickness: 1);
330 // Bottom middle
331 rectangle(img: temp_window, rec: Rect(resized.x + resized.width, resized.y + 2*resized.height, resized.width, resized.height), color: Scalar(255), thickness: 1);
332 // Bottom right
333 rectangle(img: temp_window, rec: Rect(resized.x + 2*resized.width, resized.y + 2*resized.height, resized.width, resized.height), color: Scalar(255), thickness: 1);
334
335 if(draw_planes){
336 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);
337 // Top left
338 rectangle(img: single_feature, rec: resized_inner, color: Scalar(255), thickness: 1);
339 // Top middle
340 rectangle(img: single_feature, rec: Rect(resized_inner.x + resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: 1);
341 // Top right
342 rectangle(img: single_feature, rec: Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: 1);
343 // Middle left
344 rectangle(img: single_feature, rec: Rect(resized_inner.x, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: 1);
345 // Middle middle
346 rectangle(img: single_feature, rec: Rect(resized_inner.x + resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: FILLED);
347 // Middle right
348 rectangle(img: single_feature, rec: Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: 1);
349 // Bottom left
350 rectangle(img: single_feature, rec: Rect(resized_inner.x, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: 1);
351 // Bottom middle
352 rectangle(img: single_feature, rec: Rect(resized_inner.x + resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: 1);
353 // Bottom right
354 rectangle(img: single_feature, rec: Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), color: Scalar(255), thickness: 1);
355
356 single_feature.copyTo(m: image_plane(Rect(0 + (fid%cols_prefered)*single_feature.cols, 0 + (fid/cols_prefered) * single_feature.rows, single_feature.cols, single_feature.rows)));
357 }
358
359 putText(img: temp_metadata, text: meta1.str(), org: Point(15,15), fontFace: FONT_HERSHEY_SIMPLEX, fontScale: 0.5, color: Scalar(255));
360 putText(img: temp_metadata, text: meta2.str(), org: Point(15,40), fontFace: FONT_HERSHEY_SIMPLEX, fontScale: 0.5, color: Scalar(255));
361 imshow(winname: "metadata", mat: temp_metadata);
362 imshow(winname: "features", mat: temp_window);
363 putText(img: temp_window, text: meta1.str(), org: Point(15,15), fontFace: FONT_HERSHEY_SIMPLEX, fontScale: 0.5, color: Scalar(255));
364 result_video.write(image: temp_window);
365
366 waitKey(delay: timing);
367 }
368
369 //Store the stage image if needed
370 if(draw_planes){
371 stringstream save_location;
372 save_location << output_folder << "stage_" << sid << ".png";
373 imwrite(filename: save_location.str(), img: image_plane);
374 }
375 }
376 }
377 return 0;
378}
379

Provided by KDAB

Privacy Policy
Improve your Profiling and Debugging skills
Find out more

source code of opencv/apps/visualisation/opencv_visualisation.cpp