图像处理之LBP特征(C++)

图像处理之LBP特征(C++)本文主要介绍了 LBP CircleLBP LBPH 的 C 实现和原理

大家好,欢迎来到IT知识分享网。

图像处理之LBP特征(C++)



前言

LBP(Local Binary Pattern)指局部二值模式,是一种用来描述图像局部特征的算子,LBP特征具有灰度不变性和旋转不变性等显著优点。由于LBP特征计算简单、效果较好,因此LBP特征在计算机视觉的许多领域都得到了广泛的应用。


一、LBP特征描述

原始的LBP算子定义为在33的窗口内,以窗口中心像素为阈值,将相邻的8个像素的灰度值与其进行比较,若周围像素值大于中心像素值,则该像素点的位置被标记为1,否则为0。这样,33邻域内的8个点经比较可产生8位二进制数(通常转换为十进制数即LBP码,共256种),然后按照顺时针依次排列形成一个二进制数字就是窗口中心像素点的LBP值,并用这个值来反映该区域的纹理信息。如下图:
LBP原理示意图
公式为:
LBP计算公式
参数解释:



  1. (xc,yc)为窗口中心的像素坐标;
  2. ic为未进行LBP计算前的窗口中心(xc,yc)像素值;
  3. ip为窗口中心邻域的灰度值;
  4. p为邻域的编码(0、1、2…7);
  5. s(x)是符号函数,当x>=0,s(x)=1;否则,s(x)=0。即,如下图
    符号函数

二、圆形LBP特征

基本的 LBP 算子的最大缺陷在于它只覆盖了一个固定半径范围内的小区域,这显然不能满足不同尺寸和频率纹理的需要。为了适应不同尺度的纹理特征,并达到灰度和旋转不变性的要求,对 LBP 算子进行了改进,将 3×3 邻域扩展到任意邻域,并用圆形邻域代替了正方形邻域,改进后的 LBP 算子允许在半径为 R 的圆形邻域内有任意多个像素点(P为采样点的个数)。如图。圆形LBP
对于给定中心点(xc,yc),其邻域像素位置为(xp,yp),p∈P,其采样点(xp,yp)用如下公式计算:

采样点坐标计算
参数解释:
P为采样点的总个数;
R是采样半径;
p是第p个采样点。
对于采样点未落在整数坐标上,进行双线性插值计算。
双线性插值





三、LBPH特征描述

四、代码实现

1.LBP实现

#include <iostream> #include <opencv.hpp> using namespace std; /* * @param cv::Mat src 输入图像 * @param cv::Mat dst 输出图像 * @brief 计算原始的LBP特征 */ void getOriginLBP(cv::Mat& src, cv::Mat& dst) { 
       dst.create(cv::Size(src.cols - 2, src.rows - 2), CV_8UC1); dst.setTo(0); unsigned char temp = 0; for (int i = 1; i < src.rows - 1; i++) for (int j = 1; j < src.cols - 1; j++) { 
       temp = 0; temp += (src.at<uchar>(i, j) < src.at<uchar>(i - 1, j - 1)) << 7; temp += (src.at<uchar>(i, j) < src.at<uchar>(i - 1, j)) << 6; temp += (src.at<uchar>(i, j) < src.at<uchar>(i - 1, j + 1)) << 5; temp += (src.at<uchar>(i, j) < src.at<uchar>(i, j + 1)) << 4; temp += (src.at<uchar>(i, j) < src.at<uchar>(i + 1, j + 1)) << 3; temp += (src.at<uchar>(i, j) < src.at<uchar>(i + 1, j)) << 2; temp += (src.at<uchar>(i, j) < src.at<uchar>(i + 1, j - 1)) << 1; temp += (src.at<uchar>(i, j) < src.at<uchar>(i, j - 1)) << 0; dst.at<uchar>(i - 1, j - 1) = temp; } } int main() { 
       //读取图片 string filepath = "F://work_study//algorithm_demo//baby.jpg"; cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE); if (src.empty()) { 
       std::cout << "imread error" << std::endl; return -1; } cv::Mat dst; getOriginLBP(src, dst); cv::imshow("dst", dst); cv::waitKey(0); return 0; } 

LBP结果图

2.圆形LBP实现

#include <iostream> #include <opencv.hpp> using namespace std; /* * @param cv::Mat src 输入图像 * @param cv::Mat dst 输出图像 * @param int radius 采样半径 * @param int neighbors 采样点个数 * @brief 计算圆形的LBP特征 */ void getCircleLBP(cv::Mat& src, cv::Mat& dst, int radius, int neighbors=8) { 
       //重要:LBP特征图像的行数和列数 dst.create(cv::Size(src.cols - 2 * radius, src.rows - 2 * radius), CV_8UC1); dst.setTo(0); for (int m = 0; m < neighbors; m++) { 
       //计算x,y的偏移量 float x_offset = radius * cos(2 * CV_PI * m / neighbors); float y_offset = -radius * sin(2 * CV_PI * m / neighbors); //进行双线性插值 //计算各个插值点最近的坐标,对采样点的偏移量进行上下取整 int x1 = floor(x_offset); int x2 = ceil(x_offset); int y1 = floor(y_offset); int y2 = ceil(y_offset); //映射到0-1之间 float x_scale = x_offset - x1; float y_scale = y_offset - y1; //计算权重系数 float w1 = (1 - x_scale) * (1 - y_scale); float w2 = x_scale * (1 - y_scale); float w3 = (1 - x_scale) * y_scale; float w4 = x_scale * y_scale; //循环处理每个像素 for (int i = radius; i < src.rows - radius; i++) for (int j = radius; j < src.rows - radius; j++) { 
       //计算经过二次插值得到的灰度值 float temp = src.at<uchar>(i + x1, j + y1) * w1 + src.at<uchar>(i + x1, j + y2) * w2 \ + src.at<uchar>(i + x2, j + y1) * w3 + src.at<uchar>(i + x2, j + y2) * w4; //LBP对每个邻居的LBP值累加 dst.at<uchar>(i - radius, j - radius) += ((temp > src.at<uchar>(i, j)) << (neighbors - m - 1)); } } } int main() { 
       //读取图片 string filepath = "F://work_study//algorithm_demo//baby.jpg"; cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE); if (src.empty()) { 
       std::cout << "imread error" << std::endl; return -1; } cv::Mat dst; getCircleLBP(src, dst, 3); cv::imshow("dst", dst); cv::waitKey(0); return 0; } 

Circlel LBP结果图

3.LBPH实现

#include <iostream> #include <opencv.hpp> using namespace std; /* * @param cv::Mat src 输入图像 * @param cv::Mat dst 输出图像 * @brief 计算原始的LBP特征 */ void getOriginLBP(cv::Mat& src, cv::Mat& dst) { 
       dst.create(cv::Size(src.cols - 2, src.rows - 2), CV_8UC1); dst.setTo(0); unsigned char temp = 0; for (int i = 1; i < src.rows - 1; i++) for (int j = 1; j < src.cols - 1; j++) { 
       temp = 0; temp += (src.at<uchar>(i, j) < src.at<uchar>(i - 1, j - 1)) << 7; temp += (src.at<uchar>(i, j) < src.at<uchar>(i - 1, j)) << 6; temp += (src.at<uchar>(i, j) < src.at<uchar>(i - 1, j + 1)) << 5; temp += (src.at<uchar>(i, j) < src.at<uchar>(i, j + 1)) << 4; temp += (src.at<uchar>(i, j) < src.at<uchar>(i + 1, j + 1)) << 3; temp += (src.at<uchar>(i, j) < src.at<uchar>(i + 1, j)) << 2; temp += (src.at<uchar>(i, j) < src.at<uchar>(i + 1, j - 1)) << 1; temp += (src.at<uchar>(i, j) < src.at<uchar>(i, j - 1)) << 0; dst.at<uchar>(i - 1, j - 1) = temp; } } /* * @param cv::Mat src 输入图像 * @param cv::Mat dst 输出图像 * @param int radius 采样半径 * @param int neighbors 采样点个数 * @brief 计算圆形的LBP特征 */ void getCircleLBP(cv::Mat& src, cv::Mat& dst, int radius, int neighbors=8) { 
       //重要:LBP特征图像的行数和列数 dst.create(cv::Size(src.cols - 2 * radius, src.rows - 2 * radius), CV_8UC1); dst.setTo(0); for (int m = 0; m < neighbors; m++) { 
       //计算x,y的偏移量 float x_offset = radius * cos(2 * CV_PI * m / neighbors); float y_offset = -radius * sin(2 * CV_PI * m / neighbors); //进行双线性插值 //计算各个插值点最近的坐标,对采样点的偏移量进行上下取整 int x1 = floor(x_offset); int x2 = ceil(x_offset); int y1 = floor(y_offset); int y2 = ceil(y_offset); //映射到0-1之间 float x_scale = x_offset - x1; float y_scale = y_offset - y1; //计算权重系数 float w1 = (1 - x_scale) * (1 - y_scale); float w2 = x_scale * (1 - y_scale); float w3 = (1 - x_scale) * y_scale; float w4 = x_scale * y_scale; //循环处理每个像素 for (int i = radius; i < src.rows - radius; i++) for (int j = radius; j < src.rows - radius; j++) { 
       //计算经过二次插值得到的灰度值 float temp = src.at<uchar>(i + x1, j + y1) * w1 + src.at<uchar>(i + x1, j + y2) * w2 \ + src.at<uchar>(i + x2, j + y1) * w3 + src.at<uchar>(i + x2, j + y2) * w4; //LBP对每个邻居的LBP值累加 dst.at<uchar>(i - radius, j - radius) += ((temp > src.at<uchar>(i, j)) << (neighbors - m - 1)); } } } /* * @param cv::Mat src 输入图像,计算得到的LBP特征图 * @param int minValue LBP特征值的最小值 * @param int maxValue LBP特征值的最大值(==numPatterns - 1) * @param bool normed 是否归一化 * @brief 计算一个LBP特征图像块的直方图 */ cv::Mat getLocalRegionLBPH(const cv::Mat& src, int minValue, int maxValue, bool normed) { 
       //定义存储直方图的矩阵 cv::Mat result; //计算得到直方图bin的数目,直方图数组的大小 int histSize = maxValue - minValue + 1; //定义直方图每一维的bin的变化范围 float range[] = { 
       static_cast<float>(minValue),static_cast<float>(maxValue + 1) }; //定义直方图所有bin的变化范围 const float* ranges = { 
       range }; //计算直方图,src是要计算直方图的图像,1是要计算直方图的图像数目,0是计算直方图所用的图像的通道序号,从0索引 //Mat()是要用的掩模,result为输出的直方图,1为输出的直方图的维度,histSize直方图在每一维的变化范围 //ranges,所有直方图的变化范围(起点和终点) calcHist(&src, 1, 0, cv::Mat(), result, 1, &histSize, &ranges, true, false); //归一化 if (normed) { 
       result /= (int)src.total(); } //结果表示成只有1行的矩阵 return result.reshape(1, 1); } /* * @param cv::Mat src 输入图像,计算得到的LBP特征图 * @param int numPatterns LBP特征值种类(范围) * @param int grid_x 水平方向的块的数量 * @param int grid_y 竖直方向的块的数量 * @param bool normed 是否归一化 * @brief 计算LBP特征图像的直方图LBPH */ cv::Mat getLBPH(cv::InputArray _src, int numPatterns, int grid_x, int grid_y, bool normed) { 
       cv::Mat src = _src.getMat(); int width = src.cols / grid_x; int height = src.rows / grid_y; //定义LBPH的行和列,grid_x*grid_y表示将图像分割成这么些块,numPatterns表示LBP值的模式种类 cv::Mat result = cv::Mat::zeros(grid_x * grid_y, numPatterns, CV_32FC1); if (src.empty()) { 
       return result.reshape(1, 1); } int resultRowIndex = 0; //对图像进行分割,分割成grid_x*grid_y块,grid_x,grid_y默认为8 for (int i = 0; i < grid_x; i++) { 
       for (int j = 0; j < grid_y; j++) { 
       //图像分块 cv::Mat src_cell = cv::Mat(src, cv::Range(i * height, (i + 1) * height), cv::Range(j * width, (j + 1) * width)); //计算直方图 cv::Mat hist_cell = getLocalRegionLBPH(src_cell, 0, (numPatterns - 1), true); //将直方图放到result中 cv::Mat rowResult = result.row(resultRowIndex); hist_cell.reshape(1, 1).convertTo(rowResult, CV_32FC1); resultRowIndex++; } } return result.reshape(1, 1); } int main() { 
       //读取图片 string filepath = "F://work_study//algorithm_demo//baby.jpg"; cv::Mat src = cv::imread(filepath, cv::IMREAD_GRAYSCALE); if (src.empty()) { 
       std::cout << "imread error" << std::endl; return -1; } cv::Mat circleLBPImg,dst; //计算得到LBP特征图 getCircleLBP(src, circleLBPImg,3,8); //dst即为LBPH特征向量 dst=getLBPH(circleLBPImg, 256, 8, 8, true).clone(); cv::waitKey(0); return 0; } 

总结

本文主要介绍了LBP、Circle LBP、LBPH的C++实现和原理。

免责声明:本站所有文章内容,图片,视频等均是来源于用户投稿和互联网及文摘转载整编而成,不代表本站观点,不承担相关法律责任。其著作权各归其原作者或其出版社所有。如发现本站有涉嫌抄袭侵权/违法违规的内容,侵犯到您的权益,请在线联系站长,一经查实,本站将立刻删除。 本文来自网络,若有侵权,请联系删除,如若转载,请注明出处:https://haidsoft.com/124788.html

(0)
上一篇 2025-10-03 16:20
下一篇 2025-10-03 16:33

相关推荐

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注微信