Суть приложения в том, чтобы распознать изображение из уже установленного списка изображений. Для списка изображений были извлечены дескрипторы SIFT и сохранены в файлах. Здесь ничего интересного:
std::vector<cv::KeyPoint> detectedKeypoints;
cv::Mat objectDescriptors;
// Extract data
cv::SIFT sift;
sift.detect(image, detectedKeypoints);
sift.compute(image, detectedKeypoints, objectDescriptors);
// Save the file
cv::FileStorage fs(file, cv::FileStorage::WRITE);
fs << "descriptors" << objectDescriptors;
fs << "keypoints" << detectedKeypoints;
fs.release();
Затем устройство делает снимок. Таким же образом извлекаются дескрипторы SIFT. Теперь идея заключалась в том, чтобы сравнить дескрипторы с дескрипторами из файлов. Я делаю это, используя сопоставитель FLANN из OpenCV. Я пытаюсь количественно определить сходство, изображение за изображением. После просмотра всего списка у меня должно быть лучшее совпадение.
const cv::Ptr<cv::flann::IndexParams>& indexParams = new cv::flann::KDTreeIndexParams(1);
const cv::Ptr<cv::flann::SearchParams>& searchParams = new cv::flann::SearchParams(64);
// Match using Flann
cv::Mat indexMat;
cv::FlannBasedMatcher matcher(indexParams, searchParams);
std::vector< cv::DMatch > matches;
matcher.match(objectDescriptors, readDescriptors, matches);
После сопоставления я понимаю, что получаю список ближайших найденных расстояний между векторами признаков. Я нахожу минимальное расстояние и с его помощью могу подсчитать «хорошие совпадения» и даже получить список соответствующих точек:
// Count the number of mathes where the distance is less than 2 * min_dist
int goodCount = 0;
for (int i = 0; i < objectDescriptors.rows; i++)
{
if (matches[i].distance < 2 * min_dist)
{
++goodCount;
// Save the points for the homography calculation
obj.push_back(detectedKeypoints[matches[i].queryIdx].pt);
scene.push_back(readKeypoints[matches[i].trainIdx].pt);
}
}
Я показываю простые части кода только для того, чтобы их было легче понять, я знаю, что некоторые из них не должны быть здесь.
Продолжая, я надеялся, что простого подсчета количества хороших совпадений будет достаточно, но оказалось, что в основном я просто указываю на изображение с наибольшим количеством дескрипторов. После этого я попытался вычислить гомографию. Цель состояла в том, чтобы вычислить его и посмотреть, является ли это действительной гомографией или нет. Была надежда, что хорошее совпадение, и только хорошее совпадение, будет иметь омографию, являющуюся хорошим преобразованием. Создание гомографии было выполнено просто с помощью cv::findHomography для obj и scene, которые являются std::vector‹ cv:: Point2f>. Я проверил правильность гомографии, используя код, который нашел в Интернете:
bool niceHomography(cv::Mat H)
{
std::cout << H << std::endl;
const double det = H.at<double>(0, 0) * H.at<double>(1, 1) - H.at<double>(1, 0) * H.at<double>(0, 1);
if (det < 0)
{
std::cout << "Homography: bad determinant" << std::endl;
return false;
}
const double N1 = sqrt(H.at<double>(0, 0) * H.at<double>(0, 0) + H.at<double>(1, 0) * H.at<double>(1, 0));
if (N1 > 4 || N1 < 0.1)
{
std::cout << "Homography: bad first column" << std::endl;
return false;
}
const double N2 = sqrt(H.at<double>(0, 1) * H.at<double>(0, 1) + H.at<double>(1, 1) * H.at<double>(1, 1));
if (N2 > 4 || N2 < 0.1)
{
std::cout << "Homography: bad second column" << std::endl;
return false;
}
const double N3 = sqrt(H.at<double>(2, 0) * H.at<double>(2, 0) + H.at<double>(2, 1) * H.at<double>(2, 1));
if (N3 > 0.002)
{
std::cout << "Homography: bad third row" << std::endl;
return false;
}
return true;
}
Я не понимаю математику, стоящую за этим, поэтому во время тестирования я иногда заменял эту функцию простой проверкой, был ли определитель гомографии положительным. Проблема в том, что у меня были проблемы здесь. Гомографии были либо все плохие, либо хорошие, когда не должны были быть (когда я проверял только определитель).
Я решил, что должен использовать гомографию и для ряда точек просто вычислить их положение в целевом изображении, используя их положение в исходном изображении. Затем я бы сравнил эти средние расстояния, и в идеале я бы получил очень очевидное меньшее среднее расстояние в случае правильного изображения. Это не сработало совсем. Все расстояния были колоссальными. Я думал, что мог бы использовать гомографию наоборот, чтобы вычислить правильную позицию, но переключение obj и scene друг с другом дало аналогичные результаты.
Другие вещи, которые я пробовал: дескрипторы SURF вместо SIFT, BFMatcher (грубая сила) вместо FLANN, получение n наименьших расстояний для каждого изображения вместо числа в зависимости от минимального расстояния или получение расстояний в зависимости от глобальное максимальное расстояние. Ни один из этих подходов не дал мне определенных хороших результатов, и сейчас я чувствую себя застрявшим.
Моей единственной следующей стратегией будет повышение резкости изображений или даже превращение их в бинарные изображения с использованием некоторого локального порога или некоторых алгоритмов, используемых для сегментации. Я ищу любые предложения или ошибки, которые кто-либо может увидеть в моей работе.
Я не знаю, актуально ли это, но я добавил несколько изображений, на которых тестирую это. Много раз в тестовых изображениях большинство векторов SIFT исходили из кадра (более контрастного), чем из картины. Вот почему я думаю, что повышение резкости изображений может сработать, но я не хочу углубляться, если что-то, что я сделал ранее, окажется неправильным.
Галерея изображений находится здесь с описаниями в заголовках. Изображения имеют довольно высокое разрешение, пожалуйста, просмотрите, если это может дать некоторые подсказки.