imageproc/
local_binary_patterns.rs

1//! Functions for computing [local binary patterns](https://en.wikipedia.org/wiki/Local_binary_patterns).
2
3use image::{GenericImage, Luma};
4use std::cmp;
5
6/// Computes the basic local binary pattern of a pixel, or None
7/// if it's too close to the image boundary.
8///
9/// The neighbors of a pixel p are enumerated in the following order:
10///
11/// <pre>
12/// 7  0  1
13/// 6  p  2
14/// 5  4  3
15/// </pre>
16///
17/// The nth most significant bit of the local binary pattern at p is 1
18/// if p is strictly brighter than the neighbor in position n.
19///
20/// # Examples
21/// ```
22/// # extern crate image;
23/// # #[macro_use]
24/// # extern crate imageproc;
25/// # fn main() {
26/// use imageproc::local_binary_patterns::local_binary_pattern;
27///
28/// let image = gray_image!(
29///     06, 11, 14;
30///     09, 10, 10;
31///     19, 00, 22);
32///
33/// let expected = 0b11010000;
34/// let pattern = local_binary_pattern(&image, 1, 1).unwrap();
35/// assert_eq!(pattern, expected);
36/// # }
37/// ```
38pub fn local_binary_pattern<I>(image: &I, x: u32, y: u32) -> Option<u8>
39where
40    I: GenericImage<Pixel = Luma<u8>>,
41{
42    let (width, height) = image.dimensions();
43    if width == 0 || height == 0 {
44        return None;
45    }
46
47    // TODO: It might be better to make this function private, and
48    // TODO: require the caller to only provide valid x and y coordinates
49    // TODO: the function may probably need to be unsafe then to leverage
50    // TODO: on `unsafe_get_pixel`
51    if x == 0 || x >= width - 1 || y == 0 || y >= height - 1 {
52        return None;
53    }
54
55    // TODO: As with the fast corner detectors, this would be more efficient if
56    // TODO: generated a list of pixel offsets once per image, and used those
57    // TODO: offsets directly when reading pixels. To do this we'd need some traits
58    // TODO: for images whose pixels are stored in contiguous rows/columns.
59    let mut pattern = 0u8;
60
61    // The sampled pixels have the following labels.
62    //
63    // 7  0  1
64    // 6  p  2
65    // 5  4  3
66    //
67    // The nth bit of a pattern is 1 if the pixel p
68    // is strictly brighter than the neighbor in position n.
69    let (center, neighbors) = unsafe {
70        (
71            image.unsafe_get_pixel(x, y)[0],
72            [
73                image.unsafe_get_pixel(x, y - 1)[0],
74                image.unsafe_get_pixel(x + 1, y - 1)[0],
75                image.unsafe_get_pixel(x + 1, y)[0],
76                image.unsafe_get_pixel(x + 1, y + 1)[0],
77                image.unsafe_get_pixel(x, y + 1)[0],
78                image.unsafe_get_pixel(x - 1, y + 1)[0],
79                image.unsafe_get_pixel(x - 1, y)[0],
80                image.unsafe_get_pixel(x - 1, y - 1)[0],
81            ],
82        )
83    };
84
85    for i in 0..8 {
86        pattern |= (1 & (neighbors[i] < center) as u8) << i;
87    }
88
89    Some(pattern)
90}
91
92/// Returns the least value of all rotations of a byte.
93///
94/// # Examples
95/// ```
96/// use imageproc::local_binary_patterns::min_shift;
97///
98/// let byte = 0b10110100;
99/// assert_eq!(min_shift(byte), 0b00101101);
100/// ```
101pub fn min_shift(byte: u8) -> u8 {
102    let mut min = byte;
103    for i in 1..8 {
104        min = cmp::min(min, byte.rotate_right(i));
105    }
106    min
107}
108
109/// Number of bit transitions in a byte, counting the last and final bits as adjacent.
110///
111/// # Examples
112/// ```
113/// use imageproc::local_binary_patterns::count_transitions;
114///
115/// let a = 0b11110000;
116/// assert_eq!(count_transitions(a), 2);
117/// let b = 0b00000000;
118/// assert_eq!(count_transitions(b), 0);
119/// let c = 0b10011001;
120/// assert_eq!(count_transitions(c), 4);
121/// let d = 0b10110010;
122/// assert_eq!(count_transitions(d), 6);
123/// ```
124pub fn count_transitions(byte: u8) -> u32 {
125    (byte ^ byte.rotate_right(1)).count_ones()
126}
127
128/// Maps uniform bytes (i.e. those with at most two bit transitions) to their
129/// least circular shifts, and non-uniform bytes to 10101010 (an arbitrarily chosen
130/// non-uniform representative).
131pub static UNIFORM_REPRESENTATIVE_2: [u8; 256] = [
132    0,   // 0
133    1,   // 1
134    1,   // 2
135    3,   // 3
136    1,   // 4
137    170, // 5
138    3,   // 6
139    7,   // 7
140    1,   // 8
141    170, // 9
142    170, // 10
143    170, // 11
144    3,   // 12
145    170, // 13
146    7,   // 14
147    15,  // 15
148    1,   // 16
149    170, // 17
150    170, // 18
151    170, // 19
152    170, // 20
153    170, // 21
154    170, // 22
155    170, // 23
156    3,   // 24
157    170, // 25
158    170, // 26
159    170, // 27
160    7,   // 28
161    170, // 29
162    15,  // 30
163    31,  // 31
164    1,   // 32
165    170, // 33
166    170, // 34
167    170, // 35
168    170, // 36
169    170, // 37
170    170, // 38
171    170, // 39
172    170, // 40
173    170, // 41
174    170, // 42
175    170, // 43
176    170, // 44
177    170, // 45
178    170, // 46
179    170, // 47
180    3,   // 48
181    170, // 49
182    170, // 50
183    170, // 51
184    170, // 52
185    170, // 53
186    170, // 54
187    170, // 55
188    7,   // 56
189    170, // 57
190    170, // 58
191    170, // 59
192    15,  // 60
193    170, // 61
194    31,  // 62
195    63,  // 63
196    1,   // 64
197    170, // 65
198    170, // 66
199    170, // 67
200    170, // 68
201    170, // 69
202    170, // 70
203    170, // 71
204    170, // 72
205    170, // 73
206    170, // 74
207    170, // 75
208    170, // 76
209    170, // 77
210    170, // 78
211    170, // 79
212    170, // 80
213    170, // 81
214    170, // 82
215    170, // 83
216    170, // 84
217    170, // 85
218    170, // 86
219    170, // 87
220    170, // 88
221    170, // 89
222    170, // 90
223    170, // 91
224    170, // 92
225    170, // 93
226    170, // 94
227    170, // 95
228    3,   // 96
229    170, // 97
230    170, // 98
231    170, // 99
232    170, // 100
233    170, // 101
234    170, // 102
235    170, // 103
236    170, // 104
237    170, // 105
238    170, // 106
239    170, // 107
240    170, // 108
241    170, // 109
242    170, // 110
243    170, // 111
244    7,   // 112
245    170, // 113
246    170, // 114
247    170, // 115
248    170, // 116
249    170, // 117
250    170, // 118
251    170, // 119
252    15,  // 120
253    170, // 121
254    170, // 122
255    170, // 123
256    31,  // 124
257    170, // 125
258    63,  // 126
259    127, // 127
260    1,   // 128
261    3,   // 129
262    170, // 130
263    7,   // 131
264    170, // 132
265    170, // 133
266    170, // 134
267    15,  // 135
268    170, // 136
269    170, // 137
270    170, // 138
271    170, // 139
272    170, // 140
273    170, // 141
274    170, // 142
275    31,  // 143
276    170, // 144
277    170, // 145
278    170, // 146
279    170, // 147
280    170, // 148
281    170, // 149
282    170, // 150
283    170, // 151
284    170, // 152
285    170, // 153
286    170, // 154
287    170, // 155
288    170, // 156
289    170, // 157
290    170, // 158
291    63,  // 159
292    170, // 160
293    170, // 161
294    170, // 162
295    170, // 163
296    170, // 164
297    170, // 165
298    170, // 166
299    170, // 167
300    170, // 168
301    170, // 169
302    170, // 170
303    170, // 171
304    170, // 172
305    170, // 173
306    170, // 174
307    170, // 175
308    170, // 176
309    170, // 177
310    170, // 178
311    170, // 179
312    170, // 180
313    170, // 181
314    170, // 182
315    170, // 183
316    170, // 184
317    170, // 185
318    170, // 186
319    170, // 187
320    170, // 188
321    170, // 189
322    170, // 190
323    127, // 191
324    3,   // 192
325    7,   // 193
326    170, // 194
327    15,  // 195
328    170, // 196
329    170, // 197
330    170, // 198
331    31,  // 199
332    170, // 200
333    170, // 201
334    170, // 202
335    170, // 203
336    170, // 204
337    170, // 205
338    170, // 206
339    63,  // 207
340    170, // 208
341    170, // 209
342    170, // 210
343    170, // 211
344    170, // 212
345    170, // 213
346    170, // 214
347    170, // 215
348    170, // 216
349    170, // 217
350    170, // 218
351    170, // 219
352    170, // 220
353    170, // 221
354    170, // 222
355    127, // 223
356    7,   // 224
357    15,  // 225
358    170, // 226
359    31,  // 227
360    170, // 228
361    170, // 229
362    170, // 230
363    63,  // 231
364    170, // 232
365    170, // 233
366    170, // 234
367    170, // 235
368    170, // 236
369    170, // 237
370    170, // 238
371    127, // 239
372    15,  // 240
373    31,  // 241
374    170, // 242
375    63,  // 243
376    170, // 244
377    170, // 245
378    170, // 246
379    127, // 247
380    31,  // 248
381    63,  // 249
382    170, // 250
383    127, // 251
384    63,  // 252
385    127, // 253
386    127, // 254
387    255, // 255
388];
389
390/// Lookup table for the least circular shift of a byte.
391pub static MIN_SHIFT: [u8; 256] = [
392    0,   // 0
393    1,   // 1
394    1,   // 2
395    3,   // 3
396    1,   // 4
397    5,   // 5
398    3,   // 6
399    7,   // 7
400    1,   // 8
401    9,   // 9
402    5,   // 10
403    11,  // 11
404    3,   // 12
405    13,  // 13
406    7,   // 14
407    15,  // 15
408    1,   // 16
409    17,  // 17
410    9,   // 18
411    19,  // 19
412    5,   // 20
413    21,  // 21
414    11,  // 22
415    23,  // 23
416    3,   // 24
417    25,  // 25
418    13,  // 26
419    27,  // 27
420    7,   // 28
421    29,  // 29
422    15,  // 30
423    31,  // 31
424    1,   // 32
425    9,   // 33
426    17,  // 34
427    25,  // 35
428    9,   // 36
429    37,  // 37
430    19,  // 38
431    39,  // 39
432    5,   // 40
433    37,  // 41
434    21,  // 42
435    43,  // 43
436    11,  // 44
437    45,  // 45
438    23,  // 46
439    47,  // 47
440    3,   // 48
441    19,  // 49
442    25,  // 50
443    51,  // 51
444    13,  // 52
445    53,  // 53
446    27,  // 54
447    55,  // 55
448    7,   // 56
449    39,  // 57
450    29,  // 58
451    59,  // 59
452    15,  // 60
453    61,  // 61
454    31,  // 62
455    63,  // 63
456    1,   // 64
457    5,   // 65
458    9,   // 66
459    13,  // 67
460    17,  // 68
461    21,  // 69
462    25,  // 70
463    29,  // 71
464    9,   // 72
465    37,  // 73
466    37,  // 74
467    45,  // 75
468    19,  // 76
469    53,  // 77
470    39,  // 78
471    61,  // 79
472    5,   // 80
473    21,  // 81
474    37,  // 82
475    53,  // 83
476    21,  // 84
477    85,  // 85
478    43,  // 86
479    87,  // 87
480    11,  // 88
481    43,  // 89
482    45,  // 90
483    91,  // 91
484    23,  // 92
485    87,  // 93
486    47,  // 94
487    95,  // 95
488    3,   // 96
489    11,  // 97
490    19,  // 98
491    27,  // 99
492    25,  // 100
493    43,  // 101
494    51,  // 102
495    59,  // 103
496    13,  // 104
497    45,  // 105
498    53,  // 106
499    91,  // 107
500    27,  // 108
501    91,  // 109
502    55,  // 110
503    111, // 111
504    7,   // 112
505    23,  // 113
506    39,  // 114
507    55,  // 115
508    29,  // 116
509    87,  // 117
510    59,  // 118
511    119, // 119
512    15,  // 120
513    47,  // 121
514    61,  // 122
515    111, // 123
516    31,  // 124
517    95,  // 125
518    63,  // 126
519    127, // 127
520    1,   // 128
521    3,   // 129
522    5,   // 130
523    7,   // 131
524    9,   // 132
525    11,  // 133
526    13,  // 134
527    15,  // 135
528    17,  // 136
529    19,  // 137
530    21,  // 138
531    23,  // 139
532    25,  // 140
533    27,  // 141
534    29,  // 142
535    31,  // 143
536    9,   // 144
537    25,  // 145
538    37,  // 146
539    39,  // 147
540    37,  // 148
541    43,  // 149
542    45,  // 150
543    47,  // 151
544    19,  // 152
545    51,  // 153
546    53,  // 154
547    55,  // 155
548    39,  // 156
549    59,  // 157
550    61,  // 158
551    63,  // 159
552    5,   // 160
553    13,  // 161
554    21,  // 162
555    29,  // 163
556    37,  // 164
557    45,  // 165
558    53,  // 166
559    61,  // 167
560    21,  // 168
561    53,  // 169
562    85,  // 170
563    87,  // 171
564    43,  // 172
565    91,  // 173
566    87,  // 174
567    95,  // 175
568    11,  // 176
569    27,  // 177
570    43,  // 178
571    59,  // 179
572    45,  // 180
573    91,  // 181
574    91,  // 182
575    111, // 183
576    23,  // 184
577    55,  // 185
578    87,  // 186
579    119, // 187
580    47,  // 188
581    111, // 189
582    95,  // 190
583    127, // 191
584    3,   // 192
585    7,   // 193
586    11,  // 194
587    15,  // 195
588    19,  // 196
589    23,  // 197
590    27,  // 198
591    31,  // 199
592    25,  // 200
593    39,  // 201
594    43,  // 202
595    47,  // 203
596    51,  // 204
597    55,  // 205
598    59,  // 206
599    63,  // 207
600    13,  // 208
601    29,  // 209
602    45,  // 210
603    61,  // 211
604    53,  // 212
605    87,  // 213
606    91,  // 214
607    95,  // 215
608    27,  // 216
609    59,  // 217
610    91,  // 218
611    111, // 219
612    55,  // 220
613    119, // 221
614    111, // 222
615    127, // 223
616    7,   // 224
617    15,  // 225
618    23,  // 226
619    31,  // 227
620    39,  // 228
621    47,  // 229
622    55,  // 230
623    63,  // 231
624    29,  // 232
625    61,  // 233
626    87,  // 234
627    95,  // 235
628    59,  // 236
629    111, // 237
630    119, // 238
631    127, // 239
632    15,  // 240
633    31,  // 241
634    47,  // 242
635    63,  // 243
636    61,  // 244
637    95,  // 245
638    111, // 246
639    127, // 247
640    31,  // 248
641    63,  // 249
642    95,  // 250
643    127, // 251
644    63,  // 252
645    127, // 253
646    127, // 254
647    255, // 255
648];
649
650#[cfg(test)]
651mod tests {
652    use super::*;
653    use image::{GrayImage, Luma};
654    use test::{black_box, Bencher};
655
656    #[test]
657    fn test_uniform_representative_2() {
658        let a = 0b11110000;
659        assert_eq!(UNIFORM_REPRESENTATIVE_2[a], 0b00001111);
660        let b = 0b00000000;
661        assert_eq!(UNIFORM_REPRESENTATIVE_2[b], 0b00000000);
662        let c = 0b10011001;
663        assert_eq!(UNIFORM_REPRESENTATIVE_2[c], 0b10101010);
664    }
665
666    #[bench]
667    fn bench_local_binary_pattern(b: &mut Bencher) {
668        let image = GrayImage::from_fn(100, 100, |x, y| Luma([x as u8 % 2 + y as u8 % 2]));
669        b.iter(|| {
670            for y in 0..20 {
671                for x in 0..20 {
672                    let pattern = local_binary_pattern(&image, x, y);
673                    black_box(pattern);
674                }
675            }
676        });
677    }
678}