0
点赞
收藏
分享

微信扫一扫

OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)

沈芏 2022-07-18 阅读 52


文章目录

  • ​​一、LBP 简介​​
  • ​​原始的LBP算子​​
  • ​​LBP的改进版本:​​
  • ​​MB-LBP特征​​
  • ​​LBPH,图像的LBP特征向量​​
  • ​​二、LBP 演示​​
  • ​​原始 LBP 效果演示​​
  • ​​eLBP效果演示​​

一、LBP 简介

LBP(Local Binary Pattern,局部二值模式) 是一种用来描述图像局部纹理特征的算子;
具有 旋转不变性灰度不变性 等显著的优点。用于提取图像的局部的纹理特征

关于算法详情,写的非常好:
]]

原始的LBP算子

定义为在 3*3 的窗口内,以窗口中心像素为阈值,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素 点的位置被标记为1,否则为0。

这样,3*3邻域内的8个点经比较可产生8位二进制数(通常转换为十进制数即LBP码,共256种),即得到该窗口中心像 素点的LBP值,并用这个值来反映该区域的纹理信息(如下图)。

OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_直方图

一些LBP特征的表达:

OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_直方图_02

LBP的改进版本:

  1. 圆形LBP算子
    基本的 LBP算子的最大缺陷在于它只覆盖了一个固定半径范围内的小区域,这显然不能满足不同尺寸和频率纹理的需要。为了适应不同尺度的纹理特征,并达到灰度和旋转不变性的要求。
    Ojala等对 LBP 算子进行了改进,将 3×3邻域扩展到任意邻域,并用圆形邻域代替了正方形邻域,改进后的 LBP 算子允许在半径为 R 的圆形邻域内有任意多个像素点。从而得到了诸如半径为R的圆形区域内含有P个采样点的LBP算子;
    比如下图定了一个5x5的邻域:
  2. OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_直方图_03

  3. 但是计算得到的坐标未必完全是整数,所以可以通过双线性插值来得到该采样点的像素值:
  4. OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_opencv_04

  5. LBP旋转不变模式:
    从 LBP 的定义可以看出,LBP 算子是灰度不变的,但却不是旋转不变的。图像的旋转就会得到不同的 LBP值。Maenpaa等人又将 LBP算子进行了扩展,提出了具有旋转不变性的 LBP 算子,即不断旋转圆形邻域得到一系列初始定义的 LBP值,取其最小值作为该邻域的 LBP 值。
  6. OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_邻域_05

  7. LBP等价模式:
    一个LBP算子可以产生不同的二进制模式,对于半径为R的圆形区域内含有P个采样点的LBP算子将会产生
    例如,将LBP算子用于纹理分类或人脸识别时,常采用LBP模式的统计直方图来表达图像的信息,而较多的模式种类将使得数据量过大,且直方图过于稀疏。因此,需要对原始的LBP模式进行降维,使得数据量减少的情况下能最好的代表图像的信息。
    为了解决二进制模式过多的问题,提高统计性,Ojala提出了采用一种“等价模式”(Uniform Pattern) 来对LBP算子的模式种类进行降维。Ojala等认为,在实际图像中,绝大多数LBP模式最多只包含两次从1到0或从0到1的跳变。因此,Ojala将“等价模式”定义为:当某个LBP所对应的循环二进制数从0到1或从1到0最多有两次跳变时,该LBP所对应的二进制就称为一个等价模式类。如00000000(0次跳变),00000111(只含一次从0到1的跳变),10001111(先由1跳到0,再由0跳到1,共两次跳变)都是等价模式类。
    除等价模式类以外的模式都归为另一类,称为混合模式类,例如10010111(共四次跳变) 通过这样的改进,二进制模式的种类大大减少,而不会丢失任何信息。模式数量由原来的 种减少为 P ( P-1)+2种,其中P表示邻域集内的采样点数。对于3×3邻域内8个采样点来说,二进制模式由原始的256种减少为58种,这使得特征向量的维数更少,并且可以减少高频噪声带来的影响。
  8. OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_邻域_08

MB-LBP特征

MB-LBP特征,全称为Multiscale Block LBP,在Traincascade级联目标训练检测中的LBP特征使用的就是MB-LBP。

OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_邻域_09


将图像分成小块(Block),每个小块再分为一个个的小区域(类似于HOG中的cell),小区域内的灰度平均值作为当前小区域的灰度值,与周围小区域灰度进行比较形成LBP特征,生成的特征称为MB-LBP。Block大小为且小区域的大小为1时,就是原始的LBP特征。

作者对得到LBP特征又进行了均值模式编码,通过对得到的特征图求直方图,得到了LBP特征值0-255之间(0-255即直方图中的bin)的特征数量,通过对bin中的数值进行排序,通过权衡,将排序在前63位的特征值看作是等价模式类,其他的为混合模式类,总共64类,作者在论文中称之为SEMB-LBP(Statistically Effective MB-LBP )。类似于等价模式LBP,等价模式的LBP的等价模式类为58种,混合模式类1种,共59种。二者除了等价模式类的数量不同之外,主要区别在于:对等价模式类的定义不同,等价模式LBP是根据0-1的跳变次数定义的,而SEMB-LBP是通过对直方图排序得到的。

ULBP = 59

直方图表示 = 59 Bins

OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_邻域_11

LBPH,图像的LBP特征向量

LBPH,Local Binary Patterns Histograms,即LBP特征的统计直方图,LBPH将LBP特征与图像的空间信息结合在一起。将LBP特征图像分成m个局部块,并提取每个局部块的直方图,然后将这些直方图依次连接在一起形成LBP特征的统计直方图,即LBPH。

一幅图像具体的计算LBPH的过程(以Opencv中的人脸识别为例):

  1. 计算图像的LBP特征图像。
  2. 将LBP特征图像进行分块,Opencv中默认将LBP特征图像分成8行8列64块区域
  3. 计算每块区域特征图像的直方图cell_LBPH,将直方图进行归一化
  4. 将每块区域的直方图按空间顺序依次排列成一行,形成LBP特征向量
  5. 用机器学习的方法对LBP特征向量进行训练,用来检测和识别目标

一定程度上消除了光照变化的问题

具有旋转不变性

纹理特征维度低,计算速度快

缺点: 当光照变化不均匀时,各像素间的大小关系被破坏,对应的LBP算子也就发生了变化。

通过引入旋转不变的定义,使LBP算子更具鲁棒性。但这也使得LBP算子丢失了方向信息。

二、LBP 演示

头文件 ​​image_feature_all.h​​:声明类与公共函数

#pragma once
#include <opencv2/opencv.hpp>
#include <iostream>

using namespace cv;
using namespace std;


class ImageFeature {
public:
void lbp_demo(Mat& image);
void elbp_demo(Mat& image);

};

主函数​​main.cpp​​调用该类的公共成员函数

#include "image_feature_all.h"



int main(int argc, char** argv) {
const char* img_path = "D:\\Desktop\\jianzhu.jpg";
Mat image = imread(img_path, IMREAD_GRAYSCALE); //灰度图读入
if (image.empty()) {
cout << "图像数据为空,读取文件失败!" << endl;
}
ImageFeature imgfeature;
imgfeature.elbp_demo(image);
imgfeature.lbp_demo(image);

imshow("image", image);
waitKey(0);
destroyAllWindows();
return 0;
}

原始 LBP 效果演示

源文件 ​​feature_extract.cpp​​:实现类与公共函数

#include "image_feature_all.h"

void ImageFeature::lbp_demo(Mat& image) {
int height = image.rows;
int width = image.cols;

double t0 = cv::getTickCount();
uchar code;
uchar* mat_ptr = image.data;
Mat LBP_dst = Mat::zeros(height - 2, width - 2, CV_8UC1);
for (size_t h = 1; h < height - 1; h++){
uchar* lbp_ptr = LBP_dst.ptr<uchar>(h-1);
for (size_t w = 1; w < width - 1; w++) {
uchar center = *(mat_ptr + h * width + w);
code = 0;
code |= (*(mat_ptr + (h - 1) * width + w - 1) > center) << 7;
code |= (*(mat_ptr + (h - 1) * width + w ) > center) << 6;
code |= (*(mat_ptr + (h - 1) * width + w + 1) > center) << 5;
code |= (*(mat_ptr + (h ) * width + w + 1) > center) << 4;
code |= (*(mat_ptr + (h + 1) * width + w + 1) > center) << 3;
code |= (*(mat_ptr + (h + 1) * width + w ) > center) << 2;
code |= (*(mat_ptr + (h + 1) * width + w - 1) > center) << 1;
code |= (*(mat_ptr + (h ) * width + w - 1) > center) << 0;
lbp_ptr[w - 1] = code;
}
}
double t1 = cv::getTickCount();
cout << "Total cost is :" << ((t1 - t0) / cv::getTickFrequency()) << endl;
imshow("LBP_dst", LBP_dst);
}

OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_opencv_12

eLBP效果演示

static void elbp_change(int radius, void*userdata) {
if (radius < 1) {
radius = 1;
}
cout << "radius = " << radius << endl;

Mat src = *((Mat*)userdata);
int height = src.rows;
int width = src.cols;
int offset = radius * 2;
Mat elbpImg = Mat::zeros(height - offset, width - offset, CV_8UC1);
int neighbors = 8;
for (size_t n = 0; n < neighbors; n++){
float tmp = 2.0 * CV_PI * n / static_cast<float>(neighbors);
float x = static_cast<float>(-radius) * sin(tmp);
float y = static_cast<float>(radius) * cos(tmp);

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));

float tx = x - fx;
float ty = y - fy;

float w1 = (1 - tx) * (1 - ty);
float w2 = tx * (1 - ty);
float w3 = (1 - tx) *ty;
float w4 = tx * ty;

for (int i = radius; i < height - radius; i++){
for (int j = radius; j < width - radius; j++){
float t = w1 * src.at<uchar>(i + fy, j + fx)
+ w2 * src.at<uchar>(i + fy, j + cx)
+ w3 * src.at<uchar>(i + cy, j + fx)
+ w4 * src.at<uchar>(i + cy, j + cx);
elbpImg.at<uchar>(i - radius, j - radius) += (
(t > src.at<uchar>(i, j)) &&
(std::abs(t - src.at<uchar>(i, j)) >
std::numeric_limits<float>::epsilon())) << n;
}
}
}
imshow("elbp_result", elbpImg);
}


void ImageFeature::elbp_demo(Mat& image) {
namedWindow("elbp_result", WINDOW_AUTOSIZE);
int current_radius = 3;
int max_count = 20;
createTrackbar("elbp_radius ", "elbp_result", &current_radius, max_count, elbp_change, &image);
elbp_change(current_radius, &image);
}

OpenCV + CPP 系列(卅七)图像特征提取(LBP特征检测)_直方图_13

参考



举报

相关推荐

0 条评论