目录

  • 一、实现原理
    • 步骤1:使用Canny 算法提取图像边缘
      • 高斯滤波
      • 计算梯度
      • 非极大值抑制
    • 步骤2:在边缘图上利用Hough变换计算圆心与半径
  • 二、具体代码
    • 代码1:直接调用opencv库
    • 代码2:自主实现代码
      • my_Canny.py
      • my_hough.py
      • main.py
    • 效果图

任务是编写一个钱币定位系统,其不仅能够检测出输入图像中各个钱币的边缘,同时,还能给出各个钱币的圆心坐标与半径。

一、实现原理

步骤1:使用Canny 算法提取图像边缘

① 使用高斯滤波器滤波;

② 计算图像的梯度图并获得梯度方向;

③ 对梯度图进行非极大化抑制;

④ 使用双阈值法获得最终的边缘图。

高斯滤波

我们假设一个3*3的高斯卷积核模板

中心的小格子为(0,0),左上角的小格子为(-1,-1),以此类推。

将卷积核中每个小格子的坐标带入到高斯函数中,就可以得到每个小格子的权值。

卷积核的权值总和应该为1,因此需要进行归一化处理,

图像是以像素值来表示的。

在这个灰度图像,每个小格子中都有一个像素值,将卷积核模板覆盖到图像上,如图红色和黑色格子。将图像像素值与对应位置的卷积核的权值相乘,并求其总和,即为黑色格子进行高斯卷积之后所对应的像素值。

通过将这个卷积核模版平移,可计算整个图像中所有格子进行卷积后的值,这样就得到进行高斯卷积之后的图像。

高斯卷积会使得图像丢失最外围的像素。因此通常会在卷积之前,在图像的周围填充一些像素,来保证图像卷积后大小不变。

计算梯度

为什么要求偏导或者梯度?

图像的边缘是由于像素值的突变引起的,因此我们需要通过求梯度强度和方向来获取到突变发生的位置,检测到边缘特征点。

首先需要计算偏导

因为图像像素格最小单位是1,我们近似伊普西隆等于1,那么该点的导数等于它右边的点减去它自身,我们可以用卷积来实现这个过程。


在求x偏导时,用这个卷积核与图像的像素值相乘,就得到了偏导后的图像值,对y求偏导同理

梯度表示了信号变化的大小和方向,梯度值越强,表明该点越有可能是边,梯度方向与边的方向垂直


非极大值抑制

我们通过上述过程提取到的边缘会很粗,我们想要得到的图像的边缘应该只有一个像素的粗细。因此我们要提取突变最大的点作为图像的边缘

非极大值抑制法 ,沿梯度方向,取一像素点,与该像素点前后各一像素点,进行比较,保留最大值,把其他的值舍弃

假设梯度方向是沿x轴方向的,我们需要比较点(-1,0)和点(0,0)两处的梯度强度,若点(0,0)的梯度强度大于(-1,0),则比较(0,0)和(1,0)的梯度强度,若(0,0)小于(1,0),则继续比较(1,0)和(2,0),若(0,0)大于(-1,0)且大于(1,0)则(0,0)最大,可保留,其余的均丢弃,即像素值设为0.

然而实际应用中,梯度方向和边垂直,但边并不都是水平或者垂直的,因此我们可以选取其所靠近的值,采用不同的权重来计算该点的梯度强度。

图像的点1和点2标反了

具体的实现是首先通过for循环遍历所有像素点,梯度值小于4的点直接舍弃,如果该梯度方向的正切的绝对值大于1,就说明离y轴近,y方向的梯度值大,记录其前后两个像素的梯度值;然后如果该点的梯度方向的正切大于0,说明位于一三象限,我们再记录两个对角像素的梯度值;将1号、2号格子去权重得到点1,7号和8号格子去权重得到点2,比较中心点和点1、点2的大小,保留最大值,把其他点舍弃。

步骤2:在边缘图上利用Hough变换计算圆心与半径

① 建立参数空间;

② 依据边缘点的梯度方向对参数空间进行投票;

③ 依据预设定的投票阈值筛选出初步结果;

④ 对已筛选出的结果进行非极大化抑制,得到精确的参数(圆心和半径)。

二、具体代码

代码1:直接调用opencv库

import cv2
import math
import numpy as npPath = "picture_source/picture.jpg" #图像路径
Save_Path = "1/"   #图像保存路径
Reduced_ratio = 2#预处理
img_gray = cv2.imread(Path, cv2.IMREAD_GRAYSCALE)
img_RGB = cv2.imread(Path)
y, x = img_gray.shape[0:2]
img_gray = cv2.resize(img_gray, (int(x / Reduced_ratio), int(y / Reduced_ratio)))
img_RGB = cv2.resize(img_RGB, (int(x / Reduced_ratio), int(y / Reduced_ratio)))#检测边缘
edges = cv2.Canny(img_gray, 100, 200)
cv2.imwrite(Save_Path + "canny_result.jpg", edges)  #生成图像#检测圆
circles=cv2.HoughCircles(edges, cv2.HOUGH_GRADIENT, 1, y/8, param1= 100,param2= 30,minRadius= 10, maxRadius= 100)
if circles is not None:circles = np.uint16(np.around(circles))for circle in circles[0, :]:cv2.circle(img_RGB, (circle[0], circle[1]), circle[2], (132, 135, 239), 2)
cv2.imwrite(Save_Path + "hough_result.jpg", img_RGB)
print('Finished!')

效果图:

代码2:自主实现代码

my_Canny.py

import cv2
import numpy as npclass Canny:def __init__(self, Guassian_kernal_size, img, HT_high_threshold, HT_low_threshold):''':param Guassian_kernal_size: 高斯滤波器尺寸:param img: 输入的图片,在算法过程中改变:param HT_high_threshold: 滞后阈值法中的高阈值:param HT_low_threshold: 滞后阈值法中的低阈值'''self.Guassian_kernal_size = Guassian_kernal_sizeself.img = imgself.y, self.x = img.shape[0:2]self.angle = np.zeros([self.y, self.x]) #梯度方向矩阵self.img_origin = Noneself.x_kernal = np.array([[-1, 1]]) #x偏导卷积核self.y_kernal = np.array([[-1], [1]])   #y偏导卷积核self.HT_high_threshold = HT_high_thresholdself.HT_low_threshold = HT_low_thresholddef Get_gradient_img(self):'''计算梯度图和梯度方向矩阵。:return: 生成的梯度图'''print ('Get_gradient_img')#求x,y偏导矩阵new_img_x = np.zeros([self.y, self.x], dtype=np.float)new_img_y = np.zeros([self.y, self.x], dtype=np.float)for i in range(0, self.x):for j in range(0, self.y):if j == 0:new_img_y[j][i] = 1else:new_img_y[j][i] = np.sum(np.array([[self.img[j - 1][i]], [self.img[j][i]]]) * self.y_kernal)if i == 0:new_img_x[j][i] = 1else:new_img_x[j][i] = np.sum(np.array([self.img[j][i - 1], self.img[j][i]]) * self.x_kernal)#求梯度矩阵gradient_img, self.angle = cv2.cartToPolar(new_img_x, new_img_y)    #直角坐标系转换成极坐标向self.angle = np.tan(self.angle) #梯度方向的正切矩阵self.img = gradient_img.astype(np.uint8)return self.imgdef Non_maximum_suppression (self):'''对生成的梯度图进行非极大化抑制,将tan值的大小与正负结合,确定离散中梯度的方向。:return: 生成的非极大化抑制结果图'''print ('Non_maximum_suppression')result = np.zeros([self.y, self.x])for i in range(1, self.y - 1):for j in range(1, self.x - 1):if abs(self.img[i][j]) <= 4:result[i][j] = 0continueelif abs(self.angle[i][j]) > 1: #大小决定x还是y方向的梯度值大gradient2 = self.img[i - 1][j]gradient4 = self.img[i + 1][j]# g1 g2#    C#    g4 g3if self.angle[i][j] > 0:    #正负决定梯度方向是一三象限还是二四象限gradient1 = self.img[i - 1][j - 1]gradient3 = self.img[i + 1][j + 1]#    g2 g1#    C# g3 g4else:gradient1 = self.img[i - 1][j + 1]gradient3 = self.img[i + 1][j - 1]else:gradient2 = self.img[i][j - 1]gradient4 = self.img[i][j + 1]# g1# g2 C g4#      g3if self.angle[i][j] > 0:gradient1 = self.img[i - 1][j - 1]gradient3 = self.img[i + 1][j + 1]#      g3# g2 C g4# g1else:gradient3 = self.img[i - 1][j + 1]gradient1 = self.img[i + 1][j - 1]#该点梯度方向前后两个点的梯度值temp1 = abs(self.angle[i][j]) * gradient1 + (1 - abs(self.angle[i][j])) * gradient2temp2 = abs(self.angle[i][j]) * gradient3 + (1 - abs(self.angle[i][j])) * gradient4if self.img[i][j] >= temp1 and self.img[i][j] >= temp2:result[i][j] = self.img[i][j]else:result[i][j] = 0self.img = resultreturn self.imgdef Hysteresis_thresholding(self):'''对生成的非极大化抑制结果图进行滞后阈值法,用强边延伸弱边,这里的延伸方向为梯度的垂直方向,将比低阈值大比高阈值小的点置为高阈值大小,方向在离散点上的确定与非极大化抑制相似。:return: 滞后阈值法结果图'''print ('Hysteresis_thresholding')for i in range(1, self.y - 1):for j in range(1, self.x - 1):if self.img[i][j] >= self.HT_high_threshold:if abs(self.angle[i][j]) < 1:   #y方向if self.img_origin[i - 1][j] > self.HT_low_threshold:self.img[i - 1][j] = self.HT_high_thresholdif self.img_origin[i + 1][j] > self.HT_low_threshold:self.img[i + 1][j] = self.HT_high_threshold# g1 g2#    C#    g4 g3if self.angle[i][j] < 0:    #二四象限if self.img_origin[i - 1][j - 1] > self.HT_low_threshold:self.img[i - 1][j - 1] = self.HT_high_thresholdif self.img_origin[i + 1][j + 1] > self.HT_low_threshold:self.img[i + 1][j + 1] = self.HT_high_threshold#    g2 g1#    C# g3 g4else:if self.img_origin[i - 1][j + 1] > self.HT_low_threshold:self.img[i - 1][j + 1] = self.HT_high_thresholdif self.img_origin[i + 1][j - 1] > self.HT_low_threshold:self.img[i + 1][j - 1] = self.HT_high_thresholdelse:if self.img_origin[i][j - 1] > self.HT_low_threshold:self.img[i][j - 1] = self.HT_high_thresholdif self.img_origin[i][j + 1] > self.HT_low_threshold:self.img[i][j + 1] = self.HT_high_threshold# g1# g2 C g4#      g3if self.angle[i][j] < 0:if self.img_origin[i - 1][j - 1] > self.HT_low_threshold:self.img[i - 1][j - 1] = self.HT_high_thresholdif self.img_origin[i + 1][j + 1] > self.HT_low_threshold:self.img[i + 1][j + 1] = self.HT_high_threshold#      g3# g2 C g4# g1else:if self.img_origin[i - 1][j + 1] > self.HT_low_threshold:self.img[i + 1][j - 1] = self.HT_high_thresholdif self.img_origin[i + 1][j - 1] > self.HT_low_threshold:self.img[i + 1][j - 1] = self.HT_high_thresholdreturn self.imgdef canny_algorithm(self):'''按照顺序和步骤调用以上所有成员函数。:return: Canny 算法的结果'''#高斯滤波器滤波self.img = cv2.GaussianBlur(self.img, (self.Guassian_kernal_size, self.Guassian_kernal_size), 0)self.Get_gradient_img()self.img_origin = self.img.copy()self.Non_maximum_suppression()self.Hysteresis_thresholding()return self.img

my_hough.py

import numpy as np
import mathclass Hough_transform:def __init__(self, img, angle, step=5, threshold=135):''':param img: 输入的图像:param angle: 输入的梯度方向矩阵:param step: Hough 变换步长大小:param threshold: 筛选单元的阈值'''self.img = imgself.angle = angleself.y, self.x = img.shape[0:2]self.radius = math.ceil(math.sqrt(self.y**2 + self.x**2))self.step = step#向上取整self.vote_matrix = np.zeros([math.ceil(self.y / self.step), math.ceil(self.x / self.step), math.ceil(self.radius / self.step)])self.threshold = thresholdself.circles = []def Hough_transform_algorithm(self):'''按照 x,y,radius 建立三维空间,图片中边上的点沿梯度方向沿步长经过的单元进行投票。每个点投出来结果为一折线。:return:  投票矩阵'''print ('Hough_transform_algorithm')for i in range(1, self.y - 1):for j in range(1, self.x - 1):if self.img[i][j] > 0:y = ix = jr = 0while y < self.y and x < self.x and y >= 0 and x >= 0:  #圆心在图像中#向下取整self.vote_matrix[math.floor(y / self.step)][math.floor(x / self.step)][math.floor(r / self.step)] += 1y = y + self.step * self.angle[i][j]x = x + self.stepr = r + math.sqrt((self.step * self.angle[i][j])**2 + self.step**2)y = i - self.step * self.angle[i][j]x = j - self.stepr = math.sqrt((self.step * self.angle[i][j])**2 + self.step**2)while y < self.y and x < self.x and y >= 0 and x >= 0:self.vote_matrix[math.floor(y / self.step)][math.floor(x / self.step)][math.floor(r / self.step)] += 1y = y - self.step * self.angle[i][j]x = x - self.stepr = r + math.sqrt((self.step * self.angle[i][j])**2 + self.step**2)return self.vote_matrixdef Select_Circle(self):'''按照阈值从投票矩阵中筛选出合适的圆,并作非极大值抑制,这里的非极大值抑制我采用的是邻近点结果取平均值的方法,而非单纯的取极大值。:return: None'''print ('Select_Circle')#筛选出候选圆列表houxuanyuan = []for i in range(0, math.ceil(self.y / self.step)):for j in range(0, math.ceil(self.x / self.step)):for r in range(0, math.ceil(self.radius / self.step)):if self.vote_matrix[i][j][r] >= self.threshold:y = i * self.step + self.step / 2x = j * self.step + self.step / 2r = r * self.step + self.step / 2houxuanyuan.append((math.ceil(x), math.ceil(y), math.ceil(r)))if len(houxuanyuan) == 0:print("No Circle in this threshold.")return#非极大值抑制x, y, r = houxuanyuan[0]possible = []middle = []for circle in houxuanyuan:#两点的误差不超过20像素,认为是同一圆心,将x,y,r求均值if abs(x - circle[0]) <= 20 and abs(y - circle[1]) <= 20:possible.append([circle[0], circle[1], circle[2]])else:#求一列的均值result = np.array(possible).mean(axis=0)middle.append((result[0], result[1], result[2]))possible.clear()x, y, r = circlepossible.append([x, y, r])result = np.array(possible).mean(axis=0)middle.append((result[0], result[1], result[2]))def takeFirst(elem):return elem[0]middle.sort(key=takeFirst)x, y, r = middle[0]possible = []for circle in middle:if abs(x - circle[0]) <= 20 and abs(y - circle[1]) <= 20:possible.append([circle[0], circle[1], circle[2]])else:result = np.array(possible).mean(axis=0)print("Circle core: (%f, %f)  Radius: %f" % (result[0], result[1], result[2]))self.circles.append((result[0], result[1], result[2]))possible.clear()x, y, r = circlepossible.append([x, y, r])result = np.array(possible).mean(axis=0)print("Circle core: (%f, %f)  Radius: %f" % (result[0], result[1], result[2]))self.circles.append((result[0], result[1], result[2]))def Calculate(self):'''按照算法顺序调用以上成员函数:return: 圆形拟合结果图,圆的坐标及半径集合'''self.Hough_transform_algorithm()self.Select_Circle()return self.circles

main.py

import cv2
import math
from my_hough import Hough_transform
from  my_canny import Canny# np.set_printoptions(threshold=np.inf)
Path = "picture_source/picture.jpg" #图像路径
Save_Path = "picture_result/"   #图像保存路径
Reduced_ratio = 2   #
Guassian_kernal_size = 3    #高斯核窗口大小(尺寸)
HT_high_threshold = 45  #高阈值(梯度)
HT_low_threshold = 25
Hough_transform_step = 6    #霍夫变换的步长
Hough_transform_threshold = 110 #霍夫变换的阈值投票数if __name__ == '__main__':img_gray = cv2.imread(Path, cv2.IMREAD_GRAYSCALE)   #读入灰度图片img_RGB = cv2.imread(Path)y, x = img_gray.shape[0:2]  #高度和宽度img_gray = cv2.resize(img_gray, (int(x / Reduced_ratio), int(y / Reduced_ratio)))#修改图片尺寸,方便计算img_RGB = cv2.resize(img_RGB, (int(x / Reduced_ratio), int(y / Reduced_ratio)))# canny takes about 40 secondsprint ('Canny ...')canny = Canny(Guassian_kernal_size, img_gray, HT_high_threshold, HT_low_threshold)canny.canny_algorithm()cv2.imwrite(Save_Path + "canny_result.jpg", canny.img)  #生成图像# hough takes about 30 secondsprint ('Hough ...')Hough = Hough_transform(canny.img, canny.angle, Hough_transform_step, Hough_transform_threshold)circles = Hough.Calculate()for circle in circles:cv2.circle(img_RGB, (math.ceil(circle[0]), math.ceil(circle[1])), math.ceil(circle[2]), (132, 135, 239), 2)cv2.imwrite(Save_Path + "hough_result.jpg", img_RGB)print ('Finished!')

效果图

python使用Canny算法和HoughCiecle算法实现圆的检测与定位相关推荐

  1. 用python实现PLA算法和Pocket算法

    1.数据生成 利用随机数生成30个数据,包括坐标点和标签,并将生成的随机数据进行划分来确定标签 def create_random_data(w0,w1):samples = []lables = [ ...

  2. 用Spark学习FP Tree算法和PrefixSpan算法

    在FP Tree算法原理总结和PrefixSpan算法原理总结中,我们对FP Tree和PrefixSpan这两种关联算法的原理做了总结,这里就从实践的角度介绍如何使用这两个算法.由于scikit-l ...

  3. 使用Apriori算法和FP-growth算法进行关联分析

    目录 1. 关联分析 2. Apriori原理 3. 使用Apriori算法来发现频繁集 4. 使用FP-growth算法来高效发现频繁项集 5. 示例:从新闻网站点击流中挖掘新闻报道 扩展阅读 系列 ...

  4. 数据结构与算法之美笔记——基础篇(下):图、字符串匹配算法(BF 算法和 RK 算法、BM 算法和 KMP 算法 、Trie 树和 AC 自动机)

    图 如何存储微博.微信等社交网络中的好友关系?图.实际上,涉及图的算法有很多,也非常复杂,比如图的搜索.最短路径.最小生成树.二分图等等.我们今天聚焦在图存储这一方面,后面会分好几节来依次讲解图相关的 ...

  5. dijkstra算法和A*算法

    转自: https://www.cnblogs.com/21207-iHome/p/6048969.html#undefined Dijkstra算法 迪杰斯特拉(Dijkstra)算法是典型的最短路 ...

  6. 关联规则挖掘算法: Aprior算法和Fpgrowth算法

      关联规则挖掘的目的是挖掘不同物品(item)之前的相关性,啤酒和尿布的故事就是一个典型的成功例子.关联规则挖掘思想简单高效,在广告推荐领域也有较多的应用,主要用于推荐模型落地前的流量探索以及构建规 ...

  7. BF算法和KMP算法

    给定两个字符串S和T,在主串S中查找子串T的过程称为串匹配(string matching,也称模式匹配),T称为模式.这里将介绍处理串匹配问题的两种算法,BF算法和KMP算法. BF算法 (暴力匹配 ...

  8. Algorithm:C++语言实现之字符串相关算法(字符串的循环左移、字符串的全排列、带有同个字符的全排列、串匹配问题的BF算法和KMP算法)

    Algorithm:C++语言实现之字符串相关算法(字符串的循环左移.字符串的全排列.带有同个字符的全排列.串匹配问题的BF算法和KMP算法) 目录 一.字符串的算法 1.字符串的循环左移 2.字符串 ...

  9. 操作系统之存储管理——FIFO算法和LRU算法

    操作系统之进程调度--优先权法和轮转法(附上样例讲解) 操作系统之银行家算法-详解流程及案例数据 操作系统之多线程编程-读者优先/写者优先详解 操作系统之存储管理--FIFO算法和LRU算法 操作系统 ...

最新文章

  1. 有没有一种简单的方法可以按值删除列表元素?
  2. ASP.NET 2.0的页面缓存功能介绍
  3. CTFshow 命令执行 web43
  4. Linux命令行下登录ssl加密的ftp
  5. 一行代码搞定 FTP 服务
  6. c++11-std::functionbind
  7. android 代码设置 键盘适应_硬核软件,能在电脑上控制iPhone和Android手机
  8. 沃尔沃挖机计算机故障,沃尔沃挖掘机常见故障及原因总结,用户们可以看看
  9. 很实用的21个SQL小技巧!
  10. 车辆工程用得到python吗_如今车辆工程真的不如以前了吗?
  11. C++设计模式-适配器模式
  12. JPA通用Dao类设计
  13. arcpy.SpatialJoin_analysis 空间连接分析
  14. 合并多个 SQL 文件,并用 Navicat 执行
  15. 如何批量等比例缩放图片尺寸?
  16. 解决mac上复制粘贴失效问题
  17. 记一次公众号开发途中的诡异事件
  18. html第三章练习1:音乐排行榜
  19. 统计本段话的高频词汇——报错:KeyError
  20. 周末和新润视频聊天了

热门文章

  1. 大数据毕设/课设 - 水质情况实时监测预警可视化设计与实现
  2. 【看好了】如何使用fiddler实现手机抓包,Filters过滤器!
  3. 高清视频文件丢了怎么恢复丨电脑下载好的缓存数据
  4. P5055 【模板】可持久化文艺平衡树 可持久化fhqtreap
  5. python从邮箱获取指定邮件_用python提取并统计指定邮箱里的特定邮件 | 学步园
  6. 广告刷屏世界杯,联想Filez助力海信全球营销运营
  7. 前海征信“好信杯”大数据算法大赛——入门篇笔记
  8. windows中WinRAR命令行参数
  9. Matlab代码模板,图像处理,色彩补偿,色彩平衡,显示连通分量数量
  10. 自媒体人必备神器,200w+自媒体人都在用