├── .gitignore ├── README.md ├── bin └── face.bmp ├── local_binary_pattern.cpp ├── local_binary_pattern.h ├── local_binary_pattern.pro ├── local_binary_pattern.py └── main.cpp /.gitignore: -------------------------------------------------------------------------------- 1 | build* 2 | *.pro.user 3 | *.exe 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # local_binary_pattern 2 | 用python和c++实现了原始的LBP、圆形LBP、旋转不变的圆形LBP、等价模式的圆形LBP、旋转不变的等价模式的圆形LBP。python版本LBP用于模型训练,c++版本LBP用于模型部署。 3 | -------------------------------------------------------------------------------- /bin/face.bmp: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhongqianli/local_binary_pattern/4cfd4928eb4309606adce3a90d6859baec8c8f01/bin/face.bmp -------------------------------------------------------------------------------- /local_binary_pattern.cpp: -------------------------------------------------------------------------------- 1 | /* 2 | Author: tim 3 | Function: LBP 4 | */ 5 | 6 | #include "local_binary_pattern.h" 7 | 8 | #include 9 | #include 10 | 11 | using namespace std; 12 | 13 | void compute_LBP_1_8_hist(const cv::Mat& image, cv::Mat &lbps_hist) 14 | { 15 | // fast, 3x3 LBP(1,8) Uniform; gray invariance, rotation invariance 16 | uniform_pattern_histogram(image, lbps_hist); 17 | } 18 | 19 | void compute_LBP_2_16_hist(const cv::Mat& image, cv::Mat &lbps_hist) 20 | { 21 | int P2 = 16; 22 | int R2 = 2; 23 | 24 | // LBP_C_216 25 | int bins = P2 + 2; 26 | 27 | lbps_hist = cv::Mat::zeros(1, bins, CV_32FC1); 28 | 29 | // LBP_C_216 30 | cv::Mat norm_lbp_hist2; 31 | local_binary_pattern_histogram(image, P2, R2, 0, norm_lbp_hist2); 32 | 33 | for(int c = 0; c < norm_lbp_hist2.cols; c++) 34 | { 35 | lbps_hist.at(0, c) = norm_lbp_hist2.at(0, c); 36 | } 37 | } 38 | 39 | void compute_LBP_3_24_hist(const cv::Mat& image, cv::Mat &lbps_hist) 40 | { 41 | int P3 = 24; 42 | int R3 = 3; 43 | 44 | int bins = P3 + 2; 45 | 46 | lbps_hist = cv::Mat::zeros(1, bins, CV_32FC1); 47 | 48 | cv::Mat norm_lbp_hist3; 49 | local_binary_pattern_histogram(image, P3, R3, 0, norm_lbp_hist3); 50 | 51 | for(int c = 0; c < norm_lbp_hist3.cols; c++) 52 | { 53 | lbps_hist.at(0, c) = norm_lbp_hist3.at(0, c); 54 | } 55 | } 56 | 57 | void compute_lbps_hist(const cv::Mat& image, cv::Mat &lbps_hist) 58 | { 59 | // fast, 3x3 LBP(1,8) Uniform; gray invariance, rotation invariance 60 | // uniform_pattern_histogram(image, lbps_hist); 61 | 62 | int P1 = 8; 63 | int R1 = 1; 64 | int P2 = 16; 65 | int R2 = 2; 66 | //// int P3 = 24; 67 | //// int R3 = 3; 68 | // // LBP(1,8)的bins = 8 + 2 69 | // // LBP(2,16)的bins = 16 + 2 70 | // // LBP(R,P)的bins = P + 2 71 | // int bins = P1 + 1 + P2 + 2; 72 | 73 | // LBP_C_216 74 | int bins = P2 + 2; 75 | 76 | lbps_hist = cv::Mat::zeros(1, bins, CV_32FC1); 77 | 78 | // cv::Mat norm_lbp_hist1; 79 | // local_binary_pattern_histogram(image, P1, R1, 0, norm_lbp_hist1); 80 | 81 | // for(int c = 0; c < norm_lbp_hist1.cols; c++) 82 | // { 83 | // lbps_hist.at(0, c) = norm_lbp_hist1.at(0, c); 84 | // } 85 | 86 | // cv::Mat norm_lbp_hist2; 87 | // local_binary_pattern_histogram(image, P2, R2, 0, norm_lbp_hist2); 88 | 89 | // for(int c = norm_lbp_hist1.cols; c < norm_lbp_hist1.cols + norm_lbp_hist2.cols; c++) 90 | // { 91 | // lbps_hist.at(0, c) = norm_lbp_hist2.at(0, c); 92 | // } 93 | 94 | // cv::Mat norm_lbp_hist3; 95 | // local_binary_pattern_histogram(image, P3, R3, 0, norm_lbp_hist3); 96 | 97 | // for(int c = norm_lbp_hist2.cols; c < norm_lbp_hist2.cols + norm_lbp_hist3.cols; c++) 98 | // { 99 | // lbps_hist.at(0, c) = norm_lbp_hist3.at(0, c); 100 | // } 101 | 102 | // LBP_C_216 103 | cv::Mat norm_lbp_hist2; 104 | local_binary_pattern_histogram(image, P2, R2, 0, norm_lbp_hist2); 105 | 106 | for(int c = 0; c < norm_lbp_hist2.cols; c++) 107 | { 108 | lbps_hist.at(0, c) = norm_lbp_hist2.at(0, c); 109 | } 110 | 111 | // cout << lbps_hist << endl; 112 | 113 | } 114 | 115 | cv::Mat compute_lbps_hist(const cv::Mat& image) 116 | { 117 | cv::Mat lbps_hist; 118 | compute_lbps_hist(image, lbps_hist); 119 | return lbps_hist; 120 | } 121 | 122 | /** 123 | """Bilinear interpolation at a given position in the image. 124 | Parameters 125 | ---------- 126 | image : double array 127 | Input image. 128 | rows, cols : int 129 | Shape of image. 130 | r, c : double 131 | Position at which to interpolate. 132 | mode : {'C', 'W', 'S', 'E', 'R'} 133 | Wrapping mode. Constant, Wrap, Symmetric, Edge or Reflect. 134 | cval : double 135 | Constant value to use for constant mode. 136 | Returns 137 | ------- 138 | value : double 139 | Interpolated value. 140 | """ 141 | */ 142 | double bilinear_interpolation(const cv::Mat &image, double r, double c) 143 | { 144 | int minr = int(floor(r)); 145 | int minc = int(floor(c)); 146 | int maxr = int(ceil(r)); 147 | int maxc = int(ceil(c)); 148 | double dr = r - minr; 149 | double dc = c - minc; 150 | double top = (1 - dc) * (int)image.at(minr, minc) + dc * (int)image.at(minr, maxc); 151 | double bottom = (1 - dc) * (int)image.at(maxr, minc) + dc * (int)image.at(maxr, maxc); 152 | return (1 - dr) * top + dr * bottom; 153 | } 154 | 155 | /** 156 | * @brief local_binary_pattern 157 | * @param image 158 | * @param P : points_num 159 | * @param R : radius 160 | * @param method : only uniform. TODO: default, ror, unifrom, var 161 | * @param lbp_image 162 | */ 163 | void local_binary_pattern(const cv::Mat &image, int P, int R, int method, cv::Mat &lbp_image) 164 | { 165 | std::vector texture(P); 166 | std::vector signed_texture(P); 167 | int rows = image.rows; 168 | int cols = image.cols; 169 | lbp_image = cv::Mat::zeros(rows - 2 * R, cols - 2 * R, CV_8UC1); 170 | 171 | int lbp = 0; 172 | 173 | double rr; 174 | double cc; 175 | double rp; 176 | double cp; 177 | 178 | int changes; 179 | 180 | for(int r = R; r < rows - 2 * R; r++) 181 | { 182 | for(int c = R; c < cols - 2 * R; c++) 183 | { 184 | lbp = 0; 185 | for(int i = 0; i < P; i++) 186 | { 187 | rp = - R * sin(2 * CV_PI * i / P); 188 | cp = R * cos(2 * CV_PI * i / P); 189 | // rp = floor(rr * 100000.000f) / 100000.000f; 190 | // cp = floor(cc * 100000.000f) / 100000.000f; 191 | 192 | texture[i] = (uchar)(cvRound(bilinear_interpolation(image, r + rp, c + cp))); 193 | // signed / thresholded texture 194 | if((int)texture[i] - (int)image.at(r, c) >= 0) 195 | { 196 | signed_texture[i] = (uchar)1; 197 | } 198 | else 199 | { 200 | signed_texture[i] = (uchar)0; 201 | } 202 | // cout << (int)signed_texture[i] << "\t" << (int)texture[i] << endl; 203 | } 204 | 205 | changes = 0; 206 | for(int i = 0; i < P-2; i++) 207 | { 208 | if(signed_texture[i] != signed_texture[i+1]) 209 | { 210 | changes++; 211 | } 212 | } 213 | if(changes <= 2) 214 | { 215 | for(int i = 0; i < P; i++) 216 | { 217 | lbp += (int)signed_texture[i]; 218 | } 219 | } 220 | else 221 | { 222 | lbp = P + 1; // P+1 223 | } 224 | 225 | lbp_image.at(r,c) = (uchar)lbp; 226 | // printf("(r,c,lbp) = (%d, %d, %d)\n", r, c, lbp); 227 | } 228 | } 229 | } 230 | 231 | void local_binary_pattern_histogram(const cv::Mat& image, int P, int R, int method, cv::Mat &norm_lbp_hist) 232 | { 233 | cv::Mat lbp_image; 234 | 235 | local_binary_pattern(image, P, R, method, lbp_image); 236 | 237 | // Uniform Pattern LBP(1,8), bins = 10 238 | cv::Mat lbp_hist = cv::Mat::zeros(1, P + 2, CV_32SC1); 239 | 240 | for(int r = 0; r < lbp_image.rows; r++) 241 | { 242 | for(int c = 0; c < lbp_image.cols; c++) 243 | { 244 | int bin = (int)lbp_image.at(r,c); 245 | lbp_hist.at(0, bin) += 1; 246 | } 247 | } 248 | 249 | // cout << lbp_hist << endl; 250 | 251 | norm_lbp_hist = cv::Mat::zeros(1, P + 2, CV_32FC1); 252 | 253 | 254 | for(int c = 0; c < norm_lbp_hist.cols; ++c) 255 | { 256 | norm_lbp_hist.at(0, c) = lbp_hist.at(0, c) / (lbp_image.rows * lbp_image.cols * 1.0); 257 | } 258 | } 259 | 260 | cv::Mat local_binary_pattern(const cv::Mat &image, int P, int R, int method) 261 | { 262 | cv::Mat lbp_image; 263 | local_binary_pattern(image, P, R, method, lbp_image); 264 | return lbp_image; 265 | } 266 | 267 | cv::Mat local_binary_pattern_histogram(const cv::Mat& image, int P, int R, int method) 268 | { 269 | cv::Mat norm_lbp_hist; 270 | local_binary_pattern_histogram(image, P, R, method, norm_lbp_hist); 271 | return norm_lbp_hist; 272 | } 273 | 274 | 275 | /** 276 | * @brief get_texture : 获取中心点的8领域像素点的像素值 277 | * @param image 278 | * @param rows 279 | * @param cols 280 | * @param r 281 | * @param c 282 | * @param i 283 | * @return 284 | */ 285 | uchar get_texture(const cv::Mat &image, int rows, int cols, int r, int c, int i) 286 | { 287 | uchar texture = 0; 288 | if(r == 0 && c == 0) 289 | { 290 | if(i == 0) 291 | {} 292 | else if( i == 1) 293 | {} 294 | else if( i == 2) 295 | {} 296 | else if( i == 3) 297 | texture = image.at(r, c + 1); 298 | else if( i == 4) 299 | texture = image.at(r + 1, c + 1); 300 | else if( i == 5) 301 | texture = image.at(r + 1, c); 302 | else if( i == 6) 303 | {} 304 | else if( i == 7) 305 | {} 306 | } 307 | else if( r == 0 && c > 0 && c < cols - 1) 308 | { 309 | if(i == 0) 310 | {} 311 | else if( i == 1) 312 | {} 313 | else if( i == 2) 314 | {} 315 | else if( i == 3) 316 | texture = image.at(r, c + 1); 317 | else if( i == 4) 318 | texture = image.at(r + 1, c + 1); 319 | else if( i == 5) 320 | texture = image.at(r + 1, c); 321 | else if( i == 6) 322 | texture = image.at(r + 1, c - 1); 323 | else if( i == 7) 324 | texture = image.at(r, c - 1); 325 | } 326 | else if( r == 0 && c == cols - 1) 327 | { 328 | if(i == 0) 329 | {} 330 | else if( i == 1) 331 | {} 332 | else if( i == 2) 333 | {} 334 | else if( i == 3) 335 | {} 336 | else if( i == 4) 337 | {} 338 | else if( i == 5) 339 | texture = image.at(r + 1, c); 340 | else if( i == 6) 341 | texture = image.at(r + 1, c - 1); 342 | else if( i == 7) 343 | texture = image.at(r, c - 1); 344 | } 345 | else if( r > 0 && r < rows -1 && c == cols - 1) 346 | { 347 | if(i == 0) 348 | texture = image.at(r - 1, c - 1); 349 | else if( i == 1) 350 | texture = image.at(r - 1, c); 351 | else if( i == 2) 352 | {} 353 | else if( i == 3) 354 | {} 355 | else if( i == 4) 356 | {} 357 | else if( i == 5) 358 | texture = image.at(r + 1, c); 359 | else if( i == 6) 360 | texture = image.at(r + 1, c - 1); 361 | else if( i == 7) 362 | texture = image.at(r, c - 1); 363 | } 364 | else if( r == rows - 1 && c == cols - 1) 365 | { 366 | if(i == 0) 367 | texture = image.at(r - 1, c - 1); 368 | else if( i == 1) 369 | texture = image.at(r - 1, c); 370 | else if( i == 2) 371 | {} 372 | else if( i == 3) 373 | {} 374 | else if( i == 4) 375 | {} 376 | else if( i == 5) 377 | {} 378 | else if( i == 6) 379 | {} 380 | else if( i == 7) 381 | texture = image.at(r, c - 1); 382 | } 383 | else if( r == rows - 1 && c > 0 && c < cols - 1) 384 | { 385 | if(i == 0) 386 | texture = image.at(r - 1, c - 1); 387 | else if( i == 1) 388 | texture = image.at(r - 1, c); 389 | else if( i == 2) 390 | texture = image.at(r - 1, c + 1); 391 | else if( i == 3) 392 | texture = image.at(r, c + 1); 393 | else if( i == 4) 394 | {} 395 | else if( i == 5) 396 | {} 397 | else if( i == 6) 398 | {} 399 | else if( i == 7) 400 | texture = image.at(r, c - 1); 401 | } 402 | else if( r == rows - 1 && c == 0) 403 | { 404 | if(i == 0) 405 | {} 406 | else if( i == 1) 407 | texture = image.at(r - 1, c); 408 | else if( i == 2) 409 | texture = image.at(r - 1, c + 1); 410 | else if( i == 3) 411 | texture = image.at(r, c + 1); 412 | else if( i == 4) 413 | {} 414 | else if( i == 5) 415 | {} 416 | else if( i == 6) 417 | {} 418 | else if( i == 7) 419 | {} 420 | } 421 | else if( r > 0 && r < rows - 1 && c == 0) 422 | { 423 | if(i == 0) 424 | {} 425 | else if( i == 1) 426 | texture = image.at(r - 1, c); 427 | else if( i == 2) 428 | texture = image.at(r - 1, c + 1); 429 | else if( i == 3) 430 | texture = image.at(r, c + 1); 431 | else if( i == 4) 432 | texture = image.at(r + 1, c + 1); 433 | else if( i == 5) 434 | texture = image.at(r + 1, c); 435 | else if( i == 6) 436 | {} 437 | else if( i == 7) 438 | {} 439 | } 440 | else 441 | { 442 | if(i == 0) 443 | texture = image.at(r - 1, c - 1); 444 | else if( i == 1) 445 | texture = image.at(r - 1, c); 446 | else if( i == 2) 447 | texture = image.at(r - 1, c + 1); 448 | else if( i == 3) 449 | texture = image.at(r, c + 1); 450 | else if( i == 4) 451 | texture = image.at(r + 1, c + 1); 452 | else if( i == 5) 453 | texture = image.at(r + 1, c); 454 | else if( i == 6) 455 | texture = image.at(r + 1, c - 1); 456 | else if( i == 7) 457 | texture = image.at(r, c - 1); 458 | } 459 | return texture; 460 | } 461 | 462 | /** 463 | * @brief uniform_pattern: 3x3 LBP , (P,R) = (8,1) 464 | * @param image : gray scale 465 | * @param lbp : gray scale 466 | */ 467 | void uniform_pattern(const cv::Mat &image, cv::Mat &lbp_image) 468 | { 469 | std::vector texture(8); 470 | std::vector signed_texture(8); 471 | lbp_image = cv::Mat::zeros(image.size(), CV_8UC1); 472 | int rows = image.rows; 473 | int cols = image.cols; 474 | int lbp = 0; 475 | for(int r = 0; r < image.rows; r++) 476 | { 477 | for(int c = 0; c < image.cols; c++) 478 | { 479 | lbp = 0; 480 | for(int i = 0; i < 8; i++) 481 | { 482 | texture[i] = get_texture(image, rows, cols, r, c, i); 483 | // signed / thresholded texture 484 | if((int)texture[i] - (int)image.at(r, c) >= 0) 485 | { 486 | signed_texture[i] = (uchar)1; 487 | } 488 | else 489 | { 490 | signed_texture[i] = (uchar)0; 491 | } 492 | } 493 | 494 | int changes = 0; 495 | for(int i = 0; i < 7; i++) 496 | { 497 | if(signed_texture[i] != signed_texture[i+1]) 498 | { 499 | changes++; 500 | } 501 | } 502 | if(changes <= 2) 503 | { 504 | for(int i = 0; i < 8; i++) 505 | { 506 | lbp += (int)signed_texture[i]; 507 | } 508 | } 509 | else 510 | { 511 | lbp = 9; // P+1 512 | } 513 | 514 | lbp_image.at(r,c) = (uchar)lbp; 515 | // printf("(r,c,lbp) = (%d, %d, %d)\n", r, c, lbp); 516 | } 517 | } 518 | } 519 | 520 | /** 521 | * @brief uniform_pattern_histogram : 3x3 LBP(1,8) 522 | * @param lbp_image : gray scale 523 | * @param hist : 10 bins 524 | */ 525 | void uniform_pattern_histogram(const cv::Mat& image, cv::Mat &norm_lbp_hist) 526 | { 527 | cv::Mat lbp_image; 528 | 529 | uniform_pattern(image, lbp_image); 530 | 531 | // Uniform Pattern LBP(1,8), bins = 10 532 | cv::Mat lbp_hist = cv::Mat::zeros(1, 10, CV_32SC1); 533 | 534 | for(int r = 0; r < lbp_image.rows; r++) 535 | { 536 | for(int c = 0; c < lbp_image.cols; c++) 537 | { 538 | int bin = (int)lbp_image.at(r,c); 539 | lbp_hist.at(0, bin) = lbp_hist.at(0, bin) + 1; 540 | } 541 | } 542 | 543 | // cout << lbp_hist << endl; 544 | 545 | norm_lbp_hist = cv::Mat::zeros(1, 10, CV_32FC1); 546 | 547 | 548 | for(int c = 0; c < norm_lbp_hist.cols; ++c) 549 | { 550 | norm_lbp_hist.at(0, c) = lbp_hist.at(0, c) / (image.rows * image.cols * 1.0); 551 | } 552 | 553 | } 554 | 555 | 556 | void LBP(const cv::Mat& image, cv::Mat& lbp_image) 557 | { 558 | lbp_image = cv::Mat::zeros(image.rows-2, image.cols-2, CV_8UC1); 559 | for(int i=1;i(i,j); 564 | unsigned char code = 0; 565 | code |= (image.at(i-1,j-1) > center) << 7; 566 | code |= (image.at(i-1,j) > center) << 6; 567 | code |= (image.at(i-1,j+1) > center) << 5; 568 | code |= (image.at(i,j+1) > center) << 4; 569 | code |= (image.at(i+1,j+1) > center) << 3; 570 | code |= (image.at(i+1,j) > center) << 2; 571 | code |= (image.at(i+1,j-1) > center) << 1; 572 | code |= (image.at(i,j-1) > center) << 0; 573 | lbp_image.at(i-1,j-1) = code; 574 | } 575 | } 576 | } 577 | -------------------------------------------------------------------------------- /local_binary_pattern.h: -------------------------------------------------------------------------------- 1 | /* 2 | Author: tim 3 | Function: LBP 4 | */ 5 | 6 | #ifndef LOCAL_BINARY_PATTERN_H 7 | #define LOCAL_BINARY_PATTERN_H 8 | 9 | #include 10 | 11 | void compute_LBP_1_8_hist(const cv::Mat& image, cv::Mat &lbps_hist); 12 | void compute_LBP_2_16_hist(const cv::Mat& image, cv::Mat &lbps_hist); 13 | void compute_LBP_3_24_hist(const cv::Mat& image, cv::Mat &lbps_hist); 14 | 15 | void compute_lbps_hist(const cv::Mat& image, cv::Mat &lbps_hist); 16 | 17 | cv::Mat compute_lbps_hist(const cv::Mat& image); 18 | 19 | double bilinear_interpolation(const cv::Mat &image, double r, double c); 20 | 21 | void local_binary_pattern(const cv::Mat &image, int P, int R, int method, cv::Mat &lbp_image); 22 | 23 | void local_binary_pattern_histogram(const cv::Mat& image, int P, int R, int method, cv::Mat &norm_lbp_hist); 24 | 25 | cv::Mat local_binary_pattern(const cv::Mat &image, int P, int R, int method); 26 | 27 | cv::Mat local_binary_pattern_histogram(const cv::Mat& image, int P, int R, int method); 28 | 29 | 30 | /** 31 | * @brief get_texture : 获取中心点的8领域像素点的像素值 32 | * @param image 33 | * @param rows 34 | * @param cols 35 | * @param r 36 | * @param c 37 | * @param i 38 | * @return 39 | */ 40 | uchar get_texture(const cv::Mat &image, int rows, int cols, int r, int c, int i); 41 | 42 | /** 43 | * @brief uniform_pattern: 3x3 LBP , (P,R) = (8,1) 44 | * @param image : gray scale 45 | * @param lbp : gray scale 46 | */ 47 | void uniform_pattern(const cv::Mat &image, cv::Mat &lbp_image); 48 | 49 | /** 50 | * @brief uniform_pattern_histogram : 3x3 LBP(1,8) 51 | * @param lbp_image : gray scale 52 | * @param hist : 10 bins 53 | */ 54 | void uniform_pattern_histogram(const cv::Mat& image, cv::Mat &norm_lbp_hist); 55 | 56 | void LBP(const cv::Mat& image, cv::Mat& lbp_image); 57 | 58 | #endif // LOCAL_BINARY_PATTERN_H 59 | -------------------------------------------------------------------------------- /local_binary_pattern.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | CONFIG += console 3 | CONFIG -= app_bundle 4 | CONFIG -= qt 5 | 6 | 7 | # opencv_win begin 8 | OPENCV_VER = 340 9 | COMPILER = vs2015 10 | PLATFORM = x86 11 | OPENCV_DIR = E:/0-myWorkSpace/0-3rdparty/opencv$$OPENCV_VER 12 | OPENCV_LIB_DIR = $$OPENCV_DIR/$$COMPILER/$$PLATFORM/lib 13 | 14 | message($$OPENCV_DIR) 15 | 16 | INCLUDEPATH += $$OPENCV_DIR/include 17 | INCLUDEPATH += $$OPENCV_DIR/include/opencv 18 | INCLUDEPATH += $$OPENCV_DIR/include/opencv2 19 | 20 | CONFIG(debug, debug|release){ 21 | message(debug mode) 22 | 23 | DESTDIR = $$PWD/bin-debug 24 | 25 | if(equals(COMPILER, "vs2015")){ 26 | 27 | message("compiler is" $$COMPILER) 28 | } 29 | 30 | 31 | }else{ 32 | message(release mode) 33 | 34 | DESTDIR = $$PWD/bin 35 | 36 | if(equals(COMPILER, "vs2015")){ 37 | message("compiler is" $$COMPILER) 38 | 39 | LIBS += $$OPENCV_LIB_DIR/opencv_calib3d"$$OPENCV_VER".lib 40 | LIBS += $$OPENCV_LIB_DIR/opencv_core"$$OPENCV_VER".lib 41 | LIBS += $$OPENCV_LIB_DIR/opencv_dnn"$$OPENCV_VER".lib 42 | LIBS += $$OPENCV_LIB_DIR/opencv_features2d"$$OPENCV_VER".lib 43 | LIBS += $$OPENCV_LIB_DIR/opencv_flann"$$OPENCV_VER".lib 44 | LIBS += $$OPENCV_LIB_DIR/opencv_highgui"$$OPENCV_VER".lib 45 | LIBS += $$OPENCV_LIB_DIR/opencv_imgcodecs"$$OPENCV_VER".lib 46 | LIBS += $$OPENCV_LIB_DIR/opencv_imgproc"$$OPENCV_VER".lib 47 | LIBS += $$OPENCV_LIB_DIR/opencv_ml"$$OPENCV_VER".lib 48 | LIBS += $$OPENCV_LIB_DIR/opencv_objdetect"$$OPENCV_VER".lib 49 | LIBS += $$OPENCV_LIB_DIR/opencv_photo"$$OPENCV_VER".lib 50 | LIBS += $$OPENCV_LIB_DIR/opencv_shape"$$OPENCV_VER".lib 51 | LIBS += $$OPENCV_LIB_DIR/opencv_stitching"$$OPENCV_VER".lib 52 | LIBS += $$OPENCV_LIB_DIR/opencv_superres"$$OPENCV_VER".lib 53 | LIBS += $$OPENCV_LIB_DIR/opencv_ts"$$OPENCV_VER".lib 54 | LIBS += $$OPENCV_LIB_DIR/opencv_video"$$OPENCV_VER".lib 55 | LIBS += $$OPENCV_LIB_DIR/opencv_videoio"$$OPENCV_VER".lib 56 | LIBS += $$OPENCV_LIB_DIR/opencv_videostab"$$OPENCV_VER".lib 57 | } 58 | 59 | if(equals(COMPILER, "mingw530")){ 60 | message("compiler is" $$COMPILER) 61 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_calib3d"$$OPENCV_VER".dll.a 62 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_core"$$OPENCV_VER".dll.a 63 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_dnn"$$OPENCV_VER".dll.a 64 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_features2d"$$OPENCV_VER".dll.a 65 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_flann"$$OPENCV_VER".dll.a 66 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_highgui"$$OPENCV_VER".dll.a 67 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_imgcodecs"$$OPENCV_VER".dll.a 68 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_imgproc"$$OPENCV_VER".dll.a 69 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_ml"$$OPENCV_VER".dll.a 70 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_objdetect"$$OPENCV_VER".dll.a 71 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_photo"$$OPENCV_VER".dll.a 72 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_shape"$$OPENCV_VER".dll.a 73 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_stitching"$$OPENCV_VER".dll.a 74 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_superres"$$OPENCV_VER".dll.a 75 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_ts"$$OPENCV_VER".a 76 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_video"$$OPENCV_VER".dll.a 77 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_videoio"$$OPENCV_VER".dll.a 78 | LIBS += -L $$OPENCV_LIB_DIR/libopencv_videostab"$$OPENCV_VER".dll.a 79 | } 80 | 81 | } 82 | 83 | # opencv_win end 84 | 85 | SOURCES += main.cpp \ 86 | local_binary_pattern.cpp 87 | 88 | HEADERS += \ 89 | local_binary_pattern.h 90 | 91 | -------------------------------------------------------------------------------- /local_binary_pattern.py: -------------------------------------------------------------------------------- 1 | # import the necessary packages 2 | #from skimage import feature 3 | import numpy as np 4 | import cv2 5 | 6 | ''' 7 | The LBP imitated from scikit-image. 8 | Compare to scikit-image's LBP, the imitated LBP has subtile difference. 9 | 10 | result: 11 | 12 | scikit-image's LBP, maybe based on C 13 | t = 18.5292077033 ms 14 | [ 0.01032236 0.03460219 0.03182442 0.13655693 0.29533608 0.24399863 15 | 0.08336763 0.05126886 0.06241427 0.05030864] 16 | 17 | imitated LBP, based on python 18 | t = 2072.75384571 ms 19 | [ 0.00826475 0.02280521 0.0372428 0.10874486 0.28964335 0.24543896 20 | 0.09588477 0.06323731 0.06004801 0.06868999] 21 | 22 | ''' 23 | 24 | from skimage import feature 25 | import numpy as np 26 | import cv2 27 | 28 | def calc_LBP_hist(image, P, R): 29 | lbp = feature.local_binary_pattern(image, P, R, method="uniform") 30 | hist = histogram(lbp, P, R) 31 | return hist 32 | 33 | def histogram(lbp, P, R): 34 | (hist, _) = np.histogram(lbp.ravel(), 35 | bins=np.arange(0, P + 3), 36 | range=(0, P + 2)) 37 | 38 | # normalize the histogram 39 | hist = np.float32(hist) 40 | eps = 1e-7 41 | hist /= (hist.sum() + eps) 42 | return hist 43 | 44 | def original_lbp(image): 45 | """origianl local binary pattern""" 46 | rows = image.shape[0] 47 | cols = image.shape[1] 48 | 49 | lbp_image = np.zeros((rows - 2, cols - 2), np.uint8) 50 | 51 | for i in range(1, rows - 1): 52 | for j in range(1, cols - 1): 53 | code = 0 54 | center_pix = image[i, j] 55 | if image[i - 1, j - 1] > center_pix: 56 | code = code | (1 << 7) 57 | if image[i - 1, j] > center_pix: 58 | code = code | (1 << 6) 59 | if image[i - 1, j + 1] > center_pix: 60 | code = code | (1 << 5) 61 | if image[i, j + 1] > center_pix: 62 | code = code | (1 << 4) 63 | if image[i + 1, j + 1] > center_pix: 64 | code = code | (1 << 3) 65 | if image[i + 1, j] > center_pix: 66 | code = code | (1 << 2) 67 | if image[i + 1, j - 1] > center_pix: 68 | code = code | (1 << 1) 69 | if image[i, j - 1] > center_pix: 70 | code = code | (1 << 0) 71 | lbp_image[i - 1, j - 1] = code 72 | return lbp_image 73 | 74 | 75 | # scikit-image LBP 76 | def local_binary_pattern(image, P, R, method='U'): 77 | """Gray scale and rotation invariant LBP (Local Binary Patterns). 78 | 79 | LBP is an invariant descriptor that can be used for texture classification. 80 | 81 | Parameters 82 | ---------- 83 | image : (N, M) array 84 | Graylevel image. 85 | P : int 86 | Number of circularly symmetric neighbour set points (quantization of 87 | the angular space). 88 | R : float 89 | Radius of circle (spatial resolution of the operator). 90 | method : {'default', 'ror', 'uniform', 'var'} 91 | Method to determine the pattern. 92 | 93 | * 'default': original local binary pattern which is gray scale but not 94 | rotation invariant. 95 | * 'ror': extension of default implementation which is gray scale and 96 | rotation invariant. 97 | * 'uniform': improved rotation invariance with uniform patterns and 98 | finer quantization of the angular space which is gray scale and 99 | rotation invariant. 100 | * 'nri_uniform': non rotation-invariant uniform patterns variant 101 | which is only gray scale invariant [2]_. 102 | * 'var': rotation invariant variance measures of the contrast of local 103 | image texture which is rotation but not gray scale invariant. 104 | 105 | Returns 106 | ------- 107 | output : (N, M) array 108 | LBP image. 109 | 110 | References 111 | ---------- 112 | .. [1] Multiresolution Gray-Scale and Rotation Invariant Texture 113 | Classification with Local Binary Patterns. 114 | Timo Ojala, Matti Pietikainen, Topi Maenpaa. 115 | http://www.rafbis.it/biplab15/images/stories/docenti/Danielriccio/Articoliriferimento/LBP.pdf, 2002. 116 | .. [2] Face recognition with local binary patterns. 117 | Timo Ahonen, Abdenour Hadid, Matti Pietikainen, 118 | http://citeseerx.ist.psu.edu/viewdoc/summary?doi=10.1.1.214.6851, 119 | 2004. 120 | """ 121 | # methods = { 122 | # 'default': ord('D'), 123 | # 'ror': ord('R'), 124 | # 'uniform': ord('U'), 125 | # 'nri_uniform': ord('N'), 126 | # 'var': ord('V') 127 | # } 128 | image = np.ascontiguousarray(image, dtype=np.double) 129 | output = _local_binary_pattern(image, P, R, method) 130 | return output 131 | 132 | 133 | def _bit_rotate_right(value, length): 134 | """Cyclic bit shift to the right. 135 | 136 | Parameters 137 | ---------- 138 | value : int 139 | integer value to shift 140 | length : int 141 | number of bits of integer 142 | 143 | """ 144 | return (value >> 1) | ((value & 1) << (length - 1)) 145 | 146 | 147 | def get_texture(image, rows, cols, r, c, i): 148 | texture = 0 149 | if r == 0 and c == 0: # 0 150 | if i == 0: 151 | pass 152 | elif i == 1: 153 | pass 154 | elif i == 2: 155 | pass 156 | elif i == 3: 157 | texture = image[r, c + 1] 158 | elif i == 4: 159 | texture = image[r + 1, c + 1] 160 | elif i == 5: 161 | texture = image[r + 1, c] 162 | elif i == 6: 163 | pass 164 | elif i == 7: 165 | pass 166 | elif r == 0 and c > 0 and c < cols - 1: # 1 167 | if i == 0: 168 | pass 169 | elif i == 1: 170 | pass 171 | elif i == 2: 172 | pass 173 | elif i == 3: 174 | texture = image[r, c + 1] 175 | elif i == 4: 176 | texture = image[r + 1, c + 1] 177 | elif i == 5: 178 | texture = image[r + 1, c] 179 | elif i == 6: 180 | texture = image[r + 1, c - 1] 181 | elif i == 7: 182 | texture = image[r, c - 1] 183 | elif r == 0 and c == cols - 1: # 2 184 | if i == 0: 185 | pass 186 | elif i == 1: 187 | pass 188 | elif i == 2: 189 | pass 190 | elif i == 3: 191 | pass 192 | elif i == 4: 193 | pass 194 | elif i == 5: 195 | texture = image[r + 1, c] 196 | elif i == 6: 197 | texture = image[r + 1, c - 1] 198 | elif i == 7: 199 | texture = image[r, c - 1] 200 | elif r > 0 and r < rows -1 and c == cols - 1: # 3 201 | if i == 0: 202 | texture = image[r - 1, c - 1] 203 | elif i == 1: 204 | texture = image[r - 1, c] 205 | elif i == 2: 206 | pass 207 | elif i == 3: 208 | pass 209 | elif i == 4: 210 | pass 211 | elif i == 5: 212 | texture = image[r + 1, c] 213 | elif i == 6: 214 | texture = image[r + 1, c - 1] 215 | elif i == 7: 216 | texture = image[r, c - 1] 217 | elif r == rows - 1 and c == cols - 1: # 4 218 | if i == 0: 219 | texture = image[r - 1, c - 1] 220 | elif i == 1: 221 | texture = image[r - 1, c] 222 | elif i == 2: 223 | pass 224 | elif i == 3: 225 | pass 226 | elif i == 4: 227 | pass 228 | elif i == 5: 229 | pass 230 | elif i == 6: 231 | pass 232 | elif i == 7: 233 | texture = image[r, c - 1] 234 | elif r == rows - 1 and c > 0 and c < cols - 1: # 5 235 | if i == 0: 236 | texture = image[r - 1, c - 1] 237 | elif i == 1: 238 | texture = image[r - 1, c] 239 | elif i == 2: 240 | texture = image[r - 1, c + 1] 241 | elif i == 3: 242 | texture = image[r, c + 1] 243 | elif i == 4: 244 | pass 245 | elif i == 5: 246 | pass 247 | elif i == 6: 248 | pass 249 | elif i == 7: 250 | texture = image[r, c - 1] 251 | elif r == rows - 1 and c == 0: # 6 252 | if i == 0: 253 | pass 254 | elif i == 1: 255 | texture = image[r - 1, c] 256 | elif i == 2: 257 | texture = image[r - 1, c + 1] 258 | elif i == 3: 259 | texture = image[r, c + 1] 260 | elif i == 4: 261 | pass 262 | elif i == 5: 263 | pass 264 | elif i == 6: 265 | pass 266 | elif i == 7: 267 | pass 268 | elif r > 0 and r < rows - 1 and c == 0: # 7 269 | if i == 0: 270 | pass 271 | elif i == 1: 272 | texture = image[r - 1, c] 273 | elif i == 2: 274 | texture = image[r - 1, c + 1] 275 | elif i == 3: 276 | texture = image[r, c + 1] 277 | elif i == 4: 278 | texture = image[r + 1, c + 1] 279 | elif i == 5: 280 | texture = image[r + 1, c] 281 | elif i == 6: 282 | pass 283 | elif i == 7: 284 | pass 285 | else: # 8 286 | if i == 0: 287 | texture = image[r - 1, c - 1] 288 | elif i == 1: 289 | texture = image[r - 1, c] 290 | elif i == 2: 291 | texture = image[r - 1, c + 1] 292 | elif i == 3: 293 | texture = image[r, c + 1] 294 | elif i == 4: 295 | texture = image[r + 1, c + 1] 296 | elif i == 5: 297 | texture = image[r + 1, c] 298 | elif i == 6: 299 | texture = image[r + 1, c - 1] 300 | elif i == 7: 301 | texture = image[r, c - 1] 302 | return texture 303 | 304 | 305 | def _local_binary_pattern(image, P, R, method='U'): 306 | """Gray scale and rotation invariant LBP (Local Binary Patterns). 307 | 308 | LBP is an invariant descriptor that can be used for texture classification. 309 | 310 | Parameters 311 | ---------- 312 | image : (N, M) double array 313 | Graylevel image. 314 | P : int 315 | Number of circularly symmetric neighbour set points (quantization of 316 | the angular space). 317 | R : float 318 | Radius of circle (spatial resolution of the operator). 319 | method : {'D', 'R', 'U', 'N', 'V'} 320 | Method to determine the pattern. 321 | 322 | * 'D': 'default' 323 | * 'R': 'ror' 324 | * 'U': 'uniform' 325 | * 'N': 'nri_uniform' 326 | * 'V': 'var' 327 | 328 | Returns 329 | ------- 330 | output : (N, M) array 331 | LBP image. 332 | """ 333 | # texture weights 334 | weights = 2 ** np.arange(P, dtype=np.int32) 335 | 336 | # pre-allocate arrays for computation 337 | texture = np.zeros(P, dtype=np.double) 338 | signed_texture = np.zeros(P, dtype=np.int8) 339 | rotation_chain = np.zeros(P, dtype=np.int32) 340 | 341 | output_shape = (image.shape[0], image.shape[1]) 342 | output = np.zeros(output_shape, dtype=np.double) 343 | rows = image.shape[0] 344 | cols = image.shape[1] 345 | 346 | lbp = 0 347 | r = 0 348 | c = 0 349 | changes = 0 350 | i = 0 351 | rot_index = 0 352 | n_ones = 0 353 | first_zero = 0 354 | first_one = 0 355 | sum_ = 0 356 | var_ = 0 357 | texture_i = 0 358 | 359 | for r in range(image.shape[0]): 360 | for c in range(image.shape[1]): 361 | lbp = 0 362 | for i in range(P): 363 | texture[i] = get_texture(image, rows, cols, r, c, i) 364 | # signed / thresholded texture 365 | if texture[i] - image[r, c] >= 0: 366 | signed_texture[i] = 1 367 | else: 368 | signed_texture[i] = 0 369 | 370 | # if method == 'uniform': 371 | if method == 'U': 372 | # determine number of 0 - 1 changes 373 | changes = 0 374 | for i in range(P - 1): 375 | changes += (signed_texture[i] 376 | - signed_texture[i + 1]) != 0 377 | 378 | if changes <= 2: 379 | for i in range(P): 380 | lbp += signed_texture[i] 381 | else: 382 | lbp = P + 1 383 | else: 384 | # method == 'default' 385 | for i in range(P): 386 | lbp += signed_texture[i] * weights[i] 387 | 388 | # method == 'ror' 389 | if method == 'R': 390 | # shift LBP P times to the right and get minimum value 391 | rotation_chain[0] = lbp 392 | for i in range(1, P): 393 | rotation_chain[i] = \ 394 | _bit_rotate_right(rotation_chain[i - 1], P) 395 | lbp = rotation_chain[0] 396 | for i in range(1, P): 397 | lbp = min(lbp, rotation_chain[i]) 398 | 399 | output[r, c] = lbp 400 | return np.asarray(output) 401 | 402 | 403 | if __name__ == '__main__': 404 | image = cv2.imread("../../resources/images/face.jpg", 0) 405 | 406 | cv2.imshow("image", image) 407 | 408 | rows, cols = image.shape[:2] 409 | 410 | # org_lbp_image = original_lbp(image) 411 | # cv2.imshow("org_lbp_image", org_lbp_image) 412 | 413 | P = 8 414 | R = 1 415 | 416 | hist = calc_LBP_hist(image, P, R) 417 | 418 | print hist 419 | print type(hist) 420 | 421 | # bt = cv2.getTickCount() 422 | # lbp1 = feature.local_binary_pattern(image, P, R, method="uniform") 423 | # hist = histogram(lbp1, P, R) 424 | # et = cv2.getTickCount() 425 | # 426 | # t = (et - bt) * 1000.0 / cv2.getTickFrequency() 427 | # print 't = {0} ms'.format(t) 428 | 429 | # print hist 430 | 431 | # cv2.imshow('skimage LBP U', lbp1 / 12) 432 | 433 | # bt = cv2.getTickCount() 434 | # lbp2 = local_binary_pattern(image, P, R, method='U') 435 | # hist = histogram(lbp2, P, R) 436 | # et = cv2.getTickCount() 437 | 438 | # t = (et - bt) * 1000.0 / cv2.getTickFrequency() 439 | # print 't = {0} ms'.format(t) 440 | # 441 | # 442 | # cv2.imshow('imitated LBP U', lbp2 / 12) 443 | 444 | cv2.waitKey() 445 | -------------------------------------------------------------------------------- /main.cpp: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "local_binary_pattern.h" 7 | 8 | using namespace std; 9 | using namespace cv; 10 | 11 | int main(int argc, char **argv) 12 | { 13 | string filename; 14 | 15 | if(argc != 2) 16 | { 17 | cout << "USAGE: ./lbp_image [IMAGE]\n"; 18 | // return 1; 19 | filename = "face.bmp"; 20 | } 21 | else 22 | { 23 | filename = argv[1]; 24 | } 25 | 26 | Mat image = imread(filename, CV_LOAD_IMAGE_GRAYSCALE); 27 | 28 | if(image.empty()) 29 | { 30 | cout << "no image" << endl; 31 | return -1; 32 | } 33 | 34 | cv::resize(image, image, cv::Size(200, 200)); 35 | 36 | cv::imshow("image", image); 37 | 38 | /////////////////////////////////// 39 | cv::Mat lbp_hist; 40 | compute_LBP_1_8_hist(image, lbp_hist); 41 | cout << "lbp18: " << lbp_hist << endl; 42 | compute_LBP_2_16_hist(image, lbp_hist); 43 | cout << "lbp216: " << lbp_hist << endl; 44 | compute_LBP_3_24_hist(image, lbp_hist); 45 | cout << "lbp324: " << lbp_hist << endl; 46 | //////////////////////////////////////////// 47 | 48 | Mat lbp_image; 49 | LBP(image, lbp_image); 50 | 51 | // std::cout << image.size() << endl; 52 | 53 | imshow("lbp", lbp_image); 54 | 55 | // cv::Mat lbp_image; 56 | 57 | int bt = cv::getTickCount(); 58 | uniform_pattern(image, lbp_image); 59 | int et = cv::getTickCount(); 60 | double t = (et - bt)*1000.0 / cv::getTickFrequency(); 61 | printf("uniform_pattern : t = %d ms\n", (int)t); 62 | 63 | imshow("uniform lbp", lbp_image/10.0*255.0); 64 | 65 | int P = 8; 66 | int R = 1; 67 | int method = 0; 68 | cv::Mat uniform_lbp; 69 | cv::Mat norm_lbp_hist; 70 | 71 | bt = cv::getTickCount(); 72 | 73 | uniform_lbp = local_binary_pattern(image, P, R, method); 74 | norm_lbp_hist = local_binary_pattern_histogram(image, P, R, method); 75 | 76 | et = cv::getTickCount(); 77 | t = (et - bt)*1000.0 / cv::getTickFrequency(); 78 | printf("local_binary_pattern : t = %d ms\n", (int)t); 79 | 80 | cout << norm_lbp_hist << endl; 81 | 82 | imshow("uniform_lbp", uniform_lbp/10.0*255.0); 83 | 84 | 85 | waitKey(0); 86 | 87 | return 0; 88 | 89 | } 90 | 91 | --------------------------------------------------------------------------------