|
23 | 23 | */ |
24 | 24 | tracking.Fast = {}; |
25 | 25 |
|
| 26 | + /** |
| 27 | + * Holds the threshold to determine whether the tested pixel is brighter or |
| 28 | + * darker than the corner candidate p. |
| 29 | + * @type {number} |
| 30 | + * @default 20 |
| 31 | + * @static |
| 32 | + */ |
26 | 33 | tracking.Fast.FAST_THRESHOLD = 20; |
27 | 34 |
|
| 35 | + /** |
| 36 | + * Caches coordinates values of the circle surounding the pixel candidate p. |
| 37 | + * @type {Object.<number, Int32Array>} |
| 38 | + * @private |
| 39 | + * @static |
| 40 | + */ |
28 | 41 | tracking.Fast.circles_ = {}; |
29 | 42 |
|
30 | | - tracking.Fast.findCorners = function(grayScale, width, height) { |
31 | | - var baseCircle = this.getCircle_(width), |
32 | | - circle = new Int32Array(16), |
33 | | - corners = [], |
34 | | - i, |
35 | | - j, |
36 | | - k, |
37 | | - p, |
38 | | - w = 0; |
39 | | - |
40 | | - for (i = 3; i < height - 3; i++) { |
41 | | - for (j = 3; j < width - 3; j++) { |
42 | | - w = i*width + j; |
43 | | - p = grayScale[w]; |
44 | | - |
45 | | - for (k = 0; k < 16; k++) { |
46 | | - circle[k] = grayScale[baseCircle[k] + w]; |
| 43 | + /** |
| 44 | + * Finds corners coordinates on the graysacaled image. |
| 45 | + * @param {array} The grayscale pixels in a linear [p1,p2,...] array. |
| 46 | + * @param {number} width The image width. |
| 47 | + * @param {number} height The image height. |
| 48 | + * @return {array} Array containing the coordinates of all found corners, |
| 49 | + * e.g. [x0,y0,x1,y1,...], where P(x0,y0) represents a corner coordinate. |
| 50 | + */ |
| 51 | + tracking.Fast.findCorners = function(pixels, width, height) { |
| 52 | + var circleOffsets = this.getCircleOffsets_(width), |
| 53 | + circlePixels = new Int32Array(16), |
| 54 | + corners = []; |
| 55 | + |
| 56 | + // When looping through the image pixels, skips the first three lines from |
| 57 | + // the image boundaries to constrain the surrounding circle inside the image |
| 58 | + // area. |
| 59 | + for (var i = 3; i < height - 3; i++) { |
| 60 | + for (var j = 3; j < width - 3; j++) { |
| 61 | + var w = i * width + j; |
| 62 | + var p = pixels[w]; |
| 63 | + |
| 64 | + // Loops the circle offsets to read the pixel value for the sixteen |
| 65 | + // surrounding pixels. |
| 66 | + for (var k = 0; k < 16; k++) { |
| 67 | + circlePixels[k] = pixels[w + circleOffsets[k]]; |
47 | 68 | } |
48 | 69 |
|
49 | | - if (this.isCorner(circle, p, this.FAST_THRESHOLD)) { |
| 70 | + if (this.isCorner(p, circlePixels, this.FAST_THRESHOLD)) { |
| 71 | + // The pixel p is classified as a corner, as optimization increment j |
| 72 | + // by the circle radius 3 to skip the neighbor pixels inside the |
| 73 | + // surrounding circle. This can be removed without compromising the |
| 74 | + // result. |
50 | 75 | corners.push(j, i); |
51 | | - j+=3; |
| 76 | + j += 3; |
52 | 77 | } |
53 | 78 | } |
54 | 79 | } |
55 | 80 |
|
56 | 81 | return corners; |
57 | 82 | }; |
58 | 83 |
|
59 | | - tracking.Fast.isCorner = function(circle, p, threshold) { |
| 84 | + /** |
| 85 | + * Checks if the circle pixel is brigther than the candidate pixel p by |
| 86 | + * a threshold. |
| 87 | + * @param {number} circlePixel The circle pixel value. |
| 88 | + * @param {number} p The value of the candidate pixel p. |
| 89 | + * @param {number} threshold |
| 90 | + * @return {Boolean} |
| 91 | + */ |
| 92 | + tracking.Fast.isBrighter = function(circlePixel, p, threshold) { |
| 93 | + return circlePixel - p > threshold; |
| 94 | + }; |
| 95 | + |
| 96 | + tracking.Fast.isCorner = function(p, circlePixels, threshold) { |
60 | 97 | var brighter, |
61 | | - circlePoint, |
| 98 | + circlePixel, |
62 | 99 | darker; |
63 | 100 |
|
64 | | - if (this.isTriviallyExcluded(circle, p, threshold)) { |
| 101 | + if (this.isTriviallyExcluded(circlePixels, p, threshold)) { |
65 | 102 | return false; |
66 | 103 | } |
67 | 104 |
|
|
70 | 107 | brighter = true; |
71 | 108 |
|
72 | 109 | for (var y = 0; y < 9; y++) { |
73 | | - circlePoint = circle[(x + y) & 15]; |
| 110 | + circlePixel = circlePixels[(x + y) & 15]; |
74 | 111 |
|
75 | | - if (!this.isBrighter(circlePoint, p, threshold)) { |
| 112 | + if (!this.isBrighter(p, circlePixel, threshold)) { |
76 | 113 | brighter = false; |
77 | 114 | } |
78 | 115 |
|
79 | | - if (!this.isDarker(circlePoint, p, threshold)) { |
| 116 | + if (!this.isDarker(p, circlePixel, threshold)) { |
80 | 117 | darker = false; |
81 | 118 | } |
82 | 119 | } |
|
85 | 122 | return brighter || darker; |
86 | 123 | }; |
87 | 124 |
|
88 | | - tracking.Fast.isTriviallyExcluded = function(circle, p, threshold) { |
| 125 | + /** |
| 126 | + * Checks if the circle pixel is darker than the candidate pixel p by |
| 127 | + * a threshold. |
| 128 | + * @param {number} circlePixel The circle pixel value. |
| 129 | + * @param {number} p The value of the candidate pixel p. |
| 130 | + * @param {number} threshold |
| 131 | + * @return {Boolean} |
| 132 | + */ |
| 133 | + tracking.Fast.isDarker = function(circlePixel, p, threshold) { |
| 134 | + return p - circlePixel > threshold; |
| 135 | + }; |
| 136 | + |
| 137 | + /** |
| 138 | + * Fast check to test if the candidate pixel is a trivially excluded value. |
| 139 | + * In order to be a corner, the candidate pixel value should be darker or |
| 140 | + * brigther than 9-12 surrouding pixels, when at least three of the top, |
| 141 | + * bottom, left and right pixels are brither or darker it can be |
| 142 | + * automatically excluded improving the performance. |
| 143 | + * @param {number} circlePixel The circle pixel value. |
| 144 | + * @param {number} p The value of the candidate pixel p. |
| 145 | + * @param {number} threshold |
| 146 | + * @return {Boolean} |
| 147 | + */ |
| 148 | + tracking.Fast.isTriviallyExcluded = function(circlePixels, p, threshold) { |
89 | 149 | var count = 0; |
90 | | - var circleTop = circle[0]; |
91 | | - var circleRight = circle[4]; |
92 | | - var circleBottom = circle[8]; |
93 | | - var circleLeft = circle[12]; |
| 150 | + var circleBottom = circlePixels[8]; |
| 151 | + var circleLeft = circlePixels[12]; |
| 152 | + var circleRight = circlePixels[4]; |
| 153 | + var circleTop = circlePixels[0]; |
94 | 154 |
|
95 | 155 | if (this.isBrighter(circleTop, p, threshold)) { |
96 | 156 | count++; |
|
119 | 179 | if (this.isDarker(circleLeft, p, threshold)) { |
120 | 180 | count++; |
121 | 181 | } |
122 | | - |
123 | 182 | if (count < 3) { |
124 | 183 | return true; |
125 | 184 | } |
|
128 | 187 | return false; |
129 | 188 | }; |
130 | 189 |
|
131 | | - tracking.Fast.isBrighter = function(circlePoint, p, threshold) { |
132 | | - return circlePoint > p + threshold; |
133 | | - }; |
134 | | - |
135 | | - tracking.Fast.isDarker = function(circlePoint, p, threshold) { |
136 | | - return circlePoint < p - threshold; |
137 | | - }; |
138 | | - |
139 | | - tracking.Fast.getCircle_ = function(width) { |
| 190 | + /** |
| 191 | + * Gets the sixteen offset values of the circle surrounding pixel. |
| 192 | + * @param {number} width The image width. |
| 193 | + * @return {array} Array with the sixteen offset values of the circle |
| 194 | + * surrounding pixel. |
| 195 | + */ |
| 196 | + tracking.Fast.getCircleOffsets_ = function(width) { |
140 | 197 | if (this.circles_[width]) { |
141 | 198 | return this.circles_[width]; |
142 | 199 | } |
|
0 commit comments