1 /** 2 Module contains utilities common for all algorithms which operate with feature points. 3 4 Copyright: Copyright Relja Ljubobratovic 2016. 5 6 Authors: Relja Ljubobratovic 7 8 License: $(LINK3 http://www.boost.org/LICENSE_1_0.txt, Boost Software License - Version 1.0). 9 */ 10 11 module dcv.features.utils; 12 13 import std.traits : isNumeric; 14 15 import mir.ndslice; 16 17 /** 18 Feature point. 19 */ 20 struct Feature 21 { 22 /// x coordinate of the feature centroid 23 size_t x; 24 /// y coordinate of the feature centroid 25 size_t y; 26 /// octave in which the feature is detected. 27 size_t octave; 28 /// width of the feature 29 float width; 30 /// height of the feature 31 float height; 32 /// feature strengh. 33 float score; 34 } 35 36 /** 37 Extract corners as array of 2D points, from response matrix. 38 39 Params: 40 cornerResponse = Response matrix, collected as output from corner 41 detection algoritms such as harrisCorners, or shiTomasiCorners. 42 count = Number of corners which need to be extracted. Default is 43 -1 which indicate that all responses with value above the threshold 44 will be returned. 45 threshold = Response threshold - response values in the matrix 46 larger than this are considered as valid corners. 47 48 Returns: 49 Lazy array of size_t[2], as in array of 2D points, of corner reponses 50 which fit the given criteria. 51 */ 52 pure nothrow 53 auto extractCorners(T) 54 ( 55 Slice!(Contiguous, [2], T*) cornerResponse, 56 int count = -1, 57 T threshold = 0 58 ) if ( isNumeric!T ) 59 in 60 { 61 assert(!cornerResponse.empty, "Corner response matrix should not be empty."); 62 } 63 body 64 { 65 import std.array : Appender; 66 import std.typecons : Tuple; 67 68 import mir.ndslice.sorting : sort; 69 import mir.ndslice.topology : zip, flattened, ndiota; 70 71 alias Pair = Tuple!(T, "value", size_t[2], "position"); 72 73 // TODO: test if corner response is contiguous, or better yet - change 74 // the implementation not to depend on contiguous slice. 75 76 auto resultAppender = Appender!(Pair[])(); 77 78 size_t r = 0, c; 79 foreach(row; cornerResponse) { 80 c = 0; 81 foreach(value; row) { 82 if (value > threshold) { 83 resultAppender.put(Pair(value, [r, c])); 84 } 85 c++; 86 } 87 r++; 88 } 89 90 auto result = resultAppender 91 .data 92 .sliced 93 .sort!( (a, b) => a.value > b.value ) 94 .map!( p => p.position ); 95 96 if (count > 0) { 97 result = result[0..count]; 98 } 99 100 return result; 101 } 102 103 /// 104 unittest 105 { 106 auto image = [0., 0., 0., 107 0., 1., 0., 108 0., 0., 0.].sliced(3, 3); 109 110 auto res = image.extractCorners; 111 112 assert(res.length == 1); 113 assert(res[0] == [1, 1]); 114 } 115 116 /// 117 unittest 118 { 119 auto image = [0., 0.1, 0., 120 0., 0.3, 0., 121 0., 0.2, 0.].sliced(3, 3); 122 123 auto res = image.extractCorners; 124 125 assert(res.length == 3); 126 assert(res[0] == [1, 1]); 127 assert(res[1] == [2, 1]); 128 assert(res[2] == [0, 1]); 129 } 130 131 /// 132 unittest 133 { 134 auto image = [0., 0.1, 0., 135 0., 0.3, 0., 136 0., 0.2, 0.].sliced(3, 3); 137 138 auto res = image.extractCorners(1); 139 140 assert(res.length == 1); 141 assert(res[0] == [1, 1]); 142 } 143 144 /// 145 unittest 146 { 147 auto image = [0., 0.1, 0., 148 0., 0.3, 0., 149 0., 0.2, 0.].sliced(3, 3); 150 151 auto res = image.extractCorners(-1, 0.2); 152 153 assert(res.length == 1); 154 assert(res[0] == [1, 1]); 155 } 156