46
46
./opencv_annotation -images <folder location> -annotations <ouput file>
47
47
48
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
49
52
*****************************************************************************************************/
50
53
51
54
#include < opencv2/core.hpp>
@@ -68,16 +71,15 @@ using namespace cv;
68
71
69
72
// Function prototypes
70
73
void on_mouse (int , int , int , int , void *);
71
- string int2string (int );
72
- void get_annotations (Mat, stringstream*);
74
+ vector<Rect > get_annotations (Mat);
73
75
74
76
// Public parameters
75
77
Mat image;
76
78
int roi_x0 = 0 , roi_y0 = 0 , roi_x1 = 0 , roi_y1 = 0 , num_of_rec = 0 ;
77
- bool start_draw = false ;
79
+ bool start_draw = false , stop = false ;
78
80
79
81
// Window name for visualisation purposes
80
- const string window_name= " OpenCV Based Annotation Tool" ;
82
+ const string window_name = " OpenCV Based Annotation Tool" ;
81
83
82
84
// FUNCTION : Mouse response for selecting objects in images
83
85
// If left button is clicked, start drawing a rectangle as long as mouse moves
@@ -98,7 +100,8 @@ void on_mouse(int event, int x, int y, int , void * )
98
100
start_draw = false ;
99
101
}
100
102
}
101
- // Action when mouse is moving
103
+
104
+ // Action when mouse is moving and drawing is enabled
102
105
if ((event == EVENT_MOUSEMOVE) && start_draw)
103
106
{
104
107
// Redraw bounding box for annotation
@@ -109,42 +112,34 @@ void on_mouse(int event, int x, int y, int , void * )
109
112
}
110
113
}
111
114
112
- // FUNCTION : snippet to convert an integer value to a string using a clean function
113
- // instead of creating a stringstream each time inside the main code
114
- string int2string (int num)
115
+ // FUNCTION : returns a vector of Rect objects given an image containing positive object instances
116
+ vector<Rect > get_annotations (Mat input_image)
115
117
{
116
- stringstream temp_stream;
117
- temp_stream << num;
118
- return temp_stream.str ();
119
- }
118
+ vector<Rect > current_annotations;
120
119
121
- // FUNCTION : given an image containing positive object instances, add all the object
122
- // annotations to a known stringstream
123
- void get_annotations (Mat input_image, stringstream* output_stream)
124
- {
125
- // Make it possible to exit the annotation
126
- bool stop = false ;
127
-
128
- // Reset the num_of_rec element at each iteration
129
- // Make sure the global image is set to the current image
130
- num_of_rec = 0 ;
131
- image = input_image;
120
+ // Make it possible to exit the annotation process
121
+ stop = false ;
132
122
133
123
// Init window interface and couple mouse actions
134
124
namedWindow (window_name, WINDOW_AUTOSIZE);
135
125
setMouseCallback (window_name, on_mouse);
136
126
127
+ image = input_image;
137
128
imshow (window_name, image);
138
- stringstream temp_stream;
139
129
int key_pressed = 0 ;
140
130
141
131
do
142
132
{
133
+ // Get a temporary image clone
134
+ Mat temp_image = input_image.clone ();
135
+ Rect currentRect (0 , 0 , 0 , 0 );
136
+
143
137
// Keys for processing
144
138
// You need to select one for confirming a selection and one to continue to the next image
145
139
// Based on the universal ASCII code of the keystroke: http://www.asciitable.com/
146
140
// c = 99 add rectangle to current image
147
141
// n = 110 save added rectangles and show next image
142
+ // d = 100 delete the last annotation made
148
143
// <ESC> = 27 exit program
149
144
key_pressed = 0xFF & waitKey (0 );
150
145
switch ( key_pressed )
@@ -154,31 +149,51 @@ void get_annotations(Mat input_image, stringstream* output_stream)
154
149
stop = true ;
155
150
break ;
156
151
case 99 :
157
- // Add a rectangle to the list
158
- num_of_rec++;
159
152
// Draw initiated from top left corner
160
153
if (roi_x0<roi_x1 && roi_y0<roi_y1)
161
154
{
162
- temp_stream << " " << int2string (roi_x0) << " " << int2string (roi_y0) << " " << int2string (roi_x1-roi_x0) << " " << int2string (roi_y1-roi_y0);
155
+ currentRect.x = roi_x0;
156
+ currentRect.y = roi_y0;
157
+ currentRect.width = roi_x1-roi_x0;
158
+ currentRect.height = roi_y1-roi_y0;
163
159
}
164
160
// Draw initiated from bottom right corner
165
161
if (roi_x0>roi_x1 && roi_y0>roi_y1)
166
162
{
167
- temp_stream << " " << int2string (roi_x1) << " " << int2string (roi_y1) << " " << int2string (roi_x0-roi_x1) << " " << int2string (roi_y0-roi_y1);
163
+ currentRect.x = roi_x1;
164
+ currentRect.y = roi_y1;
165
+ currentRect.width = roi_x0-roi_x1;
166
+ currentRect.height = roi_y0-roi_y1;
168
167
}
169
168
// Draw initiated from top right corner
170
169
if (roi_x0>roi_x1 && roi_y0<roi_y1)
171
170
{
172
- temp_stream << " " << int2string (roi_x1) << " " << int2string (roi_y0) << " " << int2string (roi_x0-roi_x1) << " " << int2string (roi_y1-roi_y0);
171
+ currentRect.x = roi_x1;
172
+ currentRect.y = roi_y0;
173
+ currentRect.width = roi_x0-roi_x1;
174
+ currentRect.height = roi_y1-roi_y0;
173
175
}
174
176
// Draw initiated from bottom left corner
175
177
if (roi_x0<roi_x1 && roi_y0>roi_y1)
176
178
{
177
- temp_stream << " " << int2string (roi_x0) << " " << int2string (roi_y1) << " " << int2string (roi_x1-roi_x0) << " " << int2string (roi_y0-roi_y1);
179
+ currentRect.x = roi_x0;
180
+ currentRect.y = roi_y1;
181
+ currentRect.width = roi_x1-roi_x0;
182
+ currentRect.height = roi_y0-roi_y1;
178
183
}
179
-
180
- rectangle (input_image, Point (roi_x0,roi_y0), Point (roi_x1,roi_y1), Scalar (0 ,255 ,0 ), 1 );
181
-
184
+ // Draw the rectangle on the canvas
185
+ // Add the rectangle to the vector of annotations
186
+ current_annotations.push_back (currentRect);
187
+ break ;
188
+ case 100 :
189
+ // Remove the last annotation
190
+ if (current_annotations.size () > 0 ){
191
+ current_annotations.pop_back ();
192
+ }
193
+ break ;
194
+ default :
195
+ // Default case --> do nothing at all
196
+ // Other keystrokes can simply be ignored
182
197
break ;
183
198
}
184
199
@@ -187,19 +202,24 @@ void get_annotations(Mat input_image, stringstream* output_stream)
187
202
{
188
203
break ;
189
204
}
205
+
206
+ // Draw all the current rectangles onto the top image and make sure that the global image is linked
207
+ for (int i=0 ; i < (int )current_annotations.size (); i++){
208
+ rectangle (temp_image, current_annotations[i], Scalar (0 ,255 ,0 ), 1 );
209
+ }
210
+ image = temp_image;
211
+
212
+ // Force an explicit redraw of the canvas --> necessary to visualize delete correctly
213
+ imshow (window_name, image);
190
214
}
191
215
// Continue as long as the next image key has not been pressed
192
216
while (key_pressed != 110 );
193
217
194
- // If there are annotations AND the next image key is pressed
195
- // Write the image annotations to the file
196
- if (num_of_rec>0 && key_pressed==110 )
197
- {
198
- *output_stream << " " << num_of_rec << temp_stream.str () << endl;
199
- }
200
-
201
218
// Close down the window
202
219
destroyWindow (window_name);
220
+
221
+ // Return the data
222
+ return current_annotations;
203
223
}
204
224
205
225
int main ( int argc, const char ** argv )
@@ -209,13 +229,13 @@ int main( int argc, const char** argv )
209
229
cout << " Usage: " << argv[0 ] << endl;
210
230
cout << " -images <folder_location> [example - /data/testimages/]" << endl;
211
231
cout << " -annotations <ouput_file> [example - /data/annotations.txt]" << endl;
212
-
232
+ cout << " TIP: Use absolute paths to avoid any problems with the software! " << endl;
213
233
return -1 ;
214
234
}
215
235
216
236
// Read in the input arguments
217
237
string image_folder;
218
- string annotations ;
238
+ string annotations_file ;
219
239
for (int i = 1 ; i < argc; ++i )
220
240
{
221
241
if ( !strcmp ( argv[i], " -images" ) )
@@ -224,7 +244,7 @@ int main( int argc, const char** argv )
224
244
}
225
245
else if ( !strcmp ( argv[i], " -annotations" ) )
226
246
{
227
- annotations = argv[++i];
247
+ annotations_file = argv[++i];
228
248
}
229
249
}
230
250
@@ -249,14 +269,9 @@ int main( int argc, const char** argv )
249
269
}
250
270
#endif
251
271
252
- // Create the outputfilestream
253
- ofstream output (annotations.c_str ());
254
- if ( !output.is_open () ){
255
- cerr << " The path for the output file contains an error and could not be opened. Please check again!" << endl;
256
- return 0 ;
257
- }
258
-
272
+ // Start by processing the data
259
273
// Return the image filenames inside the image folder
274
+ vector< vector<Rect > > annotations;
260
275
vector<String> filenames;
261
276
String folder (image_folder);
262
277
glob (folder, filenames);
@@ -274,14 +289,32 @@ int main( int argc, const char** argv )
274
289
continue ;
275
290
}
276
291
277
- // Perform annotations & generate corresponding output
278
- stringstream output_stream;
279
- get_annotations (current_image, &output_stream);
292
+ // Perform annotations & store the result inside the vectorized structure
293
+ vector<Rect > current_annotations = get_annotations (current_image);
294
+ annotations.push_back (current_annotations);
295
+
296
+ // Check if the ESC key was hit, then exit earlier then expected
297
+ if (stop){
298
+ break ;
299
+ }
300
+ }
301
+
302
+ // When all data is processed, store the data gathered inside the proper file
303
+ // This now even gets called when the ESC button was hit to store preliminary results
304
+ ofstream output (annotations_file.c_str ());
305
+ if ( !output.is_open () ){
306
+ cerr << " The path for the output file contains an error and could not be opened. Please check again!" << endl;
307
+ return 0 ;
308
+ }
280
309
281
- // Store the annotations, write to the output file
282
- if (output_stream.str () != " " ){
283
- output << filenames[i] << output_stream.str ();
310
+ // Store the annotations, write to the output file
311
+ for (int i = 0 ; i < (int )annotations.size (); i++){
312
+ output << filenames[i] << " " << annotations[i].size ();
313
+ for (int j=0 ; j < (int )annotations[i].size (); j++){
314
+ Rect temp = annotations[i][j];
315
+ output << " " << temp.x << " " << temp.y << " " << temp.width << " " << temp.height ;
284
316
}
317
+ output << endl;
285
318
}
286
319
287
320
return 0 ;
0 commit comments