Path: blob/master/apps/visualisation/opencv_visualisation.cpp
16337 views
////////////////////////////////////////////////////////////////////////////////////////1//2// IMPORTANT: READ BEFORE DOWNLOADING, COPYING, INSTALLING OR USING.3//4// By downloading, copying, installing or using the software you agree to this license.5// If you do not agree to this license, do not download, install,6// copy or use the software.7//8//9// License Agreement10// For Open Source Computer Vision Library11//12// Copyright (C) 2000-2008, Intel Corporation, all rights reserved.13// Copyright (C) 2009, Willow Garage Inc., all rights reserved.14// Copyright (C) 2013, OpenCV Foundation, all rights reserved.15// Third party copyrights are property of their respective owners.16//17// Redistribution and use in source and binary forms, with or without modification,18// are permitted provided that the following conditions are met:19//20// * Redistribution's of source code must retain the above copyright notice,21// this list of conditions and the following disclaimer.22//23// * Redistribution's in binary form must reproduce the above copyright notice,24// this list of conditions and the following disclaimer in the documentation25// and/or other materials provided with the distribution.26//27// * The name of the copyright holders may not be used to endorse or promote products28// derived from this software without specific prior written permission.29//30// This software is provided by the copyright holders and contributors "as is" and31// any express or implied warranties, including, but not limited to, the implied32// warranties of merchantability and fitness for a particular purpose are disclaimed.33// In no event shall the Intel Corporation or contributors be liable for any direct,34// indirect, incidental, special, exemplary, or consequential damages35// (including, but not limited to, procurement of substitute goods or services;36// loss of use, data, or profits; or business interruption) however caused37// and on any theory of liability, whether in contract, strict liability,38// or tort (including negligence or otherwise) arising in any way out of39// the use of this software, even if advised of the possibility of such damage.40//41////////////////////////////////////////////////////////////////////////////////////////4243/*****************************************************************************************************4445Software for visualising cascade classifier models trained by OpenCV and to get a better46understanding of the used features.4748USAGE:49./opencv_visualisation --model=<model.xml> --image=<ref.png> --data=<video output folder>5051Created by: Puttemans Steven - April 201652*****************************************************************************************************/5354#include <opencv2/core.hpp>55#include <opencv2/highgui.hpp>56#include <opencv2/imgproc.hpp>57#include <opencv2/imgcodecs.hpp>58#include <opencv2/videoio.hpp>5960#include <fstream>61#include <iostream>6263using namespace std;64using namespace cv;6566struct rect_data{67int x;68int y;69int w;70int h;71float weight;72};7374static void printLimits(){75cerr << "Limits of the current interface:" << endl;76cerr << " - Only handles cascade classifier models, trained with the opencv_traincascade tool, containing stumps as decision trees [default settings]." << endl;77cerr << " - The image provided needs to be a sample window with the original model dimensions, passed to the --image parameter." << endl;78cerr << " - ONLY handles HAAR and LBP features." << endl;79}8081int main( int argc, const char** argv )82{83CommandLineParser parser(argc, argv,84"{ help h usage ? | | show this message }"85"{ image i | | (required) path to reference image }"86"{ model m | | (required) path to cascade xml file }"87"{ data d | | (optional) path to video output folder }"88);89// Read in the input arguments90if (parser.has("help")){91parser.printMessage();92printLimits();93return 0;94}95string model(parser.get<string>("model"));96string output_folder(parser.get<string>("data"));97string image_ref = (parser.get<string>("image"));98if (model.empty() || image_ref.empty()){99parser.printMessage();100printLimits();101return -1;102}103104// Value for timing105// You can increase this to have a better visualisation during the generation106int timing = 1;107108// Value for cols of storing elements109int cols_prefered = 5;110111// Open the XML model112FileStorage fs;113bool model_ok = fs.open(model, FileStorage::READ);114if (!model_ok){115cerr << "the cascade file '" << model << "' could not be loaded." << endl;116return -1;117}118// Get a the required information119// First decide which feature type we are using120FileNode cascade = fs["cascade"];121string feature_type = cascade["featureType"];122bool haar = false, lbp = false;123if (feature_type.compare("HAAR") == 0){124haar = true;125}126if (feature_type.compare("LBP") == 0){127lbp = true;128}129if ( feature_type.compare("HAAR") != 0 && feature_type.compare("LBP")){130cerr << "The model is not an HAAR or LBP feature based model!" << endl;131cerr << "Please select a model that can be visualized by the software." << endl;132return -1;133}134135// We make a visualisation mask - which increases the window to make it at least a bit more visible136int resize_factor = 10;137int resize_storage_factor = 10;138Mat reference_image = imread(image_ref, IMREAD_GRAYSCALE );139if (reference_image.empty()){140cerr << "the reference image '" << image_ref << "'' could not be loaded." << endl;141return -1;142}143Mat visualization;144resize(reference_image, visualization, Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), 0, 0, INTER_LINEAR_EXACT);145146// First recover for each stage the number of weak features and their index147// Important since it is NOT sequential when using LBP features148vector< vector<int> > stage_features;149FileNode stages = cascade["stages"];150FileNodeIterator it_stages = stages.begin(), it_stages_end = stages.end();151int idx = 0;152for( ; it_stages != it_stages_end; it_stages++, idx++ ){153vector<int> current_feature_indexes;154FileNode weak_classifiers = (*it_stages)["weakClassifiers"];155FileNodeIterator it_weak = weak_classifiers.begin(), it_weak_end = weak_classifiers.end();156vector<int> values;157for(int idy = 0; it_weak != it_weak_end; it_weak++, idy++ ){158(*it_weak)["internalNodes"] >> values;159current_feature_indexes.push_back( (int)values[2] );160}161stage_features.push_back(current_feature_indexes);162}163164// If the output option has been chosen than we will store a combined image plane for165// each stage, containing all weak classifiers for that stage.166bool draw_planes = false;167stringstream output_video;168output_video << output_folder << "model_visualization.avi";169VideoWriter result_video;170if( output_folder.compare("") != 0 ){171draw_planes = true;172result_video.open(output_video.str(), VideoWriter::fourcc('X','V','I','D'), 15, Size(reference_image.cols * resize_factor, reference_image.rows * resize_factor), false);173}174175if(haar){176// Grab the corresponding features dimensions and weights177FileNode features = cascade["features"];178vector< vector< rect_data > > feature_data;179FileNodeIterator it_features = features.begin(), it_features_end = features.end();180for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){181vector< rect_data > current_feature_rectangles;182FileNode rectangles = (*it_features)["rects"];183int nrects = (int)rectangles.size();184for(int k = 0; k < nrects; k++){185rect_data current_data;186FileNode single_rect = rectangles[k];187current_data.x = (int)single_rect[0];188current_data.y = (int)single_rect[1];189current_data.w = (int)single_rect[2];190current_data.h = (int)single_rect[3];191current_data.weight = (float)single_rect[4];192current_feature_rectangles.push_back(current_data);193}194feature_data.push_back(current_feature_rectangles);195}196197// Loop over each possible feature on its index, visualise on the mask and wait a bit,198// then continue to the next feature.199// If visualisations should be stored then do the in between calculations200Mat image_plane;201Mat metadata = Mat::zeros(150, 1000, CV_8UC1);202vector< rect_data > current_rects;203for(int sid = 0; sid < (int)stage_features.size(); sid ++){204if(draw_planes){205int features_nmbr = (int)stage_features[sid].size();206int cols = cols_prefered;207int rows = features_nmbr / cols;208if( (features_nmbr % cols) > 0){209rows++;210}211image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);212}213for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){214stringstream meta1, meta2;215meta1 << "Stage " << sid << " / Feature " << fid;216meta2 << "Rectangles: ";217Mat temp_window = visualization.clone();218Mat temp_metadata = metadata.clone();219int current_feature_index = stage_features[sid][fid];220current_rects = feature_data[current_feature_index];221Mat single_feature = reference_image.clone();222resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);223for(int i = 0; i < (int)current_rects.size(); i++){224rect_data local = current_rects[i];225if(draw_planes){226if(local.weight >= 0){227rectangle(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);228}else{229rectangle(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);230}231}232Rect part(local.x * resize_factor, local.y * resize_factor, local.w * resize_factor, local.h * resize_factor);233meta2 << part << " (w " << local.weight << ") ";234if(local.weight >= 0){235rectangle(temp_window, part, Scalar(0), FILLED);236}else{237rectangle(temp_window, part, Scalar(255), FILLED);238}239}240imshow("features", temp_window);241putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));242result_video.write(temp_window);243// Copy the feature image if needed244if(draw_planes){245single_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)));246}247putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));248putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));249imshow("metadata", temp_metadata);250waitKey(timing);251}252//Store the stage image if needed253if(draw_planes){254stringstream save_location;255save_location << output_folder << "stage_" << sid << ".png";256imwrite(save_location.str(), image_plane);257}258}259}260261if(lbp){262// Grab the corresponding features dimensions and weights263FileNode features = cascade["features"];264vector<Rect> feature_data;265FileNodeIterator it_features = features.begin(), it_features_end = features.end();266for(int idf = 0; it_features != it_features_end; it_features++, idf++ ){267FileNode rectangle = (*it_features)["rect"];268Rect current_feature ((int)rectangle[0], (int)rectangle[1], (int)rectangle[2], (int)rectangle[3]);269feature_data.push_back(current_feature);270}271272// Loop over each possible feature on its index, visualise on the mask and wait a bit,273// then continue to the next feature.274Mat image_plane;275Mat metadata = Mat::zeros(150, 1000, CV_8UC1);276for(int sid = 0; sid < (int)stage_features.size(); sid ++){277if(draw_planes){278int features_nmbr = (int)stage_features[sid].size();279int cols = cols_prefered;280int rows = features_nmbr / cols;281if( (features_nmbr % cols) > 0){282rows++;283}284image_plane = Mat::zeros(reference_image.rows * resize_storage_factor * rows, reference_image.cols * resize_storage_factor * cols, CV_8UC1);285}286for(int fid = 0; fid < (int)stage_features[sid].size(); fid++){287stringstream meta1, meta2;288meta1 << "Stage " << sid << " / Feature " << fid;289meta2 << "Rectangle: ";290Mat temp_window = visualization.clone();291Mat temp_metadata = metadata.clone();292int current_feature_index = stage_features[sid][fid];293Rect current_rect = feature_data[current_feature_index];294Mat single_feature = reference_image.clone();295resize(single_feature, single_feature, Size(), resize_storage_factor, resize_storage_factor, INTER_LINEAR_EXACT);296297// VISUALISATION298// The rectangle is the top left one of a 3x3 block LBP constructor299Rect resized(current_rect.x * resize_factor, current_rect.y * resize_factor, current_rect.width * resize_factor, current_rect.height * resize_factor);300meta2 << resized;301// Top left302rectangle(temp_window, resized, Scalar(255), 1);303// Top middle304rectangle(temp_window, Rect(resized.x + resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);305// Top right306rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y, resized.width, resized.height), Scalar(255), 1);307// Middle left308rectangle(temp_window, Rect(resized.x, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);309// Middle middle310rectangle(temp_window, Rect(resized.x + resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), FILLED);311// Middle right312rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + resized.height, resized.width, resized.height), Scalar(255), 1);313// Bottom left314rectangle(temp_window, Rect(resized.x, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);315// Bottom middle316rectangle(temp_window, Rect(resized.x + resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);317// Bottom right318rectangle(temp_window, Rect(resized.x + 2*resized.width, resized.y + 2*resized.height, resized.width, resized.height), Scalar(255), 1);319320if(draw_planes){321Rect 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);322// Top left323rectangle(single_feature, resized_inner, Scalar(255), 1);324// Top middle325rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);326// Top right327rectangle(single_feature, Rect(resized_inner.x + 2*resized_inner.width, resized_inner.y, resized_inner.width, resized_inner.height), Scalar(255), 1);328// Middle left329rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);330// Middle middle331rectangle(single_feature, Rect(resized_inner.x + resized_inner.width, resized_inner.y + resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), FILLED);332// Middle right333rectangle(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);334// Bottom left335rectangle(single_feature, Rect(resized_inner.x, resized_inner.y + 2*resized_inner.height, resized_inner.width, resized_inner.height), Scalar(255), 1);336// Bottom middle337rectangle(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);338// Bottom right339rectangle(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);340341single_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)));342}343344putText(temp_metadata, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));345putText(temp_metadata, meta2.str(), Point(15,40), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));346imshow("metadata", temp_metadata);347imshow("features", temp_window);348putText(temp_window, meta1.str(), Point(15,15), FONT_HERSHEY_SIMPLEX, 0.5, Scalar(255));349result_video.write(temp_window);350351waitKey(timing);352}353354//Store the stage image if needed355if(draw_planes){356stringstream save_location;357save_location << output_folder << "stage_" << sid << ".png";358imwrite(save_location.str(), image_plane);359}360}361}362return 0;363}364365366