Skip to content

Commit 7f1b514

Browse files
authored
Merge pull request #11 from JuliaImages/brisk
BRISK Descriptors
2 parents 6f6cb39 + 8e087df commit 7f1b514

File tree

11 files changed

+278
-35
lines changed

11 files changed

+278
-35
lines changed

src/ImageFeatures.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,9 @@ include("glcm.jl")
1111
include("brief.jl")
1212
include("orb.jl")
1313
include("freak.jl")
14+
include("brisk.jl")
1415

15-
export Keypoint, Keypoints, BRIEF, DescriptorParams, ORB, FREAK
16+
export Keypoint, Keypoints, Feature, Features, Params, BRIEF, ORB, FREAK, BRISK
1617

1718
export
1819
#Core

src/brief.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@ brief_params = BRIEF([size = 128], [window = 9], [sigma = 2 ^ 0.5], [sampling_ty
1212
| **seed** | Int | Random seed used for generating the sampling pairs. For matching two descriptors, the seed used to build both should be same. |
1313
1414
"""
15-
type BRIEF{F} <: DescriptorParams
15+
type BRIEF{F} <: Params
1616
size::Int
1717
window::Int
1818
sigma::Float64

src/brisk.jl

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
"""
2+
```
3+
brisk_params = BRISK([pattern_scale = 1.0])
4+
```
5+
6+
| Argument | Type | Description |
7+
|----------|------|-------------|
8+
| `pattern_scale` | `Float64` | Scaling factor for the sampling window |
9+
"""
10+
type BRISK <: Params
11+
threshold::Float64
12+
octaves::Int
13+
pattern_scale::Float64
14+
pattern_table::Vector{Vector{SamplePair}}
15+
smoothing_table::Vector{Vector{Float16}}
16+
orientation_weights::Vector{OrientationWeights}
17+
short_pairs::Vector{OrientationPair}
18+
long_pairs::Vector{OrientationPair}
19+
end
20+
21+
function BRISK(; threshold::Float64 = 0.25, octaves::Int = 4, pattern_scale = 1.0)
22+
pattern_table, smoothing_table = _brisk_tables(pattern_scale)
23+
orientation_weights = OrientationWeights[]
24+
short_pairs = OrientationPair[]
25+
long_pairs = OrientationPair[]
26+
dminsq = (brisk_dmin * pattern_scale) ^ 2
27+
dmaxsq = (brisk_dmax * pattern_scale) ^ 2
28+
for i in 2:brisk_points
29+
for j in 1:i - 1
30+
dy = pattern_table[1][j][1] - pattern_table[1][i][1]
31+
dx = pattern_table[1][j][2] - pattern_table[1][i][2]
32+
norm = dy ^ 2 + dx ^ 2
33+
if norm > dminsq
34+
push!(long_pairs, OrientationPair((j, i)))
35+
push!(orientation_weights, OrientationWeights((dy / norm, dx / norm)))
36+
elseif norm < dmaxsq
37+
push!(short_pairs, OrientationPair((j, i)))
38+
end
39+
end
40+
end
41+
BRISK(threshold, octaves, pattern_scale, pattern_table, smoothing_table, orientation_weights, short_pairs, long_pairs)
42+
end
43+
44+
function _brisk_orientation{T<:Gray}(int_img::AbstractArray{T, 2}, keypoint::Keypoint, pattern::Array{SamplePair},
45+
orientation_weights::Array{OrientationWeights}, sigmas::Array{Float16}, long_pairs::Array{OrientationPair})
46+
direction_sum_y = 0.0
47+
direction_sum_x = 0.0
48+
for (i, o) in enumerate(long_pairs)
49+
offset_1 = pattern[o[1]]
50+
offset_2 = pattern[o[2]]
51+
intensity_diff = ImageFeatures._freak_mean_intensity(int_img, keypoint, offset_1, sigmas[o[1]]) - ImageFeatures._freak_mean_intensity(int_img, keypoint, offset_2, sigmas[o[2]])
52+
direction_sum_y += orientation_weights[i][1] * intensity_diff
53+
direction_sum_x += orientation_weights[i][2] * intensity_diff
54+
end
55+
angle = atan2(direction_sum_y, direction_sum_x)
56+
scaled_angle = ceil(Int, (angle + pi) * brisk_orientation_steps / (2 * pi))
57+
scaled_angle
58+
end
59+
60+
function _brisk_tables(pattern_scale::Float64)
61+
pattern_table = Vector{SamplePair}[]
62+
smoothing_table = Vector{Float16}[]
63+
for ori in 0:brisk_orientation_steps - 1
64+
theta = ori * 2 * pi / brisk_orientation_steps
65+
pattern = SamplePair[]
66+
sigmas = Float16[]
67+
for (i, n) in enumerate(brisk_num_circular_pattern)
68+
for circle_number in 0:n - 1
69+
angle = (circle_number * 2 * pi / n) + theta
70+
71+
push!(pattern, SamplePair((brisk_radii[i] * sin(angle) * pattern_scale * 0.85,
72+
brisk_radii[i] * cos(angle) * pattern_scale * 0.85)))
73+
push!(sigmas, brisk_sigma[i] * pattern_scale * 0.85)
74+
end
75+
end
76+
push!(pattern_table, pattern)
77+
push!(smoothing_table, sigmas)
78+
end
79+
pattern_table, smoothing_table
80+
end
81+
82+
function create_descriptor{T<:Gray}(img::AbstractArray{T, 2}, features::Array{Feature}, params::BRISK)
83+
int_img = integral_image(img)
84+
descriptors = BitArray{1}[]
85+
ret_features = Feature[]
86+
window_size = ceil(Int, (brisk_radii[end] + brisk_sigma[end]) * params.pattern_scale * 0.85) + 1
87+
lim = CartesianIndex(window_size, window_size)
88+
for feature in features
89+
keypoint = Keypoint(feature)
90+
checkbounds(Bool, img, keypoint - lim) && checkbounds(Bool, img, keypoint + lim) || continue
91+
orientation = _brisk_orientation(int_img, keypoint, params.pattern_table[1], params.orientation_weights, params.smoothing_table[1], params.long_pairs)
92+
sampled_intensities = T[]
93+
for (i, p) in enumerate(params.pattern_table[orientation])
94+
push!(sampled_intensities, ImageFeatures._freak_mean_intensity(int_img, keypoint, p, params.smoothing_table[orientation][i]))
95+
end
96+
descriptor = falses(size(params.short_pairs, 1))
97+
for (i, f) in enumerate(params.short_pairs)
98+
point_1 = sampled_intensities[f[1]]
99+
point_2 = sampled_intensities[f[2]]
100+
descriptor[i] = point_1 < point_2
101+
end
102+
push!(descriptors, descriptor)
103+
push!(ret_features, Feature(keypoint, orientation))
104+
end
105+
descriptors, ret_features
106+
end

src/const.jl

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,17 @@ const freak_radii = [freak_largest_radius, freak_largest_radius - 6 * freak_circ
9696
const freak_sigma = vcat(freak_radii[1:end - 1] / 2, freak_radii[end - 1] / 2)
9797

9898
const freak_orientation_steps = 256;
99+
100+
const brisk_radii = [0, 2.9, 4.9, 7.4, 10.8]
101+
102+
const brisk_sigma = [0.65, 1.16, 1.41, 2.00, 2.19]
103+
104+
const brisk_num_circular_pattern = [1, 10, 14, 15, 20]
105+
106+
const brisk_orientation_steps = 1024
107+
108+
const brisk_dmin = 8.2
109+
110+
const brisk_dmax = 5.85
111+
112+
const brisk_points = 60

src/core.jl

Lines changed: 45 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,29 +1,70 @@
1-
abstract Detector
2-
abstract DescriptorParams
1+
abstract Params
32

43
"""
54
```
65
keypoint = Keypoint(y, x)
6+
keypoint = Keypoint(feature)
77
```
88
9-
The `Keypoint` type.
9+
A `Keypoint` may be created by passing the coordinates of the point or from a feature.
1010
"""
1111
typealias Keypoint CartesianIndex{2}
1212

1313
"""
1414
```
1515
keypoints = Keypoints(boolean_img)
16+
keypoints = Keypoints(features)
1617
```
1718
18-
Creates a `Vector{Keypoint}` of the true values in the `boolean_img`.
19+
Creates a `Vector{Keypoint}` of the `true` values in a boolean image or from a list of features.
1920
"""
2021
typealias Keypoints Vector{CartesianIndex{2}}
2122

23+
"""
24+
```
25+
feature = Feature(keypoint, orientation = 0.0, scale = 0.0)
26+
```
27+
28+
The `Feature` type has the keypoint, its orientation and its scale.
29+
"""
30+
immutable Feature
31+
keypoint::Keypoint
32+
orientation::Float64
33+
scale::Float64
34+
end
35+
36+
"""
37+
```
38+
features = Features(boolean_img)
39+
features = Features(keypoints)
40+
```
41+
42+
Returns a `Vector{Feature}` of features generated from the `true` values in a boolean image or from a
43+
list of keypoints.
44+
"""
45+
typealias Features Vector{Feature}
46+
47+
Feature(k::Keypoint) = Feature(k, 0.0, 0.0)
48+
49+
Feature(k::Keypoint, ori::Number) = Feature(k, ori, 0.0)
50+
51+
Features(keypoints::Keypoints) = map(k -> Feature(k), keypoints)
52+
53+
Features(img::AbstractArray) = Features(Keypoints(img))
54+
55+
Keypoint(feature::Feature) = feature.keypoint
56+
2257
function Keypoints(img::AbstractArray)
2358
r, c, _ = findnz(img)
2459
map((ri, ci) -> Keypoint(ri, ci), r, c)
2560
end
2661

62+
Keypoints(features::Features) = map(f -> f.keypoint, features)
63+
64+
typealias OrientationPair Tuple{Int16, Int16}
65+
typealias OrientationWeights Tuple{Float16, Float16}
66+
typealias SamplePair Tuple{Float16, Float16}
67+
2768
"""
2869
```
2970
distance = hamming_distance(desc_1, desc_2)

src/freak.jl

Lines changed: 18 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,30 +7,28 @@ freak_params = FREAK([pattern_scale = 22.0])
77
|----------|------|-------------|
88
| **pattern_scale** | Float64 | Scaling factor for the sampling window |
99
"""
10-
type FREAK{S, T, O} <: DescriptorParams
10+
type FREAK <: Params
1111
pattern_scale::Float64
12-
pattern_table::Array{Array{S, 1}, 1}
13-
smoothing_table::Array{T, 1}
14-
orientation_weights::Array{O, 1}
12+
pattern_table::Vector{Vector{SamplePair}}
13+
smoothing_table::Vector{Vector{Float16}}
14+
orientation_weights::Vector{OrientationWeights}
1515
end
1616

17-
typealias OrientationPair Vector{Int}
18-
typealias SamplePair Vector{Float64}
19-
2017
function FREAK(; pattern_scale::Float64 = 22.0)
2118
pattern_table, smoothing_table = _freak_tables(pattern_scale)
22-
orientation_weights = OrientationPair[]
19+
orientation_weights = OrientationWeights[]
2320
for o in freak_orientation_sampling_pattern
2421
offset_1 = pattern_table[1][o[1]]
2522
offset_2 = pattern_table[1][o[2]]
26-
dy, dx = offset_1 - offset_2
23+
dy = offset_1[1] - offset_2[1]
24+
dx = offset_1[2] - offset_2[2]
2725
norm = (dx ^ 2 + dy ^ 2)
28-
push!(orientation_weights, OrientationPair([round(Int, dy * 4096 / norm), round(Int, dx * 4096 / norm)]))
26+
push!(orientation_weights, OrientationWeights((dy / norm, dx / norm)))
2927
end
3028
FREAK(pattern_scale, pattern_table, smoothing_table, orientation_weights)
3129
end
3230

33-
function _freak_mean_intensity{T<:Gray}(int_img::AbstractArray{T, 2}, keypoint::Keypoint, offset::SamplePair, sigma::Float64)
31+
function _freak_mean_intensity{T<:Gray}(int_img::AbstractArray{T, 2}, keypoint::Keypoint, offset::SamplePair, sigma::Float16)
3432
y = keypoint[1] + offset[1]
3533
x = keypoint[2] + offset[2]
3634
if sigma < 0.5
@@ -45,15 +43,15 @@ function _freak_mean_intensity{T<:Gray}(int_img::AbstractArray{T, 2}, keypoint::
4543
end
4644

4745
function _freak_orientation{T<:Gray}(int_img::AbstractArray{T, 2}, keypoint::Keypoint, pattern::Array{SamplePair},
48-
orientation_weights::Array{OrientationPair}, sigmas::Array{Float64})
46+
orientation_weights::Array{OrientationWeights}, sigmas::Array{Float16})
4947
direction_sum_y = 0.0
5048
direction_sum_x = 0.0
5149
for (i, o) in enumerate(freak_orientation_sampling_pattern)
5250
offset_1 = pattern[o[1]]
5351
offset_2 = pattern[o[2]]
5452
intensity_diff = _freak_mean_intensity(int_img, keypoint, offset_1, sigmas[o[1]]) - _freak_mean_intensity(int_img, keypoint, offset_2, sigmas[o[2]])
55-
direction_sum_y += orientation_weights[i][1] * intensity_diff / 4096
56-
direction_sum_x += orientation_weights[i][2] * intensity_diff / 4096
53+
direction_sum_y += orientation_weights[i][1] * intensity_diff
54+
direction_sum_x += orientation_weights[i][2] * intensity_diff
5755
end
5856
angle = atan2(direction_sum_y, direction_sum_x)
5957
scaled_angle = ceil(Int, (angle + pi) * freak_orientation_steps / (2 * pi))
@@ -62,18 +60,18 @@ end
6260

6361
function _freak_tables(pattern_scale::Float64)
6462
pattern_table = Vector{SamplePair}[]
65-
smoothing_table = Vector{Float64}[]
63+
smoothing_table = Vector{Float16}[]
6664
for ori in 0:freak_orientation_steps - 1
6765
theta = ori * 2 * pi / freak_orientation_steps
6866
pattern = SamplePair[]
69-
sigmas = Float64[]
67+
sigmas = Float16[]
7068
for (i, n) in enumerate(freak_num_circular_pattern)
7169
for circle_number in 0:n - 1
7270
alt_offset = (pi / n) * ((i - 1) % 2)
7371
angle = (circle_number * 2 * pi / n) + alt_offset + theta
7472

75-
push!(pattern, SamplePair([freak_radii[i] * sin(angle) * pattern_scale,
76-
freak_radii[i] * cos(angle) * pattern_scale]))
73+
push!(pattern, SamplePair((freak_radii[i] * sin(angle) * pattern_scale,
74+
freak_radii[i] * cos(angle) * pattern_scale)))
7775
push!(sigmas, freak_sigma[i] * pattern_scale)
7876
end
7977
end
@@ -88,10 +86,9 @@ function create_descriptor{T<:Gray}(img::AbstractArray{T, 2}, keypoints::Keypoin
8886
descriptors = BitArray{1}[]
8987
ret_keypoints = Keypoint[]
9088
window_size = ceil(Int, (freak_radii[1] + freak_sigma[1]) * params.pattern_scale) + 1
91-
tl_lim = CartesianIndex(-window_size, -window_size)
92-
br_lim = CartesianIndex(window_size, window_size)
89+
lim = CartesianIndex(window_size, window_size)
9390
for k in keypoints
94-
checkbounds(Bool, img, k + tl_lim) && checkbounds(Bool, img, k + br_lim) || continue
91+
checkbounds(Bool, img, k - lim) && checkbounds(Bool, img, k + lim) || continue
9592
orientation = _freak_orientation(int_img, k, params.pattern_table[1], params.orientation_weights, params.smoothing_table[1])
9693
sampled_intensities = T[]
9794
for (i, p) in enumerate(params.pattern_table[orientation])

src/orb.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ orb_params = ORB([num_keypoints = 500], [n_fast = 12], [threshold = 0.25], [harr
1313
| **levels** | Int | Number of levels in the gaussian pyramid. See [`gaussian_pyramid`] in Images.jl |
1414
| **sigma** | Float64 | Used for gaussian smoothing in each level of the gaussian pyramid. See [`gaussian_pyramid`] in Images.jl |
1515
"""
16-
type ORB <: DescriptorParams
16+
type ORB <: Params
1717
num_keypoints::Int
1818
n_fast::Int
1919
threshold::Float64

0 commit comments

Comments
 (0)