当前位置: 首页 > 编程日记 > 正文

【特征匹配】ORB原理与源码解析

相关 :

   Fast原理与源码解析

   Brief描述子原理与源码解析

   Harris原理与源码解析

  •  http://blog.csdn.net/luoshixian099/article/details/48523267
  • ORB特征提取详解

  • ORB特征点检测

  为了满足实时性的要求,前面文章中介绍过快速提取特征点算法Fast,以及特征描述子Brief。本篇文章介绍的ORB算法结合了Fast和Brief的速度优势,并做了改进,且ORB是免费。

   Ethan Rublee等人2011年在《ORB:An Efficient Alternative to SIFT or SURF》文章中提出了ORB算法。结合Fast与Brief算法,并给Fast特征点增加了方向性,使得特征点具有旋转不变性,并提出了构造金字塔方法,解决尺度不变性,但文章中没有具体详述。实验证明,ORB远优于之前的SIFT与SURF算法。

-------------------------------------------------------------------------------------------------------------------------------

论文核心内容概述:

1.构造金字塔,在每层金字塔上采用Fast算法提取特征点,采用Harris角点响应函数,按角点响应值排序,选取前N个特征点。

2. oFast:计算每个特征点的主方向,灰度质心法,计算特征点半径为r的圆形邻域范围内的灰度质心位置。从中心位置到质心位置的向量,定义为该特 征点的主方向。

  定义矩的计算公式,x,y∈[-r,r]:

                                 

             质心位置:

                               

               主方向:

                                  

3.rBrief:为了解决旋转不变性,把特征点的Patch旋转到主方向上(steered Brief)。通过实验得到,描述子在各个维度上的均值比较离散(偏离0.5),同时维度间相关性很强,说明特征点描述子区分性不好,影响匹配的效果。论文中提出采取学习的方法,采用300K个训练样本点。每一个特征点,选取Patch大小为wp=31,Patch内每对点都采用wt=5大小的子窗口灰度均值做比较,子窗口的个数即为N=(wp-wt)*(wp-wt),从N个窗口中随机选两个做比较即构成描述子的一个bit,论文中采用M=205590种可能的情况:   

       ---------------------------------------------------------------------------------

        1.对所有样本点,做M种测试,构成M维的描述子,每个维度上非1即0;

        2.按均值对M个维度排序(以0.5为中心),组成向量T;

        3.贪婪搜索:把向量T中第一个元素移动到R中,然后继续取T的第二个元素,与R中的所有元素做相关性比较,如果相关性大于指定的阈值Threshold,           抛弃T的这个元素,否则加入到R中;

        4.重复第3个步骤,直到R中有256个元素,若检测完毕,少于256个元素,则降低阈值,重复上述步骤;

       ----------------------------------------------------------------------------------

    rBrief:通过上面的步骤取到的256对点,构成的描述子各维度间相关性很低,区分性好;

                                        

                                              训练前                                            训练后

---------------------------------------------------------------------------------------------------------------------------------

ORB算法步骤,参考opencv源码:

1.首先构造尺度金字塔;

   金字塔共n层,与SIFT不同,每层仅有一副图像;

   第s层的尺度为,Fator初始尺度(默认为1.2),原图在第0层;

   第s层图像大小:

                              

2.在不同尺度上采用Fast检测特征点;在每一层上按公式计算需要提取的特征点数n,在本层上按Fast角点响应值排序,提取前2n个特征点,然后根据Harris   角点响应值排序, 取前n个特征点,作为本层的特征点;

3.计算每个特征点的主方向(质心法);

4.旋转每个特征点的Patch到主方向,采用上述步骤3的选取的最优的256对特征点做τ测试,构成256维描述子,占32个字节;

                   ,,n=256

 

4.采用汉明距离做特征点匹配;

----------OpenCV源码解析-------------------------------------------------------

ORB类定义:位置..\features2d.hpp

nfeatures:需要的特征点总数;

scaleFactor:尺度因子;

nlevels:金字塔层数;

edgeThreshold:边界阈值;

firstLevel:起始层;

 WTA_K:描述子形成方法,WTA_K=2表示,采用两两比较;

 scoreType:角点响应函数,可以选择Harris或者Fast的方法;

 patchSize:特征点邻域大小;

    /*!ORB implementation.*/class CV_EXPORTS_W ORB : public Feature2D{public:// the size of the signature in bytesenum { kBytes = 32, HARRIS_SCORE=0, FAST_SCORE=1 };CV_WRAP explicit ORB(int nfeatures = 500, float scaleFactor = 1.2f, int nlevels = 8, int edgeThreshold = 31,//构造函数int firstLevel = 0, int WTA_K=2, int scoreType=ORB::HARRIS_SCORE, int patchSize=31 );// returns the descriptor size in bytesint descriptorSize() const; //描述子占用的字节数,默认32字节// returns the descriptor typeint descriptorType() const;//描述子类型,8位整形数// Compute the ORB features and descriptors on an imagevoid operator()(InputArray image, InputArray mask, vector<KeyPoint>& keypoints) const;// Compute the ORB features and descriptors on an imagevoid operator()( InputArray image, InputArray mask, vector<KeyPoint>& keypoints, //提取特征点与形成描述子OutputArray descriptors, bool useProvidedKeypoints=false ) const;AlgorithmInfo* info() const;protected:void computeImpl( const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors ) const;//计算描述子void detectImpl( const Mat& image, vector<KeyPoint>& keypoints, const Mat& mask=Mat() ) const;//检测特征点
     CV_PROP_RW int nfeatures;//特征点总数CV_PROP_RW double scaleFactor;//尺度因子CV_PROP_RW int nlevels;//金字塔内层数CV_PROP_RW int edgeThreshold;//边界阈值CV_PROP_RW int firstLevel;//开始层数CV_PROP_RW int WTA_K;//描述子形成方法,默认WTA_K=2,两两比较CV_PROP_RW int scoreType;//角点响应函数CV_PROP_RW int patchSize;//邻域Patch大小};

特征提取及形成描述子:通过这个函数对图像提取Fast特征点或者计算特征描述子

_image:输入图像;

_mask:掩码图像;

_keypoints:输入角点;

_descriptors:如果为空,只寻找特征点,不计算特征描述子;

_useProvidedKeypoints:如果为true,函数只计算特征描述子;

    /** Compute the ORB features and descriptors on an image* @param img the image to compute the features and descriptors on* @param mask the mask to apply* @param keypoints the resulting keypoints* @param descriptors the resulting descriptors* @param do_keypoints if true, the keypoints are computed, otherwise used as an input* @param do_descriptors if true, also computes the descriptors*/void ORB::operator()( InputArray _image, InputArray _mask, vector<KeyPoint>& _keypoints,OutputArray _descriptors, bool useProvidedKeypoints) const{CV_Assert(patchSize >= 2);bool do_keypoints = !useProvidedKeypoints;bool do_descriptors = _descriptors.needed();if( (!do_keypoints && !do_descriptors) || _image.empty() )return;//ROI handlingconst int HARRIS_BLOCK_SIZE = 9;//Harris角点响应需要的边界大小int halfPatchSize = patchSize / 2;.//邻域半径int border = std::max(edgeThreshold, std::max(halfPatchSize, HARRIS_BLOCK_SIZE/2))+1;//采用最大的边界
     Mat image = _image.getMat(), mask = _mask.getMat();if( image.type() != CV_8UC1 )cvtColor(_image, image, CV_BGR2GRAY);//转灰度图int levelsNum = this->nlevels;//金字塔层数if( !do_keypoints ) //不做特征点检测
    {// if we have pre-computed keypoints, they may use more levels than it is set in parameters// !!!TODO!!! implement more correct method, independent from the used keypoint detector.// Namely, the detector should provide correct size of each keypoint. Based on the keypoint size// and the algorithm used (i.e. BRIEF, running on 31x31 patches) we should compute the approximate// scale-factor that we need to apply. Then we should cluster all the computed scale-factors and// for each cluster compute the corresponding image.//// In short, ultimately the descriptor should// ignore octave parameter and deal only with the keypoint size.levelsNum = 0;for( size_t i = 0; i < _keypoints.size(); i++ )levelsNum = std::max(levelsNum, std::max(_keypoints[i].octave, 0));//提取特征点的最大层数levelsNum++;}// Pre-compute the scale pyramidsvector<Mat> imagePyramid(levelsNum), maskPyramid(levelsNum);//创建尺度金字塔图像for (int level = 0; level < levelsNum; ++level){float scale = 1/getScale(level, firstLevel, scaleFactor); //每层对应的尺度/*static inline float getScale(int level, int firstLevel, double scaleFactor){return (float)std::pow(scaleFactor, (double)(level - firstLevel));}*/Size sz(cvRound(image.cols*scale), cvRound(image.rows*scale));//每层对应的图像大小Size wholeSize(sz.width + border*2, sz.height + border*2);Mat temp(wholeSize, image.type()), masktemp;imagePyramid[level] = temp(Rect(border, border, sz.width, sz.height));if( !mask.empty() ){masktemp = Mat(wholeSize, mask.type());maskPyramid[level] = masktemp(Rect(border, border, sz.width, sz.height));}// Compute the resized imageif( level != firstLevel ) //得到金字塔每层的图像
    {if( level < firstLevel ){resize(image, imagePyramid[level], sz, 0, 0, INTER_LINEAR);if (!mask.empty())resize(mask, maskPyramid[level], sz, 0, 0, INTER_LINEAR);}else{resize(imagePyramid[level-1], imagePyramid[level], sz, 0, 0, INTER_LINEAR);if (!mask.empty()){resize(maskPyramid[level-1], maskPyramid[level], sz, 0, 0, INTER_LINEAR);threshold(maskPyramid[level], maskPyramid[level], 254, 0, THRESH_TOZERO);}}copyMakeBorder(imagePyramid[level], temp, border, border, border, border,//扩大图像的边界BORDER_REFLECT_101+BORDER_ISOLATED);if (!mask.empty())copyMakeBorder(maskPyramid[level], masktemp, border, border, border, border,BORDER_CONSTANT+BORDER_ISOLATED);}else{copyMakeBorder(image, temp, border, border, border, border,//扩大图像的四个边界
    BORDER_REFLECT_101);if( !mask.empty() )copyMakeBorder(mask, masktemp, border, border, border, border,BORDER_CONSTANT+BORDER_ISOLATED);}}// Pre-compute the keypoints (we keep the best over all scales, so this has to be done beforehandvector < vector<KeyPoint> > allKeypoints;if( do_keypoints )//提取角点
    {// Get keypoints, those will be far enough from the border that no check will be required for the descriptorcomputeKeyPoints(imagePyramid, maskPyramid, allKeypoints, //对每一层图像提取角点,见下面(1)的分析
    nfeatures, firstLevel, scaleFactor,edgeThreshold, patchSize, scoreType);// make sure we have the right number of keypoints keypoints/*vector<KeyPoint> temp;for (int level = 0; level < n_levels; ++level){vector<KeyPoint>& keypoints = all_keypoints[level];temp.insert(temp.end(), keypoints.begin(), keypoints.end());keypoints.clear();}KeyPoint::retainBest(temp, n_features_);for (vector<KeyPoint>::iterator keypoint = temp.begin(),keypoint_end = temp.end(); keypoint != keypoint_end; ++keypoint)all_keypoints[keypoint->octave].push_back(*keypoint);*/}else //不提取角点
    {// Remove keypoints very close to the border
    KeyPointsFilter::runByImageBorder(_keypoints, image.size(), edgeThreshold);// Cluster the input keypoints depending on the level they were computed at
    allKeypoints.resize(levelsNum);for (vector<KeyPoint>::iterator keypoint = _keypoints.begin(),keypointEnd = _keypoints.end(); keypoint != keypointEnd; ++keypoint)allKeypoints[keypoint->octave].push_back(*keypoint); //把角点信息存入allKeypoints内// Make sure we rescale the coordinatesfor (int level = 0; level < levelsNum; ++level) //把角点位置信息缩放到指定层位置上
    {if (level == firstLevel)continue;vector<KeyPoint> & keypoints = allKeypoints[level];float scale = 1/getScale(level, firstLevel, scaleFactor);for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)keypoint->pt *= scale; //缩放
    }}Mat descriptors;vector<Point> pattern;if( do_descriptors ) //计算特征描述子
    {int nkeypoints = 0;for (int level = 0; level < levelsNum; ++level)nkeypoints += (int)allKeypoints[level].size();//得到所有层的角点总数if( nkeypoints == 0 )_descriptors.release();else{_descriptors.create(nkeypoints, descriptorSize(), CV_8U);//创建一个矩阵存放描述子,每一行表示一个角点信息descriptors = _descriptors.getMat();}const int npoints = 512;//取512个点,共256对,产生256维描述子,32个字节
    Point patternbuf[npoints];const Point* pattern0 = (const Point*)bit_pattern_31_;//训练好的256对数据点位置if( patchSize != 31 ){pattern0 = patternbuf;makeRandomPattern(patchSize, patternbuf, npoints);}CV_Assert( WTA_K == 2 || WTA_K == 3 || WTA_K == 4 );if( WTA_K == 2 ) //WTA_K=2使用两个点之间作比较std::copy(pattern0, pattern0 + npoints, std::back_inserter(pattern));else{int ntuples = descriptorSize()*4;initializeOrbPattern(pattern0, pattern, ntuples, WTA_K, npoints);}}_keypoints.clear();int offset = 0;for (int level = 0; level < levelsNum; ++level)//依次计算每一层的角点描述子
    {// Get the features and compute their orientationvector<KeyPoint>& keypoints = allKeypoints[level];int nkeypoints = (int)keypoints.size();//本层内角点个数// Compute the descriptorsif (do_descriptors){Mat desc;if (!descriptors.empty()){desc = descriptors.rowRange(offset, offset + nkeypoints);}offset += nkeypoints; //偏移量// preprocess the resized imageMat& workingMat = imagePyramid[level];//boxFilter(working_mat, working_mat, working_mat.depth(), Size(5,5), Point(-1,-1), true, BORDER_REFLECT_101);GaussianBlur(workingMat, workingMat, Size(7, 7), 2, 2, BORDER_REFLECT_101);//高斯平滑图像computeDescriptors(workingMat, keypoints, desc, pattern, descriptorSize(), WTA_K);//计算本层内角点的描述子,(3)
    }// Copy to the output dataif (level != firstLevel) //角点位置信息返回到原图上
    {float scale = getScale(level, firstLevel, scaleFactor);for (vector<KeyPoint>::iterator keypoint = keypoints.begin(),keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint)keypoint->pt *= scale;}// And add the keypoints to the output_keypoints.insert(_keypoints.end(), keypoints.begin(), keypoints.end());//存入描述子信息,返回
    }}
View Code

(1)提取角点:computeKeyPoints

imagePyramid:即构造好的金字塔

    /** Compute the ORB keypoints on an image* @param image_pyramid the image pyramid to compute the features and descriptors on* @param mask_pyramid the masks to apply at every level* @param keypoints the resulting keypoints, clustered per level*/static void computeKeyPoints(const vector<Mat>& imagePyramid,const vector<Mat>& maskPyramid,vector<vector<KeyPoint> >& allKeypoints,int nfeatures, int firstLevel, double scaleFactor,int edgeThreshold, int patchSize, int scoreType ){int nlevels = (int)imagePyramid.size(); //金字塔层数vector<int> nfeaturesPerLevel(nlevels);// fill the extractors and descriptors for the corresponding scalesfloat factor = (float)(1.0 / scaleFactor);float ndesiredFeaturesPerScale = nfeatures*(1 - factor)/(1 - (float)pow((double)factor, (double)nlevels));//
     int sumFeatures = 0;for( int level = 0; level < nlevels-1; level++ ) //对每层图像上分配相应角点数
    {nfeaturesPerLevel[level] = cvRound(ndesiredFeaturesPerScale);sumFeatures += nfeaturesPerLevel[level];ndesiredFeaturesPerScale *= factor;}nfeaturesPerLevel[nlevels-1] = std::max(nfeatures - sumFeatures, 0);//剩下角点数,由最上层图像提取// Make sure we forget about what is too close to the boundary//edge_threshold_ = std::max(edge_threshold_, patch_size_/2 + kKernelWidth / 2 + 2);// pre-compute the end of a row in a circular patchint halfPatchSize = patchSize / 2; //计算每个特征点圆邻域的位置信息vector<int> umax(halfPatchSize + 2);int v, v0, vmax = cvFloor(halfPatchSize * sqrt(2.f) / 2 + 1);int vmin = cvCeil(halfPatchSize * sqrt(2.f) / 2);for (v = 0; v <= vmax; ++v) //
    umax[v] = cvRound(sqrt((double)halfPatchSize * halfPatchSize - v * v));// Make sure we are symmetricfor (v = halfPatchSize, v0 = 0; v >= vmin; --v){while (umax[v0] == umax[v0 + 1])++v0;umax[v] = v0;++v0;}allKeypoints.resize(nlevels);for (int level = 0; level < nlevels; ++level){int featuresNum = nfeaturesPerLevel[level];allKeypoints[level].reserve(featuresNum*2);vector<KeyPoint> & keypoints = allKeypoints[level];// Detect FAST features, 20 is a good thresholdFastFeatureDetector fd(20, true);fd.detect(imagePyramid[level], keypoints, maskPyramid[level]);//Fast角点检测// Remove keypoints very close to the borderKeyPointsFilter::runByImageBorder(keypoints, imagePyramid[level].size(), edgeThreshold);//去除邻近边界的点if( scoreType == ORB::HARRIS_SCORE ){// Keep more points than necessary as FAST does not give amazing cornersKeyPointsFilter::retainBest(keypoints, 2 * featuresNum);//按Fast强度排序,保留前2*featuresNum个特征点// Compute the Harris cornerness (better scoring than FAST)HarrisResponses(imagePyramid[level], keypoints, 7, HARRIS_K); //计算每个角点的Harris强度响应
    }//cull to the final desired level, using the new Harris scores or the original FAST scores.KeyPointsFilter::retainBest(keypoints, featuresNum);//按Harris强度排序,保留前featuresNum个float sf = getScale(level, firstLevel, scaleFactor);// Set the level of the coordinatesfor (vector<KeyPoint>::iterator keypoint = keypoints.begin(),keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint){keypoint->octave = level; //层信息keypoint->size = patchSize*sf; //
    }computeOrientation(imagePyramid[level], keypoints, halfPatchSize, umax); //计算角点的方向,(2)分析
    }}

(2)为每个角点计算主方向,质心法;

    static void computeOrientation(const Mat& image, vector<KeyPoint>& keypoints,int halfPatchSize, const vector<int>& umax){// Process each keypointfor (vector<KeyPoint>::iterator keypoint = keypoints.begin(), //为每个角点计算主方向keypointEnd = keypoints.end(); keypoint != keypointEnd; ++keypoint){keypoint->angle = IC_Angle(image, halfPatchSize, keypoint->pt, umax);//计算质心方向
    }}
    static float IC_Angle(const Mat& image, const int half_k, Point2f pt,const vector<int> & u_max){int m_01 = 0, m_10 = 0;const uchar* center = &image.at<uchar> (cvRound(pt.y), cvRound(pt.x));// Treat the center line differently, v=0for (int u = -half_k; u <= half_k; ++u)m_10 += u * center[u];// Go line by line in the circular patchint step = (int)image.step1();for (int v = 1; v <= half_k; ++v) //每次处理对称的两行v
    {// Proceed over the two linesint v_sum = 0;int d = u_max[v];for (int u = -d; u <= d; ++u){int val_plus = center[u + v*step], val_minus = center[u - v*step];v_sum += (val_plus - val_minus); //计算m_01时,位置上差一个符号m_10 += u * (val_plus + val_minus);}m_01 += v * v_sum;//计算上下两行的m_01
    }return fastAtan2((float)m_01, (float)m_10);//计算角度}

(3)计算特征点描述子

    static void computeDescriptors(const Mat& image, vector<KeyPoint>& keypoints, Mat& descriptors,const vector<Point>& pattern, int dsize, int WTA_K){//convert to grayscale if more than one colorCV_Assert(image.type() == CV_8UC1);//create the descriptor mat, keypoints.size() rows, BYTES colsdescriptors = Mat::zeros((int)keypoints.size(), dsize, CV_8UC1);for (size_t i = 0; i < keypoints.size(); i++)computeOrbDescriptor(keypoints[i], image, &pattern[0], descriptors.ptr((int)i), dsize, WTA_K);}
        static void computeOrbDescriptor(const KeyPoint& kpt,const Mat& img, const Point* pattern,uchar* desc, int dsize, int WTA_K){float angle = kpt.angle;//angle = cvFloor(angle/12)*12.f;angle *= (float)(CV_PI/180.f);float a = (float)cos(angle), b = (float)sin(angle);const uchar* center = &img.at<uchar>(cvRound(kpt.pt.y), cvRound(kpt.pt.x));int step = (int)img.step;#if 1#define GET_VALUE(idx) \ //取旋转后一个像素点的值center[cvRound(pattern[idx].x*b + pattern[idx].y*a)*step + \cvRound(pattern[idx].x*a - pattern[idx].y*b)]#elsefloat x, y;int ix, iy;#define GET_VALUE(idx) \ //取旋转后一个像素点,插值法(x = pattern[idx].x*a - pattern[idx].y*b, \y = pattern[idx].x*b + pattern[idx].y*a, \ix = cvFloor(x), iy = cvFloor(y), \x -= ix, y -= iy, \cvRound(center[iy*step + ix]*(1-x)*(1-y) + center[(iy+1)*step + ix]*(1-x)*y + \center[iy*step + ix+1]*x*(1-y) + center[(iy+1)*step + ix+1]*x*y))#endifif( WTA_K == 2 ){for (int i = 0; i < dsize; ++i, pattern += 16)//每个特征描述子长度为32个字节
        {int t0, t1, val;t0 = GET_VALUE(0); t1 = GET_VALUE(1);val = t0 < t1;t0 = GET_VALUE(2); t1 = GET_VALUE(3);val |= (t0 < t1) << 1;t0 = GET_VALUE(4); t1 = GET_VALUE(5);val |= (t0 < t1) << 2;t0 = GET_VALUE(6); t1 = GET_VALUE(7);val |= (t0 < t1) << 3;t0 = GET_VALUE(8); t1 = GET_VALUE(9);val |= (t0 < t1) << 4;t0 = GET_VALUE(10); t1 = GET_VALUE(11);val |= (t0 < t1) << 5;t0 = GET_VALUE(12); t1 = GET_VALUE(13);val |= (t0 < t1) << 6;t0 = GET_VALUE(14); t1 = GET_VALUE(15);val |= (t0 < t1) << 7;desc[i] = (uchar)val;}}else if( WTA_K == 3 ){for (int i = 0; i < dsize; ++i, pattern += 12){int t0, t1, t2, val;t0 = GET_VALUE(0); t1 = GET_VALUE(1); t2 = GET_VALUE(2);val = t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0);t0 = GET_VALUE(3); t1 = GET_VALUE(4); t2 = GET_VALUE(5);val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 2;t0 = GET_VALUE(6); t1 = GET_VALUE(7); t2 = GET_VALUE(8);val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 4;t0 = GET_VALUE(9); t1 = GET_VALUE(10); t2 = GET_VALUE(11);val |= (t2 > t1 ? (t2 > t0 ? 2 : 0) : (t1 > t0)) << 6;desc[i] = (uchar)val;}}else if( WTA_K == 4 ){for (int i = 0; i < dsize; ++i, pattern += 16){int t0, t1, t2, t3, u, v, k, val;t0 = GET_VALUE(0); t1 = GET_VALUE(1);t2 = GET_VALUE(2); t3 = GET_VALUE(3);u = 0, v = 2;if( t1 > t0 ) t0 = t1, u = 1;if( t3 > t2 ) t2 = t3, v = 3;k = t0 > t2 ? u : v;val = k;t0 = GET_VALUE(4); t1 = GET_VALUE(5);t2 = GET_VALUE(6); t3 = GET_VALUE(7);u = 0, v = 2;if( t1 > t0 ) t0 = t1, u = 1;if( t3 > t2 ) t2 = t3, v = 3;k = t0 > t2 ? u : v;val |= k << 2;t0 = GET_VALUE(8); t1 = GET_VALUE(9);t2 = GET_VALUE(10); t3 = GET_VALUE(11);u = 0, v = 2;if( t1 > t0 ) t0 = t1, u = 1;if( t3 > t2 ) t2 = t3, v = 3;k = t0 > t2 ? u : v;val |= k << 4;t0 = GET_VALUE(12); t1 = GET_VALUE(13);t2 = GET_VALUE(14); t3 = GET_VALUE(15);u = 0, v = 2;if( t1 > t0 ) t0 = t1, u = 1;if( t3 > t2 ) t2 = t3, v = 3;k = t0 > t2 ? u : v;val |= k << 6;desc[i] = (uchar)val;}}elseCV_Error( CV_StsBadSize, "Wrong WTA_K. It can be only 2, 3 or 4." );#undef GET_VALUE}

相关文章:

C# 的三种序列化方法

序列化是将一个对象转换成字节流以达到将其长期保存在内存、数据库或文件中的处理过程。它的主要目的是保存对象的状态以便以后需要的时候使用。与其相反的过程叫做反序列化。 序列化一个对象 为了序列化一个对象&#xff0c;我们需要一个被序列化的对象&#xff0c;一个容纳被…

DotNetNuke(DNN)网站发布、部署、迁移和重建

DotNetNuke(DNN)网站本质上是一个ASP.NET网站&#xff0c;由网站文件&#xff08;也就是website目录&#xff09;和数据库组成的&#xff0c;所以&#xff0c;“发布、部署、迁移和重建”实际上也就是把网站文件和数据库文件拷贝到服务器上并修改相应设置的过程。对于"发布…

PyTorch基础与简单应用:构建卷积神经网络实现MNIST手写数字分类

文章目录(一) 问题描述(二) 设计简要描述(三) 程序清单(四) 结果分析(五) 调试报告(六) 实验小结(七) 参考资料(一) 问题描述 构建卷积神经网络实现MNIST手写数字分类。 (二) 设计简要描述 机器学习的三个基本步骤—— 程序设计思路——(此图放大可看清) (三) 程序清单 …

from selenium.webdriver.support.ui import Select

from selenium.webdriver.support.ui import SelectSelect(d.find_element_by_id(ukey_开户行)).first_selected_option.text转载于:https://www.cnblogs.com/paisen/p/3669272.html

rocketmq-flink

https://github.com/apache/rocketmq-externals/tree/master/rocketmq-flink https://issues.apache.org/jira/browse/ROCKETMQ-82

基于Numpy构建全连接前馈神经网络进行手写数字识别

文章目录(一) 问题描述(二) 设计简要描述(三) 程序清单(四) 结果分析(五) 调试报告(六) 实验小结(一) 问题描述 不使用任何机器学习框架&#xff0c;仅仅通过Numpy库构建一个最简单的全连接前馈神经网络&#xff0c;并用该网络识别mnist提供的手写数字体。 (二) 设计简要描述…

sqlserver 三种恢复模式

sql server数据库提供了三种恢复模式&#xff1a;完整&#xff0c;简单和大容量日志&#xff0c;这些模式决定了sql server如何使用事务日志&#xff0c;如何选择它要记录的操作&#xff0c;以及是否截断日志。截断事务日志是删除己执行事务并把该日志空间让给新事务的过程&…

Confluence 6 配置日志

我们推荐你根据你的需求来配置你自己的 Confluence 日志。你可以有下面 2 种方法来修改你的日志&#xff1a;通过 Confluence 管理员控制台进行配置 – 你的修改仅在本次修改有效&#xff0c;下次重启后将会把所有修改重置。编辑属性文件 – 你的修改将会在下次重启后生效同时针…

最近最近在微软的Mobile Soft factory

最近公司在做PDA项目的开发&#xff0c; 我主要负责Mobile的框架设计和开发。以前都是在做winform程序&#xff0c;对Mobile 的开发 知之甚少&#xff0c;现在突然开始做mobile的项目&#xff0c;压力有点大&#xff01; 不知该从何处下手&#xff0c;幸好发现微软提供的Mobile…

神经网络训练技巧

文章目录一、问题描述二、 设计简要描述三、程序清单四、结果分析五、调试报告六、实验小结一、问题描述 基于二元函数f(x,y)z1/20 x2y2掌握梯度下降和权重初始化的技巧&#xff0c;基于手写体识别掌握正规化和两种防止过拟合的技巧。 二、 设计简要描述 梯度下降方法 1.1.1 …

Unix的轻巧“约取而实得”(上篇)

在经过上机操作实践以及掌握Unix系统相关的基础知识之后&#xff0c;可以说已跨进了迈向成功的门槛。接着要登堂入室&#xff0c;继续专进&#xff0c;就必须更加深入的了解Unix基本命令的功能、使用并掌握一些必备的技巧。这一轮学习会有一定难度&#xff0c;需要强烈的求知欲…

操作系统2--操作系统结构

操作系统结构 操作系统的结构需要从这几个方面来考虑:他为用户提供了哪些服务和界面,系统各个组成部分及其相互关系 操作系统的操作 大致可分为:进程管理,主存管理,文件管理,辅存管理和IO管理 进程管理 进程的创建和结束进程的挂起和恢复进程锁\进程交互\进程死锁的规则内存管理…

Windows环境下启动Mysql服务提示“1067 进程意外终止”的解决方案

2019独角兽企业重金招聘Python工程师标准>>> 在Windows服务里启动Mysql服务时提示“1067 进程意外终止”。 此时我们打开计算机管理查看Windows日志&#xff0c;如下图所示&#xff1a; 完整错误提示如下&#xff1a; The server option lower_case_table_names is …

不使用任何框架实现CNN网络

文章目录一、 问题描述二、 设计简要描述三、程序清单四、结果分析五、调试报告六、实验小结一、 问题描述 基于Numpy和函数im2col与col2im来实现一个简单的卷积神经网络&#xff0c;将其用于手写体识别。 二、 设计简要描述 机器学习的三个基本步骤—— 程序设计思路——(…

关于刘冬大侠Spring.NET系列学习笔记3的一点勘正

诚如他第22楼“只因渴求等待”提出的疑问一样&#xff0c;他的下面那一段代码是存在一点点问题的&#xff0c; XElement root XElement.Load(fileName);var objects from obj in root.Elements("object") select obj; 如果照搬照抄刘冬大侠的这段代码那是不会成功读…

什么叫做KDJ指标

什么叫做KDJ指标 KDJ指标的中文名称是随机指数&#xff0c;最早起源于期货市场。 KDJ指标的应用法则KDJ指标是三条曲线&#xff0c;在应用时主要从五个方面进行考虑&#xff1a;KD的取值的绝对数字&#xff1b;KD曲线的形态&#xff1b;KD指标的交叉&#xff1b;KD指标的背离&a…

vim常用命令总结 (转)

在命令状态下对当前行用 &#xff08;连按两次&#xff09;, 或对多行用n&#xff08;n是自然数&#xff09;表示自动缩进从当前行起的下面n行。你可以试试把代码缩进任意打乱再用n排版&#xff0c;相当于一般IDE里的code format。使用ggG可对整篇代码进行排版。 vim 选择文本&…

敏捷过程、极限编程和SCRUM的关系

极限编程是最知名的敏捷开发过程&#xff0c;SCRUM是最经典的极限编程。 层次关系从大到小是&#xff1a;敏捷过程>极限编程>SCRUM

C#双面打印解决方法(打印word\excel\图片)

最近需要按顺序打印word、excel、图片&#xff0c;其中有的需要单面打印&#xff0c;有的双面。网上查了很多方法。主要集中在几个方式解决 1、word的print和excel的printout里设置单双面 2、printdocument里的printsettings的duplex设置单双面 试过之后效果都不好&#xff0c;…

【leetcode】589. N-ary Tree Preorder Traversal

题目如下&#xff1a; 解题思路&#xff1a;凑数题1&#xff0c;话说我这个也是凑数博&#xff1f; 代码如下&#xff1a; class Solution(object):def preorder(self, root):""":type root: Node:rtype: List[int]"""if root None:return []re…

MSDN Visual系列:创建Feature扩展SharePoint列表项或文档的操作菜单项

原文&#xff1a;http://msdn2.microsoft.com/en-us/library/bb418731.aspx在SharePoint中我们可以通过创建一个包含CustomAction元素定义的Feature来为列表项或文档添加一个自定义操作菜单项(Entry Control Block Item)。我们可以添加自定义命令到默认的SharePoint用户界面中。…

评审过程中,A小组发现了5个缺陷,B小组发现了9个缺陷,他们发现的缺陷中有3个是相同的。请问:还有多少个潜在的缺陷没有发现?

分析&#xff1a;这一个“捉-放-捉”问题 背景&#xff1a; 求解&#xff1a; 可以将A看成是第一次捕捉&#xff0c;发现了5个缺陷&#xff0c;全部打上标记 B看成是第二次捕捉&#xff0c;发现了9个缺陷&#xff0c;其中有3个有标记 那么可以算出系统中一共存在的缺陷数量为…

Dell PowerVault TL4000 磁带机卡带问题

最近一段时间Dell PowerVault TL4000 磁带机故障频繁&#xff0c;昨天我在管理系统里面看到Library Status告警&#xff1a;HE: sled blocked, error during sled movement to rotation position Code: 8D 07 &#xff0c;Dell工程师根据Code: 8D 07判断是磁带卡带了&#xff0…

【git】git入门之把自己的项目上传到github

1. 首先当然是要有一个GIT账号&#xff1a;github首页 2. 然后在电脑上安装一个git&#xff1a;git首页 注册和安装这里我就不说了。我相信大家做这个都没有问题。 3. 上述两件事情做完了&#xff0c;就登陆到github页面 1&#xff09;首先我们点标注【1】的小三角&#xff0c;…

Java面试查漏补缺

一、基础 1、&和&&的区别。 【概述】 &&只能用作逻辑与&#xff08;and&#xff09;运算符&#xff08;具有短路功能&#xff09;&#xff1b;但是&可以作为逻辑与运算符&#xff08;是“无条件与”&#xff0c;即没有短路的功能&#xff09;&#xf…

selenium之frame操作

前言 很多时候定位元素时候总是提示元素定位不到的问题&#xff0c;明明元素就在那里&#xff0c;这个时候就要关注你所定位的元素是否在frame和iframe里面 frame标签包含frameset、frame、iframe三种&#xff0c;frameset和普通的标签一样&#xff0c;不会影响正常的定位&…

(C++)将整型数组所有成员初始化为0的三种简单方法

#include<cstdio> #include<cstring>int main(){//1.方法1 int a[10] {};//2.方法2 int b[10] {0};//3.方法3 注意&#xff1a;需要加 <cstring>头文件 int c[10];memset(c,0,sizeof(c));for(int i0;i<9;i){printf("a[%d]%d\n",i,a[i]);}prin…

(C++)对用户输入的整形数组进行冒泡排序

#include<cstdio>//冒泡排序的本质在于交换 //1.读入数组 //2.排序 //3.输出数组 int main(){int a[10];printf("%s","请依次输入数组的10个整型元素&#xff1a;\n");for(int i0;i<9;i){scanf("%d",&a[i]);} int temp 0;for(int …

U3D的Collider

被tx鄙视的体无完肤&#xff0c;回来默默的继续看书&#xff0c;今天看u3d&#xff0c;试了下collider,发现cube添加了rapidbody和boxcollider后落在terrain后就直接穿过去了... 找了一会原因&#xff0c;看到一个collider的参数说明&#xff1a; 分别选中立方体和树的模型&…

限制程序只打开一个实例(转载)

当我们在做一些管理平台类的程序&#xff08;比如Windows的任务管理器&#xff09;时&#xff0c;往往需要限制程序只能打开一个实例。解决这个问题的大致思路很简单&#xff0c;无非是在程序打开的时候判断一下是否有与自己相同的进程开着&#xff0c;如果有&#xff0c;则关闭…