CV03-03:Shi-Tomasi角點檢測與強角點處理

??本文匯總了OpenCV的特征檢測API,并補充一個Harris算法之前的Shi-Tomasi算法霞势,提供局部非最大角點抑制處理。這個算法在OpenCV實現(xiàn)就是goodFeaturesToTrack函數(shù)。我們實現(xiàn)的算法比OpenCV的算法性能存在差距,但我們的目的從理解出發(fā)阳欲,理解后可以能很快實現(xiàn)與OpenCV一樣的優(yōu)化。
??程序在OpenCV4.2環(huán)境下可以運行獲得結(jié)果陋率。


序言

  • 在OpenCV中特征檢測主要提供三種特征檢測的實現(xiàn):
    1. 邊緣Edge
    2. 角點Corner
    3. 線與線段Line
    4. 圓檢測Circle
  1. 邊緣檢測

    • Candy檢測
      • void cv::Canny (InputArray image, OutputArray edges, double threshold1, double threshold2, int apertureSize=3, bool L2gradient=false)
    • Candy檢測變形函數(shù)(使用x,y差分)
      • void cv::Canny (InputArray dx, InputArray dy, OutputArray edges, double threshold1, double threshold2, bool L2gradient=false)
  2. 角點檢測

    • 特征值與特征向量計算:
      • void cv::cornerEigenValsAndVecs (InputArray src, OutputArray dst, int blockSize, int ksize, int borderType=BORDER_DEFAULT)
    • Harris檢測算法:基于特征值R響應值計算的判定方法
      • void cv::cornerHarris (InputArray src, OutputArray dst, int blockSize, int ksize, double k, int borderType=BORDER_DEFAULT)
    • Shi-Tomasi檢測算法:基于特征值的最小特征值判斷方法
      • void cv::cornerMinEigenVal (InputArray src, OutputArray dst, int blockSize, int ksize=3, int borderType=BORDER_DEFAULT)
    • 強角點檢測算法:在Harris與Shi-Tomasi算法上提供更加準確的角點位置的算法秽晚。
      • void cv::goodFeaturesToTrack (InputArray image, OutputArray corners, int maxCorners, double qualityLevel, double minDistance, InputArray mask=noArray(), int blockSize=3, bool useHarrisDetector=false, double k=0.04)
      • void cv::goodFeaturesToTrack (InputArray image, OutputArray corners, int maxCorners, double qualityLevel, double minDistance, InputArray mask, int blockSize, int gradientSize, bool useHarrisDetector=false, double k=0.04)
        • 可以指定Sobel差分核大小瓦糟。
    • 亞像素級的角點位置檢測(更加精確的角點位置計算)
      • void cv::cornerSubPix (InputArray image, InputOutputArray corners, Size winSize, Size zeroZone, TermCriteria criteria)
    • 基于復空間導數(shù)的檢測算法:
      • void cv::preCornerDetect (InputArray src, OutputArray dst, int ksize, int borderType=BORDER_DEFAULT)
  3. 線條與線段檢測

    • 線段檢測算法:
      • Ptr< LineSegmentDetector > cv::createLineSegmentDetector (int _refine=LSD_REFINE_STD, double _scale=0.8, double _sigma_scale=0.6, double _quant=2.0, double _ang_th=22.5, double _log_eps=0, double _density_th=0.7, int _n_bins=1024)
    • Hough檢測算法:
      • void cv::HoughLines (InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0, double min_theta=0, double max_theta=CV_PI)
      • void cv::HoughLinesPointSet (InputArray _point, OutputArray _lines, int lines_max, int threshold, double min_rho, double max_rho, double rho_step, double min_theta, double max_theta, double theta_step)
    • 概率Hough檢測算法:
      • void cv::HoughLinesP (InputArray image, OutputArray lines, double rho, double theta, int threshold, double minLineLength=0, double maxLineGap=0)
  4. 圓檢測

    • Hough檢測算法:
      • void cv::HoughCircles (InputArray image, OutputArray circles, int method, double dp, double minDist, double param1=100, double param2=100, int minRadius=0, int maxRadius=0)
  • 角點檢測的說明:
    • 這里的幾個角點檢測方法都是基于差分(導數(shù))。

Shi-Tomasi檢測算法

  1. 計算特征值與特征向量
    • 這個特征值與特征向量是差分(Sobel)的協(xié)方差矩陣
    void cv::cornerEigenValsAndVecs (
        InputArray src,     // 輸入圖像
        OutputArray dst,    // 輸出的特征值與特征向量赴蝇,6通道菩浙,類型是CV_32FC(6)
                            // 因為沒有定義CV_32FC6,所有只能調(diào)用宏CV_32FC,指定通道參數(shù)劲蜻。
                            // 6個通道的數(shù)據(jù)格式分別是陆淀,特征值λ1,特征值λ2先嬉,特征向量1(u1轧苫, u2)特征向量(v1, v2)
        int blockSize,      // 差分和的領域窗口大小(沒有采用高斯模糊疫蔓,而是box模糊)
        int ksize,          // Sobel核大小
        int borderType=BORDER_DEFAULT)
  1. 協(xié)方差矩陣定義

    • M = \begin{bmatrix} \sum \limits _{S(p)}(\dfrac{dI}{dx})^2 & \sum \limits _{S(p)}\dfrac{dI}{dx} \dfrac{dI}{dy} \\ \sum \limits _{S(p)}\dfrac{dI}{dx} \dfrac{dI}{dy} & \sum \limits _{S(p)}(\dfrac{dI}{dy})^2 \end{bmatrix}
  2. Shi-Tomasi角點檢測算法

    • 取最小特征值作為輸出就是Shi-Tomasi檢測算法含懊;
    • OpenCV函數(shù)cornerMinEigenVal就是返回最小特征值。
  3. Shi-Tomasi角點檢測算法實現(xiàn)代碼

    • 包含OpenCV的標準實現(xiàn)算法衅胀。
    • 下面的實現(xiàn)與OpenCV的實現(xiàn)效果完全一樣岔乔。 效果證明,實際上上面求特征值與特征向量的函數(shù)還是進行了高斯模糊處理的滚躯。
#include <iostream>
#include <cmath>
#include <climits>

#include <opencv2/opencv.hpp>
/*
    Shi_Tomasi角點檢測算法
*/

int main(int argc, char **argv){
    cv::Mat img = cv::imread("corner.jpg");
    cv::Mat img_src;
    cv::cvtColor(img, img_src, cv::COLOR_BGR2GRAY);  // OpenCV讀取圖像的顏色是BGR雏门,灰度是8位圖像
    /*
     * 計算特征值與特征向量,
     * void cv::cornerEigenValsAndVecs  (   
     *   InputArray     src,
     *   OutputArray    dst,
     *   int    blockSize,
     *   int    ksize,
     *   int    borderType = BORDER_DEFAULT 
     * )    
     */
    cv::Mat eig_vecs;
    cv::cornerEigenValsAndVecs(img_src, eig_vecs, 5, 9);
    /*
     * 使用特征值實現(xiàn)Harris角點檢測與Shi-Tomasi角點檢測算法
     */
    cv::Mat harris(eig_vecs.rows, eig_vecs.cols, CV_32FC1);
    cv::Mat shi_tomasi(eig_vecs.rows, eig_vecs.cols, CV_32FC1);
    
    float k =0.04;
    for(int y = 0; y < eig_vecs.rows; y++){
        for(int x = 0; x < eig_vecs.cols; x++){
            cv::Vec6f val = eig_vecs.at<cv::Vec6f>(y, x);
            harris.at<float>(y, x) = val[0] * val[1] - k * (val[0] + val[1]) * (val[0] + val[1]);
            shi_tomasi.at<float>(y, x) = val[0] < val[1] ? val[0] : val[1];
        }
    }
    cv::imwrite("harris.jpg", harris);
    cv::imwrite("shi_tomasi.jpg", shi_tomasi);
    /*
     * OpenCV實現(xiàn)的實現(xiàn)Harris角點檢測與Shi-Tomasi角點檢測算法
     * 
     */
    cv::Mat harris_cv, shi_tomasi_cv;
    cv::cornerHarris(img_src, harris_cv, 5, 9, 0.04);
    cv::cornerMinEigenVal(img_src, shi_tomasi_cv, 5, 9);
    cv::imwrite("harris_cv.jpg", harris_cv);
    cv::imwrite("shi_tomasi_cv.jpg", shi_tomasi_cv);
    return 0;
}

強角點檢測算法

  1. 函數(shù)定義
void cv::goodFeaturesToTrack(
    InputArray image,                         // 原圖像
    OutputArray corners,                      // 輸出的角點坐標
    int         maxCorners,                   // 控制輸出角點的最大數(shù)掸掏,對焦點排序嗎茁影,取前面maxCorners個角點。
    double      qualityLevel,                 // 
    double      minDistance,                  // 
    InputArray  mask = noArray(),             // 用來控制計算角點的區(qū)域
    int         blockSize = 3,                // 滑動窗體的大小
    bool        useHarrisDetector = false,    // 角點的偵測算法
    double      k = 0.04                      // Harris才需要的參數(shù)阅束,上面一個參數(shù)為false(Shi-Tomasi算法)呼胚,這個參數(shù)無效。
)
  • 重載的參數(shù)擴展函數(shù)
void cv::goodFeaturesToTrack(
    InputArray    image,
    OutputArray   corners,
    int           maxCorners,
    double        qualityLevel,
    double        minDistance,
    InputArray    mask,
    int           blockSize,
    int           gradientSize,                           // 比另外一個重載函數(shù)多的參數(shù)息裸,Sobel卷積核大小
    bool          useHarrisDetector = false,
    double        k = 0.04 
)
  1. 兩個重要參數(shù)解釋

    1. qualityLevel
      • 這個參數(shù)是爭對最大角點而言的蝇更,小于\texttt{qualityLevel} \cdot \max_{x,y} qualityMeasureMap(x,y)的角點被放棄,
      • 源代碼中基本上是簡單暴力使用呼盆、:
        • minMaxLoc( eig, 0, &maxVal, 0, 0, _mask );
        • threshold( eig, eig, maxVal*qualityLevel, 0, THRESH_TOZERO );
    2. minDistance
      • 會丟棄帶最強角點距離小于minDistance的角點年扩。
      • 如果為0或者小于0,就是不限制距離访圃。
    3. corners
      • 輸出的是角點的坐標數(shù)組厨幻。(列向量)
      • 維度dims = 2,cols = 1, type = CV_32FC2, depth = CV_32F
      • 可以使用通用類型cv::Mat, 也可以直接使用std::vector<cv::Point2f>類型返回最強角點腿时。
  2. 最強角點篩選算法

    • 首先需要計算出角點的檢測值(注意:源代碼中采用了網(wǎng)格算法况脆,可以降低循環(huán)次數(shù),這里為了理解批糟,就采用了沒有優(yōu)化的算法)
    1. 局部非最大抑制(最強角點)
      1. 計算角點檢測值的最大值 maxVal格了;
      2. 根據(jù)qualityLevel條件,對 小于maxVal*qualityLevel的角點檢測值置0.
      3. 對閾值過濾后的角點檢測值做3 * 3的膨脹(這是局部非最大抑制的方法)
      4. 循環(huán)判定徽鼎,所有像素的角點檢測值沒有膨脹就是最強角點盛末。
    2. 距離與最大角點數(shù)條件篩選
      1. 對最強角點排序弹惦;
      2. 循環(huán)排序后的最強角點,最強角點分成兩個部分:滿足條件的最強角點悄但,待判定的最強角點棠隐;
      3. 每次待判定的最強角點循環(huán)與滿足條件的最強角點計算距離,滿足就添加到滿足條件的最強角點檐嚣,否則繼續(xù)下一個待檢測最強角點助泽。
      4. 判定滿足條件的最強角點是否超過設置的最大數(shù),超過就終止處理净嘀,否則繼續(xù)报咳。
  1. 算法實現(xiàn)代碼
    • 代碼參考了和源代碼(其中局部非最大抑制就是參考源代碼的,挖藏,否則就要全部自己擼代碼)
      • 其中使用了源代碼中的內(nèi)存塊的使用暑刃,以及通過指針的方式來處理,巧妙的堅決了角點檢測值與角點坐標的存儲問題膜眠。
    • 代碼沒有實現(xiàn)遮罩功能岩臣,只能對整幅圖像處理,對局部圖像的無法處理宵膨。
void good_features(
    cv::Mat &image, 
    cv::Mat &corners, 
    int maxCorners,                 // 允許為負數(shù)架谎,表示所有強角點
    double qualityLevel,  
    double minDistance,             // 允許為負數(shù),表示沒有距離
    int blockSize, 
    int gradientSize, 
    bool useHarrisDetector, 
    double k){
    // -------------------------手工實現(xiàn)
    // 1. 計算角點辟躏,根據(jù)useHarrisDetector選擇Harris算法還是Shi-Tomasi算法
    cv::Mat eig;
    if(useHarrisDetector){
        cornerHarris( image, eig, blockSize, gradientSize, k);
    }else{
        cornerMinEigenVal(image, eig, blockSize, gradientSize);
    }

    // 2. 局部非最大抑制
    double maxVal = 0;
    cv::Mat  tmp; 
    // 計算最大特征值  
    cv::minMaxLoc(eig, NULL, &maxVal);   // 第二個參數(shù)是返回最小值谷扣,使用空指針表示不需要返回
    // qualityLevel參數(shù)進行閾值過濾
    cv::threshold( eig, eig, maxVal*qualityLevel, 0, cv::THRESH_TOZERO);
    cv::dilate(eig, tmp, cv::Mat());    // 使用cv::Mat()表示3 * 3的膨脹 ,取周圍鄰域中最大值作為元素值
    // 循環(huán)判定捎琐,沒有膨脹的就是最強角點

    std::vector<const float*> tmpCorners;  // 這里需要存房角點判別值與坐標会涎,源代碼中提供了一個巧妙地方式,直接使用原來eig中地址
                                           // 地址的好處就是既可以計算出坐標瑞凑,還可以獲取角點的判別值末秃。
    cv::Size imgsize = image.size();
    // 循環(huán)判定膨脹值是否變化
    for( int y = 1; y < imgsize.height - 1; y++ ){      // 行
        const float* eig_data = (const float*)eig.ptr(y);
        const float* tmp_data = (const float*)tmp.ptr(y);
        for( int x = 1; x < imgsize.width - 1; x++ ){   // 列
            float val = eig_data[x];
            if( val != 0 && val == tmp_data[x])
                tmpCorners.push_back(eig_data + x);    // 存放地址,這是比較精妙的使用方式
        }
    }

    // --- 根據(jù)距離籽御、角點最大個數(shù)等條件過濾角點
    std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr());  // 進行降序排序

    std::vector<cv::Point2f> vec_corners;   // 存放結(jié)果
    size_t total = tmpCorners.size();   // 上面計算的強角點個數(shù) 
    size_t ncorners = 0;                // 計數(shù)器(用來記錄已經(jīng)挑選的強角點數(shù)量)
    if(minDistance < 1 ){    // 距離沒有設置
        for(int i = 0; i < total; i++ ){
            // 計算偏移地址
            int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
            // 根據(jù)偏移地址計算強角點的坐標
            int y = (int)(ofs / eig.step);
            int x = (int)((ofs - y*eig.step)/sizeof(float));

            vec_corners.push_back(cv::Point2f((float)x, (float)y));
            ++ncorners;
            // 超過設置的最大值练慕,則終止處理
            if( maxCorners > 0 && ncorners == maxCorners ){
                break;
            }
        }
    }
    else{
        // 過濾掉距離范圍內(nèi)的強角點(源代碼采用網(wǎng)格管理,比對所有點進行循環(huán)性能要優(yōu)化)
        /* 
         * 第一個點肯定是強角點技掏,后面的點铃将,必須與選擇出來的強角點循環(huán)判定距離是否在指定范圍內(nèi),范圍內(nèi)的拋棄
         * 這個算法是雙重全循環(huán)哑梳,效率比較低劲阎,可以采用網(wǎng)格管理(只對網(wǎng)格周邊的網(wǎng)格中的元素計算,這樣不用所有都編譯一次)涧衙。
         */
        minDistance *= minDistance;  // 為了不做平方根運算哪工,這里把距離變成平方。 
        for(int i = 0; i < total; i++){
            // 計算坐標
            int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
            int y = (int)(ofs / eig.step);
            int x = (int)((ofs - y*eig.step)/sizeof(float));

            bool is_corrner = true;   // 默認是滿足條件的強角點
            if(vec_corners.size() == 0){   // 第一個強角點弧哎,直接push
                vec_corners.push_back(cv::Point2f((float)x, (float)y));
                ++ncorners;
            }else{
                // 開始循環(huán)判定距離
                for(int j = 0; j < vec_corners.size(); j++){
                    // 計算距離
                    float dx = x - vec_corners[j].x;
                    float dy = y - vec_corners[j].y;
                    if(dx*dx + dy*dy < minDistance ){   
                        is_corrner = false;     // 只要有一個在范圍雁比,就不是滿足條件的強角點
                        break;
                    }
                }
                if(is_corrner){   // 循環(huán)完畢都還是強角點,說明是滿足條件的強角點撤嫩。
                    vec_corners.push_back(cv::Point2f((float)x, (float)y));
                    ++ncorners;
                }
            }
            // 檢測是否超過最大角點數(shù)限制
            if( maxCorners > 0 && ncorners == maxCorners ){
                    break; // 超過指定的角點數(shù)終止處理
            }
        }
    }
    // 返回數(shù)據(jù)偎捎,把vector轉(zhuǎn)換為Mat,并輸出
    cv::Mat(vec_corners).convertTo(corners, CV_32F);
}


  1. 效果比較
    • 我們的實現(xiàn)結(jié)果與OpenCV的計算效果完全一樣序攘。當然效率要差不少茴她。這種密集型計算,不適合在GPU上采用并發(fā)計算來提高效率程奠。
    • 運算后的圖片這里不貼圖了丈牢,直接貼實現(xiàn)完整源代碼,可以在OpenCV4.2的C++環(huán)境下運行
#include <iostream>
#include <cmath>
#include <climits>

#include <opencv2/opencv.hpp>
/*
 * 強角點檢測算法
 */
struct greaterThanPtr{    // Functor對象瞄沙,使用指針形式比較兩個值的大小
    bool operator () (const float * a, const float * b) const
    { return (*a > *b) ? true : (*a < *b) ? false : (a > b); }
};

void good_features(
    cv::Mat &image, 
    cv::Mat &corners, 
    int maxCorners, 
    double qualityLevel, 
    double minDistance, 
    int blockSize, 
    int gradientSize, 
    bool useHarrisDetector=false, 
    double k=0.04);

void call_opencv(){
    cv::Mat img = cv::imread("corner.jpg");
    cv::Mat img_src;
    cv::cvtColor(img, img_src, cv::COLOR_BGR2GRAY);  // OpenCV讀取圖像的顏色是BGR己沛,灰度是8位圖像

    /*
     * OpenCV標準的強角點檢測算法
     */
    cv::Mat corners;
    // std::vector<cv::Point2f> corners;
    cv::goodFeaturesToTrack(img_src, corners, 150, 0.01, 10, cv::noArray(), 5, 11, false, 0.04);
    std::cout << corners.dims << "," << corners.rows << "," << corners.cols << std::endl;
    std::cout << corners.type() << "->CV_32FC2:" << CV_32FC2 << std::endl;
    std::cout << corners.depth() << "->CV32F:"<< CV_32F << std::endl;
    std::cout << corners.channels() << std::endl;
    for(int i = 0; i < corners.rows; i++){
        cv::circle(img, corners.at<cv::Point2f>(i), 4, cv::Scalar(255, 0, 0, 255), 2, 8, 0);
    }
    cv::imwrite("corner_cv.jpg", img);
}

void call_myimpl(){
    cv::Mat img = cv::imread("corner.jpg");
    cv::Mat img_src;
    cv::cvtColor(img, img_src, cv::COLOR_BGR2GRAY);  // OpenCV讀取圖像的顏色是BGR,灰度是8位圖像
    /*
     * 手工實現(xiàn)強角點檢測算法
     */
    cv::Mat corners;
    // std::vector<cv::Point2f> corners;
    good_features(img_src, corners, 150, 0.01, 10, 5, 11, false, 0.04);
    std::cout << corners.dims << "," << corners.rows << "," << corners.cols << std::endl;
    std::cout << corners.type() << "->CV_32FC2:" << CV_32FC2 << std::endl;
    std::cout << corners.depth() << "->CV32F:"<< CV_32F << std::endl;
    std::cout << corners.channels() << std::endl;
    for(int i = 0; i < corners.rows; i++){
        cv::circle(img, corners.at<cv::Point2f>(i), 4, cv::Scalar(255, 0, 0, 255), 2, 8, 0);
    }
    cv::imwrite("corner_good.jpg", img);
}

int main(int argc, char **argv){
    call_opencv();
    std::cout << "------------------------------" << std::endl;
    call_myimpl();
    return 0;
}

void good_features(
    cv::Mat &image, 
    cv::Mat &corners, 
    int maxCorners,                 // 允許為負數(shù)距境,表示所有強角點
    double qualityLevel,  
    double minDistance,             // 允許為負數(shù)申尼,表示沒有距離
    int blockSize, 
    int gradientSize, 
    bool useHarrisDetector, 
    double k){
    // -------------------------手工實現(xiàn)
    // 1. 計算角點,根據(jù)useHarrisDetector選擇Harris算法還是Shi-Tomasi算法
    cv::Mat eig;
    if(useHarrisDetector){
        cornerHarris( image, eig, blockSize, gradientSize, k);
    }else{
        cornerMinEigenVal(image, eig, blockSize, gradientSize);
    }

    // 2. 局部非最大抑制
    double maxVal = 0;
    cv::Mat  tmp; 
    // 計算最大特征值  
    cv::minMaxLoc(eig, NULL, &maxVal);   // 第二個參數(shù)是返回最小值垫桂,使用空指針表示不需要返回
    // qualityLevel參數(shù)進行閾值過濾
    cv::threshold( eig, eig, maxVal*qualityLevel, 0, cv::THRESH_TOZERO);
    cv::dilate(eig, tmp, cv::Mat());    // 使用cv::Mat()表示3 * 3的膨脹 师幕,取周圍鄰域中最大值作為元素值
    // 循環(huán)判定,沒有膨脹的就是最強角點

    std::vector<const float*> tmpCorners;  // 這里需要存房角點判別值與坐標诬滩,源代碼中提供了一個巧妙地方式霹粥,直接使用原來eig中地址
                                           // 地址的好處就是既可以計算出坐標,還可以獲取角點的判別值碱呼。
    cv::Size imgsize = image.size();
    // 循環(huán)判定膨脹值是否變化
    for( int y = 1; y < imgsize.height - 1; y++ ){      // 行
        const float* eig_data = (const float*)eig.ptr(y);
        const float* tmp_data = (const float*)tmp.ptr(y);
        for( int x = 1; x < imgsize.width - 1; x++ ){   // 列
            float val = eig_data[x];
            if( val != 0 && val == tmp_data[x])
                tmpCorners.push_back(eig_data + x);    // 存放地址蒙挑,這是比較精妙的使用方式
        }
    }

    // --- 根據(jù)距離、角點最大個數(shù)等條件過濾角點
    std::sort( tmpCorners.begin(), tmpCorners.end(), greaterThanPtr());  // 進行降序排序

    std::vector<cv::Point2f> vec_corners;   // 存放結(jié)果
    size_t total = tmpCorners.size();   // 上面計算的強角點個數(shù) 
    size_t ncorners = 0;                // 計數(shù)器(用來記錄已經(jīng)挑選的強角點數(shù)量)
    if(minDistance < 1 ){    // 距離沒有設置
        for(int i = 0; i < total; i++ ){
            // 計算偏移地址
            int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
            // 根據(jù)偏移地址計算強角點的坐標
            int y = (int)(ofs / eig.step);
            int x = (int)((ofs - y*eig.step)/sizeof(float));

            vec_corners.push_back(cv::Point2f((float)x, (float)y));
            ++ncorners;
            // 超過設置的最大值愚臀,則終止處理
            if( maxCorners > 0 && ncorners == maxCorners ){
                break;
            }
        }
    }
    else{
        // 過濾掉距離范圍內(nèi)的強角點(源代碼采用網(wǎng)格管理忆蚀,比對所有點進行循環(huán)性能要優(yōu)化)
        /* 
         * 第一個點肯定是強角點,后面的點姑裂,必須與選擇出來的強角點循環(huán)判定距離是否在指定范圍內(nèi)馋袜,范圍內(nèi)的拋棄
         * 這個算法是雙重全循環(huán),效率比較低舶斧,可以采用網(wǎng)格管理(只對網(wǎng)格周邊的網(wǎng)格中的元素計算欣鳖,這樣不用所有都編譯一次)。
         */
        minDistance *= minDistance;  // 為了不做平方根運算茴厉,這里把距離變成平方泽台。 
        for(int i = 0; i < total; i++){
            // 計算坐標
            int ofs = (int)((const uchar*)tmpCorners[i] - eig.ptr());
            int y = (int)(ofs / eig.step);
            int x = (int)((ofs - y*eig.step)/sizeof(float));

            bool is_corrner = true;   // 默認是滿足條件的強角點
            if(vec_corners.size() == 0){   // 第一個強角點什荣,直接push
                vec_corners.push_back(cv::Point2f((float)x, (float)y));
                ++ncorners;
            }else{
                // 開始循環(huán)判定距離
                for(int j = 0; j < vec_corners.size(); j++){
                    // 計算距離
                    float dx = x - vec_corners[j].x;
                    float dy = y - vec_corners[j].y;
                    if(dx*dx + dy*dy < minDistance ){   
                        is_corrner = false;     // 只要有一個在范圍,就不是滿足條件的強角點
                        break;
                    }
                }
                if(is_corrner){   // 循環(huán)完畢都還是強角點怀酷,說明是滿足條件的強角點稻爬。
                    vec_corners.push_back(cv::Point2f((float)x, (float)y));
                    ++ncorners;
                }
            }
            // 檢測是否超過最大角點數(shù)限制
            if( maxCorners > 0 && ncorners == maxCorners ){
                    break; // 超過指定的角點數(shù)終止處理
            }
        }
    }
    // 返回數(shù)據(jù),把vector轉(zhuǎn)換為Mat蜕依,并輸出
    cv::Mat(vec_corners).convertTo(corners, CV_32F);
}



?著作權歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末桅锄,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子样眠,更是在濱河造成了極大的恐慌友瘤,老刑警劉巖,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件檐束,死亡現(xiàn)場離奇詭異辫秧,居然都是意外死亡,警方通過查閱死者的電腦和手機厢塘,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門茶没,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人晚碾,你說我怎么就攤上這事抓半。” “怎么了格嘁?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵笛求,是天一觀的道長。 經(jīng)常有香客問我糕簿,道長探入,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任懂诗,我火速辦了婚禮蜂嗽,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘殃恒。我一直安慰自己植旧,他們只是感情好,可當我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布离唐。 她就那樣靜靜地躺著病附,像睡著了一般。 火紅的嫁衣襯著肌膚如雪亥鬓。 梳的紋絲不亂的頭發(fā)上完沪,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天,我揣著相機與錄音嵌戈,去河邊找鬼覆积。 笑死听皿,一個胖子當著我的面吹牛,可吹牛的內(nèi)容都是我干的宽档。 我是一名探鬼主播写穴,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼雌贱!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起偿短,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤欣孤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后昔逗,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體降传,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年勾怒,在試婚紗的時候發(fā)現(xiàn)自己被綠了婆排。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡笔链,死狀恐怖段只,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情鉴扫,我是刑警寧澤赞枕,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布,位于F島的核電站坪创,受9級特大地震影響炕婶,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜莱预,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一柠掂、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧依沮,春花似錦涯贞、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至姥饰,卻和暖如春傻谁,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背列粪。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工审磁, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留谈飒,地道東北人。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓态蒂,卻偏偏與公主長得像杭措,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子钾恢,可洞房花燭夜當晚...
    茶點故事閱讀 44,700評論 2 354

推薦閱讀更多精彩內(nèi)容