标准霍夫变换的原理就是把图像空间转换成参数空间(即霍夫空间),例如霍夫变换的直线检测就是在距离 -角度空间内进行检测。圆可以表示为:
( x − a ) 2 + ( y − b ) 2 = r 2 (x-a)^2+(y-b)^2 = r^2 (x−a)2+(y−b)2=r2
其中a
和b
代表圆心坐标,r
代表圆半径。因此,霍夫变换的圆检测就是在这三个参数组成的三维空间内进行。原则上,霍夫变换可以检测任何形状。但复杂的形状需要的参数很多,霍夫空间的维数对应就多,因此在程序实现上所需的内存空间以及运行效率上都不利于把标准霍夫变换应用于实际复杂图形的检测中。 所以一些改进的霍夫变换就相继提出,它们的基本原理就是尽可能减小霍夫空间的维数
。
霍夫圆检测分为两个阶段:
检测圆心的原理是圆心是它所在圆周所有法线的交点。因此只要找到法线的交点,即可确定圆心。具体步骤如下:
public static void HoughCircles(Mat image, Mat circles, int method, double dp, double minDist, double param1, double param2, int minRadius, int maxRadius)
参数一:image,待检测圆形的图像,必须是CV_8UC1的灰度图
参数二:circles,检测结果。每个圆形用3个参数表示,即(x , y , radius),圆心坐标和圆半径
参数三:method,检测圆形的方法标志,虽然有4个可选项,但是目前只实现了HOUGH_GRADIENT
// C++: enum HoughModes
public static final int
HOUGH_STANDARD = 0,
HOUGH_PROBABILISTIC = 1,
HOUGH_MULTI_SCALE = 2,
HOUGH_GRADIENT = 3;
参数四:dp,累加器分辨率与图像分辨率的反比。例如,如果 dp = 1,则累加器具有与输入图像相同的分辨率。如果dp = 2,则累加器的宽度和高度为输入图像一半
参数五:minDist,检测结果中两个圆心之间的最小距离。如果参数太小,则除了真实的圆圈外,还可能会错误地检测到多个邻居圆圈。 如果太大,可能会错过一些圆圈。
参数六:param1,使用HOUGH_GRADIENT
检测圆形时,它是传递给Canny边缘检测器的两个阈值中的较大值(较小值为较大值的一半)。
参数七:param2,使用HOUGH_GRADIENT
检测圆形时,此参数为检测圆形的累加器阈值,阈值越大,则检测的圆形越精准。
参数八:minRadius,检测圆的最小半径。
参数九:minRadius,检测圆的最大半径。
/**
* 霍夫圆检测
* author: yidong
* 2020/9/2
*/
class HoughCircleDetectActivity : AppCompatActivity() {
private lateinit var mBinding: ActivityHoughCircleBinding
private lateinit var mGray: Mat
private lateinit var mRgb: Mat
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mBinding = DataBindingUtil.setContentView(this, R.layout.activity_hough_circle)
mBinding.presenter = this
mGray = Mat()
mRgb = Mat()
val source = Utils.loadResource(this, R.drawable.coins)
Imgproc.cvtColor(source, mGray, Imgproc.COLOR_BGR2GRAY)
Imgproc.cvtColor(source, mRgb, Imgproc.COLOR_BGR2RGB)
mBinding.ivLena.showMat(mRgb)
source.release()
}
fun doHoughCircleDetect() {
val circle = Mat()
Imgproc.GaussianBlur(mGray, mGray, Size(9.0, 9.0), 2.0, 2.0)
Imgproc.HoughCircles(
mGray,
circle,
Imgproc.HOUGH_GRADIENT,
2.0,
240.0,
100.0,
100.0,
100,
200
)
for (index in 0 until circle.cols()) {
val content = FloatArray(3)
circle.get(0, index, content)
val center =
Point(content[0].roundToInt().toDouble(), content[1].roundToInt().toDouble())
val radius = content[2].roundToInt()
Imgproc.circle(mRgb, center, 3, Scalar(0.0, 255.0, 0.0), -1, 8, 0)
Imgproc.circle(mRgb, center, radius, Scalar(0.0, 0.0, 255.0), 3, 8, 0)
mBinding.ivResult.showMat(mRgb)
}
}
override fun onDestroy() {
mGray.release()
mRgb.release()
super.onDestroy()
}
}
https://github.com/onlyloveyd/LearningAndroidOpenCV
回复【计算机视觉】【Android】【Flutter】【数字图像处理】获取对应学习资料。