OpenCV - 轨道栏的可变值范围

时间:2014-06-03 19:07:00

标签: variables opencv range trackbar

我有一组图像,想要在所有图像之间进行交叉匹配,并使用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-objects的向量向量。每个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,它使用image [0]和image [c]来存储cv :: drawMatches()的乘积。 3]

以下是GUI设置:

  • 主窗口:此处显示当前查询图像。使用轨迹栏 - 让我们称之为TRACK_QUERY - 我遍历集合中的每个图像。
  • 辅助窗口:在这里我显示匹配对(查询,火车),其中TRACK_QUERY滑块的位置与此窗口中另一个轨迹栏滑块位置的组合 - 让我们称之为TRACK_TRAIN - 允许我迭代当前查询图像的所有cv :: Mat-match-images。

这里的问题来自于每个查询可以具有可变数量的匹配的火车图像。我的TRACK_TRAIN应该能够调整到匹配的火车图像的数量,即当前查询图像的每个cv :: Mat-vector中的元素数量。可悲到目前为止,我无法找到办法做到这一点。 cv :: createTrackbar()需要一个count参数,从我看到的设置轨道栏滑块的限制, 不能 稍后更改。如果我错了,请纠正我,因为这正是困扰我的事情。一个可能的解决方案(不太优雅并且涉及各种检查以避免超出范围的错误)是获取最大匹配列车图像集的大小并将其用作我的TRACK_TRAIN的限制。如果可能的话,我想避免这样做。另一种可能的解决方案是使用适当的值范围为每个查询图像创建一个轨迹栏,并根据所选的查询图像在我的辅助窗口中交换每个轨迹栏。现在这似乎是更简单的方法,但是轨道栏的开销很大,更不用说我没有听说过允许你隐藏GUI控件的OpenCV。这里有两个例子可以更清楚地说明一些事情:

示例1:   在主窗口中,我使用TRACK_QUERY选择图像2。对于这个图像,我设法匹配我的集合中的其他5个图像。假设那些是图像4,10,17,18和20.辅助窗口自动更新并向我显示图像2和图像4之间的匹配(首先在匹配的火车图像的子集中)。 TRACK_TRAIN必须从0到4.在两个方向上移动滑块允许我在每次辅助窗口时更新图像4,10,17,18和20。

示例2:   在主窗口中,我使用TRACK_QUERY选择图像7。对于这个图像,我设法匹配我的集合中的其他3个图像。假设那些是图像0,1,11和19.辅助窗口自动更新并向我显示图像2和图像0之间的匹配(首先在匹配的火车图像的子集中)。 TRACK_TRAIN必须从0到2.在两个方向上移动滑块允许我在每次辅助窗口时更新图像0,1,1和19。

如果您有任何问题可以随意提出,我会尽可能地回答。提前谢谢!

PS:可悲的是,ROS套装的方式是OpenCV可以提供的最低限度。没有Qt集成,没有OpenMP,没有OpenGL等。

1 个答案:

答案 0 :(得分:0)

在做了一些研究之后,我很确定这目前是不可能的。这就是为什么我实现了我在我的问题中给出的第一个命题 - 使用匹配数量最多的匹配向量来确定跟踪栏的最大大小,然后使用一些检查来避免超出范围的异常。下面有一个或多或少的详细描述它是如何工作的。由于我的代码中的匹配过程涉及一些与手头问题无关的额外检查,因此我将在此处跳过它。请注意,在我们要匹配的给定图像集中,当图像(例如:卡片)当前与场景图像(例如:一组卡片)匹配时,我将图像称为对象图像 - 顶层matches-vector(见下文)并且等于processedImages中的索引(见下文)。我发现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
// ...

棘手的部分是第二个窗口中的轨迹栏,负责在“图像”窗口中访问匹配的图像到我们当前选择的图像。正如我上面所解释的,我在“当前图像的匹配”窗口中设置了轨道栏“匹配:”,其范围从0到(sceneWithMaxMatches-1)。然而,并非所有图像与图像集中的其余图像具有相同的匹配量(如果您已经进行了一些额外的过滤以确保可靠的匹配,则应用十倍,例如通过利用单应性,比率测试,最小/最大距离检查等的属性)。因为我无法找到动态调整轨迹栏范围的方法,所以我需要对索引进行验证。否则,对于某些图像及其匹配,应用程序将抛出超出范围的异常。这是由于一些简单的事实,对于某些匹配,我们尝试访问索引大于它的大小减去1的匹配向量,因为cv :: getTrackbarPos()一直到(sceneWithMaxMatches - 1)。如果轨迹栏的位置超出了当前所选矢量的匹配范围,我只需将“匹配当前图像的匹配”中的matchDraw图像设置为矢量中的最后一个。在这里我利用了索引不能低于零以及轨迹栏位置的事实,所以不需要检查这个但只需要检查初始位置0之后的内容。如果不是这种情况,请确保检查较低绑定也不仅仅是上层。

希望这有帮助!