opencv_annotation.cpp 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317
  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. USAGE:
  45. ./opencv_annotation -images <folder location> -annotations <output file>
  46. Created by: Puttemans Steven - February 2015
  47. Adapted by: Puttemans Steven - April 2016 - Vectorize the process to enable better processing
  48. + early leave and store by pressing an ESC key
  49. + enable delete `d` button, to remove last annotation
  50. *****************************************************************************************************/
  51. #include <opencv2/core.hpp>
  52. #include <opencv2/highgui.hpp>
  53. #include <opencv2/imgcodecs.hpp>
  54. #include <opencv2/videoio.hpp>
  55. #include <opencv2/imgproc.hpp>
  56. #include <fstream>
  57. #include <iostream>
  58. #include <map>
  59. using namespace std;
  60. using namespace cv;
  61. // Function prototypes
  62. void on_mouse(int, int, int, int, void*);
  63. vector<Rect> get_annotations(Mat);
  64. // Public parameters
  65. Mat image;
  66. int roi_x0 = 0, roi_y0 = 0, roi_x1 = 0, roi_y1 = 0, num_of_rec = 0;
  67. bool start_draw = false, stop = false;
  68. // Window name for visualisation purposes
  69. const string window_name = "OpenCV Based Annotation Tool";
  70. // FUNCTION : Mouse response for selecting objects in images
  71. // If left button is clicked, start drawing a rectangle as long as mouse moves
  72. // Stop drawing once a new left click is detected by the on_mouse function
  73. void on_mouse(int event, int x, int y, int , void * )
  74. {
  75. // Action when left button is clicked
  76. if(event == EVENT_LBUTTONDOWN)
  77. {
  78. if(!start_draw)
  79. {
  80. roi_x0 = x;
  81. roi_y0 = y;
  82. start_draw = true;
  83. } else {
  84. roi_x1 = x;
  85. roi_y1 = y;
  86. start_draw = false;
  87. }
  88. }
  89. // Action when mouse is moving and drawing is enabled
  90. if((event == EVENT_MOUSEMOVE) && start_draw)
  91. {
  92. // Redraw bounding box for annotation
  93. Mat current_view;
  94. image.copyTo(current_view);
  95. rectangle(current_view, Point(roi_x0,roi_y0), Point(x,y), Scalar(0,0,255));
  96. imshow(window_name, current_view);
  97. }
  98. }
  99. // FUNCTION : returns a vector of Rect objects given an image containing positive object instances
  100. vector<Rect> get_annotations(Mat input_image)
  101. {
  102. vector<Rect> current_annotations;
  103. // Make it possible to exit the annotation process
  104. stop = false;
  105. // Init window interface and couple mouse actions
  106. namedWindow(window_name, WINDOW_AUTOSIZE);
  107. setMouseCallback(window_name, on_mouse);
  108. image = input_image;
  109. imshow(window_name, image);
  110. int key_pressed = 0;
  111. do
  112. {
  113. // Get a temporary image clone
  114. Mat temp_image = input_image.clone();
  115. Rect currentRect(0, 0, 0, 0);
  116. // Keys for processing
  117. // You need to select one for confirming a selection and one to continue to the next image
  118. // Based on the universal ASCII code of the keystroke: http://www.asciitable.com/
  119. // c = 99 add rectangle to current image
  120. // n = 110 save added rectangles and show next image
  121. // d = 100 delete the last annotation made
  122. // <ESC> = 27 exit program
  123. key_pressed = 0xFF & waitKey(0);
  124. switch( key_pressed )
  125. {
  126. case 27:
  127. stop = true;
  128. break;
  129. case 99:
  130. // Draw initiated from top left corner
  131. if(roi_x0<roi_x1 && roi_y0<roi_y1)
  132. {
  133. currentRect.x = roi_x0;
  134. currentRect.y = roi_y0;
  135. currentRect.width = roi_x1-roi_x0;
  136. currentRect.height = roi_y1-roi_y0;
  137. }
  138. // Draw initiated from bottom right corner
  139. if(roi_x0>roi_x1 && roi_y0>roi_y1)
  140. {
  141. currentRect.x = roi_x1;
  142. currentRect.y = roi_y1;
  143. currentRect.width = roi_x0-roi_x1;
  144. currentRect.height = roi_y0-roi_y1;
  145. }
  146. // Draw initiated from top right corner
  147. if(roi_x0>roi_x1 && roi_y0<roi_y1)
  148. {
  149. currentRect.x = roi_x1;
  150. currentRect.y = roi_y0;
  151. currentRect.width = roi_x0-roi_x1;
  152. currentRect.height = roi_y1-roi_y0;
  153. }
  154. // Draw initiated from bottom left corner
  155. if(roi_x0<roi_x1 && roi_y0>roi_y1)
  156. {
  157. currentRect.x = roi_x0;
  158. currentRect.y = roi_y1;
  159. currentRect.width = roi_x1-roi_x0;
  160. currentRect.height = roi_y0-roi_y1;
  161. }
  162. // Draw the rectangle on the canvas
  163. // Add the rectangle to the vector of annotations
  164. current_annotations.push_back(currentRect);
  165. break;
  166. case 100:
  167. // Remove the last annotation
  168. if(current_annotations.size() > 0){
  169. current_annotations.pop_back();
  170. }
  171. break;
  172. default:
  173. // Default case --> do nothing at all
  174. // Other keystrokes can simply be ignored
  175. break;
  176. }
  177. // Check if escape has been pressed
  178. if(stop)
  179. {
  180. break;
  181. }
  182. // Draw all the current rectangles onto the top image and make sure that the global image is linked
  183. for(int i=0; i < (int)current_annotations.size(); i++){
  184. rectangle(temp_image, current_annotations[i], Scalar(0,255,0), 1);
  185. }
  186. image = temp_image;
  187. // Force an explicit redraw of the canvas --> necessary to visualize delete correctly
  188. imshow(window_name, image);
  189. }
  190. // Continue as long as the next image key has not been pressed
  191. while(key_pressed != 110);
  192. // Close down the window
  193. destroyWindow(window_name);
  194. // Return the data
  195. return current_annotations;
  196. }
  197. int main( int argc, const char** argv )
  198. {
  199. // Use the cmdlineparser to process input arguments
  200. CommandLineParser parser(argc, argv,
  201. "{ help h usage ? | | show this message }"
  202. "{ images i | | (required) path to image folder [example - /data/testimages/] }"
  203. "{ annotations a | | (required) path to annotations txt file [example - /data/annotations.txt] }"
  204. "{ maxWindowHeight m | -1 | (optional) images larger in height than this value will be scaled down }"
  205. "{ resizeFactor r | 2 | (optional) factor for scaling down [default = half the size] }"
  206. );
  207. // Read in the input arguments
  208. if (parser.has("help")){
  209. parser.printMessage();
  210. cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
  211. return 0;
  212. }
  213. string image_folder(parser.get<string>("images"));
  214. string annotations_file(parser.get<string>("annotations"));
  215. if (image_folder.empty() || annotations_file.empty()){
  216. parser.printMessage();
  217. cerr << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
  218. return -1;
  219. }
  220. int resizeFactor = parser.get<int>("resizeFactor");
  221. int const maxWindowHeight = parser.get<int>("maxWindowHeight") > 0 ? parser.get<int>("maxWindowHeight") : -1;
  222. // Start by processing the data
  223. // Return the image filenames inside the image folder
  224. map< String, vector<Rect> > annotations;
  225. vector<String> filenames;
  226. String folder(image_folder);
  227. glob(folder, filenames);
  228. // Add key tips on how to use the software when running it
  229. cout << "* mark rectangles with the left mouse button," << endl;
  230. cout << "* press 'c' to accept a selection," << endl;
  231. cout << "* press 'd' to delete the latest selection," << endl;
  232. cout << "* press 'n' to proceed with next image," << endl;
  233. cout << "* press 'esc' to stop." << endl;
  234. // Loop through each image stored in the images folder
  235. // Create and temporarily store the annotations
  236. // At the end write everything to the annotations file
  237. for (size_t i = 0; i < filenames.size(); i++){
  238. // Read in an image
  239. Mat current_image = imread(filenames[i]);
  240. bool const resize_bool = (maxWindowHeight > 0) && (current_image.rows > maxWindowHeight);
  241. // Check if the image is actually read - avoid other files in the folder, because glob() takes them all
  242. // If not then simply skip this iteration
  243. if(current_image.empty()){
  244. continue;
  245. }
  246. if(resize_bool){
  247. resize(current_image, current_image, Size(current_image.cols/resizeFactor, current_image.rows/resizeFactor), 0, 0, INTER_LINEAR_EXACT);
  248. }
  249. // Perform annotations & store the result inside the vectorized structure
  250. // If the image was resized before, then resize the found annotations back to original dimensions
  251. vector<Rect> current_annotations = get_annotations(current_image);
  252. if(resize_bool){
  253. for(int j =0; j < (int)current_annotations.size(); j++){
  254. current_annotations[j].x = current_annotations[j].x * resizeFactor;
  255. current_annotations[j].y = current_annotations[j].y * resizeFactor;
  256. current_annotations[j].width = current_annotations[j].width * resizeFactor;
  257. current_annotations[j].height = current_annotations[j].height * resizeFactor;
  258. }
  259. }
  260. annotations[filenames[i]] = current_annotations;
  261. // Check if the ESC key was hit, then exit earlier then expected
  262. if(stop){
  263. break;
  264. }
  265. }
  266. // When all data is processed, store the data gathered inside the proper file
  267. // This now even gets called when the ESC button was hit to store preliminary results
  268. ofstream output(annotations_file.c_str());
  269. if ( !output.is_open() ){
  270. cerr << "The path for the output file contains an error and could not be opened. Please check again!" << endl;
  271. return 0;
  272. }
  273. // Store the annotations, write to the output file
  274. for(map<String, vector<Rect> >::iterator it = annotations.begin(); it != annotations.end(); it++){
  275. vector<Rect> &anno = it->second;
  276. output << it->first << " " << anno.size();
  277. for(size_t j=0; j < anno.size(); j++){
  278. Rect temp = anno[j];
  279. output << " " << temp.x << " " << temp.y << " " << temp.width << " " << temp.height;
  280. }
  281. output << endl;
  282. }
  283. return 0;
  284. }