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