Pergunta

Tenho um conjunto de imagens e quero fazer uma correspondência cruzada entre todas e exibir os resultados usando trackbars usando OpenCV 2.4.6 (pacote ROS Hydro).A parte de correspondência é feita usando um vetor de vetores de vetores de cv::DMatch-objects:

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

Como omitimos a correspondência de uma imagem consigo mesma (não faz sentido fazer isso) e como uma imagem de consulta pode não corresponder a todo o resto, cada conjunto de imagens de trem correspondentes para uma imagem de consulta pode ter um tamanho diferente do resto.Observe que, da maneira correta, eu combino um par de imagens duas vezes, o que obviamente não é o ideal (especialmente porque usei um matcher BruteForce com verificação cruzada ativada, o que basicamente significa que combino um par de imagens 4 vezes! ) mas por enquanto é isso.Para evitar o desenho instantâneo de pares de imagens correspondentes, preenchi um vetor de vetores de cv::Mat-objects.Cada cv::Mat representa a imagem de consulta atual e alguns coincide imagem do trem (eu a preencho usando cv::drawMatches()):

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

Observação:No exemplo acima cv::Mat[0,3] significa cv::Mat que armazena o produto de cv::drawMatches() usando image[0] e image[3].

Aqui estão as configurações da GUI:

  • Janela principal:aqui eu exibo a imagem da consulta atual.Usando um trackbar - vamos chamá-lo de TRACK_QUERY - eu itero através de cada imagem do meu conjunto.
  • Janela secundária:aqui eu exibo o par correspondente (query,train), onde a combinação entre a posição do controle deslizante de TRACK_QUERY e a posição do controle deslizante de outro trackbar nesta janela - vamos chamá-lo de TRACK_TRAIN - me permite iterar por todos os cv::Mat -match-images para a imagem de consulta atual.

O problema aqui vem do fato de que cada consulta pode ter um número variável de imagens de trem correspondentes.Meu TRACK_TRAIN deve ser capaz de se ajustar ao número de imagens de trem correspondentes, que é o número de elementos em cada cv::Mat-vector para a imagem de consulta atual.Infelizmente, até agora não consegui encontrar uma maneira de fazer isso.O cv::createTrackbar() requer um parâmetro de contagem, que pelo que vejo define o limite do controle deslizante do trackbar e não pode ser alterado posteriormente.Corrija-me se eu estiver errado, pois é exatamente isso que está me incomodando.Uma solução possível (menos elegante e envolvendo várias verificações para evitar erros fora do intervalo) é pegar o tamanho do maior conjunto de imagens de trem correspondentes e usá-lo como limite para meu TRACK_TRAIN.Eu gostaria de evitar fazer isso, se possível.Outra solução possível envolve a criação de um trackbar por imagem de consulta com o intervalo de valores apropriado e a troca de cada uma em minhas janelas secundárias de acordo com a imagem de consulta selecionada.Por enquanto, esse parece ser o caminho mais fácil, mas representa uma grande sobrecarga de trackbars, sem mencionar o fato de que nunca ouvi falar do OpenCV, permitindo ocultar controles da GUI.Aqui estão dois exemplos que podem esclarecer um pouco mais as coisas:

Exemplo 1:Na janela principal seleciono a imagem 2 usando TRACK_QUERY.Para esta imagem consegui combinar 5 outras imagens do meu conjunto.Digamos que sejam as imagens 4, 10, 17, 18 e 20.A janela secundária é atualizada automaticamente e mostra a correspondência entre a imagem 2 e a imagem 4 (a primeira no subconjunto de imagens de trem correspondentes).TRACK_TRAIN deve ir de 0 a 4.Mover o controle deslizante em ambas as direções me permite passar pelas imagens 4, 10, 17, 18 e 20 atualizando cada vez que a janela secundária.

Exemplo 2:Na janela principal seleciono a imagem 7 usando TRACK_QUERY.Para esta imagem consegui combinar 3 outras imagens do meu conjunto.Digamos que sejam as imagens 0, 1, 11 e 19.A janela secundária é atualizada automaticamente e mostra a correspondência entre a imagem 2 e a imagem 0 (a primeira no subconjunto de imagens de trem correspondentes).TRACK_TRAIN deve ir de 0 a 2.Mover o controle deslizante em ambas as direções me permite passar pelas imagens 0, 1, 1 e 19, atualizando cada vez que a janela secundária.

Se você tiver alguma dúvida, fique à vontade para perguntar e eu responderei da melhor maneira possível.Desde já, obrigado!

PS:Infelizmente, do jeito que o pacote ROS é, ele tem o mínimo do que o OpenCV pode oferecer.Sem integração com Qt, sem OpenMP, sem OpenGL, etc.

Foi útil?

Solução

Depois de fazer mais pesquisas, tenho certeza de que isso não é possível no momento.É por isso que implementei a primeira proposição que dei na minha pergunta - use o vetor de correspondência com o maior número de correspondências para determinar um tamanho máximo para o trackbar e, em seguida, use algumas verificações para evitar exceções fora do intervalo.Abaixo há uma descrição mais ou menos detalhada de como tudo funciona.Como o procedimento de correspondência no meu código envolve algumas verificações adicionais que não dizem respeito ao problema em questão, vou ignorá-lo aqui.Observe que em um determinado conjunto de imagens que queremos corresponder, refiro-me a uma imagem como imagem-objeto quando essa imagem (exemplo:cartão) atualmente corresponde a uma imagem de cena (exemplo:um conjunto de cartas) - nível superior do vetor de correspondências (veja abaixo) e igual ao índice em processedImages (veja abaixo).Acho a notação de trem/consulta no OpenCV um tanto confusa.Esta notação de cena/objeto é retirada de http://docs.opencv.org/doc/tutorials/features2d/feature_homography/feature_homography.html.Você pode alterar ou trocar a notação ao seu gosto, mas certifique-se de alterá-la em todos os lugares de acordo, caso contrário você pode acabar com alguns resultados estranhos.

// 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
// ...

A parte complicada é o trackbar na segunda janela responsável por acessar as imagens correspondentes à nossa imagem atualmente selecionada na janela “Imagens”.Como expliquei acima, configurei o trackbar "Matches:" na janela "Matches for current image" para ter um intervalo de 0 a (sceneWithMaxMatches-1).No entanto, nem todas as imagens têm a mesma quantidade de correspondências com o restante do conjunto de imagens (aplica-se dez vezes se você tiver feito alguma filtragem adicional para garantir correspondências confiáveis, por exemplo, explorando as propriedades da homografia, teste de proporção, verificação de distância mínima/máxima, etc. .).Como não consegui encontrar uma maneira de ajustar dinamicamente o alcance do trackbar, precisei de uma validação do índice.Caso contrário, para algumas imagens e suas correspondências, o aplicativo lançará uma exceção fora do intervalo.Isso se deve ao simples fato de que para algumas correspondências tentamos acessar um vetor de correspondência com um índice maior que seu tamanho menos 1 porque cv::getTrackbarPos() vai até (sceneWithMaxMatches - 1).Se a posição da trackbar sair do intervalo para o vetor atualmente selecionado com correspondências, simplesmente defino a imagem matchDraw em "Correspondências para a imagem atual" até o último no vetor.Aqui eu exploro o fato de que a indexação não pode ir abaixo de zero, bem como a posição do trackbar, portanto não há necessidade de verificar isso, mas apenas o que vem depois da posição inicial 0.Se este não for o seu caso, verifique também o limite inferior e não apenas o superior.

Espero que isto ajude!

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top