Canny边缘检测首先要对图像进行高斯去噪,前面讲到了高斯去噪处理,这里从对图像灰度进行微分运算讲起吧。微分运算常用的方法是利用模板算子,把模板中心对应到图像的每一个像素位置,然后按照模板对应的公式对中心像素和它周围的像素进行数学运算,算出图像对应像素点的值实验中模板矩阵选取了Laplacian算子 [44] 、Soble算子、Roberts算子。拉普拉兹算子是2阶微分算子,它的精度还算比较高,但对噪声过于敏感,有噪声的情况下效果很差。罗伯特算子在光照不均匀时候效果也很差,针对噪声影响也较为敏感。下面以较为简单的模板作为样例做出讲解:
1、计算x和y方向的梯度值从而得到灰度的梯度幅值和梯度方向
Gx=(hd[x][y+1]-hd[x][y]+hd[x+1][y+1]-hd[x+1][y])/2;
Gy=(hd[x][y]-hd[x+1][y]+hd[x][y+1]-hd[x+1][y+1])/2;
G[x][y]=(int)Math.sqrt(Gy*Gy+Gx*Gx);
angle[x][y]=Math.atan2(Gy,Gx);
2、高低阈值的选取。通常canny算子的高阈值Th和低阈值Tl的0.4,Tl=0.4*Th,而高阈值根据二值化的目的选择不同的值,先验知识通常Th选择方式:梯度幅值矩阵统计在梯度值,将所有梯度累加求和,取在q%(q%在0.75-0.85之间)的那个振幅值作为高阈值。
3、非极大值抑制,这是边缘检测的关键,是将区域内的梯度振幅值的极值当作边缘点,如下图:
对整个梯度振幅图扫描,如图若(x,y)的点大于dTmp1点和dTmp2的振幅则将(x,y)视为预选边缘点,将起值置为255。由图可以看出dTmp1点振幅值可以G(g1) + (1-cot(sigma)) *(G(g2)-G(g1))同理可以得到dTmp2点的梯度振幅值。G这样得到一个预选边缘点矩阵:
int [][] mayEdgeMatrix = getMaxmaiLimitMatrix(Gxy,angle);
4、扫描 mayEdgeMatrix里 所有预选边缘点,将梯度振幅大于等于Th的则视为边缘点置为255;将低于Tl的直接置为0,视为非边缘点;介于Tl、Th之间的的置为125,视为待检测点。这样得到了一个初步的边缘图点。
5、 边缘连接 ,对上一部得到的图像进行扫描,将255周围的8领域点进行检测,若有为125的视为边缘点,置为255,再以这些新置为255的点8领域查找待检测点,若有就将其置为255,直到没有新的边缘点产生为止。
下面给出实现的类,在下面会给出调用的方法和相应的activity
package com.example.lammy.imagetest;
import android.graphics.Bitmap;
import java.util.LinkedList;
/**
* Created by Lammy on 2016/11/12.
*/
public class MyCanny {
private int Th;
private int Tl;
private float ratioOfTh;
private Bitmap bitmap;
private int h, w;
private int [][] Gxy;
private double [][] angle;
private static int mayEdgePointGrayValue = 125 ;
public MyCanny(Bitmap bitmap, float ratioOfTh) {
this .bitmap = bitmap;
this .ratioOfTh = ratioOfTh;
init();
}
private void init() {
h = bitmap.getHeight();
w = bitmap.getWidth();
Gxy = new int [h][w];
angle = new double [h][w];
}
// 得到高斯模板矩阵
public float [][] get2DKernalData( int n, float sigma) {
int size = 2 * n + 1 ;
float sigma22 = 2 * sigma * sigma;
float sigma22PI = ( float ) Math.PI * sigma22;
float [][] kernalData = new float [size][size];
int row = 0 ;
for ( int i = -n; i <= n; i++ ) {
int column = 0 ;
for ( int j = -n; j <= n; j++ ) {
float xDistance = i * i;
float yDistance = j * j;
kernalData[row][column] = ( float ) Math
.exp( -(xDistance + yDistance) / sigma22) / sigma22PI;
column ++ ;
}
row ++ ;
}
return kernalData;
}
// 获得图的灰度矩阵
public int [][] getGrayMatrix(Bitmap bitmap) {
int h = bitmap.getHeight();
int w = bitmap.getWidth();
int grayMatrix[][] = new int [h][w];
for ( int i = 0; i < h; i++ )
for ( int j = 0; j < w; j++ ) {
int argb = bitmap.getPixel(j, i);
int r = (argb >> 16) & 0xFF ;
int g = (argb >> 8) & 0xFF ;
int b = (argb >> 0) & 0xFF ;
int grayPixel = ( int ) (r + g + b) / 3 ;
grayMatrix[i][j] = grayPixel;
}
return grayMatrix;
}
// 获得高斯模糊后的灰度矩阵
public int [][] GS( int [][] hd, int size, float sigma) {
float [][] gs = get2DKernalData(size, sigma);
int outmax = 0 ;
int inmax = 0 ;
for ( int x = size; x < w - size; x++ )
for ( int y = size; y < h - size; y++ ) {
float hc1 = 0 ;
if (hd[y][x] > inmax)
inmax = hd[y][x];
for ( int k = -size; k < size + 1; k++ )
for ( int j = -size; j < size + 1; j++ ) {
hc1 = gs[size + k][j + size] * hd[y + j][x + k] + hc1;
}
hd[y][x] = ( int ) (hc1);
if (outmax < hc1)
outmax = ( int ) (hc1);
}
float rate = inmax / outmax;
for ( int x = size; x < w - size; x++ )
for ( int y = size; y < h - size; y++ ) {
hd[y][x] = ( int ) (hd[y][x] * rate);
}
return hd;
}
// 获得Gxy 和angle即梯度振幅和梯度方向
public void getGxyAndAngle( int [][] Gs) {
for ( int x = 1; x < h - 1; x++ )
for ( int y = 1; y < w - 1; y++ ) {
int Gx = (Gs[x][y + 1] - Gs[x][y] + Gs[x + 1][y + 1] - Gs[x + 1][y]) / 2; // hd[x][y+1]-hd[x][y]; //
int Gy = (Gs[x][y] - Gs[x + 1][y] + Gs[x][y + 1] - Gs[x + 1][y + 1]) / 2; // hd[x+1][y]-hd[x][y]; //
// 另外一种算子
// int Gx = (Gs[x - 1][y + 1] + 2 * Gs[x][y + 1]
// + Gs[x + 1][y + 1] - Gs[x - 1][y - 1] - 2
// * Gs[x][y - 1] - Gs[x + 1][y - 1]) / 4;
// int Gy=(Gs[x-1][y-1]+2*Gs[x-1][y]+Gs[x-1][y+1]-Gs[x+1][y-1]-2*Gs[x+1][y]-Gs[x+1][y+1])/4;
// G[x][y]=Math.sqrt(Math.pow(Gx, 2)+Math.pow(Gy, 2));
Gxy[x][y] = ( int ) Math.sqrt(Gy * Gy + Gx * Gx);
angle[x][y] = Math.atan2(Gy, Gx);
// 将梯度方向值转向(0,2*PI)
if (angle[x][y] < 0 ) {
angle[x][y] = angle[x][y] + 2 * Math.PI;
}
}
}
// 非极大值抑制,将极值点存到edge边缘矩阵中,极值点是可能为边缘的点
public int [][] getMaxmaiLimitMatrix( int [][]Gxy, double [][]angle) {
int [][] edge = new int [h][w];
for ( int x = 0; x < h - 1; x++ )
for ( int y = 0; y < w - 1; y++ ) {
double angle1 = angle[x][y] / (Math.PI);
if ((angle1 > 0 && angle1 <= 0.25) | (angle1 > 1 && angle1 <= 1.25 )) {
double dTmp1 = Gxy[x][y + 1] + Math.abs(Math.tan(angle[x][y]) * (Gxy[x - 1][y + 1] - Gxy[x][y + 1 ]));
double dTmp2 = Gxy[x][y - 1] + Math.abs(Math.tan(angle[x][y]) * (Gxy[x + 1][y - 1] - Gxy[x][y - 1 ]));
double dTmp = Gxy[x][y];
if (dTmp > dTmp1 && dTmp > dTmp2)
edge[x][y] = 255 ;
}
if ((angle1 <= 2 && angle1 > 1.75) | (angle1 <= 1 && angle1 > 0.75 )) {
double dTmp1 = Gxy[x][y + 1] + Math.abs(Math.tan(angle[x][y])) * (Gxy[x + 1][y + 1] - Gxy[x][y + 1 ]);
double dTmp2 = Gxy[x][y - 1] + Math.abs(Math.tan(angle[x][y])) * (Gxy[x - 1][y - 1] - Gxy[x][y - 1 ]);
double dTmp = Gxy[x][y];
if (dTmp > dTmp1 && dTmp > dTmp2)
edge[x][y] = 255 ;
}
if ((angle1 > 1 / 4 && angle1 <= 0.5) | (angle1 > 5 / 4 && angle1 <= 1.5 )) {
double dTmp1 = Gxy[x - 1][y] + Math.abs(1 / Math.tan(angle[x][y])) * (Gxy[x - 1][y + 1] - Gxy[x - 1 ][y]);
double dTmp2 = Gxy[x + 1][y] + Math.abs(1 / Math.tan(angle[x][y])) * (Gxy[x + 1][y - 1] - Gxy[x + 1 ][y]);
double dTmp = Gxy[x][y];
if (dTmp > dTmp1 && dTmp > dTmp2)
edge[x][y] = 255 ;
}
if ((angle1 > 1.5 && angle1 <= 1.75) | (angle1 > 0.5 && angle1 <= 0.75 )) {
double dTmp1 = Gxy[x - 1][y] + Math.abs(1 / Math.tan(angle[x][y])) * (Gxy[x - 1][y - 1] - Gxy[x - 1 ][y]);
double dTmp2 = Gxy[x + 1][y] + Math.abs(1 / Math.tan(angle[x][y])) * (Gxy[x + 1][y + 1] - Gxy[x + 1 ][y]);
double dTmp = Gxy[x][y];
if (dTmp > dTmp1 && dTmp > dTmp2)
edge[x][y] = 255 ;
}
}
return edge;
}
public void ThTlLimitPoints( int [][] maxmaiLimitMatrix, int Th , int Tl)
{
// 上面得到的为255的才可能是边缘点,下面根据高低阈值再次去掉小于Tl点,高于Th的仍然为255,定为边缘点,125的为预选点
for ( int x=1;x<h-1;x++ )
for ( int y=1;y<w-1;y++ )
{
if (maxmaiLimitMatrix[x][y]==255 )
{
if (Gxy[x][y]< Tl)
maxmaiLimitMatrix[x][y] =0 ;
if (Gxy[x][y]>Tl&&Gxy[x][y]< Th)
maxmaiLimitMatrix[x][y] = mayEdgePointGrayValue;
}
}
}
// 获得高阈值
private int getTh( int [][] Gxy)
{
// 梯度振幅统计,因为通过计算振幅的最大值不超过500,因此用500的矩阵统计
int []amplitudeStatistics= new int [500 ];
for ( int x=1;x<h-1;x++ )
for ( int y=1;y<w-1;y++ ){
amplitudeStatistics[Gxy[x][y]] ++ ;
}
int pointNumber=0 ;
int max=0 ;
for ( int i=1;i<500;i++ ){
if (amplitudeStatistics[i]>0 )
{
max = i;
}
pointNumber =pointNumber+ amplitudeStatistics[i];
}
int ThNumber=( int )(ratioOfTh* pointNumber);
int ThCount=0; int Th=0 ;
for ( int i=1;i<=max;i++ )
{
if (ThCount< ThNumber)
ThCount =ThCount+ amplitudeStatistics[i];
else
{
Th =i-1 ;
break ;
}
}
return Th;
}
private int getTl( int Th)
{
return ( int )(Th*0.4 );
}
// canny算法的边缘连接
public void traceEdge( double maybeEdgePointGrayValue, int edge[][]){
int [][]liantongbiaoji = new int [h][w];
for ( int i = 0 ; i < h ; i++ )
for ( int j = 0 ; j < w; j++ ) {
if (edge[i][j]==255&&liantongbiaoji[i][j]==0 ) {
if ((edge[i][j] >= maybeEdgePointGrayValue) && liantongbiaoji[i][j] == 0 ) {
liantongbiaoji[i][j] = 1 ;
LinkedList <Point> qu = new LinkedList<Point> ();
qu.add( new Point(i, j));
while (! qu.isEmpty()) {
Point cur = qu.removeFirst();
for ( int a = -1; a <= 1; a++ )
for ( int b = -1; b <= 1; b++ ) {
if (cur.x + a >= 0 && cur.x + a < h && cur.y + b >= 0
&& cur.y + b < w) {
if (edge[cur.x + a][cur.y + b] >= maybeEdgePointGrayValue
&& liantongbiaoji[cur.x + a][cur.y + b] == 0 ) {
qu.add( new Point(cur.x + a, cur.y + b));
liantongbiaoji[cur.x + a][cur.y + b] = 1 ;
edge[cur.x + a][cur.y + b] = 255 ;
}
}
}
}
}
}
}
}
// 由灰度矩阵创建灰度图
public Bitmap createGrayImage( int [][]grayMatrix)
{
int h= grayMatrix.length;
int w = grayMatrix[0 ].length;
Bitmap bt = Bitmap.createBitmap(w, h, Bitmap.Config.ARGB_8888);
for ( int i=0;i<h;i++ )
for ( int j=0;j<w;j++ )
{
int grayValue= grayMatrix[i][j];
int color = ((0xFF << 24)+(grayValue << 16)+(grayValue << 8)+ grayValue);
bt.setPixel(j, i, color);
}
return bt;
}
public Bitmap getEdgeBitmap()
{
int grayMatrix[][] = getGrayMatrix(bitmap);
int GS[][] = GS(grayMatrix , 1 , 0.6f );
getGxyAndAngle(GS);
Th = getTh(Gxy);
int [][] mayEdgeMatrix = getMaxmaiLimitMatrix(Gxy,angle);
Tl = getTl(Th);
ThTlLimitPoints(mayEdgeMatrix , Th , Tl);
traceEdge(mayEdgePointGrayValue , mayEdgeMatrix);
for ( int x=1;x<h-1;x++ )
for ( int y=1;y<w-1;y++ ) {
if (mayEdgeMatrix[x][y]!=255 )
mayEdgeMatrix[x][y] =0 ;
}
return createGrayImage(mayEdgeMatrix);
}
class Point {
Point( int a, int b) {
this .x = a;
this .y = b;
}
int x;
int y;
}
}
View Code
查看更多关于canny算法的实现(android加载图片,数组写入文件换行)的详细内容...