上一个教程 : 霍夫线变换
下一个教程 : 使用广义巴拉德和 Guil Hough 变换检测物体
原作者 | Ana Huamán |
---|---|
兼容性 | OpenCV >= 3.0 |
目标
在本教程中,您将学习如何
- 使用 OpenCV 函数 HoughCircles() 检测图像中的圆形。
理论
Hough 圆变换
- Hough Circle 变换的工作原理与上一教程中介绍的 Hough Line 变换大致相同。
- 在线段检测中,线段由两个参数(r,θ)定义。在圆的情况下,我们需要三个参数来定义一个圆:
C :( x c e n t e r , y c e n t e r , r ) C:(x_{center},y_{center},r) C:(xcenter,ycenter,r)
其中,(xcenter,ycenter) 定义中心位置(绿点),r 是半径,这样我们就可以完全定义一个圆,如下所示:
- 为了提高效率,OpenCV 实现了一种比标准 Hough 变换稍难的检测方法: Hough 梯度法由两个主要阶段组成。第一阶段涉及边缘检测和寻找可能的圆心,第二阶段为每个候选圆心寻找最佳半径。更多详情,请查阅《学习 OpenCV》一书或您最喜欢的《计算机视觉》参考书目。
这个程序要做什么?
- 加载图像并对其进行模糊处理以减少噪点
- 对模糊图像应用 Hough 圆变换。
- 在窗口中显示检测到的圆。
代码
更高级的版本(显示用于更改阈值的轨迹条)可在此处找到。
C++
我们将讲解的示例代码可从此处下载。
#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";
// 加载图像
Mat src = imread( samples::findFile( filename ), IMREAD_COLOR );
// 检查图片加载是否正常
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, // 改变该值可检测出彼此距离不同的圆
100, 30, 1, 30 // 更改最后两个参数
// (min_radius & max_radius) 来检测更大的圆
);
for( size_t i = 0; i < circles.size(); i++ )
{
Vec3i c = circles[i];
Point center = Point(c[0], c[1]);
// 圆心
circle( src, center, 1, Scalar(0,100,100), 3, LINE_AA);
// 圆形轮廓
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;
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);
// 加载图像
Mat src = Imgcodecs.imread(filename, Imgcodecs.IMREAD_COLOR);
// 检查图片加载是否正常
if( src.empty() ) {
System.out.println("Error opening image!");
System.out.println("Program Arguments: [image_name -- 默认 "
+ 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, // 改变该值可检测出彼此距离不同的圆
100.0, 30.0, 1, 30); // 更改最后两个参数
// (min_radius & max_radius) 来检测更大的圆
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]));
// 圆心
Imgproc.circle(src, center, 1, new Scalar(0,100,100), 3, 8, 0 );
// 圆形轮廓
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 sys
import cv2 as cv
import numpy as np
def main(argv):
default_file = 'smarties.png'
filename = argv[0] if len(argv) > 0 else default_file
# 加载图像
src = cv.imread(cv.samples.findFile(filename), cv.IMREAD_COLOR)
# 检查图像是否加载成功
if src is None:
print ('Error opening image!')
print ('Usage: hough_circle.py [image_name -- default ' + default_file + '] \n')
return -1
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
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])
# 圆心
cv.circle(src, center, 1, (0, 100, 100), 3)
# 圆的轮廓
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 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;
}
Java
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);
}
Python
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
转换为灰度图像:
C++
Mat gray;
cvtColor(src, gray, COLOR_BGR2GRAY);
Java
Mat gray = new Mat();
Imgproc.cvtColor(src, gray, Imgproc.COLOR_BGR2GRAY);
Python
# Convert it to gray
gray = cv.cvtColor(src, cv.COLOR_BGR2GRAY)
应用中值模糊以减少噪点并避免错误的圆检测:
C++
medianBlur(gray, gray, 5);
Java
Imgproc.medianBlur(gray, gray, 5);
Python
# Reduce the noise to avoid false circle detection
gray = cv.medianBlur(gray, 5)
继续应用 Hough 圆变换:
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 circles
Python
rows = gray.shape[0]
circles = cv.HoughCircles(gray, cv.HOUGH_GRADIENT, 1, rows / 8,
param1=100, param2=30,
minRadius=1, maxRadius=30)
- 参数:
- gray: 输入图像(灰度)。
- circles: 一个向量,存储每个检测到的圆的 3 个值集:xc、yc、r。
- 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。
绘制检测到的圆:
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)
您可以看到,我们将用红色绘制圆,用绿色小圆点绘制圆心。
显示检测到的圆,等待用户退出程序:
C++
imshow("detected circles", src);
waitKey();
Java
HighGui.imshow("detected circles", src);
HighGui.waitKey();
Python
cv.imshow("detected circles", src)
cv.waitKey(0)
结果
使用测试图像运行上述代码的结果如下所示: