Engee 文档
Notebook

使用ORB探测器搜索关键点

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

理论部分

ORB(Oriented FAST And Rotated BRIEF)是一个快速的关键字检测器和描述符。 它结合了快速算法(用于检测关键点)和简要(用于描述它们)。

**FAST(来自加速分段测试的功能)算法用于查找图像中的角点。
Harris角点度量
用于选择最显着点,这减少了弱关键点的数量。 为每个关键点计算方位。
这使得ORB对图像旋转不变。

Algorithm**BRIEF(Binary Robust Independent Elementary Features)**创建二进制描述符。 为了比较图像之间的点,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探测器自动查找关键点并在两幅图像上匹配它们。 构建的单应性使得对齐图像成为可能,并且组合它们使得创建全景图成为可能。