Previous tutorial: Create your own corner detector
Next tutorial: Feature Detection
Original author | Ana Huamán |
---|---|
Compatibility | OpenCV >= 3.0 |
Goals
In this tutorial you will learn how to
- Use the OpenCV function cv::cornerSubPix to find a more precise corner position (more precise than integer pixels).
Code
C++
The tutorial code is shown below. You can also download it from here
#include "opencv2/highgui.hpp" #include "opencv2/imgproc.hpp" #include <iostream> using namespace cv; using namespace std; Mat src, src_gray; int maxCorners = 10; int maxTrackbar = 25; RNG rng(12345); const char* source_window = "Image"; void goodFeaturesToTrack_Demo( int, void* ); int main(int argc, char** argv) {<!-- --> CommandLineParser parser( argc, argv, "{@input | pic3.png | input image}" ); src = imread( samples::findFile( parser.get<String>( "@input" ) ) ); if( src.empty() ) {<!-- --> cout << "Could not open or find the image!\ " << endl; cout << "Usage: " << argv[0] << " <Input image>" << endl; return -1; } cvtColor( src, src_gray, COLOR_BGR2GRAY ); namedWindow( source_window ); createTrackbar( "Max corners:", source_window, & amp;maxCorners, maxTrackbar, goodFeaturesToTrack_Demo ); imshow( source_window, src ); goodFeaturesToTrack_Demo(0, 0); waitKey(); return 0; } void goodFeaturesToTrack_Demo( int, void* ) {<!-- --> maxCorners = MAX(maxCorners, 1); vector<Point2f> corners; double qualityLevel = 0.01; double minDistance = 10; int blockSize = 3, gradientSize = 3; bool useHarrisDetector = false; double k = 0.04; Mat copy = src.clone(); goodFeaturesToTrack( src_gray, corners, maxCorners, qualityLevel, minDistance, Mat(), blockSize, gradientSize, useHarrisDetector, k ); cout << "** Number of corners detected: " << corners.size() << endl; int radius = 4; for( size_t i = 0; i < corners.size(); i + + ) {<!-- --> circle( copy, corners[i], radius, Scalar(rng.uniform(0,255), rng.uniform(0, 256), rng.uniform(0, 256)), FILLED ); } namedWindow( source_window ); imshow( source_window, copy ); Size winSize = Size( 5, 5 ); Size zeroZone = Size( -1, -1 ); TermCriteria criteria = TermCriteria( TermCriteria::EPS + TermCriteria::COUNT, 40, 0.001 ); cornerSubPix( src_gray, corners, winSize, zeroZone, criteria ); for( size_t i = 0; i < corners.size(); i + + ) {<!-- --> cout << " -- Refined Corner [" << i << "] (" << corners[i].x << "," << corners[i].y << ")" << endl; } }
Java
The tutorial code is shown below. You can also download it from here
import java.awt.BorderLayout; import java.awt.Container; import java.awt.Image; import java.util.Random; import javax.swing.BoxLayout; import javax.swing.ImageIcon; import javax.swing.JFrame; import javax.swing.JLabel; import javax.swing.JPanel; import javax.swing.JSlider; import javax.swing.event.ChangeEvent; import javax.swing.event.ChangeListener; import org.opencv.core.Core; import org.opencv.core.CvType; import org.opencv.core.Mat; import org.opencv.core.MatOfPoint; import org.opencv.core.Point; import org.opencv.core.Scalar; import org.opencv.core.Size; import org.opencv.core.TermCriteria; import org.opencv.highgui.HighGui; import org.opencv.imgcodecs.Imgcodecs; import org.opencv.imgproc.Imgproc; class CornerSubPix {<!-- --> private Mat src = new Mat(); private Mat srcGray = new Mat(); private JFrame frame; private JLabel imgLabel; private static final int MAX_CORNERS = 25; private int maxCorners = 10; private Random rng = new Random(12345); public CornerSubPix(String[] args) {<!-- --> String filename = args.length > 0 ? args[0] : "../data/pic3.png"; src = Imgcodecs.imread(filename); if (src.empty()) {<!-- --> System.err.println("Cannot read image: " + filename); System.exit(0); } Imgproc.cvtColor(src, srcGray, Imgproc.COLOR_BGR2GRAY); // Create and set up the window. frame = new JFrame("Shi-Tomasi corner detector demo"); frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); // Set up the content pane. Image img = HighGui.toBufferedImage(src); addComponentsToPane(frame.getContentPane(), img); // Use the content pane's default border layout. No need // setLayout(new BorderLayout()); //Show the window. frame.pack(); frame.setVisible(true); update(); } private void addComponentsToPane(Container pane, Image img) {<!-- --> if (!(pane.getLayout() instanceof BorderLayout)) {<!-- --> pane.add(new JLabel("Container doesn't use BorderLayout!")); return; } JPanel sliderPanel = new JPanel(); sliderPanel.setLayout(new BoxLayout(sliderPanel, BoxLayout.PAGE_AXIS)); sliderPanel.add(new JLabel("Max corners:")); JSlider slider = new JSlider(0, MAX_CORNERS, maxCorners); slider.setMajorTickSpacing(20); slider.setMinorTickSpacing(10); slider.setPaintTicks(true); slider.setPaintLabels(true); slider.addChangeListener(new ChangeListener() {<!-- --> @Override public void stateChanged(ChangeEvent e) {<!-- --> JSlider source = (JSlider) e.getSource(); maxCorners = source.getValue(); update(); } }); sliderPanel.add(slider); pane.add(sliderPanel, BorderLayout.PAGE_START); imgLabel = new JLabel(new ImageIcon(img)); pane.add(imgLabel, BorderLayout.CENTER); } private void update() {<!-- --> maxCorners = Math.max(maxCorners, 1); MatOfPoint corners = new MatOfPoint(); double qualityLevel = 0.01; double minDistance = 10; int blockSize = 3, gradientSize = 3; boolean useHarrisDetector = false; double k = 0.04; Mat copy = src.clone(); Imgproc.goodFeaturesToTrack(srcGray, corners, maxCorners, qualityLevel, minDistance, new Mat(), blockSize, gradientSize, useHarrisDetector, k); System.out.println("** Number of corners detected: " + corners.rows()); int[] cornersData = new int[(int) (corners.total() * corners.channels())]; corners.get(0, 0, cornersData); int radius = 4; Mat matCorners = new Mat(corners.rows(), 2, CvType.CV_32F); float[] matCornersData = new float[(int) (matCorners.total() * matCorners.channels())]; matCorners.get(0, 0, matCornersData); for (int i = 0; i < corners.rows(); i + + ) {<!-- --> Imgproc.circle(copy, new Point(cornersData[i * 2], cornersData[i * 2 + 1]), radius, new Scalar(rng.nextInt(256), rng.nextInt(256), rng.nextInt(256)), Imgproc.FILLED); matCornersData[i * 2] = cornersData[i * 2]; matCornersData[i * 2 + 1] = cornersData[i * 2 + 1]; } matCorners.put(0, 0, matCornersData); imgLabel.setIcon(new ImageIcon(HighGui.toBufferedImage(copy))); frame.repaint(); Size winSize = new Size(5, 5); Size zeroZone = new Size(-1, -1); TermCriteria criteria = new TermCriteria(TermCriteria.EPS + TermCriteria.COUNT, 40, 0.001); Imgproc.cornerSubPix(srcGray, matCorners, winSize, zeroZone, criteria); matCorners.get(0, 0, matCornersData); for (int i = 0; i < corners.rows(); i + + ) {<!-- --> System.out.println( " -- Refined Corner [" + i + "] (" + matCornersData[i * 2] + "," + matCornersData[i * 2 + 1] + ")"); } } } public class CornerSubPixDemo {<!-- --> public static void main(String[] args) {<!-- --> //Load the local OpenCV library System.loadLibrary(Core.NATIVE_LIBRARY_NAME); // Arrange tasks for the event dispatch thread: // Create and display the graphical user interface for this application. javax.swing.SwingUtilities.invokeLater(new Runnable() {<!-- --> @Override public void run() {<!-- --> new CornerSubPix(args); } }); } }
Python
The tutorial code is shown below. You can also download it from here
from __future__ import print_function import cv2 as cv import numpy as np import argparse import random as rng source_window = 'Image' maxTrackbar = 25 rng.seed(12345) def goodFeaturesToTrack_Demo(val): maxCorners = max(val, 1) # Parameters of Shi-Tomasi algorithm qualityLevel = 0.01 minDistance=10 blockSize = 3 gradientSize = 3 useHarrisDetector = False k = 0.04 #Copy source image copy = np.copy(src) # Apply corner detection corners = cv.goodFeaturesToTrack(src_gray, maxCorners, qualityLevel, minDistance, None, \ blockSize=blockSize, gradientSize=gradientSize, useHarrisDetector=useHarrisDetector, k=k) # Detected drawing corners print('** Number of corners detected:', corners.shape[0]) radius = 4 for i in range(corners.shape[0]): cv.circle(copy, (int(corners[i,0,0]), int(corners[i,0,1])), radius, (rng.randint(0,256), rng.randint(0,256), rng .randint(0,256)), cv.FILLED) # Display the results obtained cv.namedWindow(source_window) cv.imshow(source_window, copy) # Set the required parameters to find delicate corners winSize = (5, 5) zeroZone = (-1, -1) criteria = (cv.TERM_CRITERIA_EPS + cv.TermCriteria_COUNT, 40, 0.001) # Calculate the angular position of the refinement corners = cv.cornerSubPix(src_gray, corners, winSize, zeroZone, criteria) # write down for i in range(corners.shape[0]): print(" -- Refined Corner [", i, "] (", corners[i,0,0], ",", corners[i,0,1], ")") # Load source image and convert to grayscale image parser = argparse.ArgumentParser(description='Code for Shi-Tomasi corner detector tutorial.') parser.add_argument('--input', help='Path to input image.', default='pic3.png') args = parser.parse_args() src = cv.imread(cv.samples.findFile(args.input)) if src is None: print('Could not open or find the image:', args.input) exit(0) src_gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY) #Create windows and track bars cv.namedWindow(source_window) maxCorners = 10 #Initial threshold cv.createTrackbar('Threshold: ', source_window, maxCorners, maxTrackbar, goodFeaturesToTrack_Demo) cv.imshow(source_window, src) goodFeaturesToTrack_Demo(maxCorners) cv.waitKey()
Results
The results are as follows