Path: blob/master/apps/annotation/opencv_annotation.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/*****************************************************************************************************44USAGE:45./opencv_annotation -images <folder location> -annotations <output file>4647Created by: Puttemans Steven - February 201548Adapted by: Puttemans Steven - April 2016 - Vectorize the process to enable better processing49+ early leave and store by pressing an ESC key50+ enable delete `d` button, to remove last annotation51*****************************************************************************************************/5253#include <opencv2/core.hpp>54#include <opencv2/highgui.hpp>55#include <opencv2/imgcodecs.hpp>56#include <opencv2/videoio.hpp>57#include <opencv2/imgproc.hpp>5859#include <fstream>60#include <iostream>61#include <map>6263using namespace std;64using namespace cv;6566// Function prototypes67void on_mouse(int, int, int, int, void*);68vector<Rect> get_annotations(Mat);6970// Public parameters71Mat image;72int roi_x0 = 0, roi_y0 = 0, roi_x1 = 0, roi_y1 = 0, num_of_rec = 0;73bool start_draw = false, stop = false;7475// Window name for visualisation purposes76const string window_name = "OpenCV Based Annotation Tool";7778// FUNCTION : Mouse response for selecting objects in images79// If left button is clicked, start drawing a rectangle as long as mouse moves80// Stop drawing once a new left click is detected by the on_mouse function81void on_mouse(int event, int x, int y, int , void * )82{83// Action when left button is clicked84if(event == EVENT_LBUTTONDOWN)85{86if(!start_draw)87{88roi_x0 = x;89roi_y0 = y;90start_draw = true;91} else {92roi_x1 = x;93roi_y1 = y;94start_draw = false;95}96}9798// Action when mouse is moving and drawing is enabled99if((event == EVENT_MOUSEMOVE) && start_draw)100{101// Redraw bounding box for annotation102Mat current_view;103image.copyTo(current_view);104rectangle(current_view, Point(roi_x0,roi_y0), Point(x,y), Scalar(0,0,255));105imshow(window_name, current_view);106}107}108109// FUNCTION : returns a vector of Rect objects given an image containing positive object instances110vector<Rect> get_annotations(Mat input_image)111{112vector<Rect> current_annotations;113114// Make it possible to exit the annotation process115stop = false;116117// Init window interface and couple mouse actions118namedWindow(window_name, WINDOW_AUTOSIZE);119setMouseCallback(window_name, on_mouse);120121image = input_image;122imshow(window_name, image);123int key_pressed = 0;124125do126{127// Get a temporary image clone128Mat temp_image = input_image.clone();129Rect currentRect(0, 0, 0, 0);130131// Keys for processing132// You need to select one for confirming a selection and one to continue to the next image133// Based on the universal ASCII code of the keystroke: http://www.asciitable.com/134// c = 99 add rectangle to current image135// n = 110 save added rectangles and show next image136// d = 100 delete the last annotation made137// <ESC> = 27 exit program138key_pressed = 0xFF & waitKey(0);139switch( key_pressed )140{141case 27:142stop = true;143break;144case 99:145// Draw initiated from top left corner146if(roi_x0<roi_x1 && roi_y0<roi_y1)147{148currentRect.x = roi_x0;149currentRect.y = roi_y0;150currentRect.width = roi_x1-roi_x0;151currentRect.height = roi_y1-roi_y0;152}153// Draw initiated from bottom right corner154if(roi_x0>roi_x1 && roi_y0>roi_y1)155{156currentRect.x = roi_x1;157currentRect.y = roi_y1;158currentRect.width = roi_x0-roi_x1;159currentRect.height = roi_y0-roi_y1;160}161// Draw initiated from top right corner162if(roi_x0>roi_x1 && roi_y0<roi_y1)163{164currentRect.x = roi_x1;165currentRect.y = roi_y0;166currentRect.width = roi_x0-roi_x1;167currentRect.height = roi_y1-roi_y0;168}169// Draw initiated from bottom left corner170if(roi_x0<roi_x1 && roi_y0>roi_y1)171{172currentRect.x = roi_x0;173currentRect.y = roi_y1;174currentRect.width = roi_x1-roi_x0;175currentRect.height = roi_y0-roi_y1;176}177// Draw the rectangle on the canvas178// Add the rectangle to the vector of annotations179current_annotations.push_back(currentRect);180break;181case 100:182// Remove the last annotation183if(current_annotations.size() > 0){184current_annotations.pop_back();185}186break;187default:188// Default case --> do nothing at all189// Other keystrokes can simply be ignored190break;191}192193// Check if escape has been pressed194if(stop)195{196break;197}198199// Draw all the current rectangles onto the top image and make sure that the global image is linked200for(int i=0; i < (int)current_annotations.size(); i++){201rectangle(temp_image, current_annotations[i], Scalar(0,255,0), 1);202}203image = temp_image;204205// Force an explicit redraw of the canvas --> necessary to visualize delete correctly206imshow(window_name, image);207}208// Continue as long as the next image key has not been pressed209while(key_pressed != 110);210211// Close down the window212destroyWindow(window_name);213214// Return the data215return current_annotations;216}217218int main( int argc, const char** argv )219{220// Use the cmdlineparser to process input arguments221CommandLineParser parser(argc, argv,222"{ help h usage ? | | show this message }"223"{ images i | | (required) path to image folder [example - /data/testimages/] }"224"{ annotations a | | (required) path to annotations txt file [example - /data/annotations.txt] }"225"{ maxWindowHeight m | -1 | (optional) images larger in height than this value will be scaled down }"226"{ resizeFactor r | 2 | (optional) factor for scaling down [default = half the size] }"227);228// Read in the input arguments229if (parser.has("help")){230parser.printMessage();231cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;232return 0;233}234string image_folder(parser.get<string>("images"));235string annotations_file(parser.get<string>("annotations"));236if (image_folder.empty() || annotations_file.empty()){237parser.printMessage();238cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;239return -1;240}241242int resizeFactor = parser.get<int>("resizeFactor");243int const maxWindowHeight = parser.get<int>("maxWindowHeight") > 0 ? parser.get<int>("maxWindowHeight") : -1;244245// Start by processing the data246// Return the image filenames inside the image folder247map< String, vector<Rect> > annotations;248vector<String> filenames;249String folder(image_folder);250glob(folder, filenames);251252// Add key tips on how to use the software when running it253cout << "* mark rectangles with the left mouse button," << endl;254cout << "* press 'c' to accept a selection," << endl;255cout << "* press 'd' to delete the latest selection," << endl;256cout << "* press 'n' to proceed with next image," << endl;257cout << "* press 'esc' to stop." << endl;258259// Loop through each image stored in the images folder260// Create and temporarily store the annotations261// At the end write everything to the annotations file262for (size_t i = 0; i < filenames.size(); i++){263// Read in an image264Mat current_image = imread(filenames[i]);265bool const resize_bool = (maxWindowHeight > 0) && (current_image.rows > maxWindowHeight);266267// Check if the image is actually read - avoid other files in the folder, because glob() takes them all268// If not then simply skip this iteration269if(current_image.empty()){270continue;271}272273if(resize_bool){274resize(current_image, current_image, Size(current_image.cols/resizeFactor, current_image.rows/resizeFactor), 0, 0, INTER_LINEAR_EXACT);275}276277// Perform annotations & store the result inside the vectorized structure278// If the image was resized before, then resize the found annotations back to original dimensions279vector<Rect> current_annotations = get_annotations(current_image);280if(resize_bool){281for(int j =0; j < (int)current_annotations.size(); j++){282current_annotations[j].x = current_annotations[j].x * resizeFactor;283current_annotations[j].y = current_annotations[j].y * resizeFactor;284current_annotations[j].width = current_annotations[j].width * resizeFactor;285current_annotations[j].height = current_annotations[j].height * resizeFactor;286}287}288annotations[filenames[i]] = current_annotations;289290// Check if the ESC key was hit, then exit earlier then expected291if(stop){292break;293}294}295296// When all data is processed, store the data gathered inside the proper file297// This now even gets called when the ESC button was hit to store preliminary results298ofstream output(annotations_file.c_str());299if ( !output.is_open() ){300cerr << "The path for the output file contains an error and could not be opened. Please check again!" << endl;301return 0;302}303304// Store the annotations, write to the output file305for(map<String, vector<Rect> >::iterator it = annotations.begin(); it != annotations.end(); it++){306vector<Rect> &anno = it->second;307output << it->first << " " << anno.size();308for(size_t j=0; j < anno.size(); j++){309Rect temp = anno[j];310output << " " << temp.x << " " << temp.y << " " << temp.width << " " << temp.height;311}312output << endl;313}314315return 0;316}317318319