LBP特征提取(C++实现)
紋理特征中HOG和LBP是兩大很重要的特征,利用好這兩個特征的一些性質可以很好提高后續識別精度
一、原理的簡單解釋
LBP,是局部二值模式,最原始的LBP模式,可以很明顯用下面的圖描述
對于中心像素點,一共有8個鄰域值,大于中心則取值為1,小于中心則取值為0,只有0,1兩種取值,一共有2^8=256種模式,可以反映這一塊區域的紋理信息;
值得注意的是,它具有旋轉不變性(針對于順時針的),也就是說無論從那個鄰域值開始記錄都是同樣的結果;
一般操作是將所有的8位二進制數旋轉直至最小值,取最小值為最終模式,這樣能抗圖像旋轉帶來的影響
原始的LBP便是通過這種模式實現的,代碼比較簡單,可以供參考
#include <iostream> #include <opencv2/opencv.hpp> #include "math.h"using namespace std; using namespace cv;Mat getLBP(Mat &img){Mat gray_src;gray_src = img; // cvtColor(img, gray_src, COLOR_BGR2GRAY);//定義LBP圖像的長寬,由于最外圍一圈無8領域,所以長寬相比于原圖各減少2int width = img.cols - 2;int hight = img.rows - 2;//初始化一全為0的矩陣Mat lbpImg = Mat::zeros(hight, width, CV_8UC1);for (int row = 1; row < img.rows - 1; row++){for (int col = 1; col < img.cols - 1; col++){uchar c = gray_src.at<uchar>(row, col);uchar code = 0;//對于八個鄰域值做處理//|= 按位或,a |= b 和 a = a | b 等價;//左移<<就是將二進制的每一個數都往左移動一位,高位舍去,低位補0(超過存儲上限8位的屬于高位,需舍去)code |= (gray_src.at<uchar>(row - 1, col - 1) > c) << 7;code |= (gray_src.at<uchar>(row - 1, col ) > c) << 6;code |= (gray_src.at<uchar>(row - 1, col + 1) > c) << 5;code |= (gray_src.at<uchar>(row, col + 1) > c) << 4;code |= (gray_src.at<uchar>(row + 1, col +1) > c) << 3;code |= (gray_src.at<uchar>(row + 1, col ) > c) << 2;code |= (gray_src.at<uchar>(row + 1, col - 1) > c) << 1;code |= (gray_src.at<uchar>(row , col ) > c) << 0;//賦值操作,注意row和col是從0開始的;lbpImg.at<uchar>(row-1, col-1) = code;}}return lbpImg; }int main(){Mat src;src = imread("D:\\001_10\\311.bmp", 0);if (src.empty()) {printf("could not load image...\n");return -1;}imshow("input image", src);waitKey(0);Mat dst;dst = getLBP(src);imshow("output image", dst);waitKey(0);return 0; }原圖進行灰度處理,才能使用imshow輸出
輸出結果為(大概能看出魚的輪廓)
補充 CV_8UC1 的含義
CV_<bit_depth>(S|U|F)C<number_of_channels>//每一項含義解釋 bit_depth (比特數) 一般取值有8bite,16bites,32bites,64bites若為8,表示對于圖片的每個像素點在內存空間占8位S|U|F S代表signed int,有符號整型U代表unsigned int,無符號整型F代表float,單精度浮點型 C<number_of_channels> 一張圖片的通道數1--灰度圖片grayImg,是單通道圖像2--RGB彩色圖像,是3通道圖像3--帶Alph通道的RGB圖像,是4通道圖像而這256種模式中存在大量冗余,所以尋找了一種等價模式LBP來解決這種情況,
也就是把所有模式分為兩類,一類位均勻化模式,一類為非均勻化模式;
均勻化模式:8位二進制數(首尾相連)中存在0到1、1到零的數目小于等于兩個
例如:全0,全1,1個1、2個1相連、3個1相連直至7個1相連各8種均勻化模式,加起來就是1+1+7*8=58種;
非均勻化模式:除均勻化模式的所有的統計為1種;
故一共59種模式。
二、代碼及解釋
大致思路:
1.先得到等價模式LBP;
2.計算一個LBP特征圖像塊的直方圖;
3.計算LBP特征圖像的直方圖LBPH;
4.經過上述三步得到一個圖像的LBP特征;
5.調用4中單個圖像LBP,實現所有樣本的LBP特征提取。
先放上代碼:
//等價模式LBP特征計算 template <typename _tp> void getUniformPatternLBPFeature(InputArray _src,OutputArray _dst,int radius,int neighbors) {Mat src = _src.getMat();//LBP特征圖像的行數和列數的計算要準確_dst.create(src.rows-2*radius,src.cols-2*radius,CV_8UC1);Mat dst = _dst.getMat();dst.setTo(0);//LBP特征值對應圖像灰度編碼表,直接默認采樣點為8位uchar temp = 1;uchar table[256] = {0};for(int i=0;i<256;i++){if(getHopTimes(i)<3){table[i] = temp;temp++;}}//是否進行UniformPattern編碼的標志bool flag = false;//計算LBP特征圖for(int k=0;k<neighbors;k++){if(k==neighbors-1){flag = true;}//計算采樣點對于中心點坐標的偏移量rx,ryfloat rx = static_cast<float>(radius * cos(2.0 * CV_PI * k / neighbors));float ry = -static_cast<float>(radius * sin(2.0 * CV_PI * k / neighbors));//為雙線性插值做準備//對采樣點偏移量分別進行上下取整int x1 = static_cast<int>(floor(rx));int x2 = static_cast<int>(ceil(rx));int y1 = static_cast<int>(floor(ry));int y2 = static_cast<int>(ceil(ry));//將坐標偏移量映射到0-1之間float tx = rx - x1;float ty = ry - y1;//根據0-1之間的x,y的權重計算公式計算權重,權重與坐標具體位置無關,與坐標間的差值有關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<src.rows-radius;i++){for(int j=radius;j<src.cols-radius;j++){//獲得中心像素點的灰度值_tp center = src.at<_tp>(i,j);//根據雙線性插值公式計算第k個采樣點的灰度值float neighbor = src.at<_tp>(i+x1,j+y1) * w1 + src.at<_tp>(i+x1,j+y2) *w2 \+ src.at<_tp>(i+x2,j+y1) * w3 +src.at<_tp>(i+x2,j+y2) *w4;//LBP特征圖像的每個鄰居的LBP值累加,累加通過與操作完成,對應的LBP值通過移位取得dst.at<uchar>(i-radius,j-radius) |= (neighbor>center) <<(neighbors-k-1);//進行LBP特征的UniformPattern編碼if(flag){dst.at<uchar>(i-radius,j-radius) = table[dst.at<uchar>(i-radius,j-radius)];}}}} }//計算跳變次數 int getHopTimes(int n) {int count = 0;bitset<8> binaryCode = n;for(int i=0;i<8;i++){if(binaryCode[i] != binaryCode[(i+1)%8]){count++;}}return count; } //計算一個LBP特征圖像塊的直方圖 Mat getLocalRegionLBPH(const Mat& src, int minValue, int maxValue, bool normed) {//定義存儲直方圖的矩陣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, Mat(), result, 1, &histSize, &ranges, true, false);//歸一化if (normed){result /= (int)src.total();}//結果表示成只有1行的矩陣return result.reshape(1, 1); } //計算LBP特征圖像的直方圖LBPH Mat getLBPH(Mat src, int numPatterns, int grid_x, int grid_y, bool normed) {int width = src.cols / grid_x;int height = src.rows / grid_y;//定義LBPH的行和列,grid_x*grid_y表示將圖像分割成這么些塊,numPatterns表示LBP值的模式種類Mat result = 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默認為8for (int i = 0; i<grid_x; i++){for (int j = 0; j<grid_y; j++){//圖像分塊Mat src_cell = Mat(src, Range(i*height, (i + 1)*height), Range(j*width, (j + 1)*width));//計算直方圖Mat hist_cell = getLocalRegionLBPH(src_cell, 0, (numPatterns - 1), true);//將直方圖放到result中Mat rowResult = result.row(resultRowIndex);hist_cell.reshape(1, 1).convertTo(rowResult, CV_32FC1);resultRowIndex++;}}return result.reshape(1, 1); }對于單個圖像進行處理,也就是調用前面的代碼
//單個圖像 Mat get_single_pic_lbp(Mat& src){Mat src = imread("D:\\2.bmp", 0);Mat dst;//先得到等價模式LBP結果getUniformPatternLBPFeature<uchar>(src, dst, 1, 8); //采樣點為8個//得到LBP的特征向量Mat result = getLBPH(dst, 59, 8, 8, true); //單個圖像特征由64*256=16384降為64*59=3778維特征 // cout << result << endl;return result; }所有圖片一起調用上面單個圖片的LBP特征,并將所有返回值存起來,就可以得到一個超級大的矩陣,所以一般要進行降維處理,然而會發現PCA降維效果一點也不行,所以要想想別的辦法處理;
主函數中進行測試
int main(){//lbp_pcaMatrixXd all_pic_lbp;all_pic_lbp = get_all_lbp_feature();pca_1(all_pic_lbp, 3776, 348);return 0; }三、總結
LBP中的特征等價于看待不同模式,也需要劃分為不同幾個范圍,得到直方圖結果,進行統計分析,根據不同的實際情況,可以選擇不同的LBP得到更好的結果。網上原理以及代碼很多,只要懂得原理,就可以任意修改為自己想要的模式了。
總結
以上是生活随笔為你收集整理的LBP特征提取(C++实现)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: CRC校验算法
- 下一篇: 基于Django+链家+Bootstra