flann 1.8.4.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,162 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ class FFI::Pointer
29
+ class << self
30
+ def new_from_nmatrix nm
31
+ raise(StorageError, "dense storage expected") unless nm.dense?
32
+ c_type = Flann::dtype_to_c(nm.dtype)
33
+ c_type = :uchar if c_type == :byte
34
+ ::FFI::Pointer.new(c_type, nm.data_pointer).tap { |p| p.autorelease = false }
35
+ end
36
+ end
37
+ end
38
+
39
+ module Flann
40
+ class Index
41
+
42
+ # Constructor takes a block where we set each of the parameters. We need to be careful to do this since
43
+ # we're using the C API and not C++; so everything important needs to be initialized or there could be
44
+ # a segfault. For reasonable default definitions, see:
45
+ #
46
+ # * https://github.com/mariusmuja/flann/tree/master/src/cpp/flann/algorithms
47
+ #
48
+ def initialize index_dataset = nil, dtype: :float64, parameters: Flann::Parameters::DEFAULT
49
+ @dataset = index_dataset
50
+ #require 'pry'
51
+ #binding.pry if @dataset.nil?
52
+ @dtype = (!index_dataset.nil? && index_dataset.is_a?(NMatrix)) ? index_dataset.dtype : dtype
53
+ @index_ptr = nil
54
+
55
+ @parameters_ptr, @parameters = Flann::handle_parameters(parameters)
56
+
57
+ yield @parameters if block_given?
58
+ end
59
+ attr_reader :dtype, :dataset, :parameters, :parameters_ptr, :index_ptr
60
+
61
+ # Assign a new dataset. Requires that the old index be freed.
62
+ def dataset= index_dataset
63
+ free!
64
+ @dataset = index_dataset
65
+ end
66
+
67
+ # Build an index
68
+ def build!
69
+ raise("no dataset specified") if dataset.nil?
70
+ c_type = Flann::dtype_to_c(dtype)
71
+ c_method = "flann_build_index_#{c_type}".to_sym
72
+ speedup_float_ptr = FFI::MemoryPointer.new(:float)
73
+ @index_ptr = Flann.send(c_method, FFI::Pointer.new_from_nmatrix(dataset), dataset.shape[0], dataset.shape[1], speedup_float_ptr, parameters_ptr)
74
+ if index_ptr.address == 0
75
+ require 'pry'
76
+ binding.pry
77
+ raise("failed to allocate index_ptr")
78
+ end
79
+
80
+
81
+ # Return the speedup
82
+ speedup_float_ptr.read_float
83
+ end
84
+
85
+ # Get the nearest neighbors based on this index. Forces a build of the index if one hasn't been done yet.
86
+ def nearest_neighbors testset, k, parameters = {}
87
+ parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
88
+
89
+ self.build! if index_ptr.nil?
90
+
91
+ parameters_ptr, parameters = Flann::handle_parameters(parameters)
92
+ result_size = testset.shape[0] * k
93
+
94
+ c_type = Flann::dtype_to_c(dataset.dtype)
95
+ c_method = "flann_find_nearest_neighbors_index_#{c_type}".to_sym
96
+ indices_int_ptr, distances_t_ptr = Flann::allocate_results_space(result_size, c_type)
97
+
98
+ Flann.send c_method, index_ptr,
99
+ FFI::Pointer.new_from_nmatrix(testset),
100
+ testset.shape[0],
101
+ indices_int_ptr, distances_t_ptr,
102
+ k,
103
+ parameters_ptr
104
+
105
+
106
+ [indices_int_ptr.read_array_of_int(result_size),
107
+ c_type == :double ? distances_t_ptr.read_array_of_double(result_size) : distances_t_ptr.read_array_of_float(result_size)]
108
+ end
109
+
110
+ # Perform a radius search on a single query point
111
+ def radius_search query, radius, max_k=nil, parameters = {}
112
+ max_k ||= dataset.shape[0]
113
+ parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
114
+
115
+ self.build! if index_ptr.nil?
116
+ parameters_ptr, parameters = Flann::handle_parameters(parameters)
117
+
118
+ c_type = Flann::dtype_to_c(dataset.dtype)
119
+ c_method = "flann_radius_search_#{c_type}".to_sym
120
+ indices_int_ptr, distances_t_ptr = Flann::allocate_results_space(max_k, c_type)
121
+
122
+ Flann.send(c_method, index_ptr, FFI::Pointer.new_from_nmatrix(query), indices_int_ptr, distances_t_ptr, max_k, radius, parameters_ptr)
123
+
124
+ # Return results: two arrays, one of indices and one of distances.
125
+ indices = indices_int_ptr.read_array_of_int(max_k)
126
+ distances = c_type == :double ? distances_t_ptr.read_array_of_double(max_k) : distances_t_ptr.read_array_of_float(max_k)
127
+
128
+ # Stop where indices == -1
129
+ cutoff = indices.find_index(-1)
130
+ cutoff.nil? ? [indices, distances] : [indices[0...cutoff], distances[0...cutoff]]
131
+ end
132
+
133
+ # Save an index to a file (without the dataset).
134
+ def save filename
135
+ raise(IOError, "Cannot write an unbuilt index") if index_ptr.nil? # FIXME: This should probably have its own exception type.
136
+ c_method = "flann_save_index_#{Flann::dtype_to_c(dtype)}".to_sym
137
+ Flann.send(c_method, index_ptr, filename)
138
+ self
139
+ end
140
+
141
+ # Load an index from a file (with the dataset already known!).
142
+ #
143
+ # FIXME: This needs to free the previous dataset first.
144
+ def load! filename
145
+ c_method = "flann_load_index_#{Flann::dtype_to_c(dtype)}".to_sym
146
+
147
+ @index_ptr = Flann.send(c_method, filename, FFI::Pointer.new_from_nmatrix(dataset), dataset.shape[0], dataset.shape[1])
148
+ self
149
+ end
150
+
151
+ # Free an index
152
+ def free! parameters = {}
153
+ parameters = Parameters.new(Flann::Parameters::DEFAULT.merge(parameters))
154
+ c_method = "flann_free_index_#{Flann::dtype_to_c(dtype)}".to_sym
155
+ parameters_ptr, parameters = Flann::handle_parameters(parameters)
156
+ Flann.send(c_method, index_ptr, parameters_ptr)
157
+ @index_ptr = nil
158
+ self
159
+ end
160
+
161
+ end
162
+ end
@@ -0,0 +1,37 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ module Flann
29
+ module VERSION
30
+ MAJOR = 1
31
+ MINOR = 8
32
+ TINY = 4
33
+ MINISCULE = 2
34
+
35
+ STRING = [MAJOR, MINOR, TINY, MINISCULE].compact.join('.')
36
+ end
37
+ end
@@ -0,0 +1,104 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require File.dirname(__FILE__) + "/spec_helper.rb"
29
+
30
+ describe Flann do
31
+
32
+ it "::VERSION::STRING matches #define FLANN_VERSION_ in config.h" do
33
+ found = false
34
+ File.open(File.dirname(__FILE__) + "/../../cpp/flann/config.h", "r") do |f|
35
+ while line = f.gets
36
+ next unless line =~ /#[\s]*define[\s]+FLANN_VERSION_[\s]+"\d.\d.\d"/
37
+ fields = line.split
38
+ found = true
39
+ expect(fields.last[1...-1]).to eq(Flann::VERSION::STRING.split('.')[0...-1].join('.'))
40
+ end
41
+ end
42
+
43
+ raise("could not find version string in config.h") unless found
44
+ end
45
+
46
+ it "works on the example given in the manual" do
47
+ dataset = NMatrix.random([10000,128])
48
+ testset = NMatrix.random([1000,128])
49
+
50
+ index = Flann::Index.new(dataset) do |params|
51
+ params[:algorithm] = :kmeans
52
+ params[:branching] = 32
53
+ params[:iterations] = 7
54
+ params[:checks] = 16
55
+ end
56
+ speedup = index.build! # this is optional
57
+
58
+ results, distances = index.nearest_neighbors(testset, 5)
59
+
60
+ # Skip saving, as that's tested elsewhere, and I don't feel like cleaning up.
61
+ # index.save "my_index.save"
62
+
63
+ # Alternatively, without an index:
64
+ results, distances = Flann.nearest_neighbors(dataset, testset, 5,
65
+ algorithm: :kmeans, branching: 32,
66
+ iterations: 7, checks: 16)
67
+ end
68
+
69
+ context "#set_distance_type!" do
70
+ it "sets the distance functor without error" do
71
+ Flann.set_distance_type! :euclidean
72
+
73
+ # Version check needed before attempting get_distance_type
74
+ if Flann.respond_to?(:flann_get_distance_type)
75
+ d = Flann.get_distance_type
76
+ expect(d).to eq(:euclidean)
77
+ end
78
+ end
79
+ end
80
+
81
+ [:byte, :int32, :float32, :float64].each do |dtype|
82
+ before :each do
83
+ scale = [:byte, :int32, :int64].include?(dtype) ? 255 : 1.0
84
+ @dataset = NMatrix.random([1000,128], dtype: dtype, scale: scale)
85
+ @testset = NMatrix.random([100,128], dtype: dtype, scale: scale)
86
+ end
87
+
88
+ context "#nearest_neighbors" do
89
+ it "computes the nearest neighbors without an index" do
90
+ Flann.nearest_neighbors @dataset, @testset, 5
91
+ end
92
+ end
93
+
94
+ context "#cluster" do
95
+ it "calls flann_compute_cluster_centers_... properly" do
96
+ Flann.cluster(@dataset, 5)
97
+ end
98
+ end
99
+
100
+
101
+ end
102
+
103
+
104
+ end
@@ -0,0 +1,114 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require File.dirname(__FILE__) + "/spec_helper.rb"
29
+
30
+
31
+ describe Flann::Index do
32
+ [:byte, :int32, :float32, :float64].each do |dtype|
33
+ context dtype.inspect do
34
+ before :each do
35
+ scale = [:byte, :int32, :int64].include?(dtype) ? 255 : 1.0
36
+
37
+ @dataset = NMatrix.random([1000,128], dtype: dtype, scale: scale)
38
+ @testset = NMatrix.random([100,128], dtype: dtype, scale: scale)
39
+ @index = Flann::Index.new(@dataset) do |t|
40
+ t[:algorithm] = :kdtree
41
+ t[:trees] = 4
42
+ end
43
+ @index.build!
44
+ end
45
+
46
+
47
+ context "#nearest_neighbors" do
48
+ it "runs without error" do
49
+ @index.nearest_neighbors @testset, 5
50
+ end
51
+
52
+ if dtype == :float32
53
+ it "runs a :kdtree_single search correctly" do
54
+ @dataset = read_dataset "test.dataset"
55
+ @testset = @dataset[0,:*].clone
56
+ @index = Flann::Index.new(@dataset) do |t|
57
+ t[:algorithm] = :kdtree_single
58
+ end
59
+ @index.build!
60
+ indices, distances = @index.nearest_neighbors @testset, 11
61
+ #expect(indices).to eq([0,1,256,257,2,512,258,513,514,3,768])
62
+ expect(indices[0]).to eq(0)
63
+ expect(indices[1..2].sort).to eq([1,256])
64
+ expect(indices[3]).to eq(257)
65
+ expect(indices[4..5].sort).to eq([2,512])
66
+ expect(indices[6..7].sort).to eq([258,513])
67
+ expect(indices[8]).to eq(514)
68
+ expect(indices[9..10].sort).to eq([3,768])
69
+
70
+ expect(distances[0]).to be_within(1E-16).of(0.0)
71
+ expect(distances[1]).to be_within(1E-4).of(2.614689)
72
+ expect(distances[2]).to be_within(1E-4).of(2.614689)
73
+ expect(distances[3]).to be_within(1E-4).of(5.229378)
74
+ expect(distances[4]).to be_within(1E-4).of(10.465225)
75
+ expect(distances[5]).to be_within(1E-4).of(10.465225)
76
+ expect(distances[6]).to be_within(1E-4).of(13.079914)
77
+ expect(distances[7]).to be_within(1E-4).of(13.079914)
78
+ expect(distances[8]).to be_within(1E-4).of(20.93045)
79
+ expect(distances[9]).to be_within(1E-4).of(23.541904)
80
+ end
81
+ end
82
+ end
83
+
84
+
85
+ context "#radius_search" do
86
+ it "runs without error" do
87
+ query = NMatrix.random([1,128])
88
+ @index.radius_search query, 0.4
89
+ end
90
+ end
91
+
92
+
93
+ context "#save" do
94
+ it "saves an index to a file which can be loaded again" do
95
+ FileUtils.rm("temp_index.save_file", :force => true)
96
+ @index.save("temp_index.save_file")
97
+
98
+ raise(IOError, "save failed") unless File.exists?("temp_index.save_file")
99
+
100
+ post_index = Flann::Index.new(@dataset)
101
+ post_index.load!("temp_index.save_file")
102
+ FileUtils.rm("temp_index.save_file", :force => true)
103
+ end
104
+ end
105
+
106
+
107
+ context "#free!" do
108
+ it "frees an index" do
109
+ @index.free!
110
+ end
111
+ end
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,50 @@
1
+ # Copyright 2014 John O. Woods (john.o.woods@gmail.com), West Virginia
2
+ # University's Applied Space Exploration Lab, and West Virginia Robotic
3
+ # Technology Center. All rights reserved.
4
+ #
5
+ # THE BSD LICENSE
6
+ #
7
+ # Redistribution and use in source and binary forms, with or without
8
+ # modification, are permitted provided that the following conditions
9
+ # are met:
10
+ #
11
+ # 1. Redistributions of source code must retain the above copyright
12
+ # notice, this list of conditions and the following disclaimer.
13
+ # 2. Redistributions in binary form must reproduce the above copyright
14
+ # notice, this list of conditions and the following disclaimer in the
15
+ # documentation and/or other materials provided with the distribution.
16
+ #
17
+ # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18
+ # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19
+ # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20
+ # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21
+ # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22
+ # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23
+ # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24
+ # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25
+ # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26
+ # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
+
28
+ require 'rspec'
29
+ require 'rspec/longrun'
30
+
31
+ require "./lib/flann"
32
+
33
+ # Helper function for reading a test dataset so we can test nearest neighbors
34
+ # and radius search and such.
35
+ def read_dataset filename
36
+ Dir.chdir("spec") do
37
+ f = File.new(filename, 'r')
38
+ n = NMatrix.new([65536, 3], dtype: :float32)
39
+ i = 0
40
+ while line = f.gets
41
+ line.chomp!
42
+ fields = line.split
43
+ n[i,:*] = fields.map { |field| field.to_f }
44
+
45
+ i += 1
46
+ end
47
+
48
+ n
49
+ end
50
+ end