Android OpenCV(三十三):霍夫圆检测

标准霍夫变换的原理就是把图像空间转换成参数空间(即霍夫空间),例如霍夫变换的直线检测就是在距离 -角度空间内进行检测。圆可以表示为:
(xa)2+(yb)2=r2 (x-a)^2+(y-b)^2 = r^2
其中ab代表圆心坐标,r代表圆半径。因此,霍夫变换的圆检测就是在这三个参数组成的三维空间内进行。原则上,霍夫变换可以检测任何形状。但复杂的形状需要的参数很多,霍夫空间的维数对应就多,因此在程序实现上所需的内存空间以及运行效率上都不利于把标准霍夫变换应用于实际复杂图形的检测中。 所以一些改进的霍夫变换就相继提出,它们的基本原理就是尽可能减小霍夫空间的维数

霍夫圆检测

霍夫圆检测分为两个阶段:

  • 检测圆心
  • 从圆心推导出圆半径

检测圆心

检测圆心的原理是圆心是它所在圆周所有法线的交点。因此只要找到法线的交点,即可确定圆心。具体步骤如下:

  • 边缘检测;
  • 计算图像梯度,并确定圆周线。圆周线的梯度即为法线;
  • 在二维霍夫空间内,绘制所有图形的梯度直线,某坐标点上累加和的值越大,说明在该点上直线相交的次数越多,也就越可能是圆心;
  • 在霍夫空间内,4领域内进行非最大值抑制;
  • 设定阈值,霍夫空间内累加和大于该阈值的点就对应于圆心。

从圆心推导出圆半径

  • 计算某一个圆心到所有圆周线的距离;
  • 设定两个阈值,定义为最大半径和最小半径,保留距离在这两个半径之间的值,这意味着我们检测的圆不能太大,也不能太小;
  • 对保留下来的距离进行排序;
  • 找到距离相同的那些值,并计算相同值的数量;
  • 设定一个阈值,只有相同值的数量大于该阈值,才认为该值是该圆心对应的圆半径;
  • 对每一个圆心,完成以上步骤,得到所有的圆半径。

API

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】【数字图像处理】获取对应学习资料。

onlyloveyd CSDN认证博客专家 Android Kotlin OpenCV
个人公众号【Android or OpenCV】,热爱Android、Kotlin、Flutter和OpenCV。毕业于华中科技大学计算机专业,曾就职于华为武汉研究所。目前在三线小城市生活,专注技术与研发。

opencv连续读取多张图片,并对每张图片进行霍夫操作后,只能读取第一张图片

01-18
当单独对第一个for循环进行操作时,会显示文件下的4张图片,但是加上霍夫变换后,只能显示一张图片及霍夫变换后的图片,其他的3张图片及变换后的图片都显示不出来,请问这是为什么? #include "cv.h" #include "opencv2/highgui/highgui.hpp" #include "opencv2/imgproc/imgproc.hpp" #include "opencv2/opencv.hpp" #include <iostream> char filename[100]; char windowname[100]; using namespace cv; using namespace std; /** @function main */ int main() { for (int j = 1; j <= 4; j++) { sprintf(filename, "E:/projects/%d.jpg", j); sprintf(windowname, "window%d.jpg", j); IplImage* pScr = cvLoadImage(filename, 1); cvNamedWindow(windowname, CV_WINDOW_AUTOSIZE); cvShowImage(windowname, pScr); /// Read the image Mat src=pScr;//将IplImage类型的图片pScr转换为Mat型的src,因为霍夫变换是对Mat类型进行操作的,不知道这里理解对不对 Mat src_gray; if (!src.data) { return -1; } /// Convert it to gray cvtColor(src, src_gray, CV_BGR2GRAY); /// Reduce the noise so we avoid false circle detection GaussianBlur(src_gray, src_gray, Size(9, 9), 2, 2); vector<Vec3f> circles; /// Apply the Hough Transform to find the circles HoughCircles(src_gray, circles, CV_HOUGH_GRADIENT, 1.5, 10, 200, 100, 0, 0); /// Draw the circles detected for (size_t i = 0; i < circles.size(); i++) { Point center(cvRound(circles[i][0]), cvRound(circles[i][1])); int radius = cvRound(circles[i][2]); // circle center circle(src, center, 3, Scalar(0, 255, 0), -1, 8, 0); // circle outline circle(src, center, radius, Scalar(0, 0, 255), 3, 8, 0); cout << "第" << j << "个图片的直径是:" << 2 * radius << endl; } /// Show your results namedWindow("Hough Circle Transform Demo", CV_WINDOW_AUTOSIZE); imshow("Hough Circle Transform Demo", src); waitKey(0); } waitKey(0); return 0; }
©️2020 CSDN 皮肤主题: 数字20 设计师: CSDN官方博客 返回首页
实付0元
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值