【问题标题】:Error in calculating perspective transform for opencv in Matlab在 Matlab 中计算 opencv 透视变换时出错
【发布时间】:2013-12-17 07:25:09
【问题描述】:

我正在尝试使用 mexopencv 重新编码 feature matching and homography .Mexopencv 将 OpenCV 视觉工具箱移植到 Matlab 中。

我在 Matlab 中使用 OpenCV 工具箱的代码:

function hello

    close all;clear all;

    disp('Feature matching demo, press key when done');

    boxImage = imread('D:/pic/500_1.jpg');

    boxImage = rgb2gray(boxImage);

    [boxPoints,boxFeatures] = cv.ORB(boxImage);

    sceneImage = imread('D:/pic/100_1.jpg');

    sceneImage = rgb2gray(sceneImage);

    [scenePoints,sceneFeatures] = cv.ORB(sceneImage);

    if (isempty(scenePoints)|| isempty(boxPoints)) 
        return;
    end;


    matcher = cv.DescriptorMatcher('BruteForce');
    matches = matcher.match(boxFeatures,sceneFeatures);


    %Box contains pixels coordinates where there are matches
    box = [boxPoints([matches(2:end).queryIdx]).pt];

    %Scene contains pixels coordinates where there are matches
    scene = [scenePoints([matches(2:end).trainIdx]).pt];

    %Please refer to http://stackoverflow.com/questions/4682927/matlab-using-mat2cell

    %Box arrays contains coordinates the form [ (x1,y1), (x2,y2) ...]
    %after applying mat2cell function
    [nRows, nCols] = size(box);
    nSubCols = 2;
    box = mat2cell(box,nRows,nSubCols.*ones(1,nCols/nSubCols));

    %Scene arrays contains coordinates the form [ (x1,y1), (x2,y2) ...]
    %after applying mat2cell function

    [nRows, nCols] = size(scene);
    nSubCols = 2;
    scene = mat2cell(scene,nRows,nSubCols.*ones(1,nCols/nSubCols));

    %Finding homography between box and scene
    H = cv.findHomography(box,scene);

    boxCorners = [1, 1;...                           % top-left
        size(boxImage, 2), 1;...                 % top-right
        size(boxImage, 2), size(boxImage, 1);... % bottom-right
        1, size(boxImage, 1)];

  %Fine until this point , problem starts with perspectiveTransform   
  sceneCorners= cv.perspectiveTransform(boxCorners,H); 

end

错误:

    Error using cv.perspectiveTransform
Unexpected Standard exception from MEX file.
What()
is:C:\slave\builds\WinInstallerMegaPack\src\opencv\modules\core\src\matmul.cpp:1926:
error: (-215) scn + 1 == m.cols && (depth == CV_32F || depth == CV_64F)

..

Error in hello (line 58)
  sceneCorners= cv.perspectiveTransform(boxCorners,H);

问题从检查perspectiveTranform(boxCorners, H) 开始,直到找到homography 很好。另请注意,在计算样本和场景的匹配坐标时,我从2:endbox = [boxPoints([matches(2:end).queryIdx]).pt] 索引,因为访问第一个元素的queryIdx 会产生无法访问的第零个位置。不过,我认为,这不会是一个问题。无论如何,我期待我的解决方案的答案。谢谢。

PS:这是我的原始帖子的编辑版本。我在下面收到的解决方案还不够充分,并且该错误不断重复。

第二次更新:

根据@Amro,我已经更新了我的代码,如下。内点给出了很好的响应,但是计算透视变换的坐标不知何故被扭曲了。

function hello
    close all; clear all; clc;

    disp('Feature matching with ORB');

    %Feature detector and extractor for object
    imgObj = imread('D:/pic/box.png');
    %boxImage = rgb2gray(boxImage);
    [keyObj,featObj] = cv.ORB(imgObj);

    %Feature detector and extractor for scene
    imgScene = imread('D:/pic/box_in_scene.png');
    %sceneImage = rgb2gray(sceneImage);
    [keyScene,featScene] = cv.ORB(imgScene);

    if (isempty(keyScene)|| isempty(keyObj)) 
        return;
    end;

    matcher = cv.DescriptorMatcher('BruteForce-HammingLUT');
    m = matcher.match(featObj,featScene);

    %im_matches = cv.drawMatches(boxImage, boxPoints, sceneImage, scenePoints,m);

    % extract keypoints from the filtered matches
    % (C zero-based vs. MATLAB one-based indexing)
    ptsObj = cat(1, keyObj([m.queryIdx]+1).pt);
    ptsObj = num2cell(ptsObj, 2);
    ptsScene = cat(1, keyScene([m.trainIdx]+1).pt);
    ptsScene = num2cell(ptsScene, 2);

    % compute homography
    [H,inliers] = cv.findHomography(ptsObj, ptsScene, 'Method','Ransac');

    % remove outliers reported by RANSAC
    inliers = logical(inliers);
    m = m(inliers);

    % show the final matches
    imgMatches = cv.drawMatches(imgObj, keyObj, imgScene, keyScene, m, ...
    'NotDrawSinglePoints',true);
    imshow(imgMatches);

    % apply the homography to the corner points of the box
    [h,w] = size(imgObj);
    corners = permute([0 0; w 0; w h; 0 h], [3 1 2]);
    p = cv.perspectiveTransform(corners, H)
    p = permute(p, [2 3 1])
    p = bsxfun(@plus, p, [size(imgObj,2) 0]);

    % draw lines between the transformed corners (the mapped object)
    opts = {'Color',[0 255 0], 'Thickness',4};
    imgMatches = cv.line(imgMatches, p(1,:), p(2,:), opts{:});
    imgMatches = cv.line(imgMatches, p(2,:), p(3,:), opts{:});
    imgMatches = cv.line(imgMatches, p(3,:), p(4,:), opts{:});
    imgMatches = cv.line(imgMatches, p(4,:), p(1,:), opts{:});
    imshow(imgMatches)
    title('Matches & Object detection')

end

输出很好,但是,perspectiveTranform 没有给出适合问题的正确坐标。 到目前为止我的输出:

第三次更新:

我已经运行了所有代码,并且对单应性很好。然而,一个角落里的情况让我很难受。 如果我做 imgObj = imread('D:/pic/box.png')imgScene = imread('D:/pic/box_in_scene.png') ,我得到很好的单应矩形,但是,当我做 imgScene = imread('D:/pic/box.png') 时,即对象和场景是 same ,我得到这个错误 -

Error using cv.findHomography
Unexpected Standard exception from MEX file.
What()
is:C:\slave\builds\WinInstallerMegaPack\src\opencv\modules\calib3d\src\fundam.cpp:1074:
error: (-215) npoints >= 0 && points2.checkVector(2) == npoints && points1.type() ==
points2.type()

..

Error in hello (line 37)
    [H,inliers] = cv.findHomography(ptsObj, ptsScene, 'Method','Ransac');

现在,我过去遇到过这个错误,当ptsObjptsScene 的数量较少时会发生这种情况,例如,当场景只是一个白/黑屏幕时,该场景的关键点是零 。在这个特殊问题中,有大量的ptsObjptsScene。问题出在哪里。我已经使用SURFan 测试了这段代码,同样的错误正在重新出现。

【问题讨论】:

    标签: matlab opencv image-processing computer-vision perspectivecamera


    【解决方案1】:

    尽量使用H的转置

    我们将单应矩阵计算为:x'=H*x,但在 MATLAB 中,它看起来像这样的类型:x'^{T}=x^{T}*H^{T} (x'^{ T} 表示 x' ) 的转置。所以,转置你的单应性然后再试一次。

    【讨论】:

    • 只是为了明确透视变换函数的输入需要在上述设置中转置。
    【解决方案2】:

    图像处理工具箱和计算机视觉系统工具箱中的函数使用与您在大多数教科书中看到的不同的惯例来转换点。在大多数教科书中,点都用列向量表示。所以你的变换看起来像这样:H * x,其中 H 是变换矩阵,x 是一个矩阵,其列是点。

    另一方面,在 MATLAB 中,点通常表示为行向量。所以你必须切换乘法的顺序并转置H:x'* H'。

    最后,如果您有适用于 MATLAB 的计算机视觉系统 Toolobx,您可以用更少的代码解决您的问题。看看这个example

    【讨论】:

    • 感谢链接中的示例,非常感谢。只是另一个查询,我想使用 opencv 将 surf 检测器与图像数据库进行匹配,是否有任何教程。我知道 OpenCV 网站提供了一个示例,将一张测试图像与一张火车图像进行匹配。我希望它适用于多个训练图像。链接是docs.opencv.org/doc/tutorials/features2d/feature_homography/…
    • 执行此操作的简单方法是将测试图像与数据库中的每个图像成对匹配。更好的方法是将数据库图像中的特征描述符存储在 KD 树中并使用近似最近邻。 OpenCV 包含一个叫做 FLANN 的东西。 MATLAB 的统计工具箱有一个 KDTreeSearcher 对象。
    • 我不想成为用户吸血鬼,但是你能不能给我一些关于这个问题的反馈,任何有思想的 cmets 都会做。底部评论有图片。链接:stackoverflow.com/questions/20329255/…
    • 非常感谢您的链接。我几乎设法完成了我的项目。让我们看看我的老师是怎么想的。
    • @Dima:不管怎样,计算机视觉工具箱中的许多功能都基于 OpenCV(Mathworks 构建了自己的 MEX 包装器):)
    【解决方案3】:

    几点说明:

    • 匹配器返回从零开始的索引(以及由于 OpenCV 在 C++ 中实现的各种其他函数)。因此,如果您想获得相应的关键点,您必须调整一(MATLAB 数组是基于一的)。 mexopencvintentionally 不会为此自动调整。

    • cv.findHomography MEX 函数接受点作为大小为1xNx2 的数值数组(例如:cat(3, [x1,x2,...], [y1,y2,...]))或作为N 大小的二元素向量元胞数组(即{[x1,y1], [x2,y2], ...})。在这种情况下,我不确定您的代码是否正确打包了这些点,无论哪种方式都可以变得更简单..

    这是从 C++ 翻译成 MATLAB 的完整 demo

    % input images
    imgObj = imread('box.png');
    imgScene = imread('box_in_scene.png');
    
    % detect keypoints and calculate descriptors using SURF
    detector = cv.FeatureDetector('SURF');
    keyObj = detector.detect(imgObj);
    keyScene = detector.detect(imgScene);
    
    extractor = cv.DescriptorExtractor('SURF');
    featObj = extractor.compute(imgObj, keyObj);
    featScene = extractor.compute(imgScene, keyScene);
    
    % match descriptors using FLANN
    matcher = cv.DescriptorMatcher('FlannBased');
    m = matcher.match(featObj, featScene);
    
    % keep only "good" matches (whose distance is less than k*min_dist )
    dist = [m.distance];
    m = m(dist < 3*min(dist));
    
    % extract keypoints from the filtered matches
    % (C zero-based vs. MATLAB one-based indexing)
    ptsObj = cat(1, keyObj([m.queryIdx]+1).pt);
    ptsObj = num2cell(ptsObj, 2);
    ptsScene = cat(1, keyScene([m.trainIdx]+1).pt);
    ptsScene = num2cell(ptsScene, 2);
    
    % compute homography
    [H,inliers] = cv.findHomography(ptsObj, ptsScene, 'Method','Ransac');
    
    % remove outliers reported by RANSAC
    inliers = logical(inliers);
    m = m(inliers);
    
    % show the final matches
    imgMatches = cv.drawMatches(imgObj, keyObj, imgScene, keyScene, m, ...
        'NotDrawSinglePoints',true);
    imshow(imgMatches)
    
    % apply the homography to the corner points of the box
    [h,w] = size(imgObj);
    corners = permute([0 0; w 0; w h; 0 h], [3 1 2]);
    p = cv.perspectiveTransform(corners, H);
    p = permute(p, [2 3 1]);
    p = bsxfun(@plus, p, [size(imgObj,2) 0]);
    
    % draw lines between the transformed corners (the mapped object)
    opts = {'Color',[0 255 0], 'Thickness',4};
    imgMatches = cv.line(imgMatches, p(1,:), p(2,:), opts{:});
    imgMatches = cv.line(imgMatches, p(2,:), p(3,:), opts{:});
    imgMatches = cv.line(imgMatches, p(3,:), p(4,:), opts{:});
    imgMatches = cv.line(imgMatches, p(4,:), p(1,:), opts{:});
    imshow(imgMatches)
    title('Matches & Object detection')
    

    现在您可以尝试使用其他一种算法进行特征检测/提取(在您的情况下为 ORB)。请记住,您可能需要调整上面的一些参数以获得良好的结果(例如用于控制要保留多少关键点匹配的乘数)。


    编辑:

    就像我说的,在计算机视觉领域没有一种适合所有解决方案的解决方案。您需要通过调整各种算法参数来进行实验,以便在数据上获得良好的结果。例如,ORB constructor 接受一堆选项。同样作为文档suggests,具有汉明距离的蛮力匹配器是ORB描述符的推荐匹配器。

    最后请注意,我指定RANSAC 鲁棒算法作为用于计算单应矩阵的方法;查看您发布的屏幕截图,您可以看到 outlier 匹配错误地指向场景中的黑色计算机视觉书。 RANSAC 方法的优点是即使在数据中存在大量异常值时也能准确地进行估计。 findHomography 使用的默认方法是使用所有可用的点。

    此外请注意,在您的情况下,用于估计单应性的一些控制点几乎是共线的,这可能对计算有很大的影响(有点像在数值上反转接近奇异的矩阵是一个坏主意)。

    如上所述,我在代码的相关部分下方突出显示,这些部分使用 ORB 描述符给了我很好的结果(其余部分与我之前发布的内容没有变化):

    % detect keypoints and calculate descriptors using ORB
    [keyObj,featObj] = cv.ORB(imgObj);
    [keyScene,featScene] = cv.ORB(imgScene);
    
    % match descriptors using brute force with Hamming distances
    matcher = cv.DescriptorMatcher('BruteForce-Hamming');
    m = matcher.match(featObj, featScene);
    
    % keep only "good" matches (whose distance is less than k*min_dist )
    dist = [m.distance];
    m = m(dist < 3*min(dist));
    

    我注意到您省略了最后一部分,我通过丢弃坏匹配项来过滤匹配项。您始终可以查看找到的匹配项的“距离”分布,并确定适当的阈值。这是我最初的:

    hist([m.distance])
    title('Distribution of match distances')
    

    您还可以根据响应值对原始关键点应用类似的过程,并相应地对这些点进行子采样:

    subplot(121), hist([keyObj.response]); title('box')
    subplot(122), hist([keyScene.response]); title('scene')
    

    HTH

    【讨论】:

    • 谢谢,让我尝试不同的品种,然后让你。
    • 我已经用 orb 完成了,透视变换的坐标被扭曲了,所以......在这里稍微分析一下。
    • @whoknows:显然 MEX 包装器有一些开销,但应该不会太糟糕。我看到的唯一可能的缺点是许多 OpenCV C++ 函数可以就地执行操作,但由于不允许 MEX 函数更改其输入(至少不是以记录的方式),因此将生成一个副本。 . 总体而言,优势在于,像 MATLAB 这样的语言可以让您快速制作此类​​应用程序的原型。至于实时性能,你必须实现它并自己看看:)
    • @whoknows:那是因为返回的匹配是完美的(一对一),所有距离都为零。因此,过滤步骤将修剪所有这些,导致cv.findHomograpy 失败,因为它得到零点...再次,如果您像我之前所做的那样查看直方图,您就会明白这个问题(它只会是一个峰值!) .显然,在应用上述“良好匹配”天真启发式时,您需要包括一些健全性检查:)
    • @whoknows:顺便说一句,修复非常简单,只需将测试更改为“小于或等于”(&lt;=):m = m(dist &lt;= 3*min(dist));
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-26
    • 2021-01-05
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-04-05
    相关资源
    最近更新 更多