图像处理之hough圆形检测

hough检测原理

点击图像处理之Hough变换检测直线查看
下面直接描述检测圆形的方法

基于Hough变换的圆形检测方法

对于一个半径为 r r r,圆心为 ( a , b ) (a,b) a,b的圆,我们将其表示为:
( x − a ) 2 + ( y − b ) 2 = r 2 (x-a)^2+(y-b)^2=r^2 (xa)2+(yb)2=r2
此时 x = [ x , y ] T , a = [ a , b , r ] T x=[x,y]^T,a=[a,b,r]^T x=[x,y]Ta=[a,b,r]T,其参数空间为三维。显然,图像空间上的一点 ( x , y ) (x,y) x,y,在参数空间中对应着一个圆锥,如下图所示。
在这里插入图片描述
而图像空间的一个圆就对应着这一簇圆锥相交的一个点,这个特定点在参数空间的三维参数一定,就表示一定半径一定圆心坐标的图像空间的那个圆。
上述方法是经典的Hough圆检测方法的原理,它具有精度高,抗干扰能力强等优点,但由于该方法的参数空间为三维,要在三维空间上进行证据累计的话,需要的时间和空间都是庞大的,在实际应用中不适用。为加快Hough变换检测圆的速度,学者们进行了大量研究,也出现了很多改进的Hough变换检测圆的方法。如利用图像梯度信息的Hough变换,对圆的标准方程对x求导得到下式:
2 ( x − a ) + 2 ( y − b ) d y d x = 0 2(x-a)+2(y-b)\frac{dy}{dx}=0 2(xa)+2(yb)dxdy=0
从上式看出,此时的参数空间从半径 r r r,圆心 ( a , b ) (a,b) a,b三维,变成了只有圆心 ( a , b ) (a,b) a,b的二维空间,利用这种方法检测圆其计算量明显减少了。
但这种改进的Hough变换检测圆的方法其检测精度并不高,原因在于,此种方法利用了边界斜率。
从本质上讲,边界斜率其实是用曲线在某一点的弦的斜率来代替的,这种情况下,要保证不存在误差,只有在弦长为零的情况。但在数字图像中,曲线的表现形式是离散的,其在某一点处的斜率指的是此点右向n步斜率或是左向n步斜率。如果弦长过小了,斜率的量化误差就会增大。这种方法比较适用于干扰较少的完整圆形目标。
在这里插入图片描述

主要代码:

def AHTforCircles(edge,center_threhold_factor = None,score_threhold = None,min_center_dist = None,minRad = None,maxRad = None,center_axis_scale = None,radius_scale = None,halfWindow = None,max_circle_num = None):if center_threhold_factor == None:center_threhold_factor = 10.0if score_threhold == None:score_threhold = 15.0if min_center_dist == None:min_center_dist = 80.0if minRad == None:minRad = 0.0if maxRad == None:maxRad = 1e7*1.0if center_axis_scale == None:center_axis_scale = 1.0if radius_scale == None:radius_scale = 1.0if halfWindow == None:halfWindow = 2if max_circle_num == None:max_circle_num = 6min_center_dist_square = min_center_dist**2sobel_kernel_y = np.array([[-1.0, -2.0, -1.0], [0.0, 0.0, 0.0], [1.0, 2.0, 1.0]])sobel_kernel_x = np.array([[-1.0, 0.0, 1.0], [-2.0, 0.0, 2.0], [-1.0, 0.0, 1.0]])edge_x = convolve(sobel_kernel_x,edge,[1,1,1,1],[1,1])edge_y = convolve(sobel_kernel_y,edge,[1,1,1,1],[1,1])center_accumulator = np.zeros((int(np.ceil(center_axis_scale*edge.shape[0])),int(np.ceil(center_axis_scale*edge.shape[1]))))k = np.array([[r for c in range(center_accumulator.shape[1])] for r in range(center_accumulator.shape[0])])l = np.array([[c for c in range(center_accumulator.shape[1])] for r in range(center_accumulator.shape[0])])minRad_square = minRad**2maxRad_square = maxRad**2points = [[],[]]edge_x_pad = np.pad(edge_x,((1,1),(1,1)),'constant')edge_y_pad = np.pad(edge_y,((1,1),(1,1)),'constant')Gaussian_filter_3 = 1.0 / 16 * np.array([(1.0, 2.0, 1.0), (2.0, 4.0, 2.0), (1.0, 2.0, 1.0)])for i in range(edge.shape[0]):for j in range(edge.shape[1]):if not edge[i,j] == 0:dx_neibor = edge_x_pad[i:i+3,j:j+3]dy_neibor = edge_y_pad[i:i+3,j:j+3]dx = (dx_neibor*Gaussian_filter_3).sum()dy = (dy_neibor*Gaussian_filter_3).sum()if not (dx == 0 and dy == 0):t1 = (k/center_axis_scale-i)t2 = (l/center_axis_scale-j)t3 = t1**2 + t2**2temp = (t3 > minRad_square)&(t3 < maxRad_square)&(np.abs(dx*t1-dy*t2) < 1e-4)center_accumulator[temp] += 1points[0].append(i)points[1].append(j)M = center_accumulator.mean()for i in range(center_accumulator.shape[0]):for j in range(center_accumulator.shape[1]):neibor = \center_accumulator[max(0, i - halfWindow + 1):min(i + halfWindow, center_accumulator.shape[0]),max(0, j - halfWindow + 1):min(j + halfWindow, center_accumulator.shape[1])]if not (center_accumulator[i,j] >= neibor).all():center_accumulator[i,j] = 0# 非极大值抑制plt.imshow(center_accumulator,cmap='gray')plt.axis('off')plt.show()center_threshold = M * center_threhold_factorpossible_centers = np.array(np.where(center_accumulator > center_threshold))  # 阈值化sort_centers = []for i in range(possible_centers.shape[1]):sort_centers.append([])sort_centers[-1].append(possible_centers[0,i])sort_centers[-1].append(possible_centers[1,i])sort_centers[-1].append(center_accumulator[sort_centers[-1][0],sort_centers[-1][1]])sort_centers.sort(key=lambda x:x[2],reverse=True)centers = [[],[],[]]points = np.array(points)for i in range(len(sort_centers)):radius_accumulator = np.zeros((int(np.ceil(radius_scale * min(maxRad, np.sqrt(edge.shape[0] ** 2 + edge.shape[1] ** 2)) + 1))),dtype=np.float32)if not len(centers[0]) < max_circle_num:breakiscenter = Truefor j in range(len(centers[0])):d1 = sort_centers[i][0]/center_axis_scale - centers[0][j]d2 = sort_centers[i][1]/center_axis_scale - centers[1][j]if d1**2 + d2**2 < min_center_dist_square:iscenter = Falsebreakif not iscenter:continuetemp = np.sqrt((points[0,:] - sort_centers[i][0] / center_axis_scale) ** 2 + (points[1,:] - sort_centers[i][1] / center_axis_scale) ** 2)temp2 = (temp > minRad) & (temp < maxRad)temp = (np.round(radius_scale * temp)).astype(np.int32)for j in range(temp.shape[0]):if temp2[j]:radius_accumulator[temp[j]] += 1for j in range(radius_accumulator.shape[0]):if j == 0 or j == 1:continueif not radius_accumulator[j] == 0:radius_accumulator[j] = radius_accumulator[j]*radius_scale/np.log(j) #radius_accumulator[j]*radius_scale/jscore_i = radius_accumulator.argmax(axis=-1)if radius_accumulator[score_i] < score_threhold:iscenter = Falseif iscenter:centers[0].append(sort_centers[i][0]/center_axis_scale)centers[1].append(sort_centers[i][1]/center_axis_scale)centers[2].append(score_i/radius_scale)centers = np.array(centers)centers = centers.astype(np.float64)return centers

代码效果:
在这里插入图片描述
在这里插入图片描述

全部代码可见本人GitHub仓库,如果代码有用,please click star and watching
hough检测之前需要canny算子检测基础的边缘,点击这里可以查看有关canny算法相关内容

如果本文对你有帮助,关注加点赞!!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/44160.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

平板光波导中导模的(注意不是泄露模)传播常数β的matlab计算(验证了是对的)

参照的是导波光学_王建(清华大学)的公式(3-1-2、3-1-3)&#xff0c;算的参数是这本书的图3-3的。 function []PropagationConstantsMain() clear;clc;close all lambda01.55;%真空或空气中的入射波长&#xff0c;单位um k02*pi/lambda0; m3;%导模阶数(需要人为指定) n11.62;%芯…

Manjaro KDE 22.1.3vmware无法复制文件

Wayland 是 X11 的现代替代品&#xff0c;几十年来 X11 一直是 Linux 上的默认窗口系统。 Wayland 是一种通信协议&#xff0c;定义 X Window 显示服务器和客户端应用程序之间的消息传递。 软件还不兼容 使用X11即可

短视频矩阵系统源码---开发技术源码能力

短视频矩阵系统开发涉及到多个领域的技术&#xff0c;包括视频编解码技术、大数据处理技术、音视频传输技术、电子商务及支付技术等。因此&#xff0c;短视频矩阵系统开发人员需要具备扎实的计算机基础知识、出色的编程能力、熟练掌握多种开发工具和框架&#xff0c;并掌握音视…

【指针二:穿越编程边界的超能力】

本章重点 5. 函数指针 6. 函数指针数组 7. 指向函数指针数组的指针 8. 回调函数 9. 指针和数组面试题的解析 五、函数指针 首先看一段代码&#xff1a; 输出的是两个地址相同&#xff0c;这两个相同的地址都是 test 函数的地址。 那我们的函数的地址要想保存起来&#xff0c;怎…

Linux系统中的SQL语句

本节主要学习&#xff0c;SQL语句的语句类型&#xff0c;数据库操作&#xff0c;数据表操作&#xff0c;和数据操作等。 文章目录 一、SQL语句类型 DDL DML DCL DQL 二、数据库操作 1.查看 2.创建 默认字符集 指定字符集 3.进入 4.删除 5.更改 库名称 字符集 6…

Java中对Redis的常用操作

目录 数据类型五种常用数据类型介绍各种数据类型特点 常用命令字符串操作命令哈希操作命令列表操作命令集合操作命令有序集合操作命令通用命令 在Java中操作RedisRedis的Java客户端Spring Data Redis使用方式介绍环境搭建配置Redis数据源编写配置类&#xff0c;创建RedisTempla…

子序列,回文串相关题目

class Solution { public:int dp[2510];int lengthOfLIS(vector<int>& nums) {//dp[i]表示以nums[i]为结尾的最长子序列的长度int nnums.size();for(int i0;i<n;i){dp[i]1;}for(int i1;i<n;i){for(int j0;j<i;j){if(nums[i]>nums[j]){dp[i]max(dp[i],dp[…

3ds MAX绘制简单动画

建立一个长方体和茶壶&#xff1a; 在界面右下角点击时间配置&#xff1a; 这是动画制作的必要步骤 选择【自动】&#xff0c;接下来&#xff0c;我们只要在对应的帧改变窗口中图形的位置&#xff0c;就能自动记录该时刻的模样 这就意味着&#xff0c;我们通过电脑记录某几个…

砖墙(力扣)Map + 思维 JAVA

你的面前有一堵矩形的、由 n 行砖块组成的砖墙。这些砖块高度相同&#xff08;也就是一个单位高&#xff09;但是宽度不同。每一行砖块的宽度之和相等。 你现在要画一条 自顶向下 的、穿过 最少 砖块的垂线。如果你画的线只是从砖块的边缘经过&#xff0c;就不算穿过这块砖。你…

[Linux]线程基本知识

概念 进程 一个正在执行的程序&#xff0c;它是资源分配的最小单位 进程中的事情需要按照一定的顺序逐个进行 进程出现了很多弊端: 一是由于进程是资源拥有者&#xff0c;创建、撤消与切换存在较大的时空开销&#xff0c;因此需要引入轻型进程&#xff1b; 二是由于对称多…

7.事件类型

7.1鼠标事件 案例-轮播图点击切换 需求&#xff1a;当点击左右的按钮&#xff0c;可以切换轮播图 分析: ①右侧按钮点击&#xff0c;变量&#xff0c;如果大于等于8&#xff0c;则复原0 ②左侧按钮点击&#xff0c;变量–&#xff0c;如果小于0&#xff0c;则复原最后一张 ③鼠…