同样种植基于δ函数的图象边缘检测算法,python

  原始杂文下载:
平种植基于δ函数的图象边缘检测算法

差色彩空间的易

当前,在处理器视觉中暴发两种常用之色彩空间:灰度、BGR以及HSV

1.灰度色彩空间是通过去除彩色信息来拿其变为灰阶,灰度色彩空间对中间处理特别实用,比如人口脸检测

2.BGR,即蓝绿红色彩空间,每一个诸如素点都出于一个叔首先组来表示

3.HSV,H(Hue)是色彩,S(Saturation)是饱和度,V(value)表示黑暗的水准(或光谱另一样端的知程度)

     这首论文读起来感觉不像现在底过多论文,废话一良堆,而是直入主题,反倒使人头当著作的前后跳跃有硌杀,可是算法的原理已经摆的清晰了。

傅里叶变换

Numpy里发出急忙傅里叶变换(FFT)的担保,它涵盖了fft2()函数,该函数可以总括同一抱图像的相距散傅里叶变换(DFT)。

脚通过傅里叶变更换到介绍图像的增幅谱。图像的涨幅谱是别一样种图像,幅度谱图像展现了原始图像在转移点的等同种植象征:把同帧图像中最好理解的比如说素放到图像主旨,然后逐渐变暗,在边缘上的像素最暗。这样好窥见图像中发生略显得的像素和暗的像素,以及他们分布之比重。

    一、原理

大通滤波器

赛通滤波器是检测图像的某区域,然后因像素和周围像从的亮度差来提升该像从的亮度的滤波器

盖如下的核kernel为条例:

[[0,-0.25,0],

[-0.25,1,-0.25],

[0,-0.25,0]]

核是指同一组权重的联谊,他会师采纳在来自图像的一个区域,并通过生成目的图像的一个像素。

以测算了中心像素和周围邻近像从的亮度的差值之与今后,如若亮度变化很大,主旨像从的亮度会追加,反的则无会师。

高通以及低通滤波器都出一个称作半径的特性,它决定了大半要命面积之贴近像素参预滤波运算。

这多少个滤波器中之有着值加起来为0

import cv2
import numpy as np
from scipy import ndimage
kernel_3X3=np.array([[-1,-1,-1],[-1,8,-1],[-1,-1,-1]])
kernel_5X5=np.array([[-1,-1,-1,-1,-1],[-1,1,2,1,-1],[-1,2,4,2,-1],[-1,1,2,1,-1],[-1,-1,-1,-1,-1]])

img=cv2.imread("1.jpg",0)
k3=ndimage.convolve(img,kernel_3X3)#卷积
k5=ndimage.convolve(img,kernel_5X5)#卷积

blurred=cv2.GaussianBlur(img,(11,11),0)
g_hpf=img-blurred
#两种高通滤波的效果
cv2.imshow("3x3",k3)
cv2.imshow("5x5",k5)
#通过对图像应用低通滤波器之后,与原始图像计算差值
cv2.imshow("g_hpf",g_hpf)
cv2.waitKey()
cv2.destroyAllWindows()

   
 文中提出的边缘检测算法原理为不是特别复杂,使用了一个小通滤波函数以及一个赛通滤波函数,其格局分别吗:

低通滤波器

低通滤波器(LPF)则是以像素和周围像从的亮度的亮度差值小于一个一定值时,平滑该像从的亮度。它紧要用来去叫和模糊化。比如说,高斯模糊是无比常用的歪曲滤波器(平滑滤波器)之一,它是一个消弱高频信号强度的低通滤波器。

       
 图片 1                                           
(1)

边缘检测

边缘在认类视觉与电脑视觉中皆由在要的用意

OpenCV提供了重重边缘检测滤波函数,包括Laplacian()、Sobel()以及Scharr()。这多少个滤波函数都会师拿无边缘区域易为黑色,将边缘区域转为白色或者另饱和的颜料。但是这个函数都相当易用噪声错误地辨别为边缘。缓解这一个问题的措施是以找到边缘在此以前对图像举行模糊处理。OpenCV也提供了许多歪曲滤波函数,包括blur()(简单的算术平均)、medianBlur()以及GaussianBlur()。边缘检测滤波函数和模糊滤波函数的参数有很多,但总会发出一个ksize参数,它是一个奇数,表示滤波核的方便和高(以像素为单位)。

此间运用medianBlur()作为模糊函数,它对勾数字化的视频噪声十分实用,特别是剔除彩色图像的噪音;使用Laplacian()作为边缘检测函数,他会见生出显著的边缘线条,灰度图像更是如此。在使medianBlur()函数之后,将要利用Laplacian()函数在此以前,需要用图像于BRG色彩空间更换为黄色彩空间。

在取得Laplacian()函数的结果之后,需要将该更换成为黑色边缘与反动背景的图像。然后用这归一化(使该像素值在0-1之间),并趁以自图像以便能将边缘变黑

        图片 2                 
(2)

于是定制内核做卷积

OpenCV预定义的累累滤波器(滤波函数)都谋面利用审批。其实对是一模一样组权重,它决定怎样通过近像素点来测算新的像素点。核也号称卷积矩阵,它对一个区域之像素做调和(mix
up)或卷积运算。通常依据对的滤波器(滤波函数)被称作卷积滤波器(滤波函数)。

OpenCV提供了一个分外通用的filter2D()函数,它拔取由用户指定的任意核或卷积矩阵。

cv.filter2D(src,-1,kernel,dst)

仲独参数指定了靶图像每个通道的各深度,假使也负值,则表示目的图像及源图像发一样的号深度。

对于彩色图像来说,filter2D()会指向每个通道都由此同的审核。如若一旦针对性每个通道使不同之核对,就必用split()函数和merge()函数

      
当图像被之噪声相比少时,可以直接以高通滤波器对图像举办滤波,得到图像的底细音讯(即边缘处),论文被称D算法,总括公式如下:

Canny边缘检测

Canny边缘检测算法非凡复杂,但为老有趣:它起5只步骤,即采取高斯滤波器对图像以及逆行去叫、总括梯度、在边缘上拔取未最深挫(NMS)、在检测到的边缘上行使对阈值去除假阳性,最终还会分析有的边缘及其内的连,以保留真正的边缘并免去不明确的边缘。

import cv2
import numpy as np
img=cv2.imread("1.jpg",0)
c=cv2.Canny(img,200,300)
cv2.imshow("canny",c)
cv2.waitKey()
cv2.destroyAllWindows()

       图片 3

大概检测

在微机视觉中,概略检测是别一个于重要之职责,不单是由此来检测图像或者相频帧中物体的概貌,而且还生其他操作及概况检测有关。那个操作有:统计多边形边界、形状逼近及测算感兴趣区域。这是同图像数据交互时之简练操作,因为Numpy中的矩形区域可以下数组切片(slice)来定义。

import cv2
import numpy as np

img=np.zeros((200,200),dtype=np.uint8)
img[50:150,50:150]=255
ret,thresh=cv2.threshold(img,127,255,0)
image,contours,hierarchy=cv2.findContours(thresh,cv2.RETR_TREE,cv2.CHAIN_APPROX_SIMPLE)
color=cv2.cvtColor(img,cv2.COLOR_GRAY2BGR)
img=cv2.drawContours(color,contours,-1,(0,255,0),2)
cv2.imshow("contours",color)
cv2.waitKey()
cv2.destroyAllWindows()

即段代码首先创制了一个200X200尺寸的褐色空图像,接着在图像的中心放置一个白色方块,这里以了np数组在片上赋值的功用。

搭下对图像举行二值化操作,然后调用了findContours()函数。该函数发两个参数:输入图像,层次类型和概况逼近方法。它有两只地方特别好玩:

1.这函数会改输入图像,因而指出利用旧图像的同卖拷贝

2.是因为函数重返的层系培养非常关键:cv2.RETR_TREE参数会拿走图像遭到概况的一体化层次协会,以这么些来建立概略之间的“关系”。尽管单想得最好外侧的大概,可下cv2.RETR_EXTERNAL。这对准解包含在另外概略中之概貌很有由此

findContours()函数有五只再次来到值:修改后底图像、图像的大概和他们的层次。使用轮廓来作画生图像的五颜六色版本,并显示出。

   
式中顶部的横线应该是代表开端平方的意。

      
而当图像含有噪音时,则采纳高通及低通滤波器结合形式,使用低通滤波器平滑图像遭到的噪音,高通滤波器检测边缘,那些原理则类似于高斯拉普拉斯边缘检测过程,散文被称C算法,总结公式如下:

图片 4 

      
式中w表示的是窗口大小,取值越怪,边缘的增长率越充分,指出可以取值为2。

  
上边两独姿态都已经是离散化的表明形式了,由此实际也是如出一辙栽对图像的模版操作,只是模板被之因子需要就参数的不同而反。

      
注意:D算法仅仅是同一维的沙盘操作,而C算法是二维的。

二、代码

       下边贴出D算法的中坚代码:

 

void EdgeDetail(byte* Src, byte* Dest, int Width, int Height, int Stride, int Radius = 2, double S = 1, double T = 3)
{
    int X, Y, I, J, XX, YY;
    byte* SrcP, DestP;
    int SumOne, SumTwo, Power;
    byte* SqrValue = (byte*)GlobalAlloc(GPTR, (256 * 256) * sizeof(byte));
    int* SpeedHigh = (int*)GlobalAlloc(GPTR, (Radius * 2 + 1) * sizeof(int));

    SpeedHigh += Radius;

    for (Y = 0; Y < 256 * 256; Y++) SqrValue[Y] = (byte)Math.Sqrt(Y);

    for (Y = -Radius; Y <= Radius; Y++)
    {
        if (Y == 0)
            SpeedHigh[Y] = 0;
        else
            SpeedHigh[Y] = (int)((((Math.Cos(S * Y) / Y) - (Math.Sin(S * Y) / S) * (1.0 / (Y * Y) + 1.0 / (T * T))) * Math.Exp(-((double)Y * Y) / (2 * T * T))) * 1024);
    }
    for (Y = 0; Y < Height; Y++)
    {
        DestP = Dest + Y * Stride;
        for (X = 0; X < Width; X++)
        {
            SumOne = 0; SumTwo = 0;
            for (J = -Radius; J <= Radius; J++)
            {
                XX = X + J;
                if (XX < 0) XX = 0; else if (XX >= Width) XX = Width - 1;
                SrcP = Src + Stride * Y + XX;
                SumOne += (SpeedHigh[J] * SrcP[0]) >> 10;
                YY = Y + J;
                if (YY < 0) YY = 0; else if (YY >= Height) YY = Height - 1;
                SrcP = Src + Stride * YY + X;
                SumTwo += (SpeedHigh[J] * SrcP[0]) >> 10;
            }
            Power = SumOne * SumOne + SumTwo * SumTwo;
            if (Power > 65025) Power = 65025;
            DestP[0] = SqrValue[Power];
            DestP++;
        }
    }
    SpeedHigh -= Radius;
    GlobalFree((IntPtr)SqrValue);
    GlobalFree((IntPtr)SpeedHigh);
}

 

  如达到所示,我动用了整数运算代替了浮点运算,首要目的是为提高速度,当然如此做可能会合牺牲局部精度,由于自算法的必要性上摆,Radius不需拿到很死,由此,对于里边的二重循环来说,压力不是宏大,因而无做特别之优化。而在超越边界处,直接以的是用边界元素值。

    
上述代码的里边循环里来一部分总括式可以提到表面来的,
只是为算法的清晰性,未做优化,速度咳嗽友可以自动取。

     该算法各像素之间的总计式独立的,因而可卓殊简单的便实现并行统计。

  而C算法的代码就小复杂一点:

 

void EdgeCoarse(byte* Src, byte* Dest, int Width, int Height, int Stride, int Radius = 2, double S0 = 0.3, double T0 = 3, double S1 = 0.2, double T1 = 2)
{
    int X, Y, I, J, XX, YY;
    byte* SrcP, DestP;
    int SumOne, SumTwo, Power;
    int* SqrValue = (int*)GlobalAlloc(GPTR, (256 * 256) * sizeof(int));
    int* SpeedHigh = (int*)GlobalAlloc(GPTR, (Radius * 2 + 1) * sizeof(int));
    int* SpeedLow = (int*)GlobalAlloc(GPTR, (Radius * 2 + 1) * sizeof(int));

    SpeedHigh += Radius;
    SpeedLow += Radius;

    for (Y = 0; Y < 256 * 256; Y++) SqrValue[Y] = (int)Math.Sqrt(Y);

    for (Y = -Radius; Y <= Radius; Y++)
    {
        if (Y == 0)
        {
            SpeedHigh[Y] = 0;
            SpeedLow[Y] = 1024;
        }
        else
        {
            SpeedHigh[Y] = (int)((((Math.Cos(S1 * Y) / Y) - (Math.Sin(S1 * Y) / S1) * (1.0 / (Y * Y) + 1.0 / (T1 * T1))) * Math.Exp(-((double)Y * Y) / (2 * T1 * T1))) * 1024);
            SpeedLow[Y] = (int)(((Math.Sin(S0 * Y) / (S0 * Y)) * Math.Exp(-((double)Y * Y) / (2 * T0 * T0))) * 1024);
        }
    }

    for (Y = 0; Y < Height; Y++)
    {
        DestP = Dest + Y * Stride;
        for (X = 0; X < Width; X++)
        {
            SumOne = 0; SumTwo = 0;
            for (J = -Radius; J <= Radius; J++)
            {
                YY = Y + J;
                if (YY < 0) YY = 0; else if (YY >= Height) YY = Height - 1;
                for (I = -Radius; I <= Radius; I++)
                {
                    XX = X + I;
                    if (XX < 0) XX = 0; else if (XX >= Width) XX = Width - 1;
                    SrcP = Src + Stride * YY + XX;
                    SumOne += (SpeedHigh[I] * SpeedLow[J] * SrcP[0]) >>20;
                    SumTwo += (SpeedLow[I] * SpeedHigh[J] * SrcP[0]) >>20;
                }
            }
            Power = SumOne * SumOne + SumTwo * SumTwo;
            if (Power > 65025) Power = 65025;
            DestP[0] = (byte)SqrValue[Power];
            DestP++;
        }
    }
    SpeedHigh -= Radius;
    SpeedLow -= Radius;
    GlobalFree((IntPtr)SqrValue);
    GlobalFree((IntPtr)SpeedHigh);
    GlobalFree((IntPtr)SpeedLow);

}

  
我个人聊喜欢用C#的往往组,这为是由性能角度考虑的,我欣赏一贯操作指针。这多少个可以遵照每个人团结之惯改吧。 

    相信能看精晓原理的情人于代码有的领会吧应当挺爱,这里不举行多讲。

三、效果

   c算法的结果

图片 5 图片 6 图片 7

      
          原图                      Radius=2,S=3.14,T=1                 Radius=2,S=1.57,T=1

   D算法:

图片 8
图片 9
图片 10

     原图                        
 Radius=2,S0 = 0.3, T0 = 3, S1 = 0.2, T1 = 2         
    Radius=2,S0 = 3, T0 = 3, S1 = 2, T1 = 2  

   
可见,这么些算法要落相比较好之效率,是得调动S/T这多少个参数,关于这多少个参数的取值意向,可以参照原文中之有叙。

   
那个工程相比较简单,附上C#的程序:http://files.cnblogs.com/Imageshop/EdgeDetectUseDeltaFunction.rar

 

*********************************笔者:
laviewpbt   时间: 2013.10.26    联系QQ:  33184777
 转载请保留本行新闻************************

 

相关文章

admin

网站地图xml地图