Hausdorff 距离目标检测

Hausdorff Distance Object Detection

我一直在努力尝试实现描述的大纲算法here and here

论文的总体思路是确定二值图像的Hausdorff距离,并用它从测试图像中找到模板图像。

对于模板匹配,建议构建 image pyramids 以及滑动 windows,您将使用它来滑过测试图像以进行检测。我也能做到这两点。

我对如何从这里继续前进感到困惑。我是否将模板滑过来自不同金字塔层的测试图像?还是模板上的测试图像?关于滑动 window,is/are 它们意味着测试或模板图像的 ROI?

简而言之,我有一些拼图,但不知道该从哪个方向解开这个拼图

int distance(vector<Point>const& image, vector<Point>const& tempImage)
{
    int maxDistance = 0;

    for(Point imagePoint: image)
    {
        int minDistance = numeric_limits<int>::max();

        for(Point tempPoint: tempImage)
        {
            Point diff = imagePoint - tempPoint;
            int length = (diff.x * diff.x) + (diff.y * diff.y);

            if(length < minDistance) minDistance = length;
            if(length == 0) break;
        }
        maxDistance += minDistance;
    }
    return maxDistance;
}

double hausdorffDistance(vector<Point>const& image, vector<Point>const& tempImage)
{
    double maxDistImage = distance(image, tempImage);
    double maxDistTemp = distance(tempImage, image);

    return sqrt(max(maxDistImage, maxDistTemp));
}

vector<Mat> buildPyramids(Mat& frame)
{
    vector<Mat> pyramids;

    int count = 6;

    Mat prevFrame = frame, nextFrame;

    while(count > 0)
    {
        resize(prevFrame, nextFrame, Size(), .85, .85);
        prevFrame = nextFrame;

        pyramids.push_back(nextFrame);

        --count;
    }

    return pyramids;
}

vector<Rect> slidingWindows(Mat& image, int stepSize, int width, int height)
{
    vector<Rect> windows;

    for(size_t row = 0; row < image.rows; row += stepSize)
    {
        if((row + height) > image.rows) break;

        for(size_t col = 0; col < image.cols; col += stepSize)
        {
            if((col + width) > image.cols) break;

            windows.push_back(Rect(col, row, width, height));
        }
    }

    return windows;
}

编辑 I:可以找到对我的解决方案的更多分析 here


这是一个双向任务。

前进方向


1.翻译

对于每个轮廓,计算其 moment。然后对于该轮廓中的每个点,将其平移到时刻,即 contour.point[i] = contour.point[i] - contour.moment[i]。这会将所有轮廓点移动到原点。

PS: 你需要跟踪每个轮廓的产生力矩,因为它将在下一节中使用

2。旋转

有了新翻译的点,计算它们rotated rect. This will give you the angle of rotation. Depending on this angle, you would want to calculate the new angle which you want to rotate this contour by; this answer会很有帮助。

获得新角度后,计算rotation matrix。请记住,您在这里的中心将是原点,即 (0, 0)。在计算旋转矩阵时我没有考虑缩放比例(这就是金字塔发挥作用的地方)因此我通过了 1.

PS: 你需要跟踪每个轮廓的生成矩阵,因为它会在下一节中使用

使用此矩阵,您可以继续旋转轮廓中的每个点,如图所示 here*.

完成所有这些后,您可以继续计算 Hausdorff 距离并找到通过您设置的阈值的轮廓。


反向

第一部分中完成的所有操作都必须撤消,以便我们将有效的轮廓绘制到我们的相机画面上。


1.旋转

回想一下,每个检测到的轮廓都会产生一个旋转矩阵。您想要撤消有效轮廓的旋转。只需执行相同的旋转,但使用 inverse matrix.

For each valid contour and corresponding matrix
inverse_matrix = matrix[i].inv(cv2.DECOMP_SVD)
Use * to rotate the points but with inverse_matrix as parameter

PS:求逆时,如果生成的矩阵不是方阵,会失败。 cv2.DECOMP_SVD 将生成逆矩阵,即使原始矩阵不是正方形。

2。翻译

随着有效等高线的点向后旋转,您只需撤消之前执行的平移即可。不要减去,只需将力矩添加到每个点即可。

您现在可以继续将这些轮廓绘制到您的相机画面中。


缩放


这是图像金字塔发挥作用的地方。

您所要做的就是将模板图​​像的大小固定 size/ratio 调整到您想要的次数(称为图层)。找到的教程 here 很好地解释了如何在 OpenCV 中执行此操作。

不用说,您选择的用于调整图像大小的值和层数将并且确实对您的程序的健壮性起着巨大的作用。


全部放在一起

模板图像操作

Create a pyramid consisting of n layers
For each layer in n
    Find contours
    Translate the contour points
    Rotate the contour points

这个操作应该只执行一次并且只存储旋转点的结果。

相机馈送操作

假设

将每一层模板图像的旋转轮廓存储在templ_contours中。因此,如果我说 templ_contours[0],这将给我在金字塔级别 0 的旋转轮廓。

让图像的平移、旋转轮廓和力矩分别存储在transControtContmoment中。

image_contours = Find Contours
for each contour detected in image
    moment = calculate moment

for each point in image_contours
    transCont.thisPoint = forward_translate(image_contours.thisPoint)
    rotCont.thisPoint = forward_rotate(transCont.thisPoint)

for each contour_layer in templ_contours
    for each contour in rotCont
        calculate Hausdorff Distance
        valid_contours = contours_passing_distance_threshold

for each point in valid_contours
    valid_point = backward_rotate(valid_point)

for each point in valid_contours
    valid_point = backward_translate(valid_point)

drawContours(valid_contours, image)