1 | |
2 | This program reads in a generic directory tree of cropped sample images, and is
|
3 | currently only compatible with *nix systems due to file check macros etc.
|
4 | It quickly produces a PASCAL VOC (Visual Object Challenge) compatible output,
|
5 | and was built to feed OpenCV's sample code like bagofwords_classification.cpp.
|
6 |
|
7 | 2012 Joel Mckay
|
8 | [email protected]
|
9 |
|
10 | Disclaimer:
|
11 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
12 | "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
13 | LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
|
14 | FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
|
15 | COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
|
16 | INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
|
17 | BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
18 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
19 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
20 | LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
|
21 | WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY
|
22 | OF SUCH DAMAGE.
|
23 | *****************************************************************************************/
|
24 |
|
25 |
|
26 | #include "global_headers.hpp"
|
27 |
|
28 |
|
29 |
|
30 |
|
31 |
|
32 |
|
33 |
|
34 | #define NEG_TRAIN_SET_GENERIC_PATH "negative"
|
35 |
|
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 |
|
42 | |
43 | ------------------------------------------------------------------------------------------------------------------------------
|
44 | For classification and detection there are four sets for VOC:
|
45 | ------------------------------------------------------------------------------------------------------------------------------
|
46 | "train":
|
47 | Training data
|
48 |
|
49 | "val":
|
50 | Validation data (suggested).
|
51 | The validation data may be used as addi-tional training data (see below).
|
52 |
|
53 | "trainval":
|
54 | The union of train and val.
|
55 |
|
56 | "test":
|
57 | Test data.
|
58 | The test set is not provided in the development kit.
|
59 | It will be released in good time before the deadline for submission of results.
|
60 |
|
61 |
|
62 | OpenCV BoG default file suffix used when the new VOC class loads with VocData vocData( vocPath, false ):
|
63 | m_train_set = "train";
|
64 | m_test_set = "val";
|
65 |
|
66 | These file suffix used when the new VOC class loads with VocData vocData( vocPath, true ):
|
67 | m_train_set = "trainval";
|
68 | m_test_set = "test";
|
69 |
|
70 | ---------------------------------------------------------------
|
71 | There are three ground truth labels:
|
72 | ---------------------------------------------------------------
|
73 | -1: Negative:
|
74 | The image contains no objects of the class of interest.
|
75 | A classifier should give a 'negative' output.
|
76 |
|
77 | 1: Positive:
|
78 | The image contains at least one object of the class of interest.
|
79 | A classifier should give a 'positive' output.
|
80 |
|
81 | 0: "Difficult":
|
82 | The image contains only objects of the class of interest marked as 'difficult'.
|
83 |
|
84 | ------------------------------------------------------------------------------------------------------------------------------
|
85 | */
|
86 |
|
87 |
|
88 |
|
89 |
|
90 | void loadListFromDir( string dir , vector<string>* m_object_filenames)
|
91 | {
|
92 | string filepath;
|
93 | string filename;
|
94 | string filebasename;
|
95 | string fileext;
|
96 | int arrTemplatesCount;
|
97 | DIR *dp;
|
98 | struct dirent *dirp;
|
99 | struct stat filestat;
|
100 |
|
101 | arrTemplatesCount=0;
|
102 | dp = opendir( dir.c_str() );
|
103 | if (dp == NULL)
|
104 | {
|
105 | cout << "Error opening " << dir << endl;
|
106 | return;
|
107 | }
|
108 |
|
109 | while ((dirp = readdir( dp )) && (arrTemplatesCount < 100000))
|
110 | {
|
111 | filename=dirp->d_name;
|
112 | filepath = dir + "/" + filename;
|
113 |
|
114 |
|
115 | if (stat( filepath.c_str(), &filestat )) continue;
|
116 |
|
117 |
|
118 | if (S_ISREG( filestat.st_mode ))
|
119 | {
|
120 | fileext = filename.substr(filename.find_last_of(".") + 1);
|
121 | std::transform(fileext.begin(), fileext.end(),fileext.begin(), ::tolower);
|
122 |
|
123 | if(( fileext == "png") || ( fileext == "jpg") || ( fileext == "bmp"))
|
124 | {
|
125 | filebasename = filename.substr(0, filename.find_first_of(".") );
|
126 |
|
127 | #if defined(DEBUG_MODE)
|
128 | cout << "Loaded: " << filebasename << " " << filepath.c_str() << endl;
|
129 | #endif
|
130 |
|
131 | (*m_object_filenames).push_back(filepath);
|
132 |
|
133 | }
|
134 | }
|
135 | }
|
136 |
|
137 | closedir( dp );
|
138 |
|
139 | }
|
140 |
|
141 |
|
142 |
|
143 |
|
144 | void loadClassListFromDir( string dir , vector<string>* m_object_classes, vector<string>* m_object_classes_paths)
|
145 | {
|
146 | string filepath;
|
147 | string filename;
|
148 | string filebasename;
|
149 | string fileext;
|
150 | int arrTemplatesCount;
|
151 | DIR *dp;
|
152 | struct dirent *dirp;
|
153 | struct stat filestat;
|
154 |
|
155 | arrTemplatesCount=0;
|
156 | dp = opendir( dir.c_str() );
|
157 | if (dp == NULL)
|
158 | {
|
159 | cout << "Error opening " << dir << endl;
|
160 | return;
|
161 | }
|
162 |
|
163 | while ((dirp = readdir( dp )) && (arrTemplatesCount < 100000))
|
164 | {
|
165 | filename=dirp->d_name;
|
166 | filepath = dir + "/" + filename;
|
167 |
|
168 |
|
169 | if (stat( filepath.c_str(), &filestat )) continue;
|
170 |
|
171 |
|
172 | if (S_ISDIR( filestat.st_mode ))
|
173 | {
|
174 | fileext = filename;
|
175 | std::transform(fileext.begin(), fileext.end(),fileext.begin(), ::tolower);
|
176 |
|
177 | if(( fileext != ".") && ( fileext != ".."))
|
178 | {
|
179 | (*m_object_classes).push_back(fileext);
|
180 | (*m_object_classes_paths).push_back(filepath);
|
181 |
|
182 | }
|
183 | }
|
184 | }
|
185 |
|
186 | closedir( dp );
|
187 |
|
188 | }
|
189 |
|
190 |
|
191 |
|
192 |
|
193 |
|
194 |
|
195 |
|
196 |
|
197 | const string FASTXMLOUTPUTFORMAT="<annotation>\n <folder>VOC2010</folder>\n <filename>%s.jpg</filename>\n <source>\n <database>The VOC2007 Database</database>\n <annotation>PASCAL VOC2007</annotation>\n <image>flickr</image>\n </source>\n <size>\n <width>%i</width>\n <height>%i</height>\n <depth>%i</depth>\n </size>\n <segmented>1</segmented>\n <object>\n <name>%s</name>\n <pose>Frontal</pose>\n <truncated>0</truncated>\n <difficult>0</difficult>\n <bndbox>\n <xmin>%i</xmin>\n <ymin>%i</ymin>\n <xmax>%i</xmax>\n <ymax>%i</ymax>\n </bndbox>\n </object>\n</annotation>\n";
|
198 | int createImageXMLfile( Mat* img, string* image_path, string* annotation_path, string* filename, string* classname)
|
199 | {
|
200 |
|
201 | Size size = (*img).size();
|
202 | int width=size.width;
|
203 | int height=size.height;
|
204 | int colorChans=(*img).channels();
|
205 | int colordepth=(*img).depth();
|
206 | int boxXmin=0;
|
207 | int boxYmin=0;
|
208 | int boxXmax=size.width;
|
209 | int boxYmax=size.height;
|
210 |
|
211 |
|
212 | if(!((colorChans==3) || (colorChans == 1)))
|
213 | {
|
214 | cout << "\nError: Bad color channel in: " << (*filename) << endl;
|
215 | return -1;
|
216 | }
|
217 |
|
218 |
|
219 | if((width < 40) || (height < 40))
|
220 | {
|
221 | cout << "\nError: Image too small: " << (*filename) << endl;
|
222 | return -1;
|
223 | }
|
224 |
|
225 |
|
226 | string jpgFileName= (*image_path) + "/" + (*filename) + ".jpg";
|
227 | if(imwrite(jpgFileName, (*img)) == false)
|
228 | {
|
229 | cout << "\nError: Image write fail: " << (*filename) << endl;
|
230 | return -1;
|
231 | }
|
232 |
|
233 |
|
234 |
|
235 | FILE * filePointer;
|
236 | string xmlFileName= (*annotation_path) + "/" + (*filename) + ".xml";
|
237 | filePointer = fopen(xmlFileName.c_str(),"w");
|
238 |
|
239 | if( fprintf(filePointer, FASTXMLOUTPUTFORMAT.c_str() ,(*filename).c_str(), width, height, colorChans,(*classname).c_str(),boxXmin,boxYmin,boxXmax,boxYmax) < 0 )
|
240 | {
|
241 | cout << "\nError: Could not write xml file: " << xmlFileName << endl;
|
242 | return -1;
|
243 | }
|
244 | fclose(filePointer);
|
245 |
|
246 | cout << "Wrote file: " << xmlFileName << "\r";
|
247 | return 0;
|
248 | }
|
249 |
|
250 |
|
251 |
|
252 |
|
253 | int main(int argc, char** argv)
|
254 | {
|
255 | if( argc != 3 )
|
256 | {
|
257 | cout << "\nUsage" << endl
|
258 | << "./build_classification </path/to/all/cropped/image/classdirs> </path/to/create/new/VOC2010/>" << endl;
|
259 | exit(-1);
|
260 | }
|
261 |
|
262 |
|
263 |
|
264 | string r_vocPath = argv[2];
|
265 | if( mkdir(r_vocPath.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 )
|
266 | {
|
267 | cout << "\nError: Could not create the path: \n" << r_vocPath << endl;
|
268 | }
|
269 |
|
270 | string r_annotation_path = r_vocPath + "/Annotations";
|
271 | if( mkdir(r_annotation_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 )
|
272 | {
|
273 | cout << "\nError: Could not create the path: \n" << r_annotation_path << endl;
|
274 | exit(-1);
|
275 | }
|
276 |
|
277 | string r_image_path = r_vocPath + "/JPEGImages";
|
278 | if( mkdir(r_image_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 )
|
279 | {
|
280 | cout << "\nCould not create JPEGImages path: \n" << r_image_path << endl;
|
281 | exit(-1);
|
282 | }
|
283 |
|
284 | string r_imageset_path = r_vocPath + "/ImageSets";
|
285 | if( mkdir(r_imageset_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 )
|
286 | {
|
287 | cout << "\nCould not create ImageSets path: \n" << r_imageset_path << endl;
|
288 | exit(-1);
|
289 | }
|
290 | string r_class_imageset_path = r_vocPath + "/ImageSets/Main";
|
291 | if( mkdir(r_class_imageset_path.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH) != 0 )
|
292 | {
|
293 | cout << "\nCould not create ImageSets/Main path: \n" << r_class_imageset_path << endl;
|
294 | exit(-1);
|
295 | }
|
296 |
|
297 |
|
298 |
|
299 | string template_imageset_path = r_vocPath + "/ImageSets/Main/%s.txt";
|
300 | string template_class_imageset_path = r_vocPath + "/ImageSets/Main/%s_%s.txt";
|
301 | char trainFileName[4096]="\0";
|
302 | char valFileName[4096]="\0";
|
303 | char trainvalFileName[4096]="\0";
|
304 | char testFileName[4096]="\0";
|
305 |
|
306 |
|
307 | string classesSrcBaseDir=argv[1];
|
308 | vector<string> my_classes;
|
309 | vector<string> my_classes_dir;
|
310 | vector<vector<string> > my_dataset_files;
|
311 | loadClassListFromDir(classesSrcBaseDir, &my_classes, &my_classes_dir);
|
312 |
|
313 | unsigned int fileCounter=0;
|
314 |
|
315 |
|
316 | for( size_t classIdx = 0; (classIdx < my_classes.size()); ++classIdx )
|
317 | {
|
318 |
|
319 |
|
320 | vector<string> my_files;
|
321 | vector<string> img_data_names;
|
322 | loadListFromDir(my_classes_dir[classIdx], &my_files);
|
323 |
|
324 |
|
325 | string dirclassname=my_classes[classIdx];
|
326 | cout << "\n---------------------------- Create pos set: " << dirclassname << "-----------------------"<<endl;
|
327 |
|
328 |
|
329 | sprintf(trainFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "train");
|
330 | sprintf(valFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "val");
|
331 | sprintf(trainvalFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "trainval");
|
332 | sprintf(testFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "test");
|
333 |
|
334 | FILE * trainFilePointer;
|
335 | FILE * trainvalFilePointer;
|
336 | FILE * valFilePointer;
|
337 | FILE * testFilePointer;
|
338 |
|
339 | trainFilePointer = fopen(trainFileName,"a");
|
340 | trainvalFilePointer = fopen(trainvalFileName,"a");
|
341 | valFilePointer = fopen(valFileName,"a");
|
342 | testFilePointer = fopen(testFileName,"a");
|
343 |
|
344 |
|
345 |
|
346 | for(size_t fileIdx = 0; (fileIdx < my_files.size()); ++fileIdx)
|
347 | {
|
348 | Mat img = imread(my_files[fileIdx] .c_str());
|
349 |
|
350 |
|
351 | std::ostringstream namerender;
|
352 | namerender << "0000_" << fileCounter;
|
353 | string imgname = namerender.str();
|
354 |
|
355 |
|
356 | #if defined(DEBUG_MODE)
|
357 | cout << "\nREAD: " << my_files[fileIdx] << endl
|
358 | << "\nr_image_path:" << r_image_path << endl
|
359 | << "\nr_annotation_path:" << r_annotation_path <<endl
|
360 | << "\nimgname:" << imgname <<endl
|
361 | << "\ndirclassname:" << dirclassname << endl;
|
362 | #endif
|
363 |
|
364 |
|
365 |
|
366 | if( createImageXMLfile( &img, &r_image_path, &r_annotation_path, &imgname, &dirclassname) == 0 )
|
367 | {
|
368 |
|
369 | if( imgname.compare( NEG_TRAIN_SET_GENERIC_PATH ) != 0)
|
370 | {
|
371 |
|
372 | #if defined(POS_TRAIN_SET_RATIO_OF_POSITIVE_SAMPLES)
|
373 | if((fileCounter%POS_TRAIN_SET_RATIO_OF_POSITIVE_SAMPLES)==0)
|
374 | #endif
|
375 | {
|
376 | if( fprintf(trainFilePointer, "%s 1\n" ,imgname.c_str() ) < 0 )
|
377 | {
|
378 | cout << "\nError: Could not write file: " << trainFilePointer << endl;
|
379 | exit(-1);
|
380 | }
|
381 | }
|
382 | #if defined(POS_TRAIN_SET_RATIO_OF_POSITIVE_SAMPLES)
|
383 | else
|
384 | #endif
|
385 | {
|
386 | if( fprintf(valFilePointer, "%s 1\n" ,imgname.c_str() ) < 0 )
|
387 | {
|
388 | cout << "\nError: Could not write file: " << trainFilePointer << endl;
|
389 | exit(-1);
|
390 | }
|
391 | }
|
392 |
|
393 |
|
394 |
|
395 | if( fprintf(trainvalFilePointer, "%s 1\n" ,imgname.c_str() ) < 0 )
|
396 | {
|
397 | cout << "\nError: Could not write file: " << trainvalFilePointer << endl;
|
398 | exit(-1);
|
399 | }
|
400 |
|
401 | }
|
402 |
|
403 |
|
404 | img_data_names.push_back(imgname);
|
405 |
|
406 |
|
407 | cout << imgname <<".jpg OK! \r";
|
408 | fileCounter++;
|
409 | }
|
410 | }
|
411 |
|
412 |
|
413 | my_dataset_files.push_back(img_data_names);
|
414 |
|
415 | fclose(trainFilePointer);
|
416 | fclose(trainvalFilePointer);
|
417 | fclose(valFilePointer);
|
418 | fclose(testFilePointer);
|
419 |
|
420 | }
|
421 |
|
422 |
|
423 | fileCounter=0;
|
424 |
|
425 |
|
426 | for( size_t classIdx = 0; (classIdx < my_classes.size()); ++classIdx )
|
427 | {
|
428 |
|
429 |
|
430 | string dirclassname=my_classes[classIdx];
|
431 | cout << "\n---------------------------- Create neg set: " << dirclassname << " -----------------------"<<endl;
|
432 |
|
433 |
|
434 | sprintf(trainFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "train");
|
435 | sprintf(trainvalFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "trainval");
|
436 | sprintf(valFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "val");
|
437 | sprintf(testFileName, template_class_imageset_path.c_str(), dirclassname.c_str(), "test");
|
438 |
|
439 | FILE * trainFilePointer;
|
440 | FILE * trainvalFilePointer;
|
441 | FILE * valFilePointer;
|
442 | FILE * testFilePointer;
|
443 |
|
444 | trainFilePointer = fopen(trainFileName,"a");
|
445 | trainvalFilePointer = fopen(trainvalFileName,"a");
|
446 | valFilePointer = fopen(valFileName,"a");
|
447 | testFilePointer = fopen(testFileName,"a");
|
448 |
|
449 |
|
450 |
|
451 | for( size_t classIter = 0; (classIter < my_classes.size()); ++classIter )
|
452 | {
|
453 | if(classIdx != classIter)
|
454 | {
|
455 | for(size_t fileIdx = 0; (fileIdx < my_dataset_files[classIter].size()) ; ++fileIdx)
|
456 | {
|
457 |
|
458 |
|
459 |
|
460 |
|
461 | #if defined(NEG_TRAIN_SET_RATIO_OF_NEGAITIVE_SAMPLES)
|
462 | if((fileCounter%NEG_TRAIN_SET_RATIO_OF_NEGAITIVE_SAMPLES)==0)
|
463 | #endif
|
464 | {
|
465 | if( fprintf(trainFilePointer, "%s -1\n" ,my_dataset_files[classIter][fileIdx].c_str() ) < 0 )
|
466 | {
|
467 | cout << "\nError: Could not write file: " << trainFilePointer << endl;
|
468 | exit(-1);
|
469 | }
|
470 | }
|
471 | #if defined(NEG_TRAIN_SET_RATIO_OF_NEGAITIVE_SAMPLES)
|
472 | else
|
473 | #endif
|
474 | {
|
475 | if( fprintf(valFilePointer, "%s -1\n" ,my_dataset_files[classIter][fileIdx].c_str() ) < 0 )
|
476 | {
|
477 | cout << "\nError: Could not write file: " << trainFilePointer << endl;
|
478 | exit(-1);
|
479 | }
|
480 | }
|
481 |
|
482 | if( fprintf(trainvalFilePointer, "%s -1\n" ,my_dataset_files[classIter][fileIdx].c_str() ) < 0 )
|
483 | {
|
484 | cout << "\nError: Could not write file: " << trainvalFilePointer << endl;
|
485 | exit(-1);
|
486 | }
|
487 |
|
488 | fileCounter++;
|
489 |
|
490 | }
|
491 | cout << ".";
|
492 | }
|
493 |
|
494 | }
|
495 |
|
496 | fclose(trainFilePointer);
|
497 | fclose(trainvalFilePointer);
|
498 | fclose(valFilePointer);
|
499 | fclose(testFilePointer);
|
500 |
|
501 | }
|
502 |
|
503 | cout << "\n Done!" << endl;
|
504 | return 0;
|
505 | }
|
506 |
|
507 |
|