Dirección original:
https://ieeexplore.ieee.org/document/6738053
Este artículo solo traduce el segundo capítulo del documento y presenta el contenido de BRINT en detalle
BRINT: un descriptor resistente al ruido invariable de rotación binaria
Descriptor BRINT_S
La estructura del descriptor BRINT_S local se muestra en la Figura 2. Similar al esquema de muestreo en el método LBP original, muestreamos píxeles alrededor del píxel central Xc, pero en cualquier círculo de radio r, limitamos el número de puntos de muestreo a un múltiplo de 8, por lo que p = 8q, donde q es un entero positivo. Así, los vecinos de Xc muestreados en el radio r son Xr,8q=[Xr,8q,0,····,Xr,8q8q−1]T.
A diferencia del LBP original, transformamos el vector vecino Xr,8q promediando localmente a lo largo del arco,
como se muestra en la Fig. 2, de modo que el número de vecinos en Yr,q sea siempre 8.
Figura 2 Descripción del descriptor BNT_S: BRINT introduce la idea de promediar antes de cuantificar, primero convirtiendo el vecindario original en un nuevo Yr,8q,i, donde i=0,...,7, en el valor de gris del el píxel central Yr, 8q, i tienen un umbral para generar un patrón binario, en lugar de restar directamente el valor de gris Xc del píxel central
Dado Yr,q = [yr,q,0, , yr,q,7]T, podemos simplemente calcular un patrón binario sobre el píxel central, como LBP: donde, BNT_S significa "Notación de tolerancia de ruido binaria"
. Podemos ver fácilmente que para cualquier par de parámetros (r,q), hay un total de 2^8=256 BNT_Sr,q patrones binarios. Además, la transición de Xr,8q a Yr,q hace que el patrón sea más resistente al ruido.
Dado que la invariancia de rotación es uno de nuestros objetivos declarados, para evitar las limitaciones del esquema unificado, seguimos la heurística de LBPri,r,q al agrupar versiones iguales de la representación binaria bajo rotación y asignar códigos a los grupos resultantes. Luego, la forma de BRINT_Sr,q se define como
donde la función de rotación ROR(•,•) es la misma que en LBP, reduciendo el número de celdas de histograma para una escala de 256 a 36. Por lo tanto, la motivación para fijar el número de puntos en Yr,q a una constante de 8 es limitar el crecimiento de los intervalos de histograma con escala.
Para el parámetro q que controla el número de vecinos adyacentes en el círculo de muestreo y promedio, empleamos el esquema de muestreo (r,p) ∈ {(1,8),(2,24),(3,24),·· ·,( r, 24)} como un punto de partida razonable para implementar operadores, pero no se garantiza que generen el operador óptimo para una tarea determinada.
La Figura 3 verifica el comportamiento básico de BRINT_Sr,q en función de los números de escala comparando el rendimiento de clasificación de BRINT_Sr,q con el descriptor tradicional LBPri,r,p. Los resultados de clasificación muestran una mejora significativa en el rendimiento de clasificación en las tres bases de datos de Outex, con los mejores resultados de BLP.
Figura 3 Comparación de la precisión de clasificación del descriptor BRINT_S y el descriptor LBPri tradicional utilizando los tres conjuntos de referencia en la base de datos Outex especificada por Ojala et al. (BLP). La configuración experimental es consistente con la de LBP. Los resultados muestran claramente que el descriptor BRINT_S propuesto supera significativamente al descriptor LBPri tradicional.
En términos de coste computacional, el descriptor BRINT_S propuesto no implica un aumento de complejidad respecto al LBPriu2 convencional. En particular, BRINT_S siempre trata con patrones binarios locales basados en 8 puntos, mientras que para LBPriu2, la asignación de LBP a LBPriu2 requiere una tabla de búsqueda grande con 2^p elementos.
Descriptor BRINT_M
Motivados por los notables resultados de clasificación logrados por BRINT_S, esperamos seguir utilizando el descriptor CLBP_M al proponer BRINT_M, considerando que la característica CLBP_CSM supera a la característica única CLBP LBPriu2 propuesta por Guo et al.
Dado un píxel central Xc y sus p píxeles vecinos Xr,p,0,...,Xr,p,p−1, como se muestra en la Figura 2, primero calculamos la distancia entre el píxel central Xc y sus píxeles vecinos Valor absoluto de diferencias locales:
Siguiendo el trabajo en CLBP, ∆r,8q es un componente importante de las diferencias locales. Similar a (2), ∆r,8q se transforma en Zr,q,i=
Calculamos el patrón binario basado en Z BNT_M (Magnitud de tolerancia de ruido binario):
donde µl es el umbral local. Tenga en cuenta que el descriptor CLBP_M definido en CLBP utiliza un umbral global, mientras que en el operador LBP original el umbral es el valor del píxel central, que obviamente varía de píxel a píxel. Por lo tanto, proponemos usar un umbral variable localmente en lugar de un umbral global constante:
Después de definir BNT_M, BRINT_M se define como:
Finalmente, de acuerdo con CLBP, también representamos el píxel central como uno de dos binarios:
donde µI, r es el promedio sobre toda la imagen excluyendo los píxeles del límite:
BRINT multiresolución y clasificación
Hasta ahora, los descriptores BRINT se extraen de una resolución que es un conjunto de vecindad circularmente simétrico de 8q píxeles que se encuentran en un círculo de radio r. Dado que uno de los objetivos de nuestro método es manejar una gran cantidad de escalas diferentes, al variar r podemos implementar operadores de diferentes resoluciones espaciales, idealmente representados concatenando histogramas binarios de múltiples resoluciones en un solo histograma Parches de textura, que obviamente requieren el bajo -dimensionalidad de las características del histograma generadas en cada resolución. Por lo tanto, la dimensión del histograma conjunto de BRINT_CSM, BRINT_C, BRINT_S y BRINT_M es muy alta, que es 36∗ 36∗ 2 = 2592. Para reducir el número de pilas estadísticas requeridas, usamos los descriptores BRINT_CSr, q_CMr, q, es decir, el histograma conjunto BRINT_C∗ BRINT_Sr,q está conectado con BRINT_C∗ BRINT_Mr,q para generar un histograma de baja dimensión: 36∗ 2+36∗ 2=144. Como punto de comparación, en los resultados experimentales también evaluaremos BRINT_Sr,q_Mr,q, cuya dimensión es 36+36=72.
La clasificación real se realiza mediante un clasificador de vecino más cercano simple (NNC): El clasificador de vecino más cercano (NNC) se aplica a los vectores de características de histograma de Brinell normalizados hi y hj, utilizando la métrica de distancia χ2, como en CBLP.
código C++
Referencia 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{
// find min{ROR(x, i)} i=0..i; ROR -> right circular shift
unsigned char minROR(unsigned char x, int numShifts);
// vector of cordinates of circular neighbourhood
// and interpolation weights
// [floor_x, floor_y, ceil_x, ceil_y, w1, w2, w3, w4]
vector<array<float, 8>> getNeighbourhoodCoordinates(int radius,
int neighbours);
}
#endif
misc.cpp
#include "misc.hpp"
/*
** Author: Bhavik N Gala
** Email: [email protected]
*/
using std::floor;
using std::ceil;
using std::min;
namespace misc{
vector<array<float, 8>> getNeighbourhoodCoordinates(int radius,
int neighbours){
// vector of cordinates and interpolation weights
// [floor_x, floor_y, ceil_x, ceil_y, w1, w2, w3, w4]
vector<array<float, 8>> neighbourhoodCoords;
//float theta = 2.0*M_PI/neighbours;
for(int i=0; i<neighbours; i++){
// array template
array<float, 8> neighbourhoodCoord;
// theta = 2*pi/neighbours
// x = r*cos(i * theta) + r
// y = r*sin(i * theta) + r
// adding r for translating center to image center
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);
// relative indices
neighbourhoodCoord[0] = floor(x); // fx
neighbourhoodCoord[1] = floor(y); // fy
neighbourhoodCoord[2] = ceil(x); // cx
neighbourhoodCoord[3] = ceil(y); // cy
// fractional parts
float tx = x-floor(x);
float ty = y-floor(y);
// weights
neighbourhoodCoord[4] = (1-tx) * (1-ty); // w1
neighbourhoodCoord[5] = tx * (1-ty); // w2
neighbourhoodCoord[6] = (1-tx) * ty; // w3
neighbourhoodCoord[7] = tx * ty; // w4
// add array to vector
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;
}
}
hidrogeno.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
hidrogeno.cpp
#include "brint.hpp"
#include <iostream>
/*
** Author: Bhavik N Gala
** Email: [email protected]
*/
namespace features{
void Brint::brint_s(const Mat& src, Mat& dst, Mat& hist, int radius,
int neighbours, bool normalizeHist){
// computes 'Binary Noise Tolerant Sign'
// features for an given image
// TODO: assert neighbours%8 == 0
int q = (int)(neighbours/8);
// TODO: check out other interpolation methods
// interpolating pixel values
// vector of cordinates and interpolation weights
// [floor_x, floor_y, ceil_x, ceil_y, w1, w2, w3, w4]
vector<array<float, 8>> neighbourhoodCoords = \
misc::getNeighbourhoodCoordinates(radius, neighbours);
// int dataType = src.depth();
dst = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
// iterating through each pixel value
for(int y=radius; y<src.rows-radius; y++){
for(int x=radius; x<src.cols-radius; x++){
// getting the neighbourhood
Mat neighbourhood(src, Rect(x-radius, y-radius,
2*radius+1, 2*radius+1));
uint8_t *nData = neighbourhood.data;
// array of circular neighbourhood pixel values
float neighbourVector[neighbours];
// iterating through all the points in the circular
// neighbourhood and interpolating them
for(int p=0; p<neighbours; p++){
array<float, 8> xy = neighbourhoodCoords[p];
// s = w1*src(fy,fx)+w2*src(fy,cx)+w3*src(cy,fx)+w4*src(cy,cx)
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]*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] = xy[4]*p1.val[0] + xy[5]*p2.val[0] + xy[6]*p3.val[0] + xy[7]*p4.val[0];
}
// transform the neighbour vector by local averaging
// along the arc
unsigned char bnt_s = 0;
for(int i=0; i<8; i++){
float y = 0;
// y_r,q,i=(1/q)*sum(from k=0,..,q-1)(x_r,8q,qi+k)
for(int k=0; k<q; k++){
y += neighbourVector[q*i + k];
}
y /= q;
// compute LBP
unsigned char s = ((y-nData[radius, radius])>=0) ? 1 : 0;
bnt_s += s * (unsigned char)(pow(2, i));
}
// addding rotation invariance
// BRINT_S = min{ROR(BNT_S, i)} i=0..7
dst.at<unsigned char>(y-radius, x-radius) = misc::minROR(bnt_s, q);
}
}
// compute histogram
// number of bins in the histogram
int histSize = 256;
// set the range of values, from 0 to 255
// upper value is exclusive
float range[] = {
0, 2};
const float* histRange = {
range};
// uniform size of bins
bool uniform = true;
// set this flag to accumulate values of bins with previous histogram
bool accumulate = false;
cv::calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
if(normalizeHist){
// min output value = 0; max output value = 255
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){
// computes 'Binary Noise Tolerant Magnitude'
// features for an given image
// TODO: assert neighbours%8 == 0
int q = (int)(neighbours/8);
// TODO: check out other interpolation methods
// interpolating pixel values
// vector of cordinates and interpolation weights
// [floor_x, floor_y, ceil_x, ceil_y, w1, w2, w3, w4]
vector<array<float, 8>> neighbourhoodCoords = \
misc::getNeighbourhoodCoordinates(radius, neighbours);
dst = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
// iterating through each pixel value
for(int y=radius; y<src.rows-radius; y++){
for(int x=radius; x<src.cols-radius; x++){
// getting the neighbourhood
Mat neighbourhood(src, Rect(x-radius, y-radius,
2*radius+1, 2*radius+1));
uint8_t *nData = neighbourhood.data;
// array of circular neighbourhood pixel values
float neighbourVector[neighbours];
// iterating through all the points in the circular
// neighbourhood and interpolating them
for(int p=0; p<neighbours; p++){
array<float, 8> xy = neighbourhoodCoords[p];
// s = w1*src(fy,fx)+w2*src(fy,cx)+w3*src(cy,fx)+w4*src(cy,cx)
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])];
// delta_r = abs(x_r,p,i - x_c)
neighbourVector[p] = abs(neighbourVector[p] - nData[radius, radius]);
}
// transform the neighbour vector by local averaging
// along the arc
float z[8];
for(int i=0; i<8; i++){
float y = 0;
// y_r,q,i=(1/q)*sum(from k=0,..,q-1)(x_r,8q,qi+k)
for(int k=0; k<q; k++){
y += neighbourVector[q*i + k];
}
y /= q;
z[i] = y;
}
// compute mean of the array z
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++){
// compute LBP
unsigned char s = ((z[i]-mu_z)>=0) ? 1 : 0;
bnt_m += s * (unsigned char)(pow(2, i));
}
// addding rotation invariance
// BRINT_M = min{ROR(BNT_M, i)} i=0..7
dst.at<unsigned char>(y-radius, x-radius) = misc::minROR(bnt_m, q);
}
}
// compute histogram
// number of bins in the histogram
int histSize = 256;
// set the range of values, from 0 to 255
// upper value is exclusive
float range[] = {
0, 2};
const float* histRange = {
range};
// uniform size of bins
bool uniform = true;
// set this flag to accumulate values of bins with previous histogram
bool accumulate = false;
calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
if(normalizeHist){
// min output value = 0; max output value = 255
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){
// computes 'Binary Noise Tolerant Center'
// features for an given image
dst = Mat::zeros(src.rows-2*radius, src.cols-2*radius, CV_8U);
uint8_t *srcData = src.data;
// mean of all the pixels in the image
float mu = (cv::mean(src(Range(radius, src.rows-radius),
Range(radius, src.cols-radius))))[0];
// iterating through each pixel value
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;
}
}
// compute histogram
// number of bins in the histogram
int histSize = 2;
// set the range of values, from 0 to 1
// upper value is exclusive
float range[] = {
0, 2};
const float* histRange = {
range};
// uniform size of bins
bool uniform = true;
// set this flag to accumulate values of bins with previous histogram
bool accumulate = false;
calcHist(&dst, 1, 0, Mat(), hist, 1, &histSize, &histRange, uniform, accumulate);
if(normalizeHist){
// min output value = 0; max output value = 255
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){
// computes BRINT feature
// computes joint histogram from BNT_C a)nd BNT_S; BNT_C and BNT_M
// concatenates the two joint histogram
// TODO: assert neighbours%8 == 0
int q = (int)(neighbours/8);
// TODO: check out other interpolation methods
// interpolating pixel values
// vector of cordinates and interpolation weights
// [floor_x, floor_y, ceil_x, ceil_y, w1, w2, w3, w4]
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;
// mean of all the pixels in the image
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++){
// getting the neighbourhood
Mat neighbourhood(src, Rect(x-radius, y-radius,
2*radius+1, 2*radius+1));
uint8_t *nData = neighbourhood.data;
// array of circular neighbourhood pixel values
float neighbourVector[neighbours];
// iterating through all the points in the circular
// neighbourhood and interpolating them
for(int p=0; p<neighbours; p++){
array<float, 8> xy = neighbourhoodCoords[p];
// s = w1*src(fy,fx)+w2*src(fy,cx)+w3*src(cy,fx)+w4*src(cy,cx)
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];
}
// transform the neighbour vector by local averaging
// along the arc
float z[8];
for(int i=0; i<8; i++){
float y = 0;
// y_r,q,i=(1/q)*sum(from k=0,..,q-1)(x_r,8q,qi+k)
for(int k=0; k<q; k++){
y += neighbourVector[q*i + k];
}
y /= q;
z[i] = y;
}
// compute mean of the array z
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++){
// compute LBP
// bnt_s
s = ((z[i]-nData[radius, radius])>=0) ? 1 : 0;
bnt_s1 += s * (unsigned char)(pow(2, i));
// bnt_m
s = ((z[i]-mu_z)>=0) ? 1 : 0;
bnt_m1 += s * (unsigned char)(pow(2, i));
}
// bnt_c
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;
}
}
// compute histogram
// number of bins in the histogram
int histSize1 = 256;
int histSize2 = 2;
// set the range of values, from 0 to 255
// upper value is exclusive
float range1[] = {
0, 256};
// float range2[] = {0, 2};
const float* histRange1 = {
range1};
// const float* histRange2= {range2};
Mat hist_cs;
Mat hist_cm;
calcHist(&bnt_s, 1, 0, Mat(), hist_cs, 1, &histSize1, &histRange1, true, false); // clear the hist_cs array
calcHist(&bnt_c, 1, 0, Mat(), hist_cs, 1, &histSize1, &histRange1, true, true); // accumulate the hist_cs array
calcHist(&bnt_m, 1, 0, Mat(), hist_cm, 1, &histSize1, &histRange1, true, false); // clear the hist_cs array
calcHist(&bnt_c, 1, 0, Mat(), hist_cm, 1, &histSize1, &histRange1, true, true); // accumulate the hist_cs array
if(normalizeHist){
// min output value = 0; max output value = 255
normalize(hist_cs, hist_cs, 0, 255, NORM_MINMAX, -1, Mat());
normalize(hist_cm, hist_cm, 0, 255, NORM_MINMAX, -1, Mat());
}
// concatenate the histograms
vconcat(hist_cs, hist_cm, hist);
}
}
prueba_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;
// Mat saltpepper_noise = Mat::zeros(image.rows, image.cols,CV_8U);
// randu(saltpepper_noise,0,255);
//
// Mat black = saltpepper_noise < 30;
// Mat white = saltpepper_noise > 225;
// Mat saltpepper_img = image.clone();
// saltpepper_img.setTo(255,white);
// saltpepper_img.setTo(0,black);
// imwrite("./results/leaf_sp.jpg", saltpepper_img);
// namedWindow("saltNpepper", WINDOW_NORMAL);
// resizeWindow("saltNpepper", 1080, 1280);
// imshow("saltNpepper", saltpepper_img);
features::Brint::brint_s(image, brintImage, hist, 2, 32);
imwrite("./results/as_brint_s_r2_n32.jpg", brintImage);
// namedWindow( "Image", WINDOW_NORMAL);
// resizeWindow("Image", 1280, 1080);
// namedWindow( "Texture r=2, n=32", WINDOW_NORMAL );
// resizeWindow("Texture r=2, n=32", 1280, 1080);
// imshow("Image", image);
// imshow( "Texture r=2, n=32", brintImage );
// namedWindow( "Texture r=4, n=32", WINDOW_NORMAL );
// resizeWindow("Texture r=4, n=32", 1280, 1080);
features::Brint::brint_s(image, brintImage, hist, 4, 32);
imwrite("./results/as_brint_s_r4_n32.jpg", brintImage);
// imshow( "Texture r=4, n=32", brintImage );
// namedWindow( "Texture r=8, n=32", WINDOW_NORMAL );
// resizeWindow("Texture r=8, n=32", 1280, 1080);
features::Brint::brint_s(image, brintImage, hist, 8, 32);
imwrite("./results/as_brint_s_r8_n32.jpg", brintImage);
// imshow( "Texture r=8, n=32", brintImage );
// namedWindow( "Texture r=12, n=32", WINDOW_NORMAL );
// resizeWindow("Texture r=12, n=32", 1280, 1080);
features::Brint::brint_s(image, brintImage, hist, 12, 32);
imwrite("./results/as_brint_s_r12_n32.jpg", brintImage);
// imshow( "Texture r=12, n=32", brintImage );
// waitKey(0);
return 0;
}
código pitón
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 # rows
width = src.shape[1] - 2 * radius # cols
dst = np.zeros((height, width), dtype=np.uint8)
# dst = src.resize(height,width)
# cv2.imshow("1",dst)
# cv2.waitKey(0)
# print(radius,height+radius,width+radius)
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):
# neighbourhoodCoord = np.zeros(8,dtype=float)
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
principal.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)