BRIEF Descriptor
BRIEF
(Binary Robust Independent Elementary Features) is an efficient feature point descriptor. It is highly discriminative even when using relatively few bits and is computed using simple intensity difference tests. BRIEF does not have a sampling pattern thus pairs can be chosen at any point on the SxS
patch.
To build a BRIEF descriptor of length n
, we need to determine n
pairs (Xi,Yi)
. Denote by X
and Y
the vectors of point Xi
and Yi
, respectively.
In ImageFeatures.jl we have five methods to determine the vectors X
and Y
:
random_uniform
:X
andY
are randomly uniformly sampledgaussian
:X
andY
are randomly sampled using a Gaussian distribution, meaning that locations that are closer to the center of the patch are preferredgaussian_local
:X
andY
are randomly sampled using a Gaussian distribution where firstX
is sampled with a standard deviation of0.04*S^2
and then theYi’s
are sampled using a Gaussian distribution – EachYi
is sampled with meanXi
and standard deviation of0.01 * S^2
random_coarse
:X
andY
are randomly sampled from discrete location of a coarse polar gridcenter_sample
: For eachi
,Xi
is(0, 0)
andYi
takes all possible values on a coarse polar grid
As with all the binary descriptors, BRIEF’s distance measure is the number of different bits between two binary strings which can also be computed as the sum of the XOR operation between the strings.
BRIEF is a very simple feature descriptor and does not provide scale or rotation invariance (only translation invariance). To achieve those, see ORB, BRISK, FREAK examples
Example
Let us take a look at a simple example where the BRIEF descriptor is used to match two images where one has been translated by (100, 200)
pixels. We will use the lighthouse
image from the TestImages package for this example.
Now, let us create the two images we will match using BRIEF.
using ImageFeatures, TestImages, Images, ImageDraw, CoordinateTransformations
img = testimage("sudoku")
img1 = Gray.(img)
trans = Translation(-50, -50)
img2 = warp(img1, trans, axes(img1))
To calculate the descriptors, we first need to get the keypoints. For this tutorial, we will use the FAST corners to generate keypoints (see fastcorners
).
keypoints_1 = Keypoints(fastcorners(img1, 12, 0.4))
keypoints_2 = Keypoints(fastcorners(img2, 12, 0.4))
123-element Vector{CartesianIndex{2}}:
CartesianIndex(254, 214)
CartesianIndex(203, 254)
CartesianIndex(439, 267)
CartesianIndex(444, 267)
CartesianIndex(437, 268)
CartesianIndex(445, 268)
CartesianIndex(436, 269)
CartesianIndex(434, 271)
CartesianIndex(448, 271)
CartesianIndex(436, 277)
⋮
CartesianIndex(295, 489)
CartesianIndex(279, 490)
CartesianIndex(294, 490)
CartesianIndex(287, 491)
CartesianIndex(293, 491)
CartesianIndex(429, 498)
CartesianIndex(430, 499)
CartesianIndex(431, 500)
CartesianIndex(408, 505)
To create the BRIEF descriptor, we first need to define the parameters by calling the BRIEF
constructor.
brief_params = BRIEF(size = 256, window = 10, seed = 123)
BRIEF{typeof(gaussian)}(256, 10, 1.4142135623730951, ImageFeatures.gaussian, 123)
Now pass the image with the keypoints and the parameters to the create_descriptor
function.
desc_1, ret_keypoints_1 = create_descriptor(img1, keypoints_1, brief_params)
desc_2, ret_keypoints_2 = create_descriptor(img2, keypoints_2, brief_params)
(BitVector[[1, 0, 0, 1, 0, 0, 1, 1, 1, 0 … 0, 1, 1, 0, 0, 1, 1, 0, 1, 1], [1, 0, 0, 1, 0, 0, 1, 1, 1, 0 … 0, 0, 1, 1, 1, 1, 1, 0, 1, 0], [1, 1, 0, 1, 1, 1, 1, 1, 1, 0 … 0, 1, 1, 0, 1, 1, 1, 0, 1, 1], [0, 0, 1, 0, 1, 1, 1, 1, 1, 0 … 0, 1, 1, 0, 1, 1, 0, 0, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0 … 0, 1, 1, 0, 1, 1, 1, 0, 1, 1], [0, 0, 1, 0, 1, 0, 1, 1, 0, 0 … 0, 1, 1, 0, 0, 1, 0, 0, 1, 0], [0, 0, 0, 1, 1, 1, 1, 1, 1, 0 … 0, 1, 1, 0, 1, 1, 1, 0, 1, 1], [0, 0, 0, 1, 0, 1, 1, 1, 1, 1 … 0, 0, 1, 0, 1, 1, 1, 0, 1, 0], [0, 0, 0, 1, 0, 0, 1, 1, 1, 0 … 1, 0, 1, 0, 0, 1, 0, 1, 1, 0], [0, 0, 0, 1, 0, 0, 1, 1, 1, 0 … 0, 1, 1, 0, 1, 1, 0, 0, 1, 0] … [0, 0, 0, 1, 0, 1, 1, 0, 1, 1 … 1, 1, 1, 0, 1, 1, 1, 0, 1, 0], [0, 1, 0, 0, 1, 1, 0, 1, 1, 1 … 1, 1, 1, 0, 1, 1, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 1, 0, 0 … 1, 1, 1, 1, 0, 1, 0, 0, 0, 1], [0, 1, 0, 0, 1, 1, 1, 1, 1, 1 … 0, 1, 0, 1, 1, 1, 1, 0, 1, 1], [1, 0, 0, 1, 0, 0, 1, 1, 1, 0 … 0, 1, 1, 0, 0, 1, 0, 0, 1, 1], [0, 0, 0, 0, 1, 1, 1, 1, 0, 1 … 0, 1, 0, 1, 1, 1, 1, 0, 0, 1], [0, 0, 1, 0, 1, 0, 1, 1, 0, 0 … 0, 1, 1, 0, 0, 1, 0, 0, 1, 0], [0, 0, 0, 1, 0, 0, 1, 1, 1, 0 … 0, 0, 1, 0, 0, 1, 0, 1, 1, 0], [0, 0, 0, 1, 0, 0, 1, 1, 1, 0 … 1, 0, 1, 0, 0, 1, 0, 1, 1, 0], [0, 0, 0, 1, 0, 0, 0, 1, 1, 1 … 1, 1, 1, 1, 1, 1, 1, 0, 0, 0]], CartesianIndex{2}[CartesianIndex(254, 214), CartesianIndex(203, 254), CartesianIndex(439, 267), CartesianIndex(444, 267), CartesianIndex(437, 268), CartesianIndex(445, 268), CartesianIndex(436, 269), CartesianIndex(434, 271), CartesianIndex(448, 271), CartesianIndex(436, 277) … CartesianIndex(296, 487), CartesianIndex(295, 489), CartesianIndex(279, 490), CartesianIndex(294, 490), CartesianIndex(287, 491), CartesianIndex(293, 491), CartesianIndex(429, 498), CartesianIndex(430, 499), CartesianIndex(431, 500), CartesianIndex(408, 505)])
The obtained descriptors can be used to find the matches between the two images using the match_keypoints
function.
matches = match_keypoints(ret_keypoints_1, ret_keypoints_2, desc_1, desc_2, 0.1)
123-element Vector{Vector{CartesianIndex{2}}}:
[CartesianIndex(204, 164), CartesianIndex(254, 214)]
[CartesianIndex(153, 204), CartesianIndex(203, 254)]
[CartesianIndex(389, 217), CartesianIndex(439, 267)]
[CartesianIndex(394, 217), CartesianIndex(444, 267)]
[CartesianIndex(387, 218), CartesianIndex(437, 268)]
[CartesianIndex(395, 218), CartesianIndex(445, 268)]
[CartesianIndex(386, 219), CartesianIndex(436, 269)]
[CartesianIndex(384, 221), CartesianIndex(434, 271)]
[CartesianIndex(398, 221), CartesianIndex(448, 271)]
[CartesianIndex(386, 227), CartesianIndex(436, 277)]
⋮
[CartesianIndex(245, 439), CartesianIndex(295, 489)]
[CartesianIndex(229, 440), CartesianIndex(279, 490)]
[CartesianIndex(244, 440), CartesianIndex(294, 490)]
[CartesianIndex(237, 441), CartesianIndex(287, 491)]
[CartesianIndex(243, 441), CartesianIndex(293, 491)]
[CartesianIndex(379, 448), CartesianIndex(429, 498)]
[CartesianIndex(380, 449), CartesianIndex(430, 499)]
[CartesianIndex(381, 450), CartesianIndex(431, 500)]
[CartesianIndex(358, 455), CartesianIndex(408, 505)]
We can use the ImageDraw.jl package to view the results.
grid = hcat(img1, img2)
offset = CartesianIndex(0, size(img1, 2))
map(m -> draw!(grid, LineSegment(m[2] + offset,m[1] )), matches)
grid
grid
shows the results
This page was generated using DemoCards.jl and Literate.jl.