SVM-классификатор на основе функций HOG для "обнаружения объекта" в OpenCV
У меня есть проект, который я хочу обнаружить в изображениях; моя цель - использовать функции HOG. Используя OpenCV SVM-реализацию, я смог найти код для обнаружения людей, и я прочитал некоторые статьи о настройке параметров, чтобы обнаружить объект вместо людей. К сожалению, я не мог этого сделать по нескольким причинам; во-первых, я, вероятно, неправильно настроил параметры, во-вторых, я не хороший программист на С++, но я должен сделать это с помощью С++/OpenCV... здесь вы можете найти код для обнаружения функций HOG для людей с помощью С++/OpenCV.
Скажем, что я хочу обнаружить объект в этом изображении. Теперь я покажу вам, что я пытался изменить в коде, но это не сработало со мной.
Код, который я пытался изменить:
HOGDescriptor hog;
hog.setSVMDetector(HOGDescriptor::getDefaultPeopleDetector());
Я попытался изменить getDefaultPeopleDetector()
со следующими параметрами, но это не сработало:
(Size(64, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9, 0,-1, 0, 0.2, true, cv::HOGDescriptor::DEFAULT_NLEVELS)
Затем я попытался создать вектор, но когда я хотел распечатать результаты, он кажется пустым.
vector<float> detector;
HOGDescriptor hog(Size(64, 128), Size(16, 16), Size(8, 8), Size(8, 8), 9, 0,-1, 0, 0.2, true, cv::HOGDescriptor::DEFAULT_NLEVELS);
hog.setSVMDetector(detector);
Пожалуйста, мне нужна помощь в решении этой проблемы.
Ответы
Ответ 1
Чтобы обнаружить произвольные объекты с использованием дескрипторов HOP opencv и классификатора SVM, вам необходимо сначала обучить классификатор. Игра с параметрами здесь не поможет, извините:(.
В широком смысле вам нужно будет выполнить следующие шаги:
Шаг 1) Подготовьте обучающие изображения объектов, которые вы хотите обнаружить (положительные образцы). Также вам нужно будет подготовить некоторые изображения без каких-либо объектов, представляющих интерес (отрицательные образцы).
Шаг 2) Определите функции HOG учебного образца и используйте эти функции для обучения классификатора SVM (также предусмотренного в OpenCV).
Шаг 3) Используйте коэффициенты обучаемого классификатора SVM в методе HOGDescriptor:: setSVMDetector().
Только тогда вы можете использовать код примера peopledetector.cpp, чтобы обнаружить объекты, которые вы хотите обнаружить.
Ответ 2
Я столкнулся с одной и той же проблемой и удивился отсутствию некоторых чистых решений на С++, которые я создал ~ > эту оболочку SVMLight <, ~, которая представляет собой статическую библиотеку, которая предоставляет классы SVMTrainer
и SVMClassifier
, которые упрощают обучение к чему-то вроде:
// we are going to use HOG to obtain feature vectors:
HOGDescriptor hog;
hog.winSize = Size(32,48);
// and feed SVM with them:
SVMLight::SVMTrainer svm("features.dat");
то для каждой обучающей выборки:
// obtain feature vector describing sample image:
vector<float> featureVector;
hog.compute(img, featureVector, Size(8, 8), Size(0, 0));
// and write feature vector to the file:
svm.writeFeatureVectorToFile(featureVector, true); // true = positive sample
до тех пор, пока файл features.dat
не будет содержать векторы признаков для всех образцов и в конце вы просто вызываете:
std::string modelName("classifier.dat");
svm.trainAndSaveModel(modelName);
Как только у вас есть файл с моделью (или features.dat
, с которым вы можете просто обучить классификатор):
SVMLight::SVMClassifier c(classifierModelName);
vector<float> descriptorVector = c.getDescriptorVector();
hog.setSVMDetector(descriptorVector);
...
vector<Rect> found;
Size padding(Size(0, 0));
Size winStride(Size(8, 8));
hog.detectMultiScale(segment, found, 0.0, winStride, padding, 1.01, 0.1);
просто проверьте документацию HOGDescriptor для получения дополнительной информации:)
Ответ 3
Я сделал то же, что и вы: собирал образцы положительных и отрицательных изображений с помощью HOG для извлечения функций автомобиля, тренировки набора функций с использованием линейного SVM (я использую свет SVM), затем используйте модель для обнаружения автомобиля с помощью HOG многоточечная функция.
Я получаю много ложных срабатываний, затем переучиваю данные, используя положительные образцы и ложноположительные + отрицательные образцы. Полученная модель затем снова тестируется. Получающееся обнаружение улучшает (менее ложные срабатывания), но результат не удовлетворяет (средний показатель 50% и 50% ложных срабатываний). Настройка параметров многоточия улучшает результат, но не намного (на 10% меньше ложных срабатываний и увеличивает скорость попадания).
Edit
Я могу поделиться с вами исходным кодом, если вы хотите, и я очень открыт для обсуждения, так как у меня нет удовлетворительных результатов, используя HOG. Во всяком случае, я думаю, что код может быть хорошей отправной точкой при использовании HOG для обучения и обнаружения
Изменить: добавление кода
static void calculateFeaturesFromInput(const string& imageFilename, vector<float>& featureVector, HOGDescriptor& hog)
{
Mat imageData = imread(imageFilename, 1);
if (imageData.empty()) {
featureVector.clear();
printf("Error: HOG image '%s' is empty, features calculation skipped!\n", imageFilename.c_str());
return;
}
// Check for mismatching dimensions
if (imageData.cols != hog.winSize.width || imageData.rows != hog.winSize.height) {
featureVector.clear();
printf("Error: Image '%s' dimensions (%u x %u) do not match HOG window size (%u x %u)!\n", imageFilename.c_str(), imageData.cols, imageData.rows, hog.winSize.width, hog.winSize.height);
return;
}
vector<Point> locations;
hog.compute(imageData, featureVector, winStride, trainingPadding, locations);
imageData.release(); // Release the image again after features are extracted
}
...
int main(int argc, char** argv) {
// <editor-fold defaultstate="collapsed" desc="Init">
HOGDescriptor hog; // Use standard parameters here
hog.winSize.height = 128;
hog.winSize.width = 64;
// Get the files to train from somewhere
static vector<string> tesImages;
static vector<string> positiveTrainingImages;
static vector<string> negativeTrainingImages;
static vector<string> validExtensions;
validExtensions.push_back("jpg");
validExtensions.push_back("png");
validExtensions.push_back("ppm");
validExtensions.push_back("pgm");
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Read image files">
getFilesInDirectory(posSamplesDir, positiveTrainingImages, validExtensions);
getFilesInDirectory(negSamplesDir, negativeTrainingImages, validExtensions);
/// Retrieve the descriptor vectors from the samples
unsigned long overallSamples = positiveTrainingImages.size() + negativeTrainingImages.size();
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Calculate HOG features and save to file">
// Make sure there are actually samples to train
if (overallSamples == 0) {
printf("No training sample files found, nothing to do!\n");
return EXIT_SUCCESS;
}
/// @WARNING: This is really important, some libraries (e.g. ROS) seems to set the system locale which takes decimal commata instead of points which causes the file input parsing to fail
setlocale(LC_ALL, "C"); // Do not use the system locale
setlocale(LC_NUMERIC,"C");
setlocale(LC_ALL, "POSIX");
printf("Reading files, generating HOG features and save them to file '%s':\n", featuresFile.c_str());
float percent;
/**
* Save the calculated descriptor vectors to a file in a format that can be used by SVMlight for training
* @NOTE: If you split these steps into separate steps:
* 1. calculating features into memory (e.g. into a cv::Mat or vector< vector<float> >),
* 2. saving features to file / directly inject from memory to machine learning algorithm,
* the program may consume a considerable amount of main memory
*/
fstream File;
File.open(featuresFile.c_str(), ios::out);
if (File.good() && File.is_open()) {
File << "# Use this file to train, e.g. SVMlight by issuing $ svm_learn -i 1 -a weights.txt " << featuresFile.c_str() << endl; // Remove this line for libsvm which does not support comments
// Iterate over sample images
for (unsigned long currentFile = 0; currentFile < overallSamples; ++currentFile) {
storeCursor();
vector<float> featureVector;
// Get positive or negative sample image file path
const string currentImageFile = (currentFile < positiveTrainingImages.size() ? positiveTrainingImages.at(currentFile) : negativeTrainingImages.at(currentFile - positiveTrainingImages.size()));
// Output progress
if ( (currentFile+1) % 10 == 0 || (currentFile+1) == overallSamples ) {
percent = ((currentFile+1) * 100 / overallSamples);
printf("%5lu (%3.0f%%):\tFile '%s'", (currentFile+1), percent, currentImageFile.c_str());
fflush(stdout);
resetCursor();
}
// Calculate feature vector from current image file
calculateFeaturesFromInput(currentImageFile, featureVector, hog);
if (!featureVector.empty()) {
/* Put positive or negative sample class to file,
* true=positive, false=negative,
* and convert positive class to +1 and negative class to -1 for SVMlight
*/
File << ((currentFile < positiveTrainingImages.size()) ? "+1" : "-1");
// Save feature vector components
for (unsigned int feature = 0; feature < featureVector.size(); ++feature) {
File << " " << (feature + 1) << ":" << featureVector.at(feature);
}
File << endl;
}
}
printf("\n");
File.flush();
File.close();
} else {
printf("Error opening file '%s'!\n", featuresFile.c_str());
return EXIT_FAILURE;
}
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Pass features to machine learning algorithm">
/// Read in and train the calculated feature vectors
printf("Calling SVMlight\n");
SVMlight::getInstance()->read_problem(const_cast<char*> (featuresFile.c_str()));
SVMlight::getInstance()->train(); // Call the core libsvm training procedure
printf("Training done, saving model file!\n");
SVMlight::getInstance()->saveModelToFile(svmModelFile);
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Generate single detecting feature vector from calculated SVM support vectors and SVM model">
printf("Generating representative single HOG feature vector using svmlight!\n");
vector<float> descriptorVector;
vector<unsigned int> descriptorVectorIndices;
// Generate a single detecting feature vector (v1 | b) from the trained support vectors, for use e.g. with the HOG algorithm
SVMlight::getInstance()->getSingleDetectingVector(descriptorVector, descriptorVectorIndices);
// And save the precious to file system
saveDescriptorVectorToFile(descriptorVector, descriptorVectorIndices, descriptorVectorFile);
// </editor-fold>
// <editor-fold defaultstate="collapsed" desc="Test detecting vector">
cout << "Test Detecting Vector" << endl;
hog.setSVMDetector(descriptorVector); // Set our custom detecting vector
cout << "descriptorVector size: " << sizeof(descriptorVector) << endl;
getFilesInDirectory(tesSamplesDir, tesImages, validExtensions);
namedWindow("Test Detector", 1);
for( size_t it = 0; it < tesImages.size(); it++ )
{
cout << "Process image " << tesImages[it] << endl;
Mat image = imread( tesImages[it], 1 );
detectAndDrawObjects(image, hog);
for(;;)
{
int c = waitKey();
if( (char)c == 'n')
break;
else if( (char)c == '\x1b' )
exit(0);
}
}
// </editor-fold>
return EXIT_SUCCESS;
}