Monday, 7 October 2013

Harris Corners--01 : implement by openCV2

   In this post I would like to analyze the Harris corners of openCV2, original source codes are located at github of openCV.To make it easier to read, I do some change to the source codes.


     Codes


void harris_corners(cv::Mat const &input, cv::Mat &output, 
                    int block_size, int ksize, float k)
{
    //step 1 : make sure the type of the input is valid
    if(!(input.type() == CV_8U || input.type() == CV_32F)){
        throw std::runtime_error(COMMON_DEBUG_MESSAGE +
                                 "input.type() != CV_8U ||" 
                                 " input.type() != CV_32F\n");
    }

    output.create(input.size(), CV_32F);

    //step 2 : scale need to normalize the result of the sobel filter
    //I don't get it why they initialize the scale like this
    //if anyone figure it out please tell me why, thanks
    double scale = (double)(1 << ((ksize > 0 ? ksize : 3) - 1)) 
                   * block_size;

    //the range of CV_8U are in range [0, 255];
    //CV_32F are in range [0, 1]
    if(input.type() == CV_8U){
        scale *= 255.0;
    }
    scale = 1.0/scale;

    //step 3 : calculate the gradient of x and y direction
    cv::Mat dx, dy;
    cv::Sobel(input, dx, CV_32F, 1, 0, ksize, scale);
    cv::Sobel(input, dy, CV_32F, 0, 1, ksize, scale);

    cv::Size size = input.size();
    //step 4 : convolution and save dx*dx, dx*dy, dy*dy in cov
    using Type = float;    
    cv::Mat cov(size, CV_32FC3);
    for(int i = 0; i < size.height; ++i){
        Type *cov_data = cov.ptr<Type>(i);
        Type const *dxdata = dx.ptr<Type>(i);
        Type const *dydata = dy.ptr<Type>(i);

        for(int j = 0; j < size.width; ++j){
            Type const dx = dxdata[j];
            Type const dy = dydata[j];
            *cov_data = dx*dx; ++cov_data;
            *cov_data = dx*dy; ++cov_data;
            *cov_data = dy*dy; ++cov_data;
        }
    }

    //step 5 : generate the result of Harris matrix, 
    //all of the weight are set as 1
    cv::boxFilter(cov, cov, cov.depth(), cv::Size(block_size, block_size), 
                  cv::Point(-1,-1), false);

    //step 6 : optimize the iteration speed, not a neccessary 
    //step for harris_corner
    if( cov.isContinuous() && output.isContinuous() ){
        size.width = input.total();
        size.height = 1;
    }

    //step 7 : find out Mc and save it to the output Mat
    for(int i = 0; i < size.height; ++i){
        Type const *cov_data = cov.ptr<Type>(i);
        Type *dst = output.ptr<Type>(i);
        for(int j = 0; j < size.width; ++j){
            Type const a = *cov_data; ++cov_data;
            Type const b = *cov_data; ++cov_data;
            Type const c = *cov_data; ++cov_data;
            Type const temp = a + c;
            dst[j] = a*c - b*b - k*(temp)*(temp);
        }
    }
}

    The most curious step is step 2, I have no idea why are they select a scale like that?If anybody know the answer, please give me some comment or leave the answer at openCV forum.

Results

graph_00
graph_01

   Shi-Tomashi improve the result of Harris corners, openCV2 implemented it as function "cv::goodFeaturesToTrack".

   As usual, the codes can download from github.