霍夫圆变换
在本教程中,你将学到:
- 如何使用 OpenCV 函数 HoughCircles() 来检测图像中的圆。
霍夫圆变换(Hough Circle Transform)
Section titled “霍夫圆变换(Hough Circle Transform)”
-
霍夫圆变换的工作方式与上一篇教程中介绍的霍夫线变换(Hough Line Transform)大致类似。
-
在直线检测的情形中,一条直线由两个参数 定义。而在圆的情形中,我们需要三个参数来定义一个圆:
其中 定义圆心的位置(绿色点), 是半径。有了这三个参数就可以完全定义一个圆,如下图所示:
-
出于效率考虑,OpenCV 实现了一种比标准霍夫变换稍微巧妙一些的检测方法:霍夫梯度法(Hough gradient method),它由两个主要阶段组成。第一阶段进行边缘检测并找出可能的圆心,第二阶段为每个候选圆心找出最合适的半径。更多细节请参阅 Learning OpenCV 一书或你喜欢的计算机视觉参考书。
本程序做什么?
Section titled “本程序做什么?”- 加载一幅图像并对其进行模糊处理以降低噪声;
- 对模糊后的图像应用霍夫圆变换;
- 在一个窗口中显示检测到的圆。
C++:
我们将要讲解的示例代码可以从 这里 下载。一个稍复杂一点的版本(带有用于调整阈值的 Trackbar)可以在 这里 找到。
/** * @file houghcircles.cpp * @brief This program demonstrates circle finding with the Hough transform */#include "opencv2/imgcodecs.hpp"#include "opencv2/highgui.hpp"#include "opencv2/imgproc.hpp"
using namespace cv;using namespace std;
int main(int argc, char** argv){ const char* filename = argc >=2 ? argv[1] : "smarties.png";
// Loads an image Mat src = imread( samples::findFile( filename ), IMREAD_COLOR );
// Check if image is loaded fine if(src.empty()){ printf(" Error opening image\n"); printf(" Program Arguments: [image_name -- default %s] \n", filename); return EXIT_FAILURE; }
Mat gray; cvtColor(src, gray, COLOR_BGR2GRAY);
medianBlur(gray, gray, 5);
vector<Vec3f> circles; HoughCircles(gray, circles, HOUGH_GRADIENT, 1, gray.rows/16, // change this value to detect circles with different distances to each other 100, 30, 1, 30 // change the last two parameters // (min_radius & max_radius) to detect larger circles );
for( size_t i = 0; i < circles.size(); i++ ) { Vec3i c = circles[i]; Point center = Point(c[0], c[1]); // circle center circle( src, center, 1, Scalar(0,100,100), 3, LINE_AA); // circle outline int radius = c[2]; circle( src, center, radius, Scalar(255,0,255), 3, LINE_AA); }
imshow("detected circles", src); waitKey();
return EXIT_SUCCESS;}Java:
我们将要讲解的示例代码可以从 这里 下载。
package sample;/** * @file HoughCircles.java * @brief This program demonstrates circle finding with the Hough transform */
import org.opencv.core.*;import org.opencv.core.Point;import org.opencv.highgui.HighGui;import org.opencv.imgcodecs.Imgcodecs;import org.opencv.imgproc.Imgproc;
class HoughCirclesRun {
public void run(String[] args) {
String default_file = "../../../../data/smarties.png"; String filename = ((args.length > 0) ? args[0] : default_file);
// Load an image Mat src = Imgcodecs.imread(filename, Imgcodecs.IMREAD_COLOR);
// Check if image is loaded fine if( src.empty() ) { System.out.println("Error opening image!"); System.out.println("Program Arguments: [image_name -- default " + default_file +"] \n"); System.exit(-1); }
Mat gray = new Mat(); Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
Imgproc.medianBlur(gray, gray, 5);
Mat circles = new Mat(); Imgproc.HoughCircles(gray, circles, Imgproc.HOUGH_GRADIENT, 1.0, (double)gray.rows()/16, // change this value to detect circles with different distances to each other 100.0, 30.0, 1, 30); // change the last two parameters // (min_radius & max_radius) to detect larger circles
for (int x = 0; x < circles.cols(); x++) { double[] c = circles.get(0, x); Point center = new Point(Math.round(c[0]), Math.round(c[1])); // circle center Imgproc.circle(src, center, 1, new Scalar(0,100,100), 3, 8, 0 ); // circle outline int radius = (int) Math.round(c[2]); Imgproc.circle(src, center, radius, new Scalar(255,0,255), 3, 8, 0 ); }
HighGui.imshow("detected circles", src); HighGui.waitKey();
System.exit(0); }}
public class HoughCircles { public static void main(String[] args) { // Load the native library. System.loadLibrary(Core.NATIVE_LIBRARY_NAME); new HoughCirclesRun().run(args); }}Python:
我们将要讲解的示例代码可以从 这里 下载。
import sysimport cv2 as cvimport numpy as np
def main(argv): default_file = 'smarties.png' filename = argv[0] if len(argv) > 0 else default_file
# Loads an image src = cv.imread(cv.samples.findFile(filename), cv.IMREAD_COLOR)
# Check if image is loaded fine if src is None: print ('Error opening image!') print ('Usage: hough_circle.py [image_name -- default ' + default_file + '] \n') return -1
# Convert it to gray gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
# Reduce the noise to avoid false circle detection gray = cv.medianBlur(gray, 5)
rows = gray.shape[0] circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, rows / 8, param1=100, param2=30, minRadius=1, maxRadius=30)
if circles is not None: circles = np.uint16(np.around(circles)) for i in circles[0, :]: center = (i[0], i[1]) # circle center cv.circle(src, center, 1, (0, 100, 100), 3) # circle outline radius = i[2] cv.circle(src, center, radius, (255, 0, 255), 3)
cv.imshow("detected circles", src) cv.waitKey(0)
return 0
if __name__ == "__main__": main(sys.argv[1:])我们使用的测试图像可以在这里找到。
C++:
const char* filename = argc >=2 ? argv[1] : "smarties.png";
// Loads an imageMat src = imread( samples::findFile( filename ), IMREAD_COLOR );
// Check if image is loaded fineif(src.empty()){ printf(" Error opening image\n"); printf(" Program Arguments: [image_name -- default %s] \n", filename); return EXIT_FAILURE;}Java:
String default_file = "../../../../data/smarties.png";String filename = ((args.length > 0) ? args[0] : default_file);
// Load an imageMat src = Imgcodecs.imread(filename, Imgcodecs.IMREAD_COLOR);
// Check if image is loaded fineif( src.empty() ) { System.out.println("Error opening image!"); System.out.println("Program Arguments: [image_name -- default " + default_file +"] \n"); System.exit(-1);}Python:
default_file = 'smarties.png'filename = argv[0] if len(argv) > 0 else default_file
# Loads an imagesrc = cv.imread(cv.samples.findFile(filename), cv.IMREAD_COLOR)
# Check if image is loaded fineif src is None: print ('Error opening image!') print ('Usage: hough_circle.py [image_name -- default ' + default_file + '] \n') return -1转换为灰度图
Section titled “转换为灰度图”C++:
Mat gray;cvtColor(src, gray, COLOR_BGR2GRAY);Java:
Mat gray = new Mat();Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);Python:
# Convert it to graygray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)应用中值模糊以降低噪声、避免误检圆
Section titled “应用中值模糊以降低噪声、避免误检圆”C++:
medianBlur(gray, gray, 5);Java:
Imgproc.medianBlur(gray, gray, 5);Python:
# Reduce the noise to avoid false circle detectiongray = cv.medianBlur(gray, 5)应用霍夫圆变换
Section titled “应用霍夫圆变换”C++:
vector<Vec3f> circles;HoughCircles(gray, circles, HOUGH_GRADIENT, 1, gray.rows/16, // change this value to detect circles with different distances to each other 100, 30, 1, 30 // change the last two parameters // (min_radius & max_radius) to detect larger circles);Java:
Mat circles = new Mat();Imgproc.HoughCircles(gray, circles, Imgproc.HOUGH_GRADIENT, 1.0, (double)gray.rows()/16, // change this value to detect circles with different distances to each other 100.0, 30.0, 1, 30); // change the last two parameters // (min_radius & max_radius) to detect larger circlesPython:
rows = gray.shape[0]circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, rows / 8, param1=100, param2=30, minRadius=1, maxRadius=30)各参数的含义:
- gray:输入图像(灰度图)。
- circles:一个向量,为每个检测到的圆存储三个值:。
- HOUGH_GRADIENT:定义检测方法。目前 OpenCV 中只有这一种方法可用。
- dp = 1:分辨率的反比。
- min_dist = gray.rows/16:检测到的圆心之间的最小距离。
- param_1 = 200:内部 Canny 边缘检测器的上阈值。
- param_2 = 100:圆心检测的阈值。
- min_radius = 0:要检测的最小半径。如果未知,默认设为 0。
- max_radius = 0:要检测的最大半径。如果未知,默认设为 0。
绘制检测到的圆
Section titled “绘制检测到的圆”C++:
for( size_t i = 0; i < circles.size(); i++ ){ Vec3i c = circles[i]; Point center = Point(c[0], c[1]); // circle center circle( src, center, 1, Scalar(0,100,100), 3, LINE_AA); // circle outline int radius = c[2]; circle( src, center, radius, Scalar(255,0,255), 3, LINE_AA);}Java:
for (int x = 0; x < circles.cols(); x++) { double[] c = circles.get(0, x); Point center = new Point(Math.round(c[0]), Math.round(c[1])); // circle center Imgproc.circle(src, center, 1, new Scalar(0,100,100), 3, 8, 0 ); // circle outline int radius = (int) Math.round(c[2]); Imgproc.circle(src, center, radius, new Scalar(255,0,255), 3, 8, 0 );}Python:
if circles is not None: circles = np.uint16(np.around(circles)) for i in circles[0, :]: center = (i[0], i[1]) # circle center cv.circle(src, center, 1, (0, 100, 100), 3) # circle outline radius = i[2] cv.circle(src, center, radius, (255, 0, 255), 3)可以看到,我们会把圆画成红色,并用一个小的绿点标出圆心。
显示检测到的圆并等待用户退出程序
Section titled “显示检测到的圆并等待用户退出程序”C++:
imshow("detected circles", src);waitKey();Java:
HighGui.imshow("detected circles", src);HighGui.waitKey();Python:
cv.imshow("detected circles", src)cv.waitKey(0)
用一幅测试图像运行上面的代码,结果如下: