2019-09-28 18:02:07 weixin_38743084 阅读数 7727
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29903 人正在学习 去看看 张中强

EM算法:

最大期望算法是一类通过迭代进行极大似然估计的优化算法,通常作为牛顿迭代法的替代,用于对包含隐变量或缺失数据的概率模型进行参数估计。

在进行了解之前,我们先通过一个抛硬币的经典例子来解释EM算法的由来:
现在我们有两枚硬币 A 和 B,这两枚硬币和普通的硬币不一样,他们投掷出正面的概率和投掷出反面的概率不一定相同。

我们将 A 和 B 投掷出正面的概率分别记为θA和θB。独立地做 5 次试验:
随机的从这两枚硬币中抽取 1 枚,投掷 10 次,统计出现正面的次数,如下图:
在这里插入图片描述

在实验中,记录两组随机变量:
X = (X1 , X2 , X3 , X4 , X5 )
Z = (Z1 , Z2 , Z3 , Z4 , Z5 )
其中 Xi ∈ { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 } 代表试验 i 中出现正面的次数,
Zi∈{ A, B } 代表这次 试验投掷的是硬币 A 还是硬币 B。
目标是通过这个实验来估计θ= (θA ,θB) 的数值。
这个实 验中的参数估计是有完整数据的参数估计,因为我们不仅知道每次试验中投掷出正面的次数,还知道每次试验中投掷的是硬币 A还是B。可以很直接的估计θ的方法

在这里插入图片描述

这样的估计是统计上的极大似然估计的结果。用 P (X, Z | θ) 来表示 X,Z 的联合概率分布(其中带有参数 θ),对于上面的实验,可以计算出现观察到的结果:
即 x0 = (5, 9, 8, 4, 7), z0 = (B, A, A, B, A) 的概率。
函数 P (X = x (0) , Z = z (0) | θ) 就叫做 θ 的似然函数。
在这里插入图片描述
只知道每次试验有几次投掷出正面,但是不知道每次试验投掷的是哪个硬币,这个时候就称 Z 为 隐藏变量 (Hidden Variable),X 称为观察变量 (Observed Variable)。
此时估计参数 θA 和θB,就没有那么多数据可供使用了,这个时候的估计叫做不完整数据的参数估计。如果没有方法来获得更多的数据的话,那么利用迭代的方式来进行
在这里插入图片描述
如上图所示,EM算法分为两个步骤,分别是E-step和M-step:
a.E-step:
先赋给 θ 一个初始值,可以是经验也也可以是猜测,这里给定 θA = 0.6, θB = 0.5。
判断或者猜测每次投掷更像是哪枚硬币投掷的结果。
对于上述试验,如果投掷的是 A,那么出现 5个正面的概率为prob_a = ?10 5 ×0.65×0.45 ;
如果投掷的是B,出现5个正面的概率为 ????_? = ?10 5 ×0.55×0.55;
基于上述试验的结果,可以判断:
这个试验投掷的是硬币 A 的概率为 prob_a/(prob_a+ prob_b)=0.45,
是 B 的概率为 prob_b/(prob_a+ prob_b)=0.55 。

如果是A,则正面的个数为5× prob_a/(prob_a+ prob_b) = 2.2,
反面个数为(10-5) × prob_a/(prob_a+ prob_b) = 2.2。
如果是B,则正面的个数为5× prob_b/(prob_a+ prob_b) = 2.8,
反面个数为(10-5) × prob_b/(prob_a+ prob_b) = 2.8。
b.M-step:
利用完整数据的参数估计一样 (公式2) 重新计算θ的值。
迭代 E和M 步骤直到收敛,我们就得到了θ的估计

详细教程细节可观看视频学习:

Python教程


视频节选自王静老师的Python数据分析-入门案例实战

王静老师:中国科学院博士/国际期刊审稿国家重大项目负责人/国家自然科学基金专家。

课程历经三个月打磨,对于基于位置服务的应用案例进行剖析,通过具体案例结合实际,实现Python学习的从零入门。

学习完整个课程你可以学到:
1.快速的进行python语言的入门,并在使用过程中得到提升
2.python常用库的深入理解
3.机器学习算法原理的讲解及python实现
4.机器学习、python处理轨迹数据的案例应用

下面附全部课程的视频链接,希望对你有用
https://edu.csdn.net/course/detail/25576

扫码入Python技术交流群,可免费听技术讲座+领学习资料+视频课免费看!
在这里插入图片描述

2018-03-11 21:22:28 LYduring 阅读数 3409
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29903 人正在学习 去看看 张中强

1.边缘定义及特点

边缘是由灰阶等高线定义的,穿过等高线时,灰阶会迅速变化,沿着等高线走,灰阶值的变化会更加轻柔,有可能是随机变化的,因此边缘具有一个可以测量的方向。边缘像素和噪声像素相比周围的像素都有明显的变化,而边缘像素互相连接,构成等高线,因此可以通过这一特性区分边缘像素和噪声像素。

2.边缘检测的难点

(1)数码化:图像的采样不可能使得整个边缘都落在像素边缘上,灰阶的变化可能跨越多个像素,形成带锯齿的斜坡边缘,而不是阶梯型边缘。

(2)无处不在的噪声:由于受到光强度、温度、大气效应等因素的影响,导致了图像存在噪声,噪声使得场景中表示同样灰阶的像素,在图像中可能不是同样的灰阶,使灰阶产生了随机变化,因此理想边缘的平滑线条和斜坡在真实的图像中不可能实现。

3.定位边缘的常见算子

有3类常见的方法定位边缘,分别是导数算子、模板匹配、边缘数学模型。下面简单介绍前两种方法,详细介绍最后一种方法。

3.1导数算子

由于边缘是由灰阶值的变化而定义的,而导数可以表征函数的变化率,因此可以利用它来检测边缘。像素用二维变量表示,可以把梯度作为算子。由于一幅图像的像素是离散的,不能直接求像素的偏导数,而是使用差分运算,即一个像素点的导数可以由一块局部区域内灰阶值的变化来近似。我们可以设定一个阈值,任何像素的梯度值如果超过了阈值,即视为边缘像素。

3.2基于模板的边缘检测

基于模板的边缘检测的思想是将一个小的离散模板作为边缘的模板,而不是直接使用导数算子。模板既可以尝试对边缘的灰阶变化进行建模,也可以尝试近似一个导数算子。常见的基于模板的边缘检测器有Sobel算子和Kirsch算子,这种检测边缘的方法比导数算子好,尤其是在大噪声的情况下。

3.3边缘数学模型

3.3.1 canny算子

canny算子是一种非常重要的边缘检测算子,它的最优性表现在:

(1)错误率低:canny算子可以尽可能多地标识出原始图像的实际边缘。

(2)局部性:所标记出来的边缘尽可能地接近实际图像中的实际边缘。

(3)响应最小:图像中的边缘只能标识一次,并且不把噪声标识成边缘。

具体实现步骤如下:

(1)去噪声:首先对原始图像数据与二维高斯滤波模板进行卷积运算,得到的图像与原始图像相比有轻微的模糊,这样可以消除噪声。

(2)梯度计算:利用一阶微分检测找到图像灰度沿着x、y方向的导数,然后求出梯度的幅值和方向。

(3)非最大消除:考虑梯度幅度图中的小邻域,并比较中心像素与其梯度方向上的相邻像素,如果中心像素的梯度值不是最大的,则将其像素梯度值置为0,否则保留。该操作的目的是细化梯度检测得到的边缘像素所构成的边界。

(4)滞后阈值化:选取两个阈值并借助滞后阈值化方法确定最后的边缘点。首先标记梯度值大于高阈值的像素点,认为它们一定是边缘点;其次,对这些像素相连的像素使用低阈值,如果梯度值大于低阈值,则认为它们也是边缘点。该步骤可减弱噪声在最终边缘图像中的影响,并可避免阈值过低而导致的虚假边缘或由于阈值过高而导致的边缘丢失。

3.3.2 Shen-Castan(ISEF)边缘检测器

该算法基本形式与canny一致,首先利用平滑核进行卷积运算,再搜索边缘像素。但他们提出了另一个最优的滤波器函数--无穷对称指数滤波器,这个滤波器比canny的滤波器能给出更好的信噪比,而且有更好的局部性。因为canny算法是通过高斯的一阶导数得到近似的最优滤波器,而ISEF则是直接使用最优的滤波器。然而ISEF没有处理好多响应准则,因此可能会对噪声和模糊边缘产生错误的响应。

ISEF先利用无穷对称指数滤波器对图像进行过滤,接着通过寻找拉普拉斯算子的零交叉来定位这幅图像中的边缘,只要从平滑后的图像减去原始图像就可以快速地找到拉普拉斯算子的近似结果。将经过相减后得到的图像中所有数值为正的像素设置为1,其他设置为0,可以得到二值拉普拉斯图像BLI。候选边缘在BLI区域的边缘上,相当于零交叉,可以把这些像素当做边缘,也可以经过一些额外的增强操作来提升算法找出的边缘像素的质量。下面介绍两种改进的方法。

第一种改进是使用负零交叉抑制。在边缘像素的位置,在过滤的图像的二阶导数中会存在一个零交叉,即那一点的梯度要么最大,要么最小。如果二阶导数的符号从正号变成负号,就称为正零交叉,如果符号从负号变成了正号,则称为负零交叉。所有其他的零交叉都认为是错误,且不认为这些零交叉表示边缘,即只有梯度为最值的零交叉点所在的像素位置才认为是边缘。

在有些原始图像噪声非常强烈的情况下,标准的阈值方法不能满足。边缘像素可以通过将一个全局阈值应用于梯度的方法来进行阈值操作,而ISEF提出了自适应的梯度方法。一个带有固定宽度的窗口的中心在BLI中的候选边缘像素,如果确实为边缘像素,则这个窗口会包含两个区域,这两个区域被边缘分开,带有不同的灰阶,那一点的梯度最佳估计值是在两个区域之间的灰阶的差值,其中一个区域对应于BLI中的0像素,一个对应于1像素。

最后,对边缘应用滞后阈值操作方法,这和canny算法中使用的方法相同。

2019-08-21 17:06:49 baidu_34971492 阅读数 207
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29903 人正在学习 去看看 张中强

FPGA图像处理之边缘检测算法的实现

1.背景知识

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

2.边缘检测算子

一阶:Roberts Cross算子,Prewitt算子,Sobel算子, Kirsch算子,罗盘算子;二阶: Marr-Hildreth,在梯度方向的二阶导数过零点,Canny算子,Laplacian算子。今天我们要讲的是基于Sobel算子的边缘检测的FPGA算法的实现。

3.Sobel算子实现

Sobel算法是像素图像边缘检测中最重要的算子之一,在机器学习、数字媒体、计算机视觉等信息科技领域起着举足轻重的作用。在技术上,它是一个离散的一阶差分算子,用来计算图像亮度函数的一阶梯度之近似值。在图像的任何一点使用此算子,将会产生该点对应的梯度矢量或是其法矢量
Soble边缘检测算法比较简,实际应用中效率比canny边缘检测效率要高,但是边缘不如Canny检测的准确,但是很多实际应用的场合,sobel边缘却是首选,尤其是对效率要求较高,而对细纹理不太关心的时候。
Soble边缘检测通常带有方向性,可以只检测竖直边缘或垂直边缘或都检测。
-1 0 +1
-2 0 +2
-1 0 +1
Sobel算子 x方向
+1 +2 +1
0 0 0
-1 -2 -1

Y方向

(i-1,j-1) ( i,j-1) (i+1,j-1)
(i-1,j) (i,j) (i+1,j)
(i-1,j+1) (i,j+1) (i+1,j+1)

原始图像P
实现步骤:

 1.Gx = P ★Sobelx   -- 原始图像与Sobel算子X方向卷积;
 2.  Gy = P★Sobely   -- 原始图像与Sobel算子Y方向卷积;
  1. 在这里插入图片描述
  2. 阈值比较形成边缘查找后的二值图像。

4.C语言实现

/* Sobel template
a00 a01 a02
a10 a11 a12
a20 a21 a22
*/
unsigned char a00, a01, a02;
unsigned char a10, a11, a12;
unsigned char a20, a21, a22;
void MySobel(IplImage* gray, IplImage* gradient)
{
CvScalar color ;
for (int i=1; i<gray->height-1; ++i)
{
for (int j=1; j<gray->width-1; ++j)
{
a00 = cvGet2D(gray, i-1, j-1).val[0];
a01 = cvGet2D(gray, i-1, j).val[0];
a02 = cvGet2D(gray, i-1, j+1).val[0];
a10 = cvGet2D(gray, i, j-1).val[0];
a11 = cvGet2D(gray, i, j).val[0];
a12 = cvGet2D(gray, i, j+1).val[0];
a20 = cvGet2D(gray, i+1, j-1).val[0];
a21 = cvGet2D(gray, i+1, j).val[0];
a22 = cvGet2D(gray, i+1, j+1).val[0];
// x方向上的近似导数  卷积运算
double ux = a20 * (1) + a10 * (2) + a00 * (1)
+ (a02 * (-1) + a12 * (-2) + a22 * (-1));
// y方向上的近似导数  卷积运算
double uy = a02 * (1) + a01 * (2) + a00 * (1)
+ a20 * (-1) + a21 * (-2) + a22 * (-1);
color.val[0] = sqrt(ux*ux + uy*uy);
cvSet2D(gradient, i, j, color);
}
}
}
//注释:该程序需要在安装Opencv软件下运行。

5.Matlab边缘检测的实现

ps=imread('lena.jpg'); %读取图像
subplot(1,3,1)
imshow(ps);
title('原图像');
ps=rgb2gray(ps);
[m,n]=size(ps); %用Sobel微分算子进行边缘检测
pa = edge(ps,'sobel');
subplot(1,3,2);
imshow(pa);
title('Sobel边缘检测得到的图像');

结果:
在这里插入图片描述

效果图

6.FPGA实现

我将在FPGA程序中注释,表示实现过程。我们使用的图像为480x272。

/* 
Filename    : Sobel.v
Compiler    : Quartus II 13.0
Description : implement Sobel Edge Detector 
Release     : 
*/

module sobel (
  input            iCLK,
  input            iRST_N,
  input      [7:0] iTHRESHOLD,
  input            iDVAL,
  input      [9:0] iDATA,
  output reg       oDVAL,
  output reg [9:0] oDATA
);
//----------------------------------------------------
// 将Sobel算子换算成有符号数(signed)
//----------------------------------------------------
// mask x
parameter X1 = 8'hff, X2 = 8'h00, X3 = 8'h01;
parameter X4 = 8'hfe, X5 = 8'h00, X6 = 8'h02;
parameter X7 = 8'hff, X8 = 8'h00, X9 = 8'h01;

// mask y
parameter Y1 = 8'h01, Y2 = 8'h02, Y3 = 8'h01;
parameter Y4 = 8'h00, Y5 = 8'h00, Y6 = 8'h00;
parameter Y7 = 8'hff, Y8 = 8'hfe, Y9 = 8'hff;

wire  [7:0] Line0;
wire  [7:0] Line1;
wire  [7:0] Line2;

wire  [17:0]  Mac_x0;
wire  [17:0]  Mac_x1;
wire  [17:0]  Mac_x2;

wire  [17:0]  Mac_y0;
wire  [17:0]  Mac_y1;
wire  [17:0]  Mac_y2;

wire  [19:0]  Pa_x;
wire  [19:0]  Pa_y;

wire  [15:0]  Abs_mag;
//---------------------------------------------
// 实现3x3矩阵原始图像 P
//---------------------------------------------
LineBuffer LineBuffer_inst (
  .clken(iDVAL),
  .clock(iCLK),
  .shiftin(iDATA[9:2]),
  .taps0x(Line0),
  .taps1x(Line1),
  .taps2x(Line2)
);
//--------------------------------------------
// Gx = P ★Sobelx
// x方向卷积运算实现
//---------------------------------------------
MAC_3 x0 (
  .aclr3(!iRST_N),
  .clock0(iCLK),
  .dataa_0(Line0),
  .datab_0(X9),
  .datab_1(X8),
  .datab_2(X7),
  .result(Mac_x0)
);

MAC_3 x1 (
  .aclr3(!iRST_N),
  .clock0(iCLK),
  .dataa_0(Line1),
  .datab_0(X6),
  .datab_1(X5),
  .datab_2(X4),
  .result(Mac_x1)
);

MAC_3 x2 (
  .aclr3(!iRST_N),
  .clock0(iCLK),
  .dataa_0(Line2),
  .datab_0(X3),
  .datab_1(X2),
  .datab_2(X1),
  .result(Mac_x2)
);
PA_3 pa0 (
  .clock(iCLK),
  .data0x(Mac_x0),
  .data1x(Mac_x1),
  .data2x(Mac_x2),
  .result(Pa_x)
);

//---------------------------------------------------
// Gy = P★Sobely
// y方向卷积运算的实现
//---------------------------------------------------
// Y
MAC_3 y0 (
  .aclr3(!iRST_N),
  .clock0(iCLK),
  .dataa_0(Line0),
  .datab_0(Y9),
  .datab_1(Y8),
  .datab_2(Y7),
  .result(Mac_y0)
);

MAC_3 y1 (
  .aclr3(!iRST_N),
  .clock0(iCLK),
  .dataa_0(Line1),
  .datab_0(Y6),
  .datab_1(Y5),
  .datab_2(Y4),
  .result(Mac_y1)
);

MAC_3 y2 (
  .aclr3(!iRST_N),
  .clock0(iCLK),
  .dataa_0(Line2),
  .datab_0(Y3),
  .datab_1(Y2),
  .datab_2(Y1),
  .result(Mac_y2)
);
PA_3 pa1 (
  .clock(iCLK),
  .data0x(Mac_y0),
  .data1x(Mac_y1),
  .data2x(Mac_y2),
  .result(Pa_y)
);
//-----------------------------------------------
// 得到G
//-----------------------------------------------
SQRT sqrt0 (
  .clk(iCLK),
  .radical(Pa_x * Pa_x + Pa_y * Pa_y),
  .q(Abs_mag)
);
//-------------------------------------------------
// 阈值比较
//-------------------------------------------------
always@(posedge iCLK, negedge iRST_N) begin
  if (!iRST_N)
    oDVAL <= 0;
  else begin
    oDVAL <= iDVAL;
    
    if (iDVAL)
      oDATA <= (Abs_mag > iTHRESHOLD) ? 0 : 1023;
    else
      oDATA <= 0;
  end
end

endmodule

IP设置
在这里插入图片描述
LineBuffer IP设置
在这里插入图片描述

LineBuffer IP的设置

在这里插入图片描述

MAC_3 IP的设置
在这里插入图片描述
PA_3 IP的设置
在这里插入图片描述
SQRT IP的设置
FPGA基于Sobel算子图像边缘检测的实现结果:
在这里插入图片描述
lena原图
在这里插入图片描述
阈值3
在这里插入图片描述
阈值5
在这里插入图片描述
阈值7

欢迎关注微信公众号:FPGA开源工作室
获取更多学习资料。
FPGA开源工作室

2019-04-15 20:37:47 minjiuhong 阅读数 510
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29903 人正在学习 去看看 张中强

Canny算法介绍

Canny边缘检测算法是一种多级边缘检测算法,John F. Canny于 1986 年开发出来。在许多方面的应用都有着它的身影。因此,不管是为了学习或者工作,学习Canny算法的原理和实现都是非常有必要的。废话不多说了,下面我将带领大家从原理和代码方面为您讲解Canny算法的基本思想。在本文的最后,会有一个基于Canny算法比较酷炫的简单项目,有兴趣的可以试试,比较有意思。

Canny算法原理

Canny边缘检测主要分为四步来实现的,每一步都有着其主要意义,基于这四个步骤,我将一一讲解其实现的细节。

第一步:高斯模糊

对于原图像我们需要对其进行高斯模糊,高斯模糊在之前我们已经实现过了,或者你可以使用Opencv自带的函数也行。对图像进行模糊能够去除图像的细节部分,图像噪声可大大减少,而图像的边缘信息不会损失什么,这样对后续检测的效果会更好。

/* 获取高斯分布数组               (核大小, sigma值) */
double **getGaussianArray(int arr_size, double sigma)
{
	int i, j;
	// [1] 初始化权值数组
	double **array = new double*[arr_size];
	for (i = 0; i < arr_size; i++) {
		array[i] = new double[arr_size];
	}
	// [2] 高斯分布计算
	int center_i, center_j;
	center_i = center_j = arr_size / 2;
	double pi = 3.141592653589793;
	double sum = 0.0f;
	// [2-1] 高斯函数
	for (i = 0; i < arr_size; i++) {
		for (j = 0; j < arr_size; j++) {
			array[i][j] =
				//后面进行归一化,这部分可以不用
				//0.5f *pi*(sigma*sigma) * 
				exp(-(1.0f)* (((i - center_i)*(i - center_i) + (j - center_j)*(j - center_j)) /
				(2.0f*sigma*sigma)));
			sum += array[i][j];
		}
	}
	// [2-2] 归一化求权值
	for (i = 0; i < arr_size; i++) {
		for (j = 0; j < arr_size; j++) {
			array[i][j] /= sum;
			//printf(" [%.15f] ", array[i][j]);
		}
		// printf("\n");
	}
	return array;
}


// 高斯模糊
Mat gaussianFilter(Mat &src, int size, double sigma)
{
	// 深拷贝
	Mat dst = src.clone();
	double** model = getGaussianArray(size, sigma);
	double sum;
	// 遍历像素
	for (int i = 0; i < src.rows; i++)
		for (int j = 0; j < src.cols; j++)
		{
			// 初始化
			sum = 0;
			// 遍历模板
			for (int n = -size / 2; n <= size / 2; n++)
				for (int m = -size / 2; m <= size / 2; m++)
				{
					// 边缘补零
					if (i + n < 0 || j + m < 0 || i + n >= src.rows || j + m >= src.cols)
						sum += 0;
					else
						sum += model[n + size / 2][m + size / 2] * src.at<uchar>(i + n, j + m);
				}
			dst.at<uchar>(i, j) = pixesDeal(sum);
		}
	// imshow("高斯模糊", dst);
	return dst;
}

// 像素判定
uchar pixesDeal(double n)
{
	if (n < 0)
		return 0;
	else if (n > 255)
		return 255;
	else
		return static_cast<uchar>(n);
}

第二步:Sobel梯度算子

我们需要对模糊后的图像进行Sobel梯度算子的卷积,但这里的处理和之前我们实现过的Sobel图像锐化稍稍有点不同,之前我们是将x方向的Sobel模板和y方向的Sobel模板进行绝对值的相加(L1范数),但现在我们需要进行平方相加后开平方(L2范数),即幅值。还需要梯度方向的信息为下一步处理提供条件。获取的幅值图像大概描绘了整个图像的轮廓,但是这个边缘存在着很多不必要的信息,例如:边缘描绘过宽和噪声信息。

// sobel梯度计算
Mat sobelFilter(Mat &src)
{
	// 深拷贝
	Mat gradValue = src.clone();
	Mat gradDirect = src.clone();
	double sum1, sum2;
	// x方向边缘的y模板
	double model1[9] = { -1, -2, -1, 0, 0, 0, 1, 2, 1 };
	// y方向边缘的x模板
	double model2[9] = { -1, 0, 1, -2, 0, 2, -1, 0, 1 };
	int p;
	// 遍历像素
	for (int i = 0; i < src.rows; i++)
		for (int j = 0; j < src.cols; j++)
		{
			// 初始化
			p = 0, sum1 = 0, sum2 = 0;
			// 遍历模板
			for (int n = -1; n <= 1; n++)
				for (int m = -1; m <= 1; m++)
				{
					// 边缘补零
					if (i + n < 0 || j + m < 0 || i + n >= src.rows || j + m >= src.cols)
						p++;
					else
					{
						sum1 += src.at<uchar>(i + n, j + m) * model1[p];
						sum2 += src.at<uchar>(i + n, j + m) * model2[p];
						p++;
					}
				}
			gradValue.at<uchar>(i, j) = pixesDeal(pow(pow(sum1, 2) + pow(sum2, 2), 1.0 / 2));
			// 0,7垂直方向, 1,2斜对角方向\, 3,4水平方向, 5,6斜对角方向/
			gradDirect.at<uchar>(i, j) = static_cast<uchar>(atan(sum1 / (sum2 + 0.001)) * 8 / CV_PI + 4);
		}
	Mat temp[] = { gradValue, gradDirect };
	Mat grad;
	merge(temp, 2, grad);
	// imshow("梯度图像", gradValue);

	return grad;
}

第三步:非极大值抑制(NMS)

非极大值抑制是一种将宽边缘带细化,只保留边缘峰值的处理。主要的手法是根据上一步获取的梯度方向,将图像像素点沿梯度方向或逆方向的邻域像素点进行比较,如果为最大值则保留,否则抑制,即设置像素点为0。

Mat NMS(Mat &src)
{
	vector<Mat> grad;
	// 梯度通道和方向通道
	split(src, grad);
	// 遍历中梯度值会改变,先拷贝一份
	Mat gradValue = grad[0].clone();
	// 遍历像素
	for (int i = 0; i < gradValue.rows; i++)
		for (int j = 0; j < gradValue.cols; j++)
		{
			if (grad[1].at<uchar>(i, j) == 0 || grad[1].at<uchar>(i, j) == 7)
			{
				if (i < gradValue.rows - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (i > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
			if (grad[1].at<uchar>(i, j) == 1 || grad[1].at<uchar>(i, j) == 2)
			{
				if (i < gradValue.rows - 1 && j < gradValue.cols - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j + 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (i > 0 && j > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j - 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
			if (grad[1].at<uchar>(i, j) == 3 || grad[1].at<uchar>(i, j) == 4)
			{
				if (j < gradValue.cols - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i, j + 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (j > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i, j - 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
			if (grad[1].at<uchar>(i, j) == 5 || grad[1].at<uchar>(i, j) == 6)
			{
				if (i < gradValue.rows - 1 && j > 0)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i + 1, j - 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
				if (i > 0 && j < gradValue.cols - 1)
					if (gradValue.at<uchar>(i, j) <= gradValue.at<uchar>(i - 1, j + 1))
					{
						grad[0].at<uchar>(i, j) = 0;
						continue;
					}
			}
		}
	//imshow("非极大值处理", grad[0]);
	merge(grad, src);
	return src;
}

第四步:双阈值连接

设置两个阈值,一个高阈值和一个低阈值。图像像素点如果高于高阈值则表示是强边缘点,是真实边缘点,高于低阈值但小于高阈值表示是弱边缘点,其它不是边缘,抑制。之后需要对弱边缘点进行处理,若其邻域存在强边缘点则表示是真实边缘点,否则不是。这样设置两个阈值可以滤除图像中噪声,改善图像质量。弱边缘的处理原则是因为真实边缘的弱边缘点都存在强边缘点。

Mat doubleThrCon(Mat &src, uchar high, uchar low)
{
	vector<Mat> grad;
	// 梯度通道和方向通道
	split(src, grad);
	for (int i = 0; i < grad[0].rows; i++)
		for (int j = 0; j < grad[0].cols; j++)
		{
			if (grad[0].at<uchar>(i, j) > high)
			{
				grad[0].at<uchar>(i, j) = 255;
				// 标记强边缘点的位置
				grad[1].at<uchar>(i, j) = 2;
			}
			else if (grad[0].at<uchar>(i, j) > low)
			{
				grad[0].at<uchar>(i, j) = 0;
				// 标记弱边缘点的位置
				grad[1].at<uchar>(i, j) = 1;
			}
			else
			{
				grad[0].at<uchar>(i, j) = 0;
				grad[1].at<uchar>(i, j) = 0;
			}
		}
	// 真实的边缘会在弱边缘点的邻域内存在强边缘点
	for (int i = 0; i < grad[0].rows; i++)
		for (int j = 0; j < grad[0].cols; j++)
		{
			if (grad[1].at<uchar>(i, j) == 1)
			{
				for (int n = -1; n <= 1; n++)
					for (int m = -1; m <= 1; m++)
					{
						if (i + n >= 0 && j + m >= 0 && i + n < src.rows && j + m < src.cols && grad[1].at<uchar>(i + n, j + m) == 2)
							grad[0].at<uchar>(i, j) = 255;
					}
			}
		}

	return grad[0];

}

视频流Canny边缘检测(阈值可调)

应用我们自己编写的Canny算法速度比较慢,因此我们选择系统提供的Canny算法效果会流畅。可以减少对摄像头的帧数处理来加快速度。

// 主函数
int main()
{
	Mat frameImage;
	VideoCapture capture1(0 + CAP_DSHOW);
	// 我用的是手机摄像头,用电脑摄像头的不用填open()里面的参数
	capture1.open("http://admin:admin@172.30.71.223:8081");
	if (!capture1.isOpened())
		return 0;

	int n = 0;
	int highThreshold=70, lowThreshold=70;
	//创建窗口
	namedWindow("out", 1);

	//创建轨迹条
	createTrackbar("high threshold", "out", &highThreshold, 255);
	createTrackbar("low threshold", "out", &lowThreshold, 255);

	while (true)
	{
		capture1 >> frameImage;
		if (n % 3 == 0)
		{
			cvtColor(frameImage, frameImage, COLOR_BGR2GRAY);
			Canny(frameImage, frameImage, lowThreshold, highThreshold);
			//frameImage = myCanny(frameImage);
			imshow("out", frameImage);
		}
		if (char(waitKey(1)) == 'q') break;
		n++;
	}
	return 0;
}

结果视频我就不发了,有兴趣的可以自己运行一下~

2016-11-02 14:02:34 guanyuqiu 阅读数 17499
  • Java经典算法讲解

    在面试中,算法题目是必须的,通过算法能够看出一个程序员的编程思维,考察对复杂问题的设计与分析能力,对问题的严谨性都能够体现出来。一个算法的好坏,直接影响一个方法调用的性能,进而影响软件的整体性能。算法是学习所有编程语言的基础,在Java的学习过程中首先也会选择以算法起步,本次课程重点讲解Java开发中常用的基本算法。

    29903 人正在学习 去看看 张中强

1.Sobel边缘检测算法

sobel边缘算子认不同为邻域的像素对当前像素产生的影响不是等价的,所以距离不同的像素具有不同的权值,对算子结果产生的影响也不同。一般来说,距离越大,产生的影响越小。这两个卷积因子分别对垂直边缘和水平边缘影响最大,两个卷积的最大值做为该点的输出位。


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


具体计算如下:


其中f(a,b)表示图像(a,b)点的灰度值;

图像的每一个像素的横向及纵向灰度值通过以下公式结合,来计算该点灰度的大小:


通常,为了提高效率,使用不开平方的近似值:


如果梯度G大于某一阈值,则认为改点(x,y)为边缘点。

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


sobel算子根据像素点上下、左右邻点灰度加权差,在边缘处达到极值这一现象检测边缘。对噪声具有平滑作用,提供较为精确的边缘方向信息,边缘定位精度不够高。当对精度要求不是很高时,是一种较为常见的边缘检测方法。

代码:

void Sobel(unsigned char *pSource, unsigned char *pDst)
{
	int				i,j,Gx,Gy,nSum;
	unsigned char	*pDs1,*pDs2,*pDs3,*pDs4,*pDst5,*pDst6,*pDst7,*pDst8,*pDst9,*pResut;
	memset(pDst,0,sizeof(unsigned char)*m_nWidth*m_nHeight);
	pDs1 = pSource;
	pDs2 = pDs1+1;
	pDs3 = pDs2+1;
	pDs4 = pSource+m_nWidth;
	pDst5 = pDs4+1;
	pDst6 = pDst5+1;
	pDst7 = pSource+2*m_nWidth;
	pDst8 = pDst7+1;
	pDst9 = pDst8+1;
	pResut = pDst+m_nWidth+1;

	for (i=1;i<m_nHeight-1;i++)
	{
		for (j=1;j<m_nWidth-1;j++)
		{
			Gx = (*pDs3)+2*(*pDst6)+(*pDst9)
				-(*pDst7)-(*pDs1)-2*(*pDs4);
			Gy = (*pDs1)+2*(*pDs2)+(*pDs3)
				-(*pDst7)-2*(*pDst8)-(*pDst9);
			nSum = abs(Gx)+abs(Gy);
			*pResut = nSum>175?255:0;
			pDs1++;
			pDs2++;
			pDs3++;
			pDs4++;
			pDst5++;
			pDst6++;
			pDst7++;
			pDst8++;
			pDst9++;
			pResut++;
		}
		pDs1+=2;
		pDs2+=2;
		pDs3+=2;
		pDs4+=2;
		pDst5+=2;
		pDst6+=2;
		pDst7+=2;
		pDst8+=2;
		pDst9+=2;
		pResut+=2;
	}
}

2.Robert算子——无方向一阶锐化


3.Priwitt算子——无方向一阶锐化

Priwitt算子在一个方向求微分,而在另一个方向求平均,因而对噪声相对不敏感,有抑制噪声的作用,但是像素平均相对于对图像的低通滤,所以Prewitt算子对边缘的定位不如Roberts算子。与Sobel相比,有一定的抗干扰性,图像效果比较干净。


几种方法的效果比较:

  • Sobel算子与Priwitt算法的思路相同,属于同一类型,因此处理效果基本相同;
  • Roberts算子的模板时2*2,提取信息弱;
  • 单方向锐化经过处理之后,也可以对边界进行增强。

4.拉普拉斯算子

二维函数f(x,y)的拉普拉斯是一个二阶的微分,定义为:

最终结果为:



Laplacian算子利用二阶导数信息,具有各向同性,即与坐标轴方向无关,坐标轴旋转后梯度结果不变。使得图像经过二阶微分后,在边缘处产生一个陡峭的零交叉点,根据这个对零交叉点判断边缘。

为了改善锐化效果,可以脱离微分的计算原理,在原有的算子基础上,对模板系数进行改变,获得Laplacian变形算子


Laplacian算子对噪声比较敏感,Laplacian算子有一个缺点是它对图像中的某些边缘产生双重响应。所以图像一般先经过平滑处理,通常把Laplacian算子和平滑算子结合起来生成一个新的模板。





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