#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的参考图片 };
#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; }
// 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