private static void follow(int[] edgeImage, int x, int y,
int[] gradeMagnitude, int lowThreshold)
{
// 对8邻域象素进行查询
int[][] dir =
{
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, -1 },
{ 0, 1 },
{ 1, -1 },
{ 1, 0 },
{ 1, 1 } };
for (int k = 0; k < 8; k++)
{
int ii = x + dir[k][0]; //这里不理解,dir的值不是二维的吗?怎么可以加。
int jj = y + dir[k][1];
// 如果该象素为可能的边界点,又没有处理过
// 并且梯度大于阈值
if (edgeImage[ii + width * jj] == 128
&& gradeMagnitude[ii + width * jj] >= lowThreshold)
//if的条件我看不懂
{
// 把该点设置成为边界点
edgeImage[ii + width * jj] = 255;
// 以该点为中心进行跟踪
follow(edgeImage, ii, jj, gradeMagnitude, lowThreshold);
}
}
}
int[] gradeMagnitude, int lowThreshold)
{
// 对8邻域象素进行查询
int[][] dir =
{
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, -1 },
{ 0, 1 },
{ 1, -1 },
{ 1, 0 },
{ 1, 1 } };
for (int k = 0; k < 8; k++)
{
int ii = x + dir[k][0]; //这里不理解,dir的值不是二维的吗?怎么可以加。
int jj = y + dir[k][1];
// 如果该象素为可能的边界点,又没有处理过
// 并且梯度大于阈值
if (edgeImage[ii + width * jj] == 128
&& gradeMagnitude[ii + width * jj] >= lowThreshold)
//if的条件我看不懂
{
// 把该点设置成为边界点
edgeImage[ii + width * jj] = 255;
// 以该点为中心进行跟踪
follow(edgeImage, ii, jj, gradeMagnitude, lowThreshold);
}
}
}
&& gradeMagnitude[ii + width * jj] >= lowThreshold)
//if的条件我看不懂
我也不懂,因为不清楚你这个程序要达到什么目的
但dir[0][0]你说取到是哪个值?
dir[k][0]就是k行的第一个数啊。如果k = 0, 在dir[k][0]=-1. k=3, dir[k][0]=0
这个不难理解吧。
我不知道LZ为什么不明白,这里的dir[k][0]不是指明了是K行第0列吗,很明显的可以拿到K行0列的值然后进行相加很明显的啊
这很好理解吧。 dir是二维,但是dir[][]的值是一个int
if (edgeImage[ii + width * jj] == 128
&& gradeMagnitude[ii + width * jj] >= lowThreshold)
//if的条件我看不懂
{
// 把该点设置成为边界点
edgeImage[ii + width * jj] = 255;
// 以该点为中心进行跟踪
follow(edgeImage, ii, jj, gradeMagnitude, lowThreshold);
}
这个我也看不懂, 都没有说清楚要完成什么功能, 对这个函数的参数也没有说明白。 。。
/*
* CannyEdgeDetector.java
*
* Created on 2007年12月10日, 下午8:42
*
*/import java.awt.*;
import java.awt.image.*;
import java.awt.color.ColorSpace;/**
* 边缘检测
* <p>
* 实现了Canny边缘检测算法,提供静态函数 canny
*
* @author 余乐伟
*/
public class CannyEdgeDetector
{
/**
* Canny边缘检测
* <p>
* 函数 Canny 采用 CANNY 算法发现输入图像的边缘而且在输出图像中标识这些边缘。 threshold1和threshold2
* 当中的小阈值用来控制边缘连接,大的阈值用来控制强边缘的初始分割。
* <p>
* Canny边缘检测算法步骤:<br>
* step1:用高斯滤波器平滑图象;<br>
* step2:计算梯度的幅值和方向; <br>
* step3:对梯度幅值进行非极大值抑制; <br>
* step4:用双阈值算法检测和连接边缘 <br>
*
* @param sourceImage
* 输入图像
* @param lowThreshold
* 第一个阈值(低)
* @param highThreshold
* 第二个阈值(高)
* @return 用 Canny 边缘检测算法得到的边缘图
*/
public static BufferedImage canny(BufferedImage sourceImage,
int lowThreshold, int highThreshold)
{
height = sourceImage.getHeight();
width = sourceImage.getWidth();
int picsize = width * height; // 先把图片转换为灰度图
ColorSpace grayCS = ColorSpace.getInstance(ColorSpace.CS_GRAY);
ColorConvertOp colorConvertOp = new ColorConvertOp(grayCS, null);
sourceImage = colorConvertOp.filter(sourceImage, null); // sourceImage = ColorConvert.grayImage(sourceImage); // 对原图高斯滤波
sourceImage = gaussianSmooth(sourceImage); // 梯度幅值表
int[] gradeMagnitude = new int[picsize];
// 梯度方向表
int[] gradeOrientation = new int[picsize]; // 计算方向导数和梯度的幅度
grade(sourceImage, gradeMagnitude, gradeOrientation); // 应用非最大抑制,细化
int[] edgeImage = NonmMaxSuppress(gradeMagnitude, gradeOrientation); // 边界提取与轮廓跟踪
return thresholdingTracker(edgeImage, gradeMagnitude, lowThreshold,
highThreshold);
} /**
* 用高斯滤波器平滑原图像
*
* @param sourceImage
* 输入图像
* @return 高斯滤波后的图像
*/
private static BufferedImage gaussianSmooth(BufferedImage sourceImage)
{
// 高斯模板
float[] elements =
{ 1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f, 2.0f / 16.0f, 4.0f / 16.0f,
2.0f / 16.0f, 1.0f / 16.0f, 2.0f / 16.0f, 1.0f / 16.0f }; BufferedImage bi = new BufferedImage(sourceImage.getWidth(),
sourceImage.getHeight(), BufferedImage.TYPE_INT_RGB);
Graphics2D big = bi.createGraphics();
big.drawImage(sourceImage, 0, 0, null);
// 创建一个Kernel
Kernel kernel = new Kernel(3, 3, elements);
ConvolveOp blur = new ConvolveOp(kernel, ConvolveOp.EDGE_NO_OP, null);
sourceImage = blur.filter(bi, null); return sourceImage;
} /**
* 计算方向导数和梯度的幅度
*
* @param grayImage
* 要计算的图象
* @param gradeMagnitude
* 要在其中存储结果的梯度数组
* @param gradeOrientation
* 要在其中存储结果的方向导数组
*/
private static void grade(BufferedImage grayImage, int[] gradeMagnitude,
int[] gradeOrientation)
{
int height = grayImage.getHeight();
int width = grayImage.getWidth();
int[] srcGray = new int[width * height];
for (int i = 0; i < width; i++)
for (int j = 0; j < height; j++)
srcGray[i + j * width] = 0xff & grayImage.getRGB(i, j); for (int i = 1; i < width - 1; i++)
{
for (int j = 1; j < height - 1; j++)
{
int x = srcGray[(i + 1) + j * width]
- srcGray[(i - 1) + j * width];// X方向导数
int y = srcGray[i + (j + 1) * width]
- srcGray[i + (j - 1) * width];// Y方向导数 gradeMagnitude[i + j * width] = (int) Math.sqrt(x * x + y * y);
// 方向导数在非最大抑制时使用,使用时,只需知道 X 方向导数和 Y 方向导数的同号或异号情况
gradeOrientation[i + j * width] = x * y;
}
} } /**
* 非最大抑制
*
* @param gradeMagnitude
* 梯度数组
* @param gradeOrientation
* 方向数组
* @return 非最大抑制后的边缘数组
*/
private static int[] NonmMaxSuppress(int[] gradeMagnitude,
int[] gradeOrientation)
{
int[] edgeImage = new int[width * height]; for (int i = 1; i < width - 1; i++)
{
for (int j = 1; j < height - 1; j++)
{
if (gradeMagnitude[i + width * j] == 0)
{
edgeImage[i + width * j] = 0;
continue;
}
int n1 = 0, n2 = 0; // 判断 X 方向导数和 Y 方向导数的同号或异号情况
if (gradeOrientation[i + width * j] > 0)
{
n1 = gradeMagnitude[i - 1 + width * (j - 1)];
n2 = gradeMagnitude[i + 1 + width * (j + 1)];
} else
{
n1 = gradeMagnitude[i + 1 + width * (j - 1)];
n2 = gradeMagnitude[i - 1 + width * (j + 1)];
}
// 当前象素的梯度是局部的最大值
// 该点可能是个边界点
if (gradeMagnitude[i + width * j] >= n1
&& gradeMagnitude[i + width * j] >= n2)
{
edgeImage[i + width * j] = 128;
}
// 不可能是边界点
else
{
edgeImage[i + width * j] = 0;
}
}
}
return edgeImage;
} /**
* 边界提取与轮廓跟踪,利用函数寻找边界起点
*
* @param edgeImage
* 边缘数组
* @param gradeMagnitude
* 梯度数组
* @param lowThreshold
* 第一个阈值(低)
* @param highThreshold
* 第二个阈值(高)
* @return 用 Canny 边缘检测算法得到的边缘图
*/
private static BufferedImage thresholdingTracker(int[] edgeImage,
int[] gradeMagnitude, int lowThreshold, int highThreshold)
{
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
{
// 如果该象素是可能的边界点,并且梯度大于高阈值,该象素作为
// 一个边界的起点
if (edgeImage[i + width * j] == 128
&& gradeMagnitude[i + width * j] >= highThreshold)
{
// 把该点设置成为边界点
edgeImage[i + width * j] = 255;
// 以该点为中心进行跟踪
follow(edgeImage, i, j, gradeMagnitude, lowThreshold);
}
}
} BufferedImage destImage = new BufferedImage(width, height,
BufferedImage.TYPE_INT_RGB);
for (int i = 0; i < width; i++)
{
for (int j = 0; j < height; j++)
if (edgeImage[i + width * j] == 255)
destImage.setRGB(i, j, 0xff000000);
else
destImage.setRGB(i, j, -1);
} return destImage;
} /**
* 在8邻近区域里搜索
*
* @param edgeImage
* 边缘数组
* @param x
* 图像元素的x坐标位置
* @param y
* 图像元素的y坐标位置
* @param gradeMagnitude
* 梯度数组
* @param lowThreshold
* 第一个阈值(低)
*/
private static void follow(int[] edgeImage, int x, int y,
int[] gradeMagnitude, int lowThreshold)
{
// 对8邻域象素进行查询
int[][] dir =
{
{ -1, -1 },
{ -1, 0 },
{ -1, 1 },
{ 0, -1 },
{ 0, 1 },
{ 1, -1 },
{ 1, 0 },
{ 1, 1 } };
for (int k = 0; k < 8; k++)
{
int ii = x + dir[k][0];
int jj = y + dir[k][1];
// 如果该象素为可能的边界点,又没有处理过
// 并且梯度大于阈值
if (edgeImage[ii + width * jj] == 128
&& gradeMagnitude[ii + width * jj] >= lowThreshold)
{
// 把该点设置成为边界点
edgeImage[ii + width * jj] = 255;
// 以该点为中心进行跟踪
follow(edgeImage, ii, jj, gradeMagnitude, lowThreshold);
}
}
} /**
* 图片高度
*/
private static int height; /**
* 图片宽度
*/
private static int width; public static void main(String args[])
{
BufferedImage srcImage ,distImage;
srcImage= ImageAPI.getBufferedImageFromFile("test.jpg");
distImage= CannyEdgeDetector.canny(srcImage, 0, 212);
ImageAPI.saveBufferedImagetoFile(distImage, "./distTest.jpg");
System.out.println("aa");
}
}
很好的,就是根据灰度值的变化的幅度来确定边缘