Skip to content

Commit b922d49

Browse files
author
StevenPuttemans
committed
vectorize process + enable early quitting/storage + enable delete annotion option
1 parent 06ea0aa commit b922d49

File tree

1 file changed

+89
-56
lines changed

1 file changed

+89
-56
lines changed

apps/annotation/opencv_annotation.cpp

Lines changed: 89 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,9 @@
4646
./opencv_annotation -images <folder location> -annotations <ouput file>
4747
4848
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
4952
*****************************************************************************************************/
5053

5154
#include <opencv2/core.hpp>
@@ -68,16 +71,15 @@ using namespace cv;
6871

6972
// Function prototypes
7073
void on_mouse(int, int, int, int, void*);
71-
string int2string(int);
72-
void get_annotations(Mat, stringstream*);
74+
vector<Rect> get_annotations(Mat);
7375

7476
// Public parameters
7577
Mat image;
7678
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;
7880

7981
// Window name for visualisation purposes
80-
const string window_name="OpenCV Based Annotation Tool";
82+
const string window_name = "OpenCV Based Annotation Tool";
8183

8284
// FUNCTION : Mouse response for selecting objects in images
8385
// 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 * )
98100
start_draw = false;
99101
}
100102
}
101-
// Action when mouse is moving
103+
104+
// Action when mouse is moving and drawing is enabled
102105
if((event == EVENT_MOUSEMOVE) && start_draw)
103106
{
104107
// Redraw bounding box for annotation
@@ -109,42 +112,34 @@ void on_mouse(int event, int x, int y, int , void * )
109112
}
110113
}
111114

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)
115117
{
116-
stringstream temp_stream;
117-
temp_stream << num;
118-
return temp_stream.str();
119-
}
118+
vector<Rect> current_annotations;
120119

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;
132122

133123
// Init window interface and couple mouse actions
134124
namedWindow(window_name, WINDOW_AUTOSIZE);
135125
setMouseCallback(window_name, on_mouse);
136126

127+
image = input_image;
137128
imshow(window_name, image);
138-
stringstream temp_stream;
139129
int key_pressed = 0;
140130

141131
do
142132
{
133+
// Get a temporary image clone
134+
Mat temp_image = input_image.clone();
135+
Rect currentRect(0, 0, 0, 0);
136+
143137
// Keys for processing
144138
// You need to select one for confirming a selection and one to continue to the next image
145139
// Based on the universal ASCII code of the keystroke: http://www.asciitable.com/
146140
// c = 99 add rectangle to current image
147141
// n = 110 save added rectangles and show next image
142+
// d = 100 delete the last annotation made
148143
// <ESC> = 27 exit program
149144
key_pressed = 0xFF & waitKey(0);
150145
switch( key_pressed )
@@ -154,31 +149,51 @@ void get_annotations(Mat input_image, stringstream* output_stream)
154149
stop = true;
155150
break;
156151
case 99:
157-
// Add a rectangle to the list
158-
num_of_rec++;
159152
// Draw initiated from top left corner
160153
if(roi_x0<roi_x1 && roi_y0<roi_y1)
161154
{
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;
163159
}
164160
// Draw initiated from bottom right corner
165161
if(roi_x0>roi_x1 && roi_y0>roi_y1)
166162
{
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;
168167
}
169168
// Draw initiated from top right corner
170169
if(roi_x0>roi_x1 && roi_y0<roi_y1)
171170
{
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;
173175
}
174176
// Draw initiated from bottom left corner
175177
if(roi_x0<roi_x1 && roi_y0>roi_y1)
176178
{
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;
178183
}
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
182197
break;
183198
}
184199

@@ -187,19 +202,24 @@ void get_annotations(Mat input_image, stringstream* output_stream)
187202
{
188203
break;
189204
}
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);
190214
}
191215
// Continue as long as the next image key has not been pressed
192216
while(key_pressed != 110);
193217

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-
201218
// Close down the window
202219
destroyWindow(window_name);
220+
221+
// Return the data
222+
return current_annotations;
203223
}
204224

205225
int main( int argc, const char** argv )
@@ -209,13 +229,13 @@ int main( int argc, const char** argv )
209229
cout << "Usage: " << argv[0] << endl;
210230
cout << " -images <folder_location> [example - /data/testimages/]" << endl;
211231
cout << " -annotations <ouput_file> [example - /data/annotations.txt]" << endl;
212-
232+
cout << "TIP: Use absolute paths to avoid any problems with the software!" << endl;
213233
return -1;
214234
}
215235

216236
// Read in the input arguments
217237
string image_folder;
218-
string annotations;
238+
string annotations_file;
219239
for(int i = 1; i < argc; ++i )
220240
{
221241
if( !strcmp( argv[i], "-images" ) )
@@ -224,7 +244,7 @@ int main( int argc, const char** argv )
224244
}
225245
else if( !strcmp( argv[i], "-annotations" ) )
226246
{
227-
annotations = argv[++i];
247+
annotations_file = argv[++i];
228248
}
229249
}
230250

@@ -249,14 +269,9 @@ int main( int argc, const char** argv )
249269
}
250270
#endif
251271

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
259273
// Return the image filenames inside the image folder
274+
vector< vector<Rect> > annotations;
260275
vector<String> filenames;
261276
String folder(image_folder);
262277
glob(folder, filenames);
@@ -274,14 +289,32 @@ int main( int argc, const char** argv )
274289
continue;
275290
}
276291

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+
}
280309

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;
284316
}
317+
output << endl;
285318
}
286319

287320
return 0;

0 commit comments

Comments
 (0)