Обнаружение объектов с помощью HOG
В этом руководстве мы используем линейный метод SVM на основе дескриптора признаков «гистограмма направленных градиентов» (HOG) для создания детектора людей. Сначала мы создадим классификатор людей, а затем используем его со скользящим окном для идентификации и определения местонахождения людей на изображении.
Основная проблема при создании классификатора заключается в том, что ему необходимо учитывать вариации освещенности, позы и перекрытия на изображении. Для этого мы обучим классификатор на промежуточном представлении изображения вместо пиксельного представления. Наше идеальное представление (обычно называемое вектором признаков) фиксирует информацию, полезную для классификации, но является инвариантным к небольшим изменениям освещенности и перекрытиям. Дескриптор HOG представляет собой градиентное представление, которое инвариантно к локальным геометрическим и фотометрическим изменениям (то есть изменениям формы и освещенности) и поэтому хорошо подходит для нашей задачи. На практике дескрипторы HOG широко применяются для обнаружения объектов.
Скачайте скрипт, чтобы получить обучающие данные. Скачайте архив tutorial.zip, распакуйте его и запустите файл getdata.bash. (Измените переменную path*to*tutorial
в файле preprocess.jl и путь к исполняемому файлу Julia в файле getdata.bash.) Скрипт скачает необходимые наборы данных. Начнем с загрузки данных и вычисления признаков HOG для всех изображений.
using Images, ImageFeatures
path_to_tutorial = "" # укажите этот путь
pos_examples = "$path_to_tutorial/tutorial/humans/"
neg_examples = "$path_to_tutorial/tutorial/not_humans/"
n_pos = length(readdir(pos_examples)) # количество положительных примеров обучения
n_neg = length(readdir(neg_examples)) # количество отрицательных примеров обучения
n = n_pos + n_neg # количество примеров обучения
data = Array{Float64}(undef, 3780, n) # Массив для хранения дескриптора HOG каждого изображения. Каждое изображение в обучающих данных имеет размер 128x64, поэтому длина равна 3780
labels = Vector{Int}(undef, n) # Вектор для хранения метки (1 = человек, 0 = не человек) каждого изображения.
for (i, file) in enumerate([readdir(pos_examples); readdir(neg_examples)])
filename = "$(i <= n_pos ? pos_examples : neg_examples )/$file"
img = load(filename)
data[:, i] = create_descriptor(img, HOG())
labels[i] = (i <= n_pos ? 1 : 0)
end
По сути, теперь в наших обучающих данных есть закодированная версия изображений. Такое кодирование фиксирует полезную информацию, а лишняя информация (изменения освещенности, вариации позы и т. д.) отбрасывается. На этих данных мы обучим линейную модель SVM.
using LIBSVM
#Разделяем набор данных на обучающий и тестовый. Обучающий набор = 2500 изображений, тестовый набор = 294 изображения.
random_perm = randperm(n)
train_ind = random_perm[1:2500]
test_ind = random_perm[2501:end]
model = svmtrain(data[:, train_ind], labels[train_ind]);
Теперь проверим этот классификатор на нескольких изображениях.
img = load("$pos_examples/per00003.ppm")
descriptor = Array{Float64}(3780, 1)
descriptor[:, 1] = create_descriptor(img, HOG())
predicted_label, _ = svmpredict(model, descriptor);
print(predicted_label) # 1 = человек, 0 = не человек
# Получаем тестовую точность модели
predicted_labels, decision_values = svmpredict(model, data[:, test_ind]);
@printf "Accuracy: %.2f%%\n" mean((predicted_labels .== labels[test_ind])) * 100 # тестовая точность должна быть > 98 %
Попробуем протестировать обученную модель на других изображениях. Как видите, она работает довольно хорошо. Изображение
predicted_label = 1 |
predicted_label = 1 |
predicted_label = 1 |
predicted_label = 0 |
Далее мы воспользуемся нашим обученным классификатором со скользящим окном для определения положения людей на изображении.
img = load("path_to_tutorial/tutorial/humans.jpg")
rows, cols = size(img)
scores = Array{Float64}(22, 45)
descriptor = Array{Float64}(3780, 1)
#Применяем классификатор, используя метод скользящего окна, и сохраняем оценку классификации для класса «не человек» в каждой позиции в массиве оценок
for j = 32:10:cols-32
for i = 64:10:rows-64
box = img[i-63:i+64, j-31:j+32]
descriptor[:, 1] = create_descriptor(box, HOG())
predicted_label, s = svmpredict(model, descriptor)
scores[Int((i - 64) / 10)+1, Int((j - 32) / 10)+1] = s[1]
end
end
Как видите, классификатор дал низкую оценку классу «не человек» (а значит, высокую оценку классу «человек») в позициях, соответствующих людям, на исходном изображении. Ниже мы устанавливаем пороговое значение для изображения и подавляем неминимальные значения, чтобы получить местоположения людей. Затем мы строим ограничивающие прямоугольники с помощью ImageDraw
.
using ImageDraw, ImageView
scores[scores.>0] = 0
object_locations = findlocalminima(scores)
rectangles = [
[
((i[2] - 1) * 10 + 1, (i[1] - 1) * 10 + 1),
((i[2] - 1) * 10 + 64, (i[1] - 1) * 10 + 1),
((i[2] - 1) * 10 + 64, (i[1] - 1) * 10 + 128),
((i[2] - 1) * 10 + 1, (i[1] - 1) * 10 + 128),
] for i in object_locations
];
for rec in rectangles
draw!(img, Polygon(rec), RGB{N0f8}(0, 0, 1.0))
end
imshow(img)
Наш пример был удобен тем, что люди на изображении имели примерно такой же размер (128 x 64), как и в образцах в обучающем наборе. Обычно же требуется строить ограничивающие прямоугольники в разных масштабах (и с разными соотношениями сторон для некоторых классов объектов).
Эта страница была создана с помощью DemoCards.jl и Literate.jl.