bool BowVocabulary::computeTrainingData(TrainingData& trainingDataOut, const string& vocabularyImgsList, const string& samplesImgsList, bool outputAnalyzedImages) {	
	if (_bowImgDescriptorExtractor->getVocabulary().rows == 0) {
		Mat vocabulary;
		if (!computeVocabulary(vocabulary, vocabularyImgsList)) {
			return false;
		}
	}

	Mat trainSamples;
	Mat trainLabels(0, 1, CV_32SC1);

	if (loadTrainingSamples(trainSamples) && loadTrainingLabels(trainLabels)) {
		trainingDataOut.setTrainSamples(trainSamples);
		trainingDataOut.setTrainLabels(trainLabels);
		return true;
	}

	ifstream imgsList(vocabularyImgsList);
	if (imgsList.is_open()) {		
		vector<string> fileNames;
		string filename;
		while (getline(imgsList, filename)) {			
			fileNames.push_back(filename);
		}
		int numberOfFiles = fileNames.size();

		int samplesWordSize = _bowImgDescriptorExtractor->getVocabulary().rows;		

		cout << "\n    -> Analysing " << numberOfFiles << " training images..." << endl;
		PerformanceTimer performanceTimer;
		performanceTimer.start();

		//#pragma omp parallel for schedule(dynamic)
		for (int i = 0; i < numberOfFiles; ++i) {
			Mat imagePreprocessed;
			string imageFilenameShort = IMGS_DIRECTORY + fileNames[i];
			string imageFilenameFull = imageFilenameShort + IMAGE_TOKEN;
			if (_imagePreprocessor->loadAndPreprocessImage(imageFilenameFull, imagePreprocessed, CV_LOAD_IMAGE_GRAYSCALE, false)) {
				vector<KeyPoint> keypoints;				
				_featureDetector->detect(imagePreprocessed, keypoints);

				vector< vector <KeyPoint> > keypointsTargetClass;
				vector<KeyPoint> keypointsNonTargetClass;

				ImageUtils::splitKeyPoints(imageFilenameShort, keypoints, keypointsTargetClass, keypointsNonTargetClass);

				for (size_t targetClassInstancePosition = 0; targetClassInstancePosition < keypointsTargetClass.size(); ++targetClassInstancePosition) {
					if (keypointsTargetClass[targetClassInstancePosition].size() > 3) {
						Mat descriptorsTargetClass;						
						_bowImgDescriptorExtractor->compute(imagePreprocessed, keypointsTargetClass[targetClassInstancePosition], descriptorsTargetClass);

						//#pragma omp critical
						if (descriptorsTargetClass.rows > 0 && descriptorsTargetClass.cols == samplesWordSize) {
							trainSamples.push_back(descriptorsTargetClass);
							trainLabels.push_back(1);
						}
					}
				}
				
				if (keypointsNonTargetClass.size() > 3) {
					Mat descriptorsNonTargetClass;
					_bowImgDescriptorExtractor->compute(imagePreprocessed, keypointsNonTargetClass, descriptorsNonTargetClass);

					//#pragma omp critical
					if (descriptorsNonTargetClass.rows > 0 && descriptorsNonTargetClass.cols == samplesWordSize) {
						trainSamples.push_back(descriptorsNonTargetClass);
						trainLabels.push_back(0);
					}
				}

				if (outputAnalyzedImages) {
					stringstream imageOutputFilename;
					imageOutputFilename << SAMPLES_BUILD_OUTPUT_DIRECTORY << fileNames[i] << FILENAME_SEPARATOR << _vocabularyFilename << IMAGE_OUTPUT_EXTENSION;
					for (size_t targetClassInstancePosition = 0; targetClassInstancePosition < keypointsTargetClass.size(); ++targetClassInstancePosition) {
						if (keypointsTargetClass[targetClassInstancePosition].size() > 0) {
							cv::drawKeypoints(imagePreprocessed, keypointsTargetClass[targetClassInstancePosition], imagePreprocessed, TARGET_KEYPOINT_COLOR);
						}
					}

					if (keypointsNonTargetClass.size() > 0) {
						cv::drawKeypoints(imagePreprocessed, keypointsNonTargetClass, imagePreprocessed, NONTARGET_KEYPOINT_COLOR);
					}

					imwrite(imageOutputFilename.str(), imagePreprocessed);
				}					
			}
		}
		cout << "    -> Computed " << trainSamples.rows << " training samples from " << numberOfFiles << " images in " << performanceTimer.getElapsedTimeFormated() << "\n" << endl;

		if (trainSamples.rows != trainLabels.rows || trainSamples.rows == 0 || trainLabels.rows == 0) {
			cout << "\n    !> Invalid training data!\n\n\n" << endl;
			return false;
		}

		trainingDataOut.setTrainSamples(trainSamples);
		trainingDataOut.setTrainLabels(trainLabels);

		saveTrainingSamples(trainSamples);
		saveTrainingLabels(trainLabels);
		return true;
	}

	return false;
}