局部二值相似模式(LBSP)


1. LBSP.h

#pragma once

#include <opencv2/core/core.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/features2d/features2d.hpp>
#include "DistanceUtils.h"

/*!
	Local Binary Similarity Pattern (LBSP) feature extractor

	Note 1: both grayscale and RGB/BGR images may be used with this extractor.
	Note 2: using LBSP::compute2(...) is logically equivalent to using LBSP::compute(...) followed by LBSP::reshapeDesc(...).

	For more details on the different parameters, see G.-A. Bilodeau et al, "Change Detection in Feature Space Using Local
	Binary Similarity Patterns", in CRV 2013.

	This algorithm is currently NOT thread-safe.
 */
class LBSP : public cv::DescriptorExtractor {
public:
	//! constructor 1, threshold = absolute intensity 'similarity' threshold used when computing comparisons
	LBSP(size_t nThreshold);
	//! constructor 2, threshold = relative intensity 'similarity' threshold used when computing comparisons
	LBSP(float fRelThreshold, size_t nThresholdOffset=0);
	//! default destructor
	virtual ~LBSP();
	//! loads extractor params from the specified file node @@@@ not impl
	virtual void read(const cv::FileNode&);
	//! writes extractor params to the specified file storage @@@@ not impl
	virtual void write(cv::FileStorage&) const;
	//! sets the 'reference' image to be used for inter-frame comparisons (note: if no image is set or if the image is empty, the algorithm will default back to intra-frame comparisons)
	virtual void setReference(const cv::Mat&);
	//! returns the current descriptor size, in bytes
	virtual int descriptorSize() const;
	//! returns the current descriptor data type
	virtual int descriptorType() const;
	//! returns whether this extractor is using a relative threshold or not
	virtual bool isUsingRelThreshold() const;
	//! returns the current relative threshold used for comparisons (-1 = invalid/not used)
	virtual float getRelThreshold() const;
	//! returns the current absolute threshold used for comparisons (-1 = invalid/not used)
	virtual size_t getAbsThreshold() const;

	//! similar to DescriptorExtractor::compute(const cv::Mat& image, ...), but in this case, the descriptors matrix has the same shape as the input matrix (possibly slower, but the result can be displayed)
	void compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const;
	//! batch version of LBSP::compute2(const cv::Mat& image, ...), also similar to DescriptorExtractor::compute(const std::vector<cv::Mat>& imageCollection, ...)
	void compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const;

	//! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel version)
	inline static void computeGrayscaleDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _t, ushort& _res) {
		CV_DbgAssert(!oInputImg.empty());
		CV_DbgAssert(oInputImg.type()==CV_8UC1);
		CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
		CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
		CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
		const size_t _step_row = oInputImg.step.p[0];
		const uchar* const _data = oInputImg.data;
		#include "LBSP_16bits_dbcross_1ch.i"
	}

	//! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
	inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref,  const int _x, const int _y, const size_t* const _t, ushort* _res) {
		CV_DbgAssert(!oInputImg.empty());
		CV_DbgAssert(oInputImg.type()==CV_8UC3);
		CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
		CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
		CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
		const size_t _step_row = oInputImg.step.p[0];
		const uchar* const _data = oInputImg.data;
		#include "LBSP_16bits_dbcross_3ch3t.i"
	}

	//! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (3-channels version)
	inline static void computeRGBDescriptor(const cv::Mat& oInputImg, const uchar* const _ref,  const int _x, const int _y, const size_t _t, ushort* _res) {
		CV_DbgAssert(!oInputImg.empty());
		CV_DbgAssert(oInputImg.type()==CV_8UC3);
		CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
		CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
		CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
		const size_t _step_row = oInputImg.step.p[0];
		const uchar* const _data = oInputImg.data;
		#include "LBSP_16bits_dbcross_3ch1t.i"
	}

	//! utility function, shortcut/lightweight/direct single-point LBSP computation function for extra flexibility (1-channel-RGB version)
	inline static void computeSingleRGBDescriptor(const cv::Mat& oInputImg, const uchar _ref, const int _x, const int _y, const size_t _c, const size_t _t, ushort& _res) {
		CV_DbgAssert(!oInputImg.empty());
		CV_DbgAssert(oInputImg.type()==CV_8UC3 && _c<3);
		CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
		CV_DbgAssert(_x>=(int)LBSP::PATCH_SIZE/2 && _y>=(int)LBSP::PATCH_SIZE/2);
		CV_DbgAssert(_x<oInputImg.cols-(int)LBSP::PATCH_SIZE/2 && _y<oInputImg.rows-(int)LBSP::PATCH_SIZE/2);
		const size_t _step_row = oInputImg.step.p[0];
		const uchar* const _data = oInputImg.data;
		#include "LBSP_16bits_dbcross_s3ch.i"
	}

	//! utility function, used to reshape a descriptors matrix to its input image size via their keypoint locations
	static void reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput);
	//! utility function, used to illustrate the difference between two descriptor images
	static void calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels=false);
	//! utility function, used to filter out bad keypoints that would trigger out of bounds error because they're too close to the image border
	static void validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize);
	//! utility function, used to filter out bad pixels in a ROI that would trigger out of bounds error because they're too close to the image border
	static void validateROI(cv::Mat& oROI);
	//! utility, specifies the pixel size of the pattern used (width and height)
	static const size_t PATCH_SIZE = 5;
	//! utility, specifies the number of bytes per descriptor (should be the same as calling 'descriptorSize()')
	static const size_t DESC_SIZE = 2;    //LBSP描述子是16位,即2个字节

protected:
	//! classic 'compute' implementation, based on the regular DescriptorExtractor::computeImpl arguments & expected output
	virtual void computeImpl(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const;

	const bool m_bOnlyUsingAbsThreshold; //绝对阈值
	const float m_fRelThreshold;         //相对阈值
	const size_t m_nThreshold;
	cv::Mat m_oRefImage;                 //计算inter-frame comparisons的参考图片
};

2. LBSP.cpp

#include "LBSP.h"

LBSP::LBSP(size_t nThreshold)
	:	 m_bOnlyUsingAbsThreshold(true)
		,m_fRelThreshold(0) // unused
		,m_nThreshold(nThreshold)
		,m_oRefImage() {}

LBSP::LBSP(float fRelThreshold, size_t nThresholdOffset)
	:	 m_bOnlyUsingAbsThreshold(false)
		,m_fRelThreshold(fRelThreshold)
		,m_nThreshold(nThresholdOffset)
		,m_oRefImage() {
	CV_Assert(m_fRelThreshold>=0);
}

LBSP::~LBSP() {}

void LBSP::read(const cv::FileNode& /*fn*/) {
    // ... = fn["..."];
}

void LBSP::write(cv::FileStorage& /*fs*/) const {
    //fs << "..." << ...;
}

void LBSP::setReference(const cv::Mat& img) {
	CV_DbgAssert(img.empty() || img.type()==CV_8UC1 || img.type()==CV_8UC3);
	m_oRefImage = img;
}

//LBSP描述子是16位,即2个字节
int LBSP::descriptorSize() const {
	return DESC_SIZE;
}

int LBSP::descriptorType() const {
	return CV_16U;
}

bool LBSP::isUsingRelThreshold() const {
	return !m_bOnlyUsingAbsThreshold;
}

float LBSP::getRelThreshold() const {
	return m_fRelThreshold;
}

size_t LBSP::getAbsThreshold() const {
	return m_nThreshold;
}

//使用绝对阈值计算LBSP
static inline void lbsp_computeImpl(	const cv::Mat& oInputImg,						//需要计算LBSP的输入图片
										const cv::Mat& oRefImg,							//计算LBSP的参考图片,也就是使用的各个位置的中间值
										const std::vector<cv::KeyPoint>& voKeyPoints,	//去除边缘后的有效点
										cv::Mat& oDesc,								    //存放计算好的LBSP值
										size_t _t) {								    //_t是绝对阈值
	CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
	CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
	CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
	const size_t nChannels = (size_t)oInputImg.channels();
	const size_t _step_row = oInputImg.step.p[0];
	const uchar* _data = oInputImg.data;
	const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
	const size_t nKeyPoints = voKeyPoints.size();
	if(nChannels==1) {
		oDesc.create((int)nKeyPoints,1,CV_16UC1);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;         //提取点的纵坐标
			const int _y = (int)voKeyPoints[k].pt.y;		 //提取点的横坐标
			const uchar _ref = _refdata[_step_row*(_y)+_x];  //提取参考点的像素值,即找到计算相应LBSP的中间点
			ushort& _res = oDesc.at<ushort>((int)k);         //存放计算的LBSP结果值
			#include "LBSP_16bits_dbcross_1ch.i"             //通过该文件具体计算每一个像素位置的LBSP值
		}
	}
	else { //nChannels==3
		oDesc.create((int)nKeyPoints,1,CV_16UC3);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;
			const int _y = (int)voKeyPoints[k].pt.y;
			const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
			ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*k));
			#include "LBSP_16bits_dbcross_3ch1t.i"
		}
	}
}

//使用含有相对偏置的相对阈值计算LBSP
static inline void lbsp_computeImpl(	const cv::Mat& oInputImg,
										const cv::Mat& oRefImg,
										const std::vector<cv::KeyPoint>& voKeyPoints,
										cv::Mat& oDesc,
										float fThreshold,
										size_t nThresholdOffset) {
	CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
	CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
	CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
	CV_DbgAssert(fThreshold>=0);
	const size_t nChannels = (size_t)oInputImg.channels();
	const size_t _step_row = oInputImg.step.p[0];
	const uchar* _data = oInputImg.data;
	const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
	const size_t nKeyPoints = voKeyPoints.size();
	if(nChannels==1) {
		oDesc.create((int)nKeyPoints,1,CV_16UC1);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;				//提取点的纵坐标
			const int _y = (int)voKeyPoints[k].pt.y;				//提取点的横坐标
			const uchar _ref = _refdata[_step_row*(_y)+_x];			//提取参考点的像素值,即找到计算相应LBSP的中间点
			ushort& _res = oDesc.at<ushort>((int)k);                //存放计算的LBSP结果值
			const size_t _t = (size_t)(_ref*fThreshold)+nThresholdOffset;
			#include "LBSP_16bits_dbcross_1ch.i"
		}
	}
	else { //nChannels==3
		oDesc.create((int)nKeyPoints,1,CV_16UC3);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;
			const int _y = (int)voKeyPoints[k].pt.y;
			const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
			ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*k));
			const size_t _t[3] = {(size_t)(_ref[0]*fThreshold)+nThresholdOffset,(size_t)(_ref[1]*fThreshold)+nThresholdOffset,(size_t)(_ref[2]*fThreshold)+nThresholdOffset};
			#include "LBSP_16bits_dbcross_3ch3t.i"
		}
	}
}

static inline void lbsp_computeImpl2(	const cv::Mat& oInputImg,
										const cv::Mat& oRefImg,
										const std::vector<cv::KeyPoint>& voKeyPoints,
										cv::Mat& oDesc,
										size_t _t) {
	CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
	CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
	CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
	const size_t nChannels = (size_t)oInputImg.channels();
	const size_t _step_row = oInputImg.step.p[0];
	const uchar* _data = oInputImg.data;
	const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
	const size_t nKeyPoints = voKeyPoints.size();
	if(nChannels==1) {
		oDesc.create(oInputImg.size(),CV_16UC1);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;
			const int _y = (int)voKeyPoints[k].pt.y;
			const uchar _ref = _refdata[_step_row*(_y)+_x];
			ushort& _res = oDesc.at<ushort>(_y,_x);
			#include "LBSP_16bits_dbcross_1ch.i"
		}
	}
	else { //nChannels==3
		oDesc.create(oInputImg.size(),CV_16UC3);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;
			const int _y = (int)voKeyPoints[k].pt.y;
			const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
			ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*_y + oDesc.step.p[1]*_x));
			#include "LBSP_16bits_dbcross_3ch1t.i"
		}
	}
}

static inline void lbsp_computeImpl2(	const cv::Mat& oInputImg,
										const cv::Mat& oRefImg,
										const std::vector<cv::KeyPoint>& voKeyPoints,
										cv::Mat& oDesc,
										float fThreshold,
										size_t nThresholdOffset) {
	CV_DbgAssert(oRefImg.empty() || (oRefImg.size==oInputImg.size && oRefImg.type()==oInputImg.type()));
	CV_DbgAssert(oInputImg.type()==CV_8UC1 || oInputImg.type()==CV_8UC3);
	CV_DbgAssert(LBSP::DESC_SIZE==2); // @@@ also relies on a constant desc size
	CV_DbgAssert(fThreshold>=0);
	const size_t nChannels = (size_t)oInputImg.channels();
	const size_t _step_row = oInputImg.step.p[0];
	const uchar* _data = oInputImg.data;
	const uchar* _refdata = oRefImg.empty()?oInputImg.data:oRefImg.data;
	const size_t nKeyPoints = voKeyPoints.size();
	if(nChannels==1) {
		oDesc.create(oInputImg.size(),CV_16UC1);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;
			const int _y = (int)voKeyPoints[k].pt.y;
			const uchar _ref = _refdata[_step_row*(_y)+_x];
			ushort& _res = oDesc.at<ushort>(_y,_x);
			const size_t _t = (size_t)(_ref*fThreshold)+nThresholdOffset;
			#include "LBSP_16bits_dbcross_1ch.i"
		}
	}
	else { //nChannels==3
		oDesc.create(oInputImg.size(),CV_16UC3);
		for(size_t k=0; k<nKeyPoints; ++k) {
			const int _x = (int)voKeyPoints[k].pt.x;
			const int _y = (int)voKeyPoints[k].pt.y;
			const uchar* _ref = _refdata+_step_row*(_y)+3*(_x);
			ushort* _res = ((ushort*)(oDesc.data + oDesc.step.p[0]*_y + oDesc.step.p[1]*_x));
			const size_t _t[3] = {(size_t)(_ref[0]*fThreshold)+nThresholdOffset,(size_t)(_ref[1]*fThreshold)+nThresholdOffset,(size_t)(_ref[2]*fThreshold)+nThresholdOffset};
			#include "LBSP_16bits_dbcross_3ch3t.i"
		}
	}
}

void LBSP::compute2(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
	CV_Assert(!oImage.empty());
    cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImage.size(),PATCH_SIZE/2);             //去除图像边界的像素点
    cv::KeyPointsFilter::runByKeypointSize(voKeypoints,std::numeric_limits<float>::epsilon()); //去除超出一定范围的像素点
    if(voKeypoints.empty()) {
        oDescriptors.release();
        return;
    }
	if(m_bOnlyUsingAbsThreshold)
		lbsp_computeImpl2(oImage,m_oRefImage,voKeypoints,oDescriptors,m_nThreshold);
	else
		lbsp_computeImpl2(oImage,m_oRefImage,voKeypoints,oDescriptors,m_fRelThreshold,m_nThreshold);
}

void LBSP::compute2(const std::vector<cv::Mat>& voImageCollection, std::vector<std::vector<cv::KeyPoint> >& vvoPointCollection, std::vector<cv::Mat>& voDescCollection) const {
    CV_Assert(voImageCollection.size() == vvoPointCollection.size());
    voDescCollection.resize(voImageCollection.size());
    for(size_t i=0; i<voImageCollection.size(); i++)
        compute2(voImageCollection[i], vvoPointCollection[i], voDescCollection[i]);
}

void LBSP::computeImpl(const cv::Mat& oImage, std::vector<cv::KeyPoint>& voKeypoints, cv::Mat& oDescriptors) const {
	CV_Assert(!oImage.empty());
	cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImage.size(),PATCH_SIZE/2);
	cv::KeyPointsFilter::runByKeypointSize(voKeypoints,std::numeric_limits<float>::epsilon());
	if(voKeypoints.empty()) {
		oDescriptors.release();
		return;
	}
	if(m_bOnlyUsingAbsThreshold)
		lbsp_computeImpl(oImage,m_oRefImage,voKeypoints,oDescriptors,m_nThreshold);
	else
		lbsp_computeImpl(oImage,m_oRefImage,voKeypoints,oDescriptors,m_fRelThreshold,m_nThreshold);
}

//把所有计算好的LBSP描述子恢复为矩阵的形式,即把一行转变成与图片等高等宽的图片形式
void LBSP::reshapeDesc(cv::Size oSize, const std::vector<cv::KeyPoint>& voKeypoints, const cv::Mat& oDescriptors, cv::Mat& oOutput) {
	CV_DbgAssert(!voKeypoints.empty());
	CV_DbgAssert(!oDescriptors.empty() && oDescriptors.cols==1);
	CV_DbgAssert(oSize.width>0 && oSize.height>0);
	CV_DbgAssert(DESC_SIZE==2); // @@@ also relies on a constant desc size
	CV_DbgAssert(oDescriptors.type()==CV_16UC1 || oDescriptors.type()==CV_16UC3);
	const size_t nChannels = (size_t)oDescriptors.channels();
	const size_t nKeyPoints = voKeypoints.size();
	if(nChannels==1) {
		oOutput.create(oSize,CV_16UC1);
		oOutput = cv::Scalar_<ushort>(0);
		for(size_t k=0; k<nKeyPoints; ++k)
			oOutput.at<ushort>(voKeypoints[k].pt) = oDescriptors.at<ushort>((int)k);
	}
	else { //nChannels==3
		oOutput.create(oSize,CV_16UC3);
		oOutput = cv::Scalar_<ushort>(0,0,0);
		for(size_t k=0; k<nKeyPoints; ++k) {
			ushort* output_ptr = (ushort*)(oOutput.data + oOutput.step.p[0]*(int)voKeypoints[k].pt.y);
			const ushort* const desc_ptr = (ushort*)(oDescriptors.data + oDescriptors.step.p[0]*k);
			const size_t idx = 3*(int)voKeypoints[k].pt.x;
			for(size_t n=0; n<3; ++n)
				output_ptr[idx+n] = desc_ptr[n];
		}
	}
}

void LBSP::calcDescImgDiff(const cv::Mat& oDesc1, const cv::Mat& oDesc2, cv::Mat& oOutput, bool bForceMergeChannels) {
	CV_DbgAssert(oDesc1.size()==oDesc2.size() && oDesc1.type()==oDesc2.type());
	CV_DbgAssert(DESC_SIZE==2); // @@@ also relies on a constant desc size
	CV_DbgAssert(oDesc1.type()==CV_16UC1 || oDesc1.type()==CV_16UC3);
	CV_DbgAssert(CV_MAT_DEPTH(oDesc1.type())==CV_16U);
	CV_DbgAssert(DESC_SIZE*8<=UCHAR_MAX);
	CV_DbgAssert(oDesc1.step.p[0]==oDesc2.step.p[0] && oDesc1.step.p[1]==oDesc2.step.p[1]);
	const float fScaleFactor = (float)UCHAR_MAX/(DESC_SIZE*8);  //缩放因子:此处的值为16
	const size_t nChannels = CV_MAT_CN(oDesc1.type());
	const size_t _step_row = oDesc1.step.p[0];
	if(nChannels==1) {
		oOutput.create(oDesc1.size(),CV_8UC1);
		oOutput = cv::Scalar(0);
		for(int i=0; i<oDesc1.rows; ++i) {
			const size_t idx = _step_row*i;
			const ushort* const desc1_ptr = (ushort*)(oDesc1.data+idx);
			const ushort* const desc2_ptr = (ushort*)(oDesc2.data+idx);
			for(int j=0; j<oDesc1.cols; ++j)
				oOutput.at<uchar>(i,j) = (uchar)(fScaleFactor*hdist(desc1_ptr[j],desc2_ptr[j]));//! hdist  :computes the hamming distance between two N-byte vectors using an 8-bit popcount LUT
		}
	}
	else { //nChannels==3
		if(bForceMergeChannels)
			oOutput.create(oDesc1.size(),CV_8UC1);
		else
			oOutput.create(oDesc1.size(),CV_8UC3);
		oOutput = cv::Scalar::all(0);
		for(int i=0; i<oDesc1.rows; ++i) {
			const size_t idx =  _step_row*i;
			const ushort* const desc1_ptr = (ushort*)(oDesc1.data+idx);
			const ushort* const desc2_ptr = (ushort*)(oDesc2.data+idx);
			uchar* output_ptr = oOutput.data + oOutput.step.p[0]*i;
			for(int j=0; j<oDesc1.cols; ++j) {
				for(size_t n=0;n<3; ++n) {
					const size_t idx2 = 3*j+n;
					if(bForceMergeChannels)
						output_ptr[j] += (uchar)((fScaleFactor*hdist(desc1_ptr[idx2],desc2_ptr[idx2]))/3);
					else
						output_ptr[idx2] = (uchar)(fScaleFactor*hdist(desc1_ptr[idx2],desc2_ptr[idx2]));
				}
			}
		}
	}
}
//去除边界点,以免超出边界
void LBSP::validateKeyPoints(std::vector<cv::KeyPoint>& voKeypoints, cv::Size oImgSize) {
	cv::KeyPointsFilter::runByImageBorder(voKeypoints,oImgSize,PATCH_SIZE/2);
}
//去除感兴趣区域的边界
void LBSP::validateROI(cv::Mat& oROI) {
	CV_Assert(!oROI.empty() && oROI.type()==CV_8UC1);
	cv::Mat oROI_new(oROI.size(),CV_8UC1,cv::Scalar_<uchar>(0));
	const size_t nBorderSize = PATCH_SIZE/2;
	const cv::Rect nROI_inner(nBorderSize,nBorderSize,oROI.cols-nBorderSize*2,oROI.rows-nBorderSize*2);
	cv::Mat(oROI,nROI_inner).copyTo(cv::Mat(oROI_new,nROI_inner));
	oROI = oROI_new;
}

3. LBSP_16bits_dbcross_1ch.i

// note: this is the LBSP 16 bit double-cross single channel pattern as used in
// the original article by G.-A. Bilodeau et al.
// 
//  O   O   O          4 ..  3 ..  6
//    O O O           .. 15  8 13 ..
//  O O X O O    =>    0  9  X 11  1
//    O O O           .. 12 10 14 ..
//  O   O   O          7 ..  2 ..  5
//
//
// must be defined externally:
//      _t              (size_t, absolute threshold used for comparisons)
//      _ref            (uchar, 'central' value used for comparisons)
//      _data           (uchar*, single-channel data to be covered by the pattern)
//      _y              (int, pattern rows location in the image data)
//      _x              (int, pattern cols location in the image data)
//      _step_row       (size_t, step size between rows, including padding)
//      _res            (ushort, 16 bit result vector)
//       L1dist         (function, returns the absolute difference between two uchars)

#ifdef _val
#error "definitions clash detected"
#else
#define _val(x,y) _data[_step_row*(_y+y)+_x+x]
#endif

_res = ((L1dist(_val(-1, 1),_ref) > _t) << 15)
     + ((L1dist(_val( 1,-1),_ref) > _t) << 14)
     + ((L1dist(_val( 1, 1),_ref) > _t) << 13)
     + ((L1dist(_val(-1,-1),_ref) > _t) << 12)
     + ((L1dist(_val( 1, 0),_ref) > _t) << 11)
     + ((L1dist(_val( 0,-1),_ref) > _t) << 10)
     + ((L1dist(_val(-1, 0),_ref) > _t) << 9)
     + ((L1dist(_val( 0, 1),_ref) > _t) << 8)
     + ((L1dist(_val(-2,-2),_ref) > _t) << 7)
     + ((L1dist(_val( 2, 2),_ref) > _t) << 6)
     + ((L1dist(_val( 2,-2),_ref) > _t) << 5)
     + ((L1dist(_val(-2, 2),_ref) > _t) << 4)
     + ((L1dist(_val( 0, 2),_ref) > _t) << 3)
     + ((L1dist(_val( 0,-2),_ref) > _t) << 2)
     + ((L1dist(_val( 2, 0),_ref) > _t) << 1)
     + ((L1dist(_val(-2, 0),_ref) > _t));

#undef _val


参考文献:

完整的LBSP源码请点这里:https://bitbucket.org/pierre_luc_st_charles/lobster/src

猜你喜欢

转载自blog.csdn.net/ding977921830/article/details/51579004