目录
Canny基本思想
算法步骤
高斯滤波
计算梯度值和方向
非极大值抑制
双阈值的选取
滞后边界跟踪
Canny基本思想
?
基本思想:寻找图像梯度的局部最大值。
?
Canny
提出了边缘检测算子优劣评判的三条标准:
????????①
高检测率。边缘检测算子应该只对边缘进行响应,检测算子不漏检任何边缘,也不应该将非边缘标记为边缘。
????????②
精确定位。检测到的边缘与实际边缘之间的距离要尽可能的小。
????????③
明确的响应。对每一条边缘只有一次响应,只得到一个点。
?
优点:在一阶微分算子的基础上,增加了非最大值抑制和双阈值。
?
非极大值抑制:不仅可以有效地抑制多响应边缘,而且还可以提高边缘的定位精度;
?
双阈值:可以有效减少边缘的漏检率。
算法步骤
①
灰度化:常用公式
--
Gray=0.299R+0.587G+0.114B
②
高斯滤波
③
计算梯度值和方向
④
非极大值抑制
⑤
双阈值的选取
⑥
滞后边界跟踪
高斯滤波
?
目的:去除噪声
?
方法:
高斯滤波器是将高斯函数离散化,将滤波器中对应的横纵坐标索引代入到高斯函数,从而得到对应的值。
?
?二维的高斯函数如下:其中
(x , y)
为坐标,
σ
为标准差
?
不同尺寸的滤波器,得到的值也不同,下面是
(2k+1)x(2k+1)
滤波器的计算公式
:
?
?常见的高斯滤波器大小为
5*5
,
σ = 1.4
,其近似值为:
计算梯度值和方向
?
常见方法采用
Sobel
滤波器
【
水平
x
和垂直
y
方向
】
在计算梯度和方向
?
采用下列公式计算梯度和方向:
非极大值抑制
????????目的:将模糊(blurred)的边界变得清晰(sharp)。通俗的讲,就是保留了每个像素点上梯度强度的极大值,而删掉其他的值。
?近邻
法:
????????a)
将其梯度方向近似为以下值中的一个(
0,45,90,135,180,225,270,315
)(即上下左右和
45
度方向)
????????b)
比较该像素点,和其梯度方向正负方向的像素点的梯度强度
????????c)
如果该像素点梯度强度最大则保留,否则抑制(删除,即置为
0
)
?????????插值法:
双阈值的选取
?
使用双阈值来对二值化图像进行筛选,通过选取合适的大阈值与小阈值可以得出最为接近图像真实边缘的边缘图像。
?
实现方法:根据高阈值和低阈值将像素点标记为强边界点、弱边界点和非边界点三类。其中强边界点即为真实边缘点。
滞后边界跟踪
?
目的:进一步处理弱边界点
?
主要思想:
和强边界点相连的弱边界点认为是边界点,其他的弱边界点则被抑制。
?
方法:
?由真实边缘引起的弱边缘像素将连接到强边缘像素,而噪声响应未连接。为了跟踪边缘连接,
通过查看弱边缘像素及其
8
个邻域像素
,只要其中一个为强边缘像素,则该弱边缘点就可以保留为真实的边缘。
java代码如下:
main程序
package img;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public class main {
public static void main(String[] args) throws Exception {
// 读取图片
BufferedImage bufImage = ImageIO.read(new File("Lena.jpg"));
// Canny边缘检测
Canny c=new Canny();
BufferedImage cannyImg=c.getCannyImg(bufImage,0.08,0.4,2);
ImageIO.write(cannyImg, "JPEG", new File("cannyImg.jpg"));
System.out.println("Main: Successfully!");
}
}
Canny程序
package img;
import java.awt.Color;
import java.awt.image.BufferedImage;
import java.io.IOException;
public class Canny {
static int edge[][];
static int find[][];
static double vmax=0;
static BufferedImage cannyImg;
public Canny() {
// TODO Auto-generated constructor stub
}
public static BufferedImage getCannyImg(BufferedImage image,double a,double b,double g) throws IOException {
// TODO Auto-generated method stub
// 灰度化
BufferedImage grayImg=getGray(image);
// 高斯滤波
grayImg=filtering_Gaussian(grayImg, g);
// 计算梯度幅度值和梯度方向
double grad[][][]=getGradient(grayImg);
// double nms[][]=NMS(grad);
// 非极大值抑制处理(插值法)
double nms[][]=NMS_2(grad);
// 双阈值处理
double thresh=vmax*a;
double_threshold(nms, b*thresh, thresh);
// 边界跟踪处理
boundary_tracking();
BufferedImage cannyImg=showCannyImg();
return cannyImg;
}
private static BufferedImage getGray(BufferedImage image) {
// TODO Auto-generated method stub
int w = image.getWidth();
int h = image.getHeight();
BufferedImage grayImg= new BufferedImage(w,h,image.getType());
for(int x=0;x<w;x++)
for(int y=0;y<h;y++) {
int pixel=image.getRGB(x, y);
int r=(pixel & 0xff0000) >> 16;
int g=(pixel & 0xff00) >> 8;
int b=(pixel & 0xff);
int gray = (int) (0.299 * r + 0.587 * g + 0.114 * b);
grayImg.setRGB(x, y, new Color(gray,gray,gray).getRGB());
}
return grayImg;
}
private static int RGBTogray(int pixel) {
// TODO Auto-generated method stub
return pixel & 0xff;
}
private static BufferedImage filtering_Gaussian(BufferedImage image, double g) {
// TODO Auto-generated method stub
int w = image.getWidth();
int h = image.getHeight();
int length=5;
int k=length/2;
double sigma=Math.sqrt(g);
double[][] gaussian=new double[length][length];
double sum=0;
for(int i=0;i<length;i++) {
for(int j=0;j<length;j++) {
gaussian[i][j]=Math.exp(-((i-k)*(i-k)+(j-k)*(j-k))/(2*sigma*sigma));
gaussian[i][j]/=2*Math.PI*sigma*sigma;
sum+=gaussian[i][j];
}
}
for(int i=0;i<length;i++) {
for(int j=0;j<length;j++) {
gaussian[i][j]/=sum;
}
}
BufferedImage GaussianImg= new BufferedImage(w,h,image.getType());
for(int x=k;x<w-k;x++) {
for(int y=k;y<h-k;y++) {
int newpixel=0;
for(int gx=0;gx<length;gx++) {
for(int gy=0;gy<length;gy++) {
int ix=x+gx-k;
int iy=y+gy-k;
if(ix<0||iy<0||ix>=w||iy>=h)
continue;
else {
newpixel+=RGBTogray(image.getRGB(ix, iy))*gaussian[gx][gy];
}
}
}
newpixel=(int) Math.round(1.0*newpixel);
GaussianImg.setRGB(x, y, new Color(newpixel,newpixel,newpixel).getRGB());
}
}
return GaussianImg;
}
private static double[][][] getGradient(BufferedImage image){
// TODO Auto-generated method stub
int w = image.getWidth();
int h = image.getHeight();
int step=1;
double[][][] grad = new double[w][h][2];
for(int x=step;x<w-step;x++) {
for(int y=step;y<h-step;y++) {
int gx=-RGBTogray(image.getRGB(x-step, y-step))
-RGBTogray(image.getRGB(x-step, y))*2
-RGBTogray(image.getRGB(x-step, y+step))
+RGBTogray(image.getRGB(x+step, y-step))
+RGBTogray(image.getRGB(x+step, y))*2
+RGBTogray(image.getRGB(x+step, y+step));
int gy=RGBTogray(image.getRGB(x-step, y+step))
+RGBTogray(image.getRGB(x, y+step))*2
+RGBTogray(image.getRGB(x+step, y+step))
-RGBTogray(image.getRGB(x-step, y-step))
-RGBTogray(image.getRGB(x, y-step))*2
-RGBTogray(image.getRGB(x+step, y-step));
grad[x][y][0]=Math.sqrt(gx*gx+gy*gy);
if(gx==0)
grad[x][y][1]=90;
else
grad[x][y][1]=Math.toDegrees(Math.atan(1.0*gy/gx));
}
}
return grad;
}
private static double[][] NMS(double[][][] grad){
// TODO Auto-generated method stub
int w=grad.length;
int h=grad[0].length;
double[][] nms=new double[w][h];
for(int x=1;x<w-1;x++) {
for(int y=1;y<h-1;y++) {
nms[x][y]=grad[x][y][0];
if(grad[x][y][0]==0)
continue;
int a=(int) Math.round(grad[x][y][1]/45);
if(a==-2||a==2) {
if(grad[x][y][0]<grad[x][y-1][0]||grad[x][y][0]<grad[x][y+1][0])
nms[x][y]=0;
}
else if(a==-1) {
if(grad[x][y][0]<grad[x-1][y-1][0]||grad[x][y][0]<grad[x+1][y+1][0])
nms[x][y]=0;
}
else if(a==0) {
if(grad[x][y][0]<grad[x-1][y][0]||grad[x][y][0]<grad[x+1][y][0])
nms[x][y]=0;
}
else if(a==1) {
if(grad[x][y][0]<grad[x+1][y-1][0]||grad[x][y][0]<grad[x-1][y+1][0])
nms[x][y]=0;
}
if(nms[x][y]>vmax)vmax=nms[x][y];
}
}
return nms;
}
private static double[][] NMS_2(double[][][] grad) {
// TODO Auto-generated method stub
int w=grad.length;
int h=grad[0].length;
double[][] nms=new double[w][h];
for(int x=1;x<w-1;x++) {
for(int y=1;y<h-1;y++) {
nms[x][y]=grad[x][y][0];
if(grad[x][y][0]==0)
continue;
int x1,y1,x2,y2;
double weight=Math.tan(Math.toRadians(grad[x][y][1]));
if(grad[x][y][1]>45) {
x1=0;y1=1;x2=1;y2=1;
weight=1.0/weight;
}else if(grad[x][y][1]>0) {
x1=1;y1=0;x2=1;y2=1;
weight*=1;
}else if(grad[x][y][1]>-45) {
x1=1;y1=0;x2=1;y2=-1;
weight*=-1;
}else {
x1=0;y1=-1;x2=1;y2=-1;
weight=-1.0/weight;
}
double g1=grad[x+x1][y+y1][0];
double g2=grad[x+x2][y+y2][0];
double g3=grad[x-x1][y-y1][0];
double g4=grad[x-x2][y-y2][0];
double grad_count_1=g1*weight+g2*(1-weight);
double grad_count_2=g3*weight+g4*(1-weight);
if(grad[x][y][0]<grad_count_1||grad[x][y][0]<grad_count_2)
nms[x][y]=0;
if(nms[x][y]>vmax)vmax=nms[x][y];
}
}
return nms;
}
private static void double_threshold(double[][] nms,double low_th,double high_th){
// TODO Auto-generated method stub
int w=nms.length;
int h=nms[0].length;
edge=new int[w][h];
for(int x=0;x<w;x++) {
for(int y=0;y<h;y++) {
if(nms[x][y]>=high_th) {
edge[x][y]=2;
}else if(nms[x][y]>=low_th) {
edge[x][y]=1;
}
}
}
}
private static void dfs(int x,int y,int w,int h) {
// TODO Auto-generated method stub
if(x<0||x>=w||y<0||y>=h||find[x][y]==1)return ;
find[x][y]=1;
if(edge[x][y]>=1) {
edge[x][y]=2;
for(int i=-1;i<=1;i++) {
for(int j=-1;j<=1;j++) {
dfs(x+i,y+j,w,h);
}
}
}
}
private static int[][] boundary_tracking(){
// TODO Auto-generated method stub
int w=edge.length;
int h=edge[0].length;
find=new int[w][h];
for(int x=0;x<w;x++) {
for(int y=0;y<h;y++) {
if(find[x][y]==1)continue;
if(edge[x][y]==2) {
dfs(x,y,w,h);
}else if(edge[x][y]==0) {
find[x][y]=1;
}
}
}
return edge;
}
private static BufferedImage showCannyImg() throws IOException {
// TODO Auto-generated method stub
int w=edge.length;
int h=edge[0].length;
BufferedImage cannyImg= new BufferedImage(w,h,BufferedImage.TYPE_INT_RGB);
for(int x=0;x<w;x++) {
for(int y=0;y<h;y++) {
if(edge[x][y]==2) {
cannyImg.setRGB(x, y, new Color(255,255,255).getRGB());
}else {
cannyImg.setRGB(x, y, new Color(0,0,0).getRGB());
}
}
}
return cannyImg;
}
}
处理结果
?
|