图像处理之局部二值特征
一:局部二值模式(LBP)介绍
局部二值模式(Local Binary Pattern)主要用来实现2D图像纹理分析。其基本思想是用每个像素跟它周围的像素相比较得到局部图像结构,假设中心像素值大于相邻像素值则则相邻像素点赋值为1,否则赋值为0,最终对每个像素点都会得到一个二进制八位的表示,比如11100111。假设3x3的窗口大小,这样对每个像素点来说组合得到的像素值的空间为[0~2^8]。这种结果我称为图像的局部二值模式或者简写为了LBP。
二:局部二值模式(LBP)扩展
对于这种固定窗口大小方式的局部二值模式,很多人很快就发现它的弊端,不能很好的反映出图像结构,于是高人纷纷上阵把它改为窗口大小可变,而且把矩形结构改成圆形结构。而且还总结出来如下一系列的典型结构单元:
该操作是基于原来的局部二值模式的扩展,所以又被称为扩展的局部二值模式。但是一旦改为圆形的时候,寻找八个点坐标可能会产生小数坐标,这个时候就需要通过插值方式产生该像素点的像素值,最常见的插值方式基于双线性插值。这样就完成了任意尺度上的局部二值模式的采样。
三:运行
输入图像与3x3默认的LBP运行结果如下:
在扩展模式下半径分别为1、3、5、7时候的运行结果:
四:代码实现 - 基于OpenCV实现
简单说一下步骤
1. 读入图像
2. 彩色图像转灰度
3. 默认LBP处理操作
4. 扩展LBP处理操作
完整的源代码如下:
#include <opencv2/opencv.hpp>
#include <iostream>
#include "math.h"
using namespace cv;
using namespace std;
int max_thresh = 20;
int current_radius = 5;
Mat gray_src, LBP_image, ELBP_image;
void Demo_ELBP(int, void*);
int main(int argc, char** argv) {
Mat src, dst;
src = imread("D:/vcprojects/images/cat.jpg");
if (src.empty()) {
printf("could not load image...\n");
return -1;
}
const char* output_tt = "LBP Result";
namedWindow("input image", CV_WINDOW_AUTOSIZE);
namedWindow(output_tt, CV_WINDOW_AUTOSIZE);
imshow("input image", src);
// convert to gray
cvtColor(src, gray_src, COLOR_BGR2GRAY);
int width = gray_src.cols;
int height = gray_src.rows;
// default LBP image
LBP_image = Mat::zeros(src.rows - 2, src.cols - 2, CV_8UC1);
for (int row = 1; row < height-1; row++) {
for (int col = 1; col < width-1; col++) {
uchar center = gray_src.at<uchar>(row, col);
uchar code = 0;
code |= (gray_src.at<uchar>(row - 1, col - 1) > center) << 7;
code |= (gray_src.at<uchar>(row - 1, col) > center) << 6;
code |= (gray_src.at<uchar>(row - 1, col + 1) > center) << 5;
code |= (gray_src.at<uchar>(row, col + 1) > center) << 4;
code |= (gray_src.at<uchar>(row+ 1, col + 1) > center) << 3;
code |= (gray_src.at<uchar>(row + 1, col) > center) << 2;
code |= (gray_src.at<uchar>(row + 1, col - 1) > center) << 1;
code |= (gray_src.at<uchar>(row, col - 1) > center) << 0;
LBP_image.at<uchar>(row- 1, col - 1) = code;
}
}
imshow(output_tt, LBP_image);
// extend LBP
namedWindow("ELBP_Result", CV_WINDOW_AUTOSIZE);
createTrackbar("Radius:", "ELBP_Result", ¤t_radius, max_thresh, Demo_ELBP);
Demo_ELBP(0, 0);
waitKey(0);
return 0;
}
void Demo_ELBP(int, void*) {
int offset = current_radius * 2;
ELBP_image = Mat::zeros(gray_src.rows - offset, gray_src.cols - offset, CV_8UC1);
int height = gray_src.rows;
int width = gray_src.cols;
int neighbors = 8;
for (int n = 0; n<neighbors; n++) {
// sample points
float x = static_cast<float>(current_radius) * cos(2.0*CV_PI*n / static_cast<float>(neighbors));
float y = static_cast<float>(current_radius) * -sin(2.0*CV_PI*n / static_cast<float>(neighbors));
// relative indices
int fx = static_cast<int>(floor(x));
int fy = static_cast<int>(floor(y));
int cx = static_cast<int>(ceil(x));
int cy = static_cast<int>(ceil(y));
// fractional part
float ty = y - fy;
float tx = x - fx;
// set interpolation weights
float w1 = (1 - tx) * (1 - ty);
float w2 = tx * (1 - ty);
float w3 = (1 - tx) * ty;
float w4 = tx * ty;
// iterate through your data
for (int i = current_radius; i < gray_src.rows - current_radius; i++) {
for (int j = current_radius; j < gray_src.cols - current_radius; j++) {
float t = w1*gray_src.at<uchar>(i + fy, j + fx) + w2*gray_src.at<uchar>(i + fy, j + cx) + w3*gray_src.at<uchar>(i + cy, j + fx) + w4*gray_src.at<uchar>(i + cy, j + cx);
// we are dealing with floating point precision, so add some little tolerance
ELBP_image.at<uchar>(i - current_radius, j - current_radius) += ((t > gray_src.at<uchar>(i, j)) && (abs(t - gray_src.at<uchar>(i, j)) > std::numeric_limits<float>::epsilon())) << n;
}
}
}
imshow("ELBP_Result", ELBP_image);
}
请继续关注本博客,同时关注微信公众号【OpenCV学堂】定期推送更多干货,图像处理算法!