Engee 文档
Notebook

使用 ORB 检测器查找关键点

ORB 检测器用于视频稳定、全景创建、物体检测、三维重建、摄像机运动估计、图像对齐和增强现实的定位。它还可通过关键点数据库识别建筑物或地标等位置。

理论部分

ORB(Oriented FAST and Rotated BRIEF) 是一种快速关键点检测器和描述器。它结合了 FAST(用于关键点检测)和 BRIEF(用于关键点描述符)算法。

FAST(来自加速片段测试的特征)** 算法用于查找图像中的角。 Harris Corner Measure (哈里斯角测量)用于选择最重要的点,以减少弱关键点的数量。每个关键点的计算都有一个方向。 这使得 ORB 不受图像旋转的影响。

BRIEF(二元健壮独立基本特征)算法可创建二元描述符。为了比较图像之间的点,ORB 使用汉明度量对二进制描述符进行成对比较。

本例将讨论 ORB 检测器的经典用法,即在两幅图像中找出关键点并进行比较。 根据计算出的同源性,图像将被对齐和合并,从而产生一幅全景图,展示将两幅图像合并为一幅图像的过程。

连接处理图像所需的软件包

In [ ]:
import Pkg; 
Pkg.add("Images")
Pkg.add("ImageFeatures")
Pkg.add("ImageDraw")
Pkg.add("ImageProjectiveGeometry")
Pkg.add("LinearAlgebra")
Pkg.add("OffsetArrays")
Pkg.add("ImageTransformations")
Pkg.add("StaticArrays");
In [ ]:
using Pkg, Images, ImageFeatures, ImageDraw
using ImageProjectiveGeometry
using LinearAlgebra, OffsetArrays
using ImageTransformations, StaticArrays

上传图像

指定图片路径,上传并显示图片:

In [ ]:
path_to_img_1 = "$(@__DIR__)/1.jpg"   
path_to_img_2 = "$(@__DIR__)/2.jpg"   

Image_1 = load(path_to_img_1)
Image_2 = load(path_to_img_2)
Out[0]:
No description has been provided for this image

ORB 应用程序

初始化判别器

In [ ]:
orb_params = ORB(num_keypoints = 1000);

让我们定义一个使用判别器在图像中查找关键点的函数

In [ ]:
function find_features(img::AbstractArray, orb_params)
    desc, ret_features = create_descriptor(Gray.(img), orb_params)
end;

让我们定义一个在两幅图像中搜索共同关键点的函数。首先,计算每幅图像的关键点和描述符,然后使用给定的阈值匹配描述符。

In [ ]:
function match_points(img1::AbstractArray, img2::AbstractArray, orb_params, threshold::Float64=0.1)
    desc_1, ret_features_1 = find_features(img1, orb_params)
    desc_2, ret_features_2 = find_features(img2, orb_params)
    matches = match_keypoints(ret_features_1, ret_features_2, desc_1, desc_2, threshold);
end;

让我们找出匹配的关键点

In [ ]:
matches = match_points(Image_1, Image_2, orb_params, 0.35);

让我们定义一个可视化关键点关系的函数。pad_display draw_matches 绘制连接共同关键点的线条。

In [ ]:
function pad_display(img1, img2)
    img1h = length(axes(img1, 1))
    img2h = length(axes(img2, 1))
    mx = max(img1h, img2h);

    hcat(vcat(img1, zeros(RGB{Float64},
                max(0, mx - img1h), length(axes(img1, 2)))),
        vcat(img2, zeros(RGB{Float64},
                max(0, mx - img2h), length(axes(img2, 2)))))
end

function draw_matches(img1, img2, matches)
    grid = pad_display(parent(img1), parent(img2));
    offset = CartesianIndex(0, size(img1, 2));
    for m in matches
        draw!(grid, LineSegment(m[1], m[2] + offset))
    end
    grid
end;

让我们看看输出结果如何

In [ ]:
draw_matches(Image_1, Image_2, matches)
Out[0]:
No description has been provided for this image

绘制全景图

计算同构(几何变换)需要两个图像中相互对应的点的坐标。

这里,两幅图像的点坐标是从匹配关键点列表matches 中提取的。 x1 - 是第一幅图像中的坐标,x2 是第二幅图像中的坐标。

In [ ]:
x1 = hcat([Float64[m[1].I[1], m[1].I[2]] for m in matches]...)  # 2xN
x2 = hcat([Float64[m[2].I[1], m[2].I[2]] for m in matches]...);  # 2xN

在这里,使用 RANSAC 方法找到同构矩阵H ,使两组点x1x2 尽可能紧密地对齐。

In [ ]:
t = 0.01 

# Вычисление гомографии с помощью RANSAC
H, inliers = ransacfithomography(x1, x2, t);

t - 是一个阈值,用于确定在应用同轴度量法后,各点相差多少才算 "正确"(离群值)。

结构Homography 表示同构矩阵。它包含一个 3x3 矩阵,定义了两幅图像之间的变换。

In [ ]:
struct Homography{T}
    m::SMatrix{3, 3, T, 9}
end

Homography(m::AbstractMatrix{T}) where {T} = Homography{T}(SMatrix{3, 3, T, 9}(m))
function (trans::Homography{M})(x::SVector{3}) where M
    out = trans.m * x
    out = out / out[end]
    SVector{2}(out[1:2])
end
function (trans::Homography{M})(x::SVector{2}) where M
    trans(SVector{3}([x[1], x[2], 1.0]))
end
function (trans::Homography{M})(x::CartesianIndex{2}) where M
    trans(SVector{3}([collect(x.I)..., 1]))
end
function (trans::Homography{M})(x::Tuple{Int, Int}) where M
    trans(CartesianIndex(x))
end
function (trans::Homography{M})(x::Array{CartesianIndex{2}, 1}) where M
    CartesianIndex{2}.([tuple(y...) for y in trunc.(Int, collect.(trans.(x)))])
end
function Base.inv(trans::Homography{M}) where M
    i = inv(trans.m)
    Homography(i ./ i[end])
end

使用函数warp ,将同构矩阵H 应用于图像Image_1 ,创建转换后的图像new_img

In [ ]:
new_img = warp(Image_1, Homography(H))
Out[0]:
No description has been provided for this image

该函数将两幅图像--原始图像和变换后的图像--合并为一幅共同的画布,并在旧图像上添加新图像。

In [ ]:
function merge_images(img1, new_img)
    # Вычисляем размеры холста
    axis1_size = max(last(axes(new_img, 1)), size(img1, 1)) - min(first(axes(new_img, 1)), 1) + 1
    axis2_size = max(last(axes(new_img, 2)), size(img1, 2)) - min(first(axes(new_img, 2)), 1) + 1

    # Создаем OffsetArray для объединённого изображения
    combined_image = OffsetArray(
        zeros(RGB{N0f8}, axis1_size, axis2_size), (
            min(0, first(axes(new_img, 1))),
            min(0, first(axes(new_img, 2)))))

    # Копируем первое изображение в общий холст
    combined_image[1:size(img1, 1), 1:size(img1, 2)] .= img1

    # Копируем второе изображение в общий холст
    for i in axes(new_img, 1)
        for j in axes(new_img, 2)
            if new_img[i, j] != colorant"black"  # Пропускаем чёрные пиксели
                combined_image[i, j] = new_img[i, j]
            end
        end
    end

    combined_image
end
Out[0]:
merge_images (generic function with 1 method)
In [ ]:
panorama = merge_images(Image_1, new_img)
Out[0]:
No description has been provided for this image

结论

本示例演示了如何使用 ORB 检测器自动查找关键点并将其与两幅图像进行匹配。构建的同构图像使图像得以对齐,将它们组合起来就能创建一个全景图。