OpenCV - Диапазон значений переменной трекбара

У меня есть набор изображений, и я хочу сделать перекрестное совпадение между всеми и отобразить результаты, используя трекбары, используя OpenCV 2.4.6 (пакет ROS Hydro). Соответствующая часть выполняется с использованием вектора векторов векторов cv::DMatch-objects:

image[0] --- image[3] -------- image[8] ------ ...
   |             |                 |
   |         cv::DMatch-vect   cv::DMatch-vect
   |
image[1] --- ...
   |
image[2] --- ...
   |
  ...
   |
image[N] --- ...

Поскольку мы опускаем сопоставление изображения с самим собой (в этом нет никакого смысла) и поскольку изображение запроса может не совпадать со всеми остальными, каждый набор сопоставленных изображений поезда для изображения запроса может иметь размер, отличный от остальных. Обратите внимание, что при правильной реализации я фактически сопоставляю пару изображений дважды, что, конечно, не оптимально (тем более что я использовал средство сравнения BruteForce с включенной перекрестной проверкой, что в основном означает, что я сопоставляю пару изображений 4 раза!) но пока это все. Чтобы избежать наглядного отображения подходящих пар изображений, я заполнил вектор векторов объектов cv::Mat. Каждый cv::Mat представляет текущее изображение запроса и какое-то соответствующее изображение поезда (я заполняю его с помощью cv::drawMatches()):

image[0] --- cv::Mat[0,3] ---- cv::Mat[0,8] ---- ...
   |
image[1] --- ...
   |
image[2] --- ...
   |
  ...
   |
image[N] --- ...

Примечание: в приведенном выше примере cv::Mat[0,3] обозначает cv::Mat, который хранит произведение cv:: drawMatches (), используя image[0] и image[3].

Вот настройки GUI:

  • Главное окно: здесь я отображаю текущее изображение запроса. Используя трекбар - назовем его TRACK_QUERY - я перебираю каждое изображение в моем наборе.
  • Вторичное окно: здесь я отображаю совпавшую пару (запрос, поезд), где комбинация между положением ползунка TRACK_QUERY и положением ползунка другого трекбара в этом окне - назовем его TRACK_TRAIN - позволяет мне перебирать все cv::Mat-match-images для текущего изображения запроса.

Проблема здесь заключается в том, что каждый запрос может иметь переменное число подходящих изображений поездов. Мой TRACK_TRAIN должен быть в состоянии приспособиться к количеству совпавших изображений поездов, то есть количеству элементов в каждом cv::Mat-векторе для текущего изображения запроса. К сожалению, до сих пор я не смог найти способ сделать это. Для cv::createTrackbar() требуется параметр count, который, как я вижу, устанавливает предел ползунка трекбара и не может быть изменен позже. Поправьте меня, если я ошибаюсь, потому что это именно то, что беспокоит меня. Возможное решение (менее элегантное и включающее различные проверки, чтобы избежать ошибок вне допустимого диапазона) состоит в том, чтобы взять размер самого большого набора подходящих изображений поездов и использовать его в качестве ограничения для моего TRACK_TRAIN. Я хотел бы избежать этого, если это возможно. Другое возможное решение состоит в том, чтобы создать трекбар для каждого изображения запроса с соответствующим диапазоном значений и поменять местами каждое в моих дополнительных окнах в соответствии с выбранным изображением запроса. На данный момент это, кажется, более простой способ, но он требует больших затрат на трекбары, не говоря уже о том факте, что я не слышал об OpenCV, позволяющем скрывать элементы управления графическим интерфейсом. Вот два примера, которые могут прояснить ситуацию немного подробнее:

Пример 1: в главном окне я выбираю изображение 2, используя TRACK_QUERY. Для этого изображения мне удалось сопоставить 5 других изображений из моего набора. Допустим, это изображения 4, 10, 17, 18 и 20. Вспомогательное окно обновляется автоматически и показывает мне соответствие между изображением 2 и изображением 4 (первое в подмножестве совпавших изображений поездов). Значение TRACK_TRAIN должно быть от 0 до 4. Перемещение ползунка в обоих направлениях позволяет мне просматривать изображения 4, 10, 17, 18 и 20, обновляя каждый раз дополнительное окно.

Пример 2: в главном окне я выбираю изображение 7, используя TRACK_QUERY. Для этого изображения мне удалось сопоставить 3 других изображения из моего набора. Допустим, это изображения 0, 1, 11 и 19. Вторичное окно обновляется автоматически и показывает мне соответствие между изображением 2 и изображением 0 (первое в подмножестве совпавших изображений поездов). Значение TRACK_TRAIN должно быть от 0 до 2. Перемещение ползунка в обоих направлениях позволяет мне просматривать изображения 0, 1, 1 и 19, обновляя каждый раз дополнительное окно.

Если у вас есть какие-либо вопросы, не стесняйтесь спрашивать, и я отвечу на них так же, как я могу. Заранее спасибо!

PS: К сожалению, способ, которым пакет ROS - это тот самый минимум, что может предложить OpenCV. Нет интеграции с Qt, нет OpenMP, нет OpenGL и т. Д.

1 ответ

Решение

После нескольких исследований я уверен, что это невозможно. Вот почему я реализовал первое предложение, которое я дал в своем вопросе - используйте вектор совпадений с наибольшим количеством совпадений в нем, чтобы определить максимальный размер трекбара, а затем используйте некоторую проверку, чтобы избежать исключений вне диапазона. Ниже приведено более или менее подробное описание того, как все это работает. Поскольку процедура сопоставления в моем коде включает в себя некоторые дополнительные проверки, которые не касаются рассматриваемой проблемы, я пропущу ее здесь. Обратите внимание, что в данном наборе изображений, которые мы хотим сопоставить, я называю изображение объектом-изображением, когда это изображение (например, карта) в настоящее время сопоставляется с изображением сцены (например, набор карт) - верхний уровень совпадает с вектором (см. ниже) и равен индексу в ProcessingImages (см. ниже). Я нахожу запись поезда / запроса в OpenCV несколько запутанной. Это обозначение сцены / объекта взято с http://docs.opencv.org/doc/tutorials/features2d/feature_homography/feature_homography.html. Вы можете изменить или поменять нотацию по своему вкусу, но убедитесь, что вы меняете ее везде соответственно, иначе вы можете получить странные результаты.

// stores all the images that we want to cross-match
std::vector<cv::Mat> processedImages;
// stores keypoints for each image in processedImages
std::vector<std::vector<cv::Keypoint> > keypoints;
// stores descriptors for each image in processedImages 
std::vector<cv::Mat> descriptors;

// fill processedImages here (read images from files, convert to grayscale, undistort, resize etc.), extract keypoints, compute descriptors
// ...

// I use brute force matching since I also used ORB, which has binary descriptors and HAMMING_NORM is the way to go
cv::BFmatcher matcher;
// matches contains the match-vectors for each image matched to all other images in our set
// top level index matches.at(X) is equal to the image index in processedImages
// middle level index matches.at(X).at(Y) gives the match-vector for the Xth image and some other Yth from the set that is successfully matched to X
std::vector<std::vector<std::vector<cv::DMatch> > > matches;
// contains images that store visually all matched pairs
std::vector<std::vector<cv::Mat> > matchesDraw;

// fill all the vectors above with data here, don't forget about matchesDraw
// stores the highest count of matches for all pairs - I used simple exclusion by simply comparing the size() of the current std::vector<cv::DMatch> vector with the previous value of this variable
long int sceneWithMaxMatches = 0;
// ...

// after all is ready do some additional checking here in order to make sure the data is usable in our GUI. A trackbar for example requires AT LEAST 2 for its range since a range (0;0) doesn't make any sense
if(sceneWithMaxMatches < 2)
  return -1;

// in this window show the image gallery (scene-images); the user can scroll through all image using a trackbar
cv::namedWindow("Images", CV_GUI_EXPANDED | CV_WINDOW_AUTOSIZE);
// just a dummy to store the state of the trackbar 
int imagesTrackbarState = 0;
// create the first trackbar that the user uses to scroll through the scene-images
// IMPORTANT: use processedImages.size() - 1 since indexing in vectors is the same as in arrays - it starts from 0 and not reducing it by 1 will throw an out-of-range exception
cv::createTrackbar("Images:", "Images", &imagesTrackbarState, processedImages.size() - 1, on_imagesTrackbarCallback, NULL);
// in this window we show the matched object-images relative to the selected image in the "Images" window
cv::namedWindow("Matches for current image", CV_WINDOW_AUTOSIZE);
// yet another dummy to store the state of the trackbar in this new window
int imageMatchesTrackbarState = 0;
// IMPORTANT: again since sceneWithMaxMatches stores the SIZE of a vector we need to reduce it by 1 in order to be able to use it for the indexing later on
cv::createTrackbar("Matches:", "Matches for current image", &imageMatchesTrackbarState, sceneWithMaxMatches - 1, on_imageMatchesTrackbarCallback, NULL);

while(true)
{
  char key = cv::waitKey(20);
  if(key == 27)
    break;

  // from here on the magic begins
  // show the image gallery; use the position of the "Images:" trackbar to call the image at that position
  cv::imshow("Images", processedImages.at(cv::getTrackbarPos("Images:", "Images")));

  // store the index of the current scene-image by calling the position of the trackbar in the "Images:" window
  int currentSceneIndex = cv::getTrackbarPos("Images:", "Images");
  // we have to make sure that the match of the currently selected scene-image actually has something in it
  if(matches.at(currentSceneIndex).size())
  {
    // store the index of the current object-image that we have matched to the current scene-image in the "Images:" window
    int currentObjectIndex = cv::getTrackbarPos("Matches:", "Matches for current image");
    cv::imshow(
            "Matches for current image",
            matchesDraw.at(currentSceneIndex).at(currentObjectIndex < matchesDraw.at(currentSceneIndex).size() ? // is the current object index within the range of the matches for the current object and current scene
            currentObjectIndex : // yes, return the correct index
            matchesDraw.at(currentSceneIndex).size() - 1));  // if outside the range show the last matched pair!
  }
}

// do something else
// ...

Сложная часть - это трекбар во втором окне, отвечающий за доступ к сопоставленным изображениям к нашему выбранному в данный момент изображению в окне "Изображения". Как я объяснил выше, я установил трекбар "Matches:" в окне "Matches for current image" в диапазоне от 0 до (sceneWithMaxMatches-1). Однако не все изображения имеют одинаковое количество совпадений с остальными в наборе изображений (применяется в десять раз, если вы выполнили некоторую дополнительную фильтрацию для обеспечения надежных совпадений, например, с использованием свойств гомографии, теста отношения, проверки минимального / максимального расстояния и т. Д..). Поскольку я не смог найти способ динамически регулировать диапазон трекбара, мне потребовалась проверка индекса. В противном случае для некоторых изображений и их совпадений приложение выдаст исключение вне диапазона. Это связано с тем простым фактом, что для некоторых совпадений мы пытаемся получить доступ к вектору совпадений с индексом, превышающим его размер минус 1, потому что cv::getTrackbarPos() идет до (sceneWithMaxMatches - 1). Если позиция трекбара выходит за пределы диапазона для текущего выбранного вектора со совпадениями, я просто устанавливаю для matchDraw-image в "Совпадения для текущего изображения" самое последнее значение в векторе. Здесь я использую тот факт, что индексирование не может опуститься ниже нуля, а также положение трекбара, поэтому нет необходимости проверять это, а только то, что следует после начальной позиции 0. Если это не ваш случай, убедитесь, что вы проверили нижнюю связано тоже, а не только верхнее.

Надеюсь это поможет!

Другие вопросы по тегам