原文地址: https://ieeexplore.ieee.org/document/6738053 本文仅翻译论文第二章详细介绍BRINT的内容
BRINT:一种二进制旋转不变的抗噪声描述符
BRINT_S描述符
局部BRINT_S描述符的构造如图2所示。与原始LBP方法中的采样方案类似,我们对中心像素Xc周围的像素进行采样,但在半径r的任何圆上,我们限制采样点数为8的倍数,因此p=8q,q为正整数。因此,在半径r上采样的Xc的邻居是Xr,8q=[Xr,8q,0,·····,Xr,8q8q?1] T。 与原始LBP不同,我们通过沿弧局部平均来变换相邻向量Xr,8q, 如图2所示,使得Yr,q中的邻居数始终为8。 图2 BNT_S描述符的说明:BRINT引入了量化前平均的思想,首先将原始邻域转换为新的Yr,8q,i,其中i=0,…,7,在中心像素的灰度值处对Yr,8q,i进行阈值化,以生成二值模式,而不是直接从每个相邻像素Xr,8q,i, i = 0, . . . , 8q ? 1,的精确灰度值中减去中心像素的灰度值Xc
给定Yr,q = [yr,q,0, · · · , yr,q,7]T我们可以简单地计算一个关于中心像素的二进制模式,如LBP: 其中,BNT_S表示“二进制噪声容限符号”。我们可以很容易地看到,对于任何参数对(r,q),总共有2^8=256个BNT_Sr,q二进制模式。此外,从Xr,8q到Yr,q的转换使模式对噪声更加鲁棒。 由于旋转不变性是我们声明的目标之一,为了避免统一模式的局限性,我们遵循LBPri,r,q的启发,将旋转下的二进制表示的相等版本分组,并将代码分配给结果组。那么,BRINT_Sr,q的形式定义为 其中,旋转函数ROR(?,?)与LBP中的相同,将一个刻度的柱状图单元数量从256个减少到36个。因此,将Yr,q中的点数固定为常数8的动机是限制具有比例的直方图箱的增长。 对于控制采样和平均圆中相邻邻居数量的参数q,我们采用了采样方案(r,p)∈ {(1,8),(2,24),(3,24),···,(r,24)}作为实现操作符的合理起点,但不能保证它们为给定任务生成最佳操作符。 图3通过对比BRINT_Sr,q与传统LBPri,r,p描述符的分类性能,验证了BRINT_Sr,q作为尺度数函数的基本行为。分类结果显示,三个Outex数据库的分类性能有了显著的提升,BLP的最佳结果。 图3 使用Ojala等人(BLP)指定的Outex数据库中的所有三个基准测试套件,比较BRINT_S描述符和传统LBPri描述符的分类精度。实验装置与LBP保持一致。结果有力地表明,所提出的BRINT_S描述符明显优于传统的LBPri描述符。
就计算成本而言,所提出的BRINT_S描述符并不意味着比传统的LBPriu2复杂度有所增加。特别是,BRINT_S总是处理基于8个点的局部二进制模式,而对于LBPriu2,从LBP到LBPriu2的映射需要一个包含2^p个元素的大型查找表。
BRINT_M描述符
受BRINT_S取得的显著分类结果的激励,考虑到CLBP_CSM特征的性能优于郭等人提出的CLBP单一特征LBPriu2,我们希望通过提出BRINT_M来进一步利用CLBP_M描述符。 给定一个中心像素Xc及其p个相邻像素Xr,p,0,···,Xr,p,p?1,如图2所示,我们首先计算中心像素Xc与其相邻像素之间的局部差值的绝对值: 继CLBP中的工作之后,?r, 8q是局部差异的重要分量。与(2)类似,?r, 8q转换为Zr,q,i= 我们计算了基于Z的二进制模式BNT_M(二进制噪声容限幅值): 其中,μl是局部阈值。请注意,在CLBP中定义的CLBP_M描述符使用全局阈值,而在原始LBP操作符中,阈值是中心像素值,这明显随像素而异。因此,我们建议使用局部变化的阈值,而不是使用恒定的全局阈值: 定义BNT_M后,BRINT_M定义为: 最后,与CLBP一致,我们还将中心像素表示为两个二进制中的一个: 其中,μI,r是不包括边界像素的整个图像的平均值:
多分辨率BRINT和分类
到目前为止,BRINT描述符是从一个分辨率中提取出来的,该分辨率是位于半径为r的圆上的8q像素的圆对称邻域集。鉴于我们方法的一个目标是处理大量不同的尺度,通过改变r,我们可以实现不同空间分辨率的操作符,理想情况下,通过将来自多个分辨率的二进制直方图连接到单个直方图来表示纹理面片,这显然要求在每个分辨率下生成的直方图特征具有低维性。因此BRINT_CSM,BRINT_C、BRINT_S和BRINT_M的联合直方图的维数非常高,为36? 36? 2=2592,为了减少所需的统计堆栈数量,我们采用BRINT_CSr,q_CMr,q描述符,即联合直方图BRINT_C? BRINT_Sr,q与BRINT_C? BRINT_Mr,q连接,生成一个低维直方图:36? 2+36? 2=144。作为比较点,作为比较点,在实验结果中,我们还将评估BRINT_Sr,q_Mr,q,其维数为36+36=72。 实际分类通过简单最近邻分类器(NNC)执行:最近邻分类器(NNC)应用于归一化布氏直方图特征向量hi和hj,使用χ2距离度量,如CBLP所示。
C++代码
参考https://github.com/bhavikngala/BRINT-features
misc.hpp
#ifndef MISC_HPP
#define MISC_HPP
#include <vector>
#include <array>
#include <climits>
#include <cmath>
using std::vector;
using std::array;
namespace misc{
unsigned char minROR(unsigned char x, int numShifts);
vector<array<float, 8>> getNeighbourhoodCoordinates(int radius,
int neighbours);
}
#endif
misc.cpp
#include "misc.hpp"
using std::floor;
using std::ceil;
using std::min;
namespace misc{
vector<array<float, 8>> getNeighbourhoodCoordinates(int radius,
int neighbours){
vector<array<float, 8>> neighbourhoodCoords;
for(int i=0; i<neighbours; i++){
array<float, 8> neighbourhoodCoord;
float x = (float)(radius)*cos(i*2.0*M_PI/neighbours) + (float)(radius);
float y = (float)(radius)*sin(i*2.0*M_PI/neighbours) + (float)(radius);
neighbourhoodCoord[0] = floor(x);
neighbourhoodCoord[1] = floor(y);
neighbourhoodCoord[2] = ceil(x);
neighbourhoodCoord[3] = ceil(y);
float tx = x-floor(x);
float ty = y-floor(y);
neighbourhoodCoord[4] = (1-tx) * (1-ty);
neighbourhoodCoord[5] = tx * (1-ty);
neighbourhoodCoord[6] = (1-tx) * ty;
neighbourhoodCoord[7] = tx * ty;
neighbourhoodCoords.push_back(neighbourhoodCoord);
}
return neighbourhoodCoords;
}
unsigned char minROR(unsigned char x, int numShifts){
unsigned char m = x;
for(int i=1; i<numShifts; i++){
m = min((unsigned char)((x >> i)|(x << (CHAR_BIT-i))), m);
}
return m;
}
}
brint.hpp
#ifndef BRINT_HPP
#define BRINT_HPP
#include <cmath>
#include <limits>
#include <array>
#include <vector>
#include <opencv2/core/core.hpp>
#include <opencv2/imgcodecs.hpp>
#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc.hpp>
#include "misc.hpp"
using namespace cv;
using namespace std;
namespace features{
class Brint{
public:
static void brint_s(const Mat& src, Mat& dst,
Mat& hist, int radius, int neighbours, bool normalizeHist);
static void brint_m(const Mat& src, Mat& dst,
Mat& hist, int radius, int neighbours, bool normalizeHist);
static void brint_c(const Mat& src, Mat& dst,
Mat& hist, int radius, int neighbours, bool normalizeHist);
static void brint_cs_cm(const Mat& src, Mat& hist,
int radius, int neighbours, bool normalizeHist);
};
}
#endif
brint.cpp
#include "brint.hpp"
#include <iostream>
namespace features{
void Brint::brint_s(const Mat& src, Mat& dst, Mat& hist, int radius,
int neighbours, bool normalizeHist){
int q = (int)(neighbours/8);
vector<array<float, 8>> neighbourhoodCoords = \
misc::getNeighbourhoodCoordinates(radius, neighbours);
dst = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
for(int y=radius; y<src.rows-radius; y++){
for(int x=radius; x<src.cols-radius; x++){
Mat neighbourhood(src, Rect(x-radius, y-radius,
2*radius+1, 2*radius+1));
uint8_t *nData = neighbourhood.data;
float neighbourVector[neighbours];
for(int p=0; p<neighbours; p++){
array<float, 8> xy = neighbourhoodCoords[p];
Scalar p1 = neighbourhood.at<uchar>((int)(xy[1]), (int)(xy[0]));
Scalar p2 = neighbourhood.at<uchar>((int)(xy[1]), (int)(xy[2]));
Scalar p3 = neighbourhood.at<uchar>((int)(xy[3]), (int)(xy[0]));
Scalar p4 = neighbourhood.at<uchar>((int)(xy[3]), (int)(xy[2]));
neighbourVector[p] = xy[4]*p1.val[0] + xy[5]*p2.val[0] + xy[6]*p3.val[0] + xy[7]*p4.val[0];
}
unsigned char bnt_s = 0;
for(int i=0; i<8; i++){
float y = 0;
for(int k=0; k<q; k++){
y += neighbourVector[q*i + k];
}
y /= q;
unsigned char s = ((y-nData[radius, radius])>=0) ? 1 : 0;
bnt_s += s * (unsigned char)(pow(2, i));
}
dst.at<unsigned char>(y-radius, x-radius) = misc::minROR(bnt_s, q);
}
}
int histSize = 256;
float range[] = {0, 2};
const float* histRange = {range};
bool uniform = true;
bool accumulate = false;
cv::calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
if(normalizeHist){
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
}
}
void Brint::brint_m(const Mat& src, Mat& dst, Mat& hist, int radius,
int neighbours, bool normalizeHist){
int q = (int)(neighbours/8);
vector<array<float, 8>> neighbourhoodCoords = \
misc::getNeighbourhoodCoordinates(radius, neighbours);
dst = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
for(int y=radius; y<src.rows-radius; y++){
for(int x=radius; x<src.cols-radius; x++){
Mat neighbourhood(src, Rect(x-radius, y-radius,
2*radius+1, 2*radius+1));
uint8_t *nData = neighbourhood.data;
float neighbourVector[neighbours];
for(int p=0; p<neighbours; p++){
array<float, 8> xy = neighbourhoodCoords[p];
neighbourVector[p] = \
xy[4]*nData[(int)(xy[1]), (int)(xy[0])] + \
xy[5]*nData[(int)(xy[1]), (int)(xy[2])] + \
xy[6]*nData[(int)(xy[3]), (int)(xy[0])] + \
xy[7]*nData[(int)(xy[3]), (int)(xy[2])];
neighbourVector[p] = abs(neighbourVector[p] - nData[radius, radius]);
}
float z[8];
for(int i=0; i<8; i++){
float y = 0;
for(int k=0; k<q; k++){
y += neighbourVector[q*i + k];
}
y /= q;
z[i] = y;
}
float mu_z = 0;
for(int i=0; i<8; i++){
mu_z += z[i];
}
mu_z /= 8;
unsigned char bnt_m = 0;
for(int i=0; i<8; i++){
unsigned char s = ((z[i]-mu_z)>=0) ? 1 : 0;
bnt_m += s * (unsigned char)(pow(2, i));
}
dst.at<unsigned char>(y-radius, x-radius) = misc::minROR(bnt_m, q);
}
}
int histSize = 256;
float range[] = {0, 2};
const float* histRange = {range};
bool uniform = true;
bool accumulate = false;
calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
if(normalizeHist){
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
}
}
void Brint::brint_c(const Mat& src, Mat& dst, Mat& hist, int radius,
int neighbours, bool normalizeHist){
dst = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
uint8_t *srcData = src.data;
float mu = (cv::mean(src(Range(radius, src.rows-radius),
Range(radius, src.cols-radius))))[0];
for(int y=radius; y<src.rows-radius; y++){
for(int x=radius; x<src.cols-radius; x++){
unsigned char s = ((srcData[y, x]-mu)>=0) ? 1 : 0;
dst.at<unsigned char>(y-radius, x-radius) = s;
}
}
int histSize = 2;
float range[] = {0, 2};
const float* histRange = {range};
bool uniform = true;
bool accumulate = false;
calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
if(normalizeHist){
normalize(hist, hist, 0, 255, NORM_MINMAX, -1, Mat());
}
}
void Brint::brint_cs_cm(const Mat& src, Mat& hist, int radius, int neighbours, bool normalizeHist){
int q = (int)(neighbours/8);
vector<array<float, 8>> neighbourhoodCoords = \
misc::getNeighbourhoodCoordinates(radius, neighbours);
Mat bnt_s = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
Mat bnt_m = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
Mat bnt_c = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
uint8_t *srcData = src.data;
float mu = (cv::mean(src(Range(radius, src.rows-radius),
Range(radius, src.cols-radius))))[0];
for(int y=radius; y<src.rows-radius; y++){
for(int x=radius; x<src.cols-radius; x++){
Mat neighbourhood(src, Rect(x-radius, y-radius,
2*radius+1, 2*radius+1));
uint8_t *nData = neighbourhood.data;
float neighbourVector[neighbours];
for(int p=0; p<neighbours; p++){
array<float, 8> xy = neighbourhoodCoords[p];
Scalar p1 = neighbourhood.at<uchar>((int)(xy[1]), (int)(xy[0]));
Scalar p2 = neighbourhood.at<uchar>((int)(xy[1]), (int)(xy[2]));
Scalar p3 = neighbourhood.at<uchar>((int)(xy[3]), (int)(xy[0]));
Scalar p4 = neighbourhood.at<uchar>((int)(xy[3]), (int)(xy[2]));
neighbourVector[p] = xy[4]*p1.val[0] + xy[5]*p2.val[0] + xy[6]*p3.val[0] + xy[7]*p4.val[0];
}
float z[8];
for(int i=0; i<8; i++){
float y = 0;
for(int k=0; k<q; k++){
y += neighbourVector[q*i + k];
}
y /= q;
z[i] = y;
}
float mu_z = 0;
for(int i=0; i<8; i++){
mu_z += z[i];
}
mu_z /= 8;
unsigned char bnt_s1 = 0;
unsigned char bnt_m1 = 0;
unsigned char s;
for(int i=0; i<8; i++){
s = ((z[i]-nData[radius, radius])>=0) ? 1 : 0;
bnt_s1 += s * (unsigned char)(pow(2, i));
s = ((z[i]-mu_z)>=0) ? 1 : 0;
bnt_m1 += s * (unsigned char)(pow(2, i));
}
s = ((srcData[y, x]-mu)>=0) ? 1 : 0;
bnt_s.at<unsigned char>(y-radius, x-radius) = misc::minROR(bnt_s1, q);
bnt_m.at<unsigned char>(y-radius, x-radius) = misc::minROR(bnt_m1, q);
bnt_c.at<unsigned char>(y-radius, x-radius) = s;
}
}
int histSize1 = 256;
int histSize2 = 2;
float range1[] = {0, 256};
const float* histRange1 = {range1};
Mat hist_cs;
Mat hist_cm;
calcHist(&bnt_s, 1, 0, Mat(), hist_cs, 1, &histSize1, &histRange1, true, false);
calcHist(&bnt_c, 1, 0, Mat(), hist_cs, 1, &histSize1, &histRange1, true, true);
calcHist(&bnt_m, 1, 0, Mat(), hist_cm, 1, &histSize1, &histRange1, true, false);
calcHist(&bnt_c, 1, 0, Mat(), hist_cm, 1, &histSize1, &histRange1, true, true);
if(normalizeHist){
normalize(hist_cs, hist_cs, 0, 255, NORM_MINMAX, -1, Mat());
normalize(hist_cm, hist_cm, 0, 255, NORM_MINMAX, -1, Mat());
}
vconcat(hist_cs, hist_cm, hist);
}
}
test_brint.cpp
#include "../utils/misc.hpp"
#include "../utils/brint.hpp"
using namespace std;
using namespace cv;
int main(int argc, char** argv){
Mat image = imread("./data/leaf.png", IMREAD_GRAYSCALE);
Mat brintImage;
Mat hist;
features::Brint::brint_s(image, brintImage, hist, 2, 32);
imwrite("./results/as_brint_s_r2_n32.jpg", brintImage);
features::Brint::brint_s(image, brintImage, hist, 4, 32);
imwrite("./results/as_brint_s_r4_n32.jpg", brintImage);
features::Brint::brint_s(image, brintImage, hist, 8, 32);
imwrite("./results/as_brint_s_r8_n32.jpg", brintImage);
features::Brint::brint_s(image, brintImage, hist, 12, 32);
imwrite("./results/as_brint_s_r12_n32.jpg", brintImage);
return 0;
}
python代码
BRINT.py
import cv2
import numpy as np
import math
def brint_s(srcimg, radius, neighbours):
src = cv2.cvtColor(srcimg, cv2.COLOR_RGB2GRAY)
q = int(neighbours / 8)
neighbourhoodCoords = getNeighbourhoodCoordinates(radius, neighbours)
height = src.shape[0] - 2 * radius
width = src.shape[1] - 2 * radius
dst = np.zeros((height, width), dtype=np.uint8)
for y in range(radius, height+radius):
for x in range(radius, width+radius):
neighbourhood = src[int(x - radius):int(x + radius + 1), int(y - radius):int(y + radius + 1)]
nData = neighbourhood.data
neighbourVector = np.zeros(neighbours, dtype=float)
for p in range(0, neighbours):
xy = neighbourhoodCoords[p]
p1 = neighbourhood[int(xy[1]), int(xy[0])]
p2 = neighbourhood[int(xy[1]), int(xy[2])]
p3 = neighbourhood[int(xy[3]), int(xy[0])]
p4 = neighbourhood[int(xy[3]), int(xy[2])]
neighbourVector[p] = xy[4] * p1 + xy[5] * p2 + xy[6] * p3 + xy[7] * p4
bnt_s = 0
for i in range(0, 8):
m = 0
for k in range(0, q):
m += neighbourVector[q * i + k]
m /= q
if m - nData[radius, radius] >= 0:
s = 1
else:
s = 0
bnt_s += s * (2 ** i)
dst[y - radius, x - radius] = float(minROR(bnt_s, q))
tmp1 = np.zeros((height, width), dtype=np.uint8)
cv2.transpose(dst,tmp1)
return tmp1
def getNeighbourhoodCoordinates(radius,neighbours):
neighbourhoodCoords = np.zeros((neighbours,8),dtype=float)
for i in range(0,neighbours):
x = float(radius*math.cos(i*2*math.pi/neighbours)+radius)
y = float(radius*math.sin(i*2*math.pi/neighbours+radius))
neighbourhoodCoords[i,0] = math.floor(x)
neighbourhoodCoords[i,1] = math.floor(y)
neighbourhoodCoords[i,2] = math.ceil(x)
neighbourhoodCoords[i,3] = math.ceil(y)
tx = float(x-math.floor(x))
ty = float(y-math.floor(y))
neighbourhoodCoords[i,4] = (1-tx)*(1-ty)
neighbourhoodCoords[i,5] = tx*(1-ty)
neighbourhoodCoords[i,6] = (1-tx)*ty
neighbourhoodCoords[i,7] = tx*ty
return neighbourhoodCoords
def minROR(x,numShifts):
m=x
for i in range(1,numShifts):
m = min(m,(x>>i)|(x<<(8-i)))
return m
main.py
import cv2
import numpy as np
from BRINT import brint_s
if __name__ == '__main__':
img = cv2.imread("C:/Users/WRP/Desktop/3.jpg")
dst = brint_s(img,2,32)
cv2.imshow("BRINT_result",dst)
cv2.waitKey(0)
|