opencv进阶学习笔记11:cannny边缘检测,直线检测,圆检测

基础版笔记传送门
python3+opencv学习笔记汇总目录(适合基础入门学习)
进阶版笔记目录链接:
python+opencv进阶版学习笔记目录(适合有一定基础)

cannny边缘检测

基础版边缘讲解链接:
opencv学习笔记18:canny算子边缘检测原理及其函数使用

1cannny算法介绍

非极大值抑制:在获得梯度和方向,去除所有不是边界的点。实现方向:逐渐遍历像素点,判断当前像素点是否是周围像素点中具有相同方向梯度的最大值。是保留,不是则为0.

cannny代码实现
edges=cv2.Canny(image,threshold1,threshold2)
edges:处理结果
image:原始图像
threshold1:minVal
threshold2:maxVal
如果想让边界细节更多,则把threshold1和threshold2设小些。

import numpy as np
import cv2 as cv

def edge_demo(image):
    blurred = cv.GaussianBlur(image, (3, 3), 0)#高斯模糊,降低噪声。canny对噪声比较敏感,也不能模糊太厉害,去掉了边缘信息。
    gray = cv.cvtColor(blurred, cv.COLOR_BGR2GRAY)#转化为灰度图
    # X Gradient 求x梯度
    xgrad = cv.Sobel(gray, cv.CV_16SC1, 1, 0)
    # Y Gradient #求y梯度
    ygrad = cv.Sobel(gray, cv.CV_16SC1, 0, 1)
    #edge
    edge_output = cv.Canny(xgrad, ygrad, 50, 150)
    #edge_output = cv.Canny(gray, 50, 150)
    cv.imshow("Canny Edge", edge_output)

    dst = cv.bitwise_and(image, image, mask=edge_output)#生成彩色边界图
    cv.imshow("Color Edge", dst)
print("--------- Python OpenCV Tutorial ---------")
src = cv.imread("duoren.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
edge_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()

blurred = cv.GaussianBlur(image, (3, 3), 0)#高斯模糊,降低噪声。canny对噪声比较敏感,也不能模糊太厉害,去掉了边缘信息。
edge_output = cv.Canny(xgrad, ygrad, 50, 150)
#edge_output = cv.Canny(gray, 50, 150)
是相同的
高阈值应该是低阈值的3倍

直线检测

一、原理介绍:

1、对于直角坐标系中的任意一点A(x0,y0),经过点A的直线满足Y0=k*X0+b.(k是斜率,b是截距)

2、那么在X-Y平面过点A(x0,y0)的直线簇可以用Y0=k*X0+b表示,但对于垂直于X轴的直线斜率是无穷大的则无法表示。因此将直角坐标系转换到极坐标系就能解决该特殊情况。

3、在极坐标系中表示直线的方程为ρ=xCosθ+ySinθ(ρ为原点到直线的距离),如图所示:

直线检测代码实现方法1
1、标准霍夫线变换
void HoughLines(InputArray image, OutputArray lines, double rho, double theta, int threshold, double srn=0, double stn=0 )
参数:
image:边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图)
lines:储存着检测到的直线的参数对 的容器,存储的是rho,theta
rho:参数极径 以像素值为单位的分辨率. 我们使用 1 像素.
theta:参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
theta:要”检测” 一条直线所需最少的的曲线交点
srn and stn: 参数默认为0.

import cv2 as cv
import numpy as np


def line_detection(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)#转化为灰度
    edges = cv.Canny(gray, 50, 150, apertureSize=3)#求取边缘 窗口大小apertureSize=3
    lines = cv.HoughLines(edges, 1, np.pi/180, 200)#np.pi/180每次偏转1度
    for line in lines:
        #print(type(lines))
        print(line)
        rho, theta = line[0]
        a = np.cos(theta)
        b = np.sin(theta)
        x0 = a * rho
        y0 = b * rho
        x1 = int(x0+1000*(-b))
        y1 = int(y0+1000*(a))
        x2 = int(x0-1000*(-b))
        y2 = int(y0-1000*(a))
        cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
    cv.imshow("image-lines", image)

print("--------- Python OpenCV Tutorial ---------")
src = cv.imread("zhiixian.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
line_detection(src)
cv.waitKey(0)

cv.destroyAllWindows()

直线检测代码实现方法2
2、统计概率霍夫线变换
void HoughLinesP(InputArray image, OutputArray lines, double rho, double theta, int threshold,double minLineLength=0, double maxLineGap=0 )
参数:
image: 边缘检测的输出图像. 它应该是个灰度图 (但事实上是个二值化图) *
lines: 储存着检测到的直线的参数对 的容器,也就是线段两个端点的坐标
rho :  参数极径 以像素值为单位的分辨率. 我们使用 1 像素.
theta: 参数极角 以弧度为单位的分辨率. 我们使用 1度 (即CV_PI/180)
threshold: 要”检测” 一条直线所需最少的的曲线交点
minLinLength: 能组成一条直线的最少点的数量. 点数量不足的直线将被抛弃.线段的最小长度
maxLineGap:线段上最近两点之间的阈值

import cv2 as cv
import numpy as np


def line_detect_possible_demo(image):
    gray = cv.cvtColor(image, cv.COLOR_BGR2GRAY)
    edges = cv.Canny(gray, 50, 150, apertureSize=3)
    lines = cv.HoughLinesP(edges, 1, np.pi/180, 100, minLineLength=50, maxLineGap=10)
    for line in lines:
        print(type(line))
        x1, y1, x2, y2 = line[0]
        cv.line(image, (x1, y1), (x2, y2), (0, 0, 255), 2)
    cv.imshow("line_detect_possible_demo", image)
print("--------- Python OpenCV Tutorial ---------")
src = cv.imread("zhiixian.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
line_detect_possible_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()

圆检测

1原理
圆周上任意三点所确定的圆,经Hough变换后在三维参数空间应对应一点。遍历圆周上所有点,任意三个点所确定的候选圆进行
投票。遍历结束后,得票数最高点(理论上圆周上任意三点确定的圆在Hough变换后均对应三维参数空间中的同一点)所确定的圆
即为该圆周上,绝大多数点所确定的圆(以下称为当选圆),即绝大多数点均在该当选圆的圆周上,以此确定该圆。

圆形的表达式为(x−xcenter)2+(y−ycenter)2=r2(x−xcenter)2+(y−ycenter)2=r2,一个圆环的确定需要三个参数。那么霍夫变换的累加器必须是三维的,但是这样的计算效率很低。
这里opencv中使用霍夫梯度的方法,这里利用了边界的梯度信息。
首先对图像进行canny边缘检测,对边缘中的每一个非0点,通过Sobel算法计算局部梯度。那么计算得到的梯度方向,实际上就是圆切线的法线。三条法线即可确定一个圆心,同理在累加器中对圆心通过的法线进行累加,就得到了圆环的判定。

2opencv API

因为霍夫圆检测对噪声比较敏感,所以首先要对图像做中值滤波。
基于效率考虑,Opencv中实现的霍夫变换圆检测是基于图像梯度的实现,分为两步:
检测边缘,发现可能的圆心
基于第一步的基础上从候选圆心开始计算最佳半径大小

cv2.HoughCircles函数的参数

cv2.HoughCircles(image, method, dp, minDist, circles, param1, param2, minRadius, maxRadius)

image为输入图像,需要灰度图

method为检测方法,常用CV_HOUGH_GRADIENT

dp为检测内侧圆心的累加器图像的分辨率于输入图像之比的倒数,如dp=1,累加器和输入图像具有相同的分辨率,如果dp=2,累计器便有输入图像一半那么大的宽度和高度

minDist表示两个圆之间圆心的最小距离,圆心距离小于mimDist认为为同一个圆

param1有默认值100,它是method设置的检测方法的对应的参数,对当前唯一的方法霍夫梯度法cv2.HOUGH_GRADIENT,它表示传递给canny边缘检测算子的高阈值,而低阈值为高阈值的一半

param2有默认值100,它是method设置的检测方法的对应的参数,对当前唯一的方法霍夫梯度法cv2.HOUGH_GRADIENT,它表示在检测阶段圆心的累加器阈值,它越小,就越可以检测到更多根本不存在的圆,而它越大的话,能通过检测的圆就更加接近完美的圆形了

minRadius有默认值0,圆半径的最小值

maxRadius有默认值0,圆半径的最大值

import cv2 as cv
import numpy as np


def detect_circles_demo(image):
    dst = cv.pyrMeanShiftFiltering(image, 10, 100)#均值偏移滤波
    cv.imshow("dst", dst)
    cimage = cv.cvtColor(dst, cv.COLOR_BGR2GRAY)#灰度图
    circles = cv.HoughCircles(cimage, cv.HOUGH_GRADIENT, 1, 20, param1=50, param2=30, minRadius=0, maxRadius=0)
    circles = np.uint16(np.around(circles))#取整
    for i in circles[0, :]:
        cv.circle(image, (i[0], i[1]), i[2], (0, 0, 255), 2)#在原图上画圆,圆心,半径,颜色,线框
        cv.circle(image, (i[0], i[1]), 2, (255, 0, 0), 2)#在原图上画圆心 
    cv.imshow("circles", image)

print("--------- Python OpenCV Tutorial ---------")
src = cv.imread("coins.jpg")
cv.namedWindow("input image", cv.WINDOW_AUTOSIZE)
cv.imshow("input image", src)
detect_circles_demo(src)
cv.waitKey(0)
cv.destroyAllWindows()

如果没有下面这一行

dst = cv.pyrMeanShiftFiltering(image, 10, 100)#均值偏移滤波

结果

原理:
meanShfit均值漂移算法是一种通用的聚类算法,它的基本原理是:对于给定的一定数量样本,任选其中一个样本,以该样本为中心点划定一个圆形区域,求取该圆形区域内样本的质心,即密度最大处的点,再以该点为中心继续执行上述迭代过程,直至最终收敛。可以利用均值偏移算法的这个特性,实现彩色图像分割,
Opencv中对应的函数是pyrMeanShiftFiltering。这个函数严格来说并不是图像的分割,而是图像在色彩层面的平滑滤波,它可以中和色彩分布相近的颜色,平滑色彩细节,侵蚀掉面积较小的颜色区域,
第一个参数src,输入图像,8位,三通道的彩色图像,并不要求必须是RGB格式,HSV、YUV等Opencv中的彩色图像格式均可;

第二个参数dst,输出图像,跟输入src有同样的大小和数据格式;

第三个参数sp,定义的漂移物理空间半径大小;

第四个参数sr,定义的漂移色彩空间半径大小;

第五个参数maxLevel,定义金字塔的最大层数;

第六个参数termcrit,定义的漂移迭代终止条件,可以设置为迭代次数满足终止,迭代目标与中心点偏差满足终止,或者两者的结合;

pyrMeanShiftFiltering函数的执行过程是这样的:

迭代空间构建
求取迭代空间的向量并移动迭代空间球体后重新计算向量,直至收敛(一个图像,然后选取一个球形,求得所有点相对于中心点的色彩向量之和后,移动选取的球形继续操作,有点类似卷积层)
更新输出图像dst上对应的初始原点P0的色彩值为本轮迭代的终点Pn的色彩值,如此完成一个点的色彩均值漂移。
4.输入图像src上其他点,依次执行步骤1,、2、3,遍历完所有点位后,整个均值偏移色彩滤波完成

半径越大,图像的细节就丢失的越多

电气专业的计算机萌新,写博文不容易。如果你觉得本文对你有用,请点个赞再走,谢谢。

猜你喜欢

转载自blog.csdn.net/kobeyu652453/article/details/107376051