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}