图像处理之局部二值特征
一:局部二值模式(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); }