Now, lets implement our corner detector!
Take a look at the function declarations and documentation in filters.h.
Then study how we have written create1DGaussianKernel()
in filters.cpp.
This function takes a standard deviation and an optional filter radius to produce a filter kernel that samples and normalizes the Gaussian function
Follow our implementation of create1DGaussianKernel()
to implement the derivated Gaussian kernel in create1DDerivatedGaussianKernel()
, where the derivative of a Gaussian is given by
Hint: Use create1DGaussianKernel()
!
Check that your implementation returns the correct result, for example by printing the result to the console.
When the kernel looks reasonable, we are ready to implement CornerDetector
.
Take a look at the class declaration and documentation in corner_detector.h. What corner metrics are supported by the detector?
In CornerDetector
, we have available the private members g_kernel_
and dg_kernel_
, constructed from the filter kernel functions we implemented above (see the implementation of the constructor CornerDetector::CornerDetector(...)
in corner_detector.cpp).
Go to CornerDetector::detect(...)
in corner_detector.cpp.
Recall from the earlier lecture about image filtering that we can apply a linearly separable 2D filter by convolving an image with each of the two corresponding 1D filter components consecutively. Also recall that we can estimate the image gradients in noisy images by convolving with a filter that corresponds to a derivated Gaussian in one direction, and a Gaussian in the other.
Use the 1D filter kernels in g_kernel_
and dg_kernel_
to compute the 2D gradient images Ix and Iy by using the OpenCV function cv::sepFilter2D.
Hint: Use the commented code.
Compile and run the code.
Since do_visualize_
should be true
, you should be able to inspect the gradient images, and check that they look reasonable.
Recall from the lectures that
where
and w(x, y) is a Gaussian windowing function. The A, B and C images have the same size as the gradient images (and the original image).
First, compute the unwindowed version of images A, B and C by performing element-wise multiplication on the correct gradient images. See the cv::Mat::mul() member function.
Then, convolve these images with the Gaussian windowing filter.
Use the 1D kernel given in the private member win_kernel_
, and perform separable filtering as we did above.
Compile and run the code, and check that the results look reasonable.
We are now ready to compute the metrics for cornerishness!
Recall the following corner metrics from the lecture:
Implement these metrics in the member functions CornerDetector::harrisMetric()
, CornerDetector::harmonicMeanMetric()
and CornerDetector::minEigenMetric()
in corner_detector.cpp.
Hint: Use image operations on the A, B and C images. You don't need to use any loops!
Compile and run the code, and check that the resulting metric image looks reasonable. You can change between the different metrics by changing the first argument in the construction of the detector object at lab_corners.cpp.
We now want to find local maximum response values by dilating the response image with an appropriate kernel. In this way, each pixel will be equal to the maximum in the neighborhood (defined by the kernel). We will soon use this dilated image to perform so called non-maximum suppression.
Apply cv::dilate on the response image with an appropriate kernel.
We will compute the threshold by setting it to an appropriate fraction of the maximal response.
First, find the maximum response in the response image by applying cv::minMaxLoc.
This function needs pointers to variables that will hold the resulting max- and min values.
You can get a pointer to a variable by using the &-operator: &max_val
.
Then, find the threshold by computing max_val * quality_level_
using the private member quality_level_
.
Compile and run. Check that the threshold seems reasonable.
The final step is to extract the local maximum response values above the computed threshold.
Use logical image operations to compute a logical image over pixels that are above the threshold and equal to the local maximum response value. How will this last check suppress locally non-maximum values? (Try without it!)
Extract detected points with cv::findNonZero on the logical image.
Compile and run. Check that the logical image looks reasonable.
Congratulations, you have implemented your own corner detector!
Play around with the detector a bit. Try different metrics, parameters and scenes. Then, continue to the next step, so we can use the corners to find circles!