Ответ 1
Мне пришлось иметь дело с этим в последнее время, и вот что я в итоге сделал, чтобы заставить SVM работать с изображениями.
Чтобы обучить ваш SVM набору изображений, сначала вам нужно построить обучающую матрицу для SVM. Эта матрица задается следующим образом: каждая строка матрицы соответствует одному изображению, и каждый элемент в этой строке соответствует одной функции класса - в данном случае - цвет пикселя в определенной точке. Поскольку ваши изображения 2D, вам необходимо преобразовать их в 1D-матрицу. Длина каждой строки будет областью изображений (обратите внимание, что изображения должны быть одного размера).
Предположим, вы хотели тренировать SVM на 5 разных изображениях, и каждое изображение было 4x3 пикселя. Сначала вам нужно будет инициализировать матрицу обучения. Количество строк в матрице равно 5, а число столбцов будет областью изображения, 4 * 3 = 12.
int num_files = 5;
int img_area = 4*3;
Mat training_mat(num_files,img_area,CV_32FC1);
В идеале num_files
и img_area
не будут жестко закодированы, но получены из цикла через каталог и подсчета количества изображений и принятия фактической области изображения.
Следующий шаг - "заполнить" строки training_mat
данными из каждого изображения. Ниже приведен пример того, как это сопоставление будет работать для одной строки.
Я пронумеровал каждый элемент матрицы изображения, где он должен идти в соответствующей строке в обучающей матрице. Например, если это было третье изображение, это будет третья строка в обучающей матрице.
Вам нужно будет прокрутить каждое изображение и соответственно установить значение в выходной матрице. Вот пример для нескольких изображений:
Что касается того, как вы это сделаете в коде, вы можете использовать reshape()
, но у меня были проблемы с этим из-за отсутствия непрерывности матриц. По моему опыту я сделал что-то вроде этого:
Mat img_mat = imread(imgname,0); // I used 0 for greyscale
int ii = 0; // Current column in training_mat
for (int i = 0; i<img_mat.rows; i++) {
for (int j = 0; j < img_mat.cols; j++) {
training_mat.at<float>(file_num,ii++) = img_mat.at<uchar>(i,j);
}
}
Сделайте это для каждого обучающего изображения (помните о приращении file_num
). После этого вы должны правильно настроить свою обучающую матрицу, чтобы перейти в функции SVM. Остальные шаги должны быть очень похожи на примеры онлайн.
Обратите внимание, что при этом вам также необходимо настроить метки для каждого учебного изображения. Например, если вы классифицировали глаза и неглазы на основе изображений, вам нужно указать, какая строка в обучающей матрице соответствует глазу и неглазу. Это задается как 1D-матрица, где каждый элемент в 1D-матрице соответствует каждой строке в 2D-матрице. Выберите значения для каждого класса (например, -1 для неглаза и 1 для глаз) и установите их в матрицу меток.
Mat labels(num_files,1,CV_32FC1);
Итак, если 3-й элемент в этой матрице labels
равен -1, это означает, что 3-я строка в обучающей матрице находится в классе "неглазый". Вы можете установить эти значения в цикле, где вы оцениваете каждое изображение. Одна вещь, которую вы можете сделать, это сортировать данные обучения в отдельные каталоги для каждого класса и циклически перемещать изображения в каждом каталоге и устанавливать метки на основе каталога.
Следующее, что нужно сделать, это настроить параметры SVM. Эти значения будут зависеть от вашего проекта, но в основном вы объявите объект CvSVMParams
и установите значения:
CvSVMParams params;
params.svm_type = CvSVM::C_SVC;
params.kernel_type = CvSVM::POLY;
params.gamma = 3;
// ...etc
Есть несколько примеров онлайн о том, как установить эти параметры, например, в ссылке, опубликованной в вопросе.
Затем вы создаете объект CvSVM
и обучаете его на основе ваших данных!
CvSVM svm;
svm.train(training_mat, labels, Mat(), Mat(), params);
В зависимости от того, сколько данных у вас есть, это может занять много времени. Однако после тренировки вы можете сохранить подготовленный SVM, чтобы не переучивать его каждый раз.
svm.save("svm_filename"); // saving
svm.load("svm_filename"); // loading
Чтобы протестировать ваши изображения с помощью обученного SVM, просто прочитайте изображение, преобразуйте его в 1D-матрицу и передайте в svm.predict()
:
svm.predict(img_mat_1d);
Он вернет значение, основанное на том, что вы установили в качестве ваших меток (например, -1 или 1, на основе моего примера eye/non-eye выше). В качестве альтернативы, если вы хотите протестировать несколько изображений за раз, вы можете создать матрицу, которая имеет тот же формат, что и ранее разработанная матрица обучения, и передать ее как аргумент. Возвращаемое значение будет отличаться.
Удачи!