2018-08-08 00:25:13 qq_40962368 阅读数 7007

边缘检测

边缘检测是图像处理和计算机视觉中的基本问题,边缘检测的目的是标识数字图像中亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。边缘检测是特征提取中的一个研究领域。

图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大值和最小值来检测边界,通常是将边界定位在梯度最大的方向。基于零穿越的方法通过寻找图像二阶导数零穿越来寻找边界,通常是Laplacian过零点或者非线性差分表示的过零点。

如果将边缘认为是一定数量点亮度发生变化的地方,那么边缘检测大体上就是计算这个亮度变化的导数。

(一)检测方法

边缘检测的方法大致可分为两类:基于搜索基于零交叉

基于搜索的边缘检测方法首先计算边缘强度,通常用一阶导数表示,例如梯度模,然后,用计算估计边缘的局部方向,通常采用梯度的方向,并利用此方向找到局部梯度模的最大值。

基于零交叉的方法找到由图像得到的二阶导数的零交叉点来定位边缘,通常用拉普拉斯算子或非线性微分方程的零交叉点。

滤波作为边缘检测的预处理通常是必要的,通常采用高斯滤波

(二)Sobel边缘检测算子

Sobel边缘检测算法比较简单,实际应用中效率比canny边缘检测效率要高,但是边缘不如Canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选,Sobel算子是高斯平滑与微分操作的结合体,所以其抗噪声能力很强,用途较多。尤其是效率要求较高,而对细纹理不太关系的时候。算子的模板为:

Sobel算子是一种带有方向的过滤器,openCV中Sobel算子的函数为cv2.Sobel ()。

Sobel_x_or_y = cv2.Sobel(src, ddepth, dx, dy, dst, ksize, scale, delta, borderType)

dst及dst之后的参数都是可选参数。 

第一个参数是传入的图像,第二个参数是图像的深度,dx和dy指的是求导的阶数,0表示这个方向上没有求导,所填的数一般为0、1、2。ksize是Sobel算子的大小,即卷积核的大小,必须为奇数1、3、5、7。如果ksize=-1,就演变成为3x3的Scharr算子,scale是缩放导数的比例常数,默认情况为没有伸缩系数。borderType是判断图像边界的模式,这个参数默认值为cv2.BORDER_DEFAULT。

# Sobel边缘检测算子
img = cv2.imread('luotuo.jpg', 0)
x = cv2.Sobel(img, cv2.CV_16S, 1, 0)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1)
# cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
# 可选参数alpha是伸缩系数,beta是加到结果上的一个值,结果返回uint类型的图像
Scale_absX = cv2.convertScaleAbs(x)  # convert 转换  scale 缩放
Scale_absY = cv2.convertScaleAbs(y)
result = cv2.addWeighted(Scale_absX, 0.5, Scale_absY, 0.5, 0)
cv2.imshow('img', img)
cv2.imshow('Scale_absX', Scale_absX)
cv2.imshow('Scale_absY', Scale_absY)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

Sobel函数求完导数后会有负值,还有会大于255的值。而原图像是uint8,即8位无符号数,所以Sobel建立的图像位数不够,会有截断。因此要使用16位有符号的数据类型,即cv2.CV_16S。处理完图像后,再使用cv2.convertScaleAbs()函数将其转回原来的uint8格式,否则图像无法显示。

Sobel算子是在两个方向计算的,最后还需要用cv2.addWeighted( )函数将其组合起来。

result = cv2.addWeighted(src1, alpha, src2, beta, gamma[, dst[, dtype]])

其中alpha是第一幅图片中元素的权重,beta是第二个图像的权重,gamma是加到最后结果上的一个值。

(三)Scharr算子

由(二)中可知,当Sobel()函数的参数ksize=-1时,就演变成了3x3的Scharr算子。算子的模板为:

# Scharr算子
img = cv2.imread('luotuo.jpg', 0)
x = cv2.Sobel(img, cv2.CV_16S, 1, 0, ksize=-1)
y = cv2.Sobel(img, cv2.CV_16S, 0, 1, ksize=-1)
# ksize=-1 Scharr算子
# cv2.convertScaleAbs(src[, dst[, alpha[, beta]]])
# 可选参数alpha是伸缩系数,beta是加到结果上的一个值,结果返回uint类型的图像
Scharr_absX = cv2.convertScaleAbs(x)  # convert 转换  scale 缩放
Scharr_absY = cv2.convertScaleAbs(y)
result = cv2.addWeighted(Scharr_absX, 0.5, Scharr_absY, 0.5, 0)
cv2.imshow('img', img)
cv2.imshow('Scharr_absX', Scharr_absX)
cv2.imshow('Scharr_absY', Scharr_absY)
cv2.imshow('result', result)
cv2.waitKey(0)
cv2.destroyAllWindows()

从Scharr算子与Sobel算子最终的结果比较可以看出,Scharr对图像梯度的变化更加敏感。

(四)拉普拉斯(Laplacian)算子

Laplacian函数实现的方法是先用Sobel算子计算二阶x和y导数,再求和。

Laplacian(I)=\frac{\partial ^{2}I}{\partial x ^{2}}+\frac{\partial ^{2}I}{\partial y ^{2}}

laplacian = cv2.Laplacian(src, ddepth[, dst[, ksize[, scale[, delta[, borderType]]]]])

前两个参数是必选参数,其后是可选参数。第一个参数是需要处理的图像,第二个参数是图像的深度,-1表示采用的是原图像相同的深度,目标图像的深度必须大于等于原图像的深度;ksize参数是算子的大小,即卷积核的大小,必须为1,3,5,7。默认为1。

scale是缩放导数的比例chan常数,默认情况下没有伸缩系数;borderType是判断图像边界的模式。这个参数默认值为cv2.BORDER_DEFAULT。

# 拉普拉斯算子
img = cv2.imread('luotuo.jpg', 0)
laplacian = cv2.Laplacian(img, cv2.CV_16S, ksize=3)
dst = cv2.convertScaleAbs(laplacian)
cv2.imshow('laplacian', dst)
cv2.waitKey(0)
cv2.destroyAllWindows()

当ksize=1时,图像为

ksize=5时

ksize=7时

由此可见,当参数ksize越大即卷积核越大时,算子对图像梯度的变化越敏感。ksize=3时的图像还不是很好,在经过高斯模糊处理一下,去掉了很多噪声。

blur = cv2.GaussianBlur(img, (3, 3), 0)
laplacian = cv2.Laplacian(blur, cv2.CV_16S, ksize=3)

(五)Canny算子

图像边缘检测必须满足两个条件,一能有效地抑制噪声;二必须尽量精确确定边缘的位置。

根据对信噪比与定位乘积进行测度,得到最优化逼近算子。这就是Canny边缘检测算子。

算法的基本步骤为:

1.用高斯滤波器平滑图像;

2.用一阶偏导的有限差分来计算梯度的幅值和方向;

3.对梯度幅值进行非极大抑制;

4.用双阈值算法检测和连接边缘。

更详细的介绍可以参考:canny算子1      canny算子2

canny = cv2.Canny(image, threshold1, threshold2[, edges[, apertureSize[, L2gradient ]]]) 

第一个参数是需要处理的原图像单通道的灰度图,第二个参数是阈值1,第二个参数是阈值2,较大的阈值2用于检测图像中明显的边缘,但一般情况下检测的效果不会那么完美,边缘检测出来是断断续续的。所以这时候应用较小的第一个阈值来将这些间断的边缘连接起来。可选参数中aperaperturesize参数就是卷积核的大,而L2gradient参数就是一个布尔值,如果为true,则就使用更精确的L2范数进行计算(即两个方向的倒数的平方和再开方),否则使用L1范数(直接将两个方向导数的绝对值相加)。

# canny算子
img = cv2.imread('luotuo.jpg', 0)
blur = cv2.GaussianBlur(img, (3, 3), 0)  # 用高斯滤波处理原图像降噪
canny = cv2.Canny(blur, 50, 150)  # 50是最小阈值,150是最大阈值
cv2.imshow('canny', canny)
cv2.waitKey(0)
cv2.destroyAllWindows()

可以看到对图像的处理效果还是比较不错的,在阈值的选取上我找到了一个可以在运行时调整阈值大小的程序。

lowThreshold = 0
max_lowThreshold = 100
ratio = 3
kernel_size = 3

img = cv2.imread('luotuo.jpg')
gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)

cv2.namedWindow('canny demo')

cv2.createTrackbar('Min threshold', 'canny demo', lowThreshold, max_lowThreshold, CannyThreshold)

CannyThreshold(0)  # initialization
if cv2.waitKey(0) == 27:
    cv2.destroyAllWindows()

2019-04-29 11:59:09 GLM_Gavin 阅读数 101

                                           边缘检测

一、边缘检测的目的:

       减少数据量,剔除不相关的信息,保留图像重要的结构属性

二、边缘检测通常分为两类:

     1)基于查找一类(通过寻找图像1阶导数的最大值和最小值来检测边界)

     2)基于过零点穿越一类(通过寻找图像2阶导数的过零点来检测边界,通常是Laplacian过零点或者非线性差分表示过零点)

1阶:Roberts Cross算子,Prewitt算子,Sobel算子,Kirsch算子,罗盘算子

2阶:Marr-Hildreth,在梯度方向的二阶导数过零点,Canny算子,Laplacian算子

边缘检测步骤:

1)滤波:滤除图像中的噪声,在滤除噪声的同时边缘强度也会被削弱,因此增强边缘和滤除噪声要折中处理

2)增强:增强边缘的基础是确定图像各点邻域强度的变化值,通过增强算法将邻域强度值有显著变化的点突显出来,

边缘增强一般是通过计算梯度幅值来完成的

3)检测:通过边缘检测判据是梯度幅值阈值判据

4)定位

2016-02-18 11:13:22 hpm21 阅读数 1638
Shader "基础/透明度测试剔除尖锐边缘的模糊处理" 
{
	//-------------------------------【属性】-----------------------------------------
   Properties 
   {
        _Color ("主颜色", Color) = (.5, .5, .5, .5)
        _MainTex ("基础纹理 (RGB)-透明度(A)", 2D) = "white" {}
        _Cutoff ("Alpha透明度阈值", Range (0,.9)) = .5
    }

    //--------------------------------【子着色器】--------------------------------
    SubShader 
	{
        //【1】定义材质
        Material 
		{
            Diffuse [_Color]
            Ambient [_Color]
        }

		//【2】开启光照
        Lighting On

		//【3】关闭裁剪,渲染所有面,用于接下来渲染几何体的两面
        Cull Off

		//--------------------------【通道一】-------------------------------
		//		说明:渲染所有超过[_Cutoff] 不透明的像素
		//----------------------------------------------------------------------
        Pass 
		{
            AlphaTest Greater [_Cutoff]
            SetTexture [_MainTex] {
                combine texture * primary, texture
            }
        }

		//----------------------------【通道二】-----------------------------
		//		说明:渲染半透明的细节
		//----------------------------------------------------------------------
        Pass 
		{
			// 不写到深度缓冲中
            ZWrite off

			// 不写已经写过的像素
            ZTest Less

			// 深度测试中,只渲染小于或等于的像素值
            AlphaTest LEqual [_Cutoff]

			// 设置透明度混合
            Blend SrcAlpha OneMinusSrcAlpha
			
			// 进行纹理混合
            SetTexture [_MainTex] 
			{
                combine texture * primary, texture
            }
        }
    }
}

先渲染主体,因为经过透明度剔除之后,边缘会有锯齿。

再渲染锯齿部分,这部分没有深度信息。


如果想要2d的效果,sprite之类的效果,就需要显示层级来控制显示顺序的正确性,就没有了3d正确的遮挡效果(此效果依靠的是深度缓存,Zwrite On)

2013-08-07 10:46:28 u011469591 阅读数 1826

边缘检测是图像处理计算机视觉中的基本问题,边缘检测的目的是标识数字图像亮度变化明显的点。图像属性中的显著变化通常反映了属性的重要事件和变化。这些包括(i)深度上的不连续、(ii)表面方向不连续、(iii)物质属性变化和(iv)场景照明变化。
 边缘检测是图像处理计算机视觉中,尤其是特征提取中的一个研究领域。

图像边缘检测大幅度地减少了数据量,并且剔除了可以认为不相关的信息,保留了图像重要的结构属性。有许多方法用于边缘检测,它们的绝大部分可以划分为两类:基于查找一类和基于零穿越的一类。基于查找的方法通过寻找图像一阶导数中的最大和最小值来检测边界,通常是将边界定位在梯度最大的方向。基于零穿越的方法通过寻找图像二阶导数零穿越来寻找边界,通常是Laplacian过零点或者非线性差分表示的过零点。

Canny 边缘检测算子是John F. Canny于 1986 年开发出来的一个多级边缘检测算法。更为重要的是 Canny 创立了边缘检测计(Computational theory of edge detection)解释这项技术如何工作。

Canny 的目标是找到一个最优的边缘检测算法,最优边缘检测的含义是:

  • 好的检测 - 算法能够尽可能多地标识出图像中的实际边缘。
  • 好的定位 - 标识出的边缘要尽可能与实际图像中的实际边缘尽可能接近。
  • 最小响应 - 图像中的边缘只能标识一次,并且可能存在的图像噪声不应标识为边缘。

为了满足这些要求 Canny 使用了变分法,这是一种寻找满足特定功能函数的方法。最优检测使用四个指数函数项的和表示,但是它非常近似于高斯函数的一阶导数

/***********************************************Canny算法实现过程*************************************************/

分为以下几个步骤:

1、图像灰度化:只有灰度图才能进行边缘检测

2、去噪:噪声点将影响边缘检测的准确性

3、求解梯度幅度和方向:利用sobel算子求解

4、非极大值抑制:定位准确的边缘同时可缩小边缘线宽

5、双阀值算法检测及连接边缘


1、图像灰度化

Canny算法通常处理的图像为灰度图,因此如果摄像机获取的是彩色图像,那首先就得进行灰度化。对一幅彩色图进行灰度化,就是根据图像各个通道的采样值进行加权平均。以RGB格式的彩图为例,通常灰度化采用的方法主要有:

        方法1:Gray=(R+G+B)/3;

        方法2:Gray=0.299R+0.587G+0.114B;

注意1:至于其他格式的彩色图像,可以根据相应的转换关系转为RGB然后再进行灰度化;

注意2:在编程时要注意图像格式中RGB的顺序通常为BGR


2、去噪

噪声的存在影响到边缘的检测,首先将图片进行高斯去噪,高斯去噪其实就是一个低通滤波器,滤除高频噪声。

3、求解梯度幅度和方向

梯度的幅度及方向采用sobel算子求解,索贝尔算子(Sobel operator)是图像处理中的算子之一,主要用作边缘检测。在技术上,它是一离散性差分算子,用来运算图像亮度函数的梯度之近似值。在图像的任何一点使用此算子,将会产生对应的梯度矢量或是其法矢量。

该算子包含两组3x3的矩阵,分别为横向及纵向,将之与图像作平面卷积,即可分别得出横向及纵向的亮度差分近似值。如果以\mathbf{A}代表原始图像,\mathbf{G_x}\mathbf{G_y}分别代表经横向及纵向边缘检测的图像,其公式如下:


图像的每一个像素的横向及纵向梯度近似值可用以下的公式结合,来计算梯度的大小。


然后可用以下公式计算梯度方向。


在以上例子中,如果以上的角度\Theta等于零,即代表图像该处拥有纵向边缘,左方较右方暗。


4、非极大值抑制

在求出的幅值图像中,可能存在多个较大幅值临近的情况,但真正的边缘点只有一个,针对这样的情况我们进行非极大值抑制,找出局部最大值,从而可以剔除大部分非边缘点


如上图所示,我们对每一像素点做如下处理:

根据该像素点的梯度方向,确定需进行比较的临近像素点位置,如上图所示(过像素点方向为梯度方向的直线与该像素点临近的八像素点所组成的矩形,相交于dTmp1和dTmp2,根据g1,g2a和g3,g4估算出交点像素,如果该像素点均大于两交点像素则为极大值边缘点,否则为非边缘点


5、双阀值算法检测及连接边缘

  1. 滞后阈值: 最后一步,Canny 使用了滞后阈值,滞后阈值需要两个阈值(高阈值和低阈值):

    1. 如果某一像素位置的幅值超过 高 阈值, 该像素被保留为边缘像素。
    2. 如果某一像素位置的幅值小于 低 阈值, 该像素被排除。
    3. 如果某一像素位置的幅值在两个阈值之间,该像素仅仅在连接到一个高于 高 阈值的像素时被保留。

    Canny 推荐的 高:低 阈值比在 2:1 到3:1之间。

下面我们将看到基于opencv中的canny边缘算法的具体实现(opencv中已经实现了canny算法我们只需调用即可,具体实现可见源码):

/**************************************************
 * C++ Canny:Canny边缘检测
 **************************************************/

#include "stdafx.h"

#include "cv.h"
#include "cxcore.h"
#include "highgui.h"
 
using namespace cv;
using namespace std;
 
int edgeThresh = 1;
 
// 声明 原始图片,灰度图片,和 canny边缘图片
IplImage *image;
IplImage *gray, *edge;
 
void onTrackbar(int, void*)
{
	// 高斯滤波
	cvSmooth(gray, edge, CV_GAUSSIAN, 3, 3, 0 );
 
	// Canny 边缘检测
	cvCanny(gray,edge, edgeThresh, edgeThresh*3, 3);
    
	// 显示图片
	cvShowImage("Edge map", edge);
}
 
int main()
{
 
	// 载入图片
	image = cvLoadImage("E:\\test\\lenna.bmp");
 
	// 判断载入图片是否成功
	if( image == NULL )
	{
		printf("miss the image file\n");
		return -1;
	}
 
	// 生成灰度图片,因为只有灰度图片才能生成边缘图片
	gray = cvCreateImage( cvGetSize(image),IPL_DEPTH_8U ,1 );
	edge = cvCreateImage( cvGetSize(image),IPL_DEPTH_8U ,1 );
	cvCvtColor(image,gray, CV_BGR2GRAY);
 
	// 新建一个窗口
	namedWindow("Edge map", 1);
 
	// 生成一个进度条来控制边缘检测
	createTrackbar("Canny Threshold", "Edge map", &edgeThresh, 100, onTrackbar);
 
	// 初始化图像
	onTrackbar(0,0);
 
	waitKey(0);
	cvReleaseImage( &image );
	cvReleaseImage( &edge );
	cvReleaseImage( &gray );
 
	return 0;
}


2019-02-28 23:28:43 ltshan139 阅读数 445

一。概要

计算Harris角点和sfit特征点时 会把其中的边缘点剔除掉,其原理是不一样的,它们采用的矩阵形式容易令人混淆,这里着重把它们讲清楚。

二。Harris剔除边缘点

  Harris角点的原理是 该点在各个方向上变化都比较大,其对应的数学表达式为:

这个可以看成普通型椭圆方程,其中间的系数矩阵是对角矩阵,可以基于其特征值以及特征向量来做对角分解化成标准椭圆方程。 从标准椭圆方程易知,其特征值分别等于椭圆轴的平方再倒数(\lambda1=1/a^{^{2}}, \lambda2=1/b^{^{2}})。 

由上可知,特征值越大,轴越短,其变化就越快;反之亦然。 对于普通点而已,两个特征值都比较小,而边缘点是一个大,另一个小,而角点则是两个特征值都比较大。  此外,直接求方阵特征值比较麻烦,通常通过另外的公式比如\lambda1*\lambda2 / ()\lambda1+\lambda2)来区分这三种类型的点。

重点来了: 该系数矩阵的对角线上的元素是 一阶求导再平方,而不是二阶求导啊, 这个很容易搞错。 

三。SIFT特征点

  在SIFT特征点中剔除边缘点 是直接用到了著名的Hessian矩阵

在图像处理中,Hessian方阵实际上就是二维变量的二阶导数,用来表示各个方向上的梯度变化。其几何意义就是,其两个特征值越大,该点所在局部区域凸性越强,变化就越大。而边缘点则是一个特征值很小,而另一个特征值较大。 

重点: hesssian矩阵是二阶求导。

 

 

 

 

                                                                              

没有更多推荐了,返回首页