图像边缘检测 Laplacian边缘检测

时间:2024-11-11 08:00:48

底下有详细代码

一、介绍

1、图像检测的原理。

        图像检测的原理是检测相邻的几个点像素值之间的变化率,相对于对函数求导。求点P(x,y)的变换率,可以在点P周围选取一些点,求x方向的距离Gx,再求y方向上的距离Gy。最后变换率G等于Gx平方加上Gy平方的和的平方差,即G=(Gx^2+Gy^2)。

2、Laplacian算子。

        拉普拉斯算子对噪声敏感。对图像中的阶跃性边缘点定位正确,对噪声十分的敏感,会丢失一部分边缘的方向信息,造成一些不连续的检测边缘。Laplacian 算子是n维欧几里德空间中的一个二阶微分算子。

3、Laplacian算子模版有两个。

(1)V1

        G^2=f(x-1,y)+f(x+1,y)+f(x,y-1)+f(x,y+1)-4f(x,y)
        G^2=[0  1  0]
                [1 -4  1]
                [0  1  0]

(2)V2
        G^2=f(x-1,y-1)+f(x,y-1)+f(x+1,y-1)+f(x-1,y)+f(x+1,y)+f(x-1,y+1)+f(x,y+1)+f(x+1,y+1)-8f(x,y)
        G^2=[1  1  1]
                [1 -8  1]
                [1  1  1]

二、Laplacian模版V1

1、主流程代码。

package ;

import ;
import ;
import ;

import ;
import ;
import ;

/**
 * 图像边缘检测
 */
public class EdgeDetectionTest {
    public static void main(String[] args) {
        laplacianTest();
    }

    private static void laplacianTest() {
        String path = "G:\\xiaojie-java-test\\img\\边缘检测\\";
        String sourcePath = path + "Lena灰度图.jpg";

        //laplacianV1
        String targetPath1 = path + "";
        laplacianV1(sourcePath, targetPath1);
        //二值化
        (targetPath1, path + "", 96);
        (targetPath1, path + "", 64);
        (targetPath1, path + "", 32);
    }

    private static void laplacianV1(String sourcePath, String targetPath) {
        try {
            //获取原图像对象,并获取原图像的二维数组
            BufferedImage image = (new File(sourcePath));
            int[][] imgArrays = (image);
            //生成新图像的二维数组
            int[][] newImgArrays = EdgeDetectionUtils.laplacianV1(imgArrays);
            //生成新图片对象,填充像素
            BufferedImage newImage = new BufferedImage((), (), BufferedImage.TYPE_INT_RGB);
            (newImage, newImgArrays);
            //生成图片文件
            (newImage, "JPEG", new File(targetPath));
            (1);
        } catch (Exception e) {
            ();
        }
    }
}

2、核心代码。

package ;

/**
 * 图像边缘检测
 * G = (Gx^2 + Gy^2)
 * angle = arctan(Gy / Gx)
 */
public class EdgeDetectionUtils {

    /**
     * 边缘检测--Laplacian--v1--噪声敏感
     * G^2=f(x-1,y)+f(x+1,y)+f(x,y-1)+f(x,y+1)-4f(x,y)
     * G^2=[0  1  0]
     * ----[1 -4  1]
     * ----[0  1  0]
     */
    public static int[][] laplacianV1(int[][] array) {
        int row = ;
        int column = array[0].length;
        int[][] newArray = new int[row][column];
        //注意:x = j = column , y = i = row
        for (int i = 1; i < row - 1; i++) {//图片第几行
            for (int j = 1; j < column - 1; j++) {//图片第几列
                //G^2=f(x-1,y)+f(x+1,y)+f(x,y-1)+f(x,y+1)-4f(x,y)
                int sum = array[i][j - 1] + array[i][j + 1] + array[i - 1][j] + array[i + 1][j] - 4 * array[i][j];
                newArray[i][j] = (int) ((sum));
            }
        }
        return (newArray);
    }
}

3、结果。

(1)原图和结果。

 

(2)结果二值化,阈值分别为32、64、96。

(3)截图。

三、Laplacian模版V2

1、主流程代码。

package ;

import ;
import ;
import ;

import ;
import ;
import ;

/**
 * 图像边缘检测
 */
public class EdgeDetectionTest {
    public static void main(String[] args) {
        laplacianTest();
    }

    private static void laplacianTest() {
        String path = "G:\\xiaojie-java-test\\img\\边缘检测\\";
        String sourcePath = path + "Lena灰度图.jpg";

        //laplacianV2
        String targetPath2 = path + "";
        laplacianV2(sourcePath, targetPath2);
        //二值化
        (targetPath2, path + "", 96);
        (targetPath2, path + "", 64);
        (targetPath2, path + "", 32);
    }

    private static void laplacianV2(String sourcePath, String targetPath) {
        try {
            //获取原图像对象,并获取原图像的二维数组
            BufferedImage image = (new File(sourcePath));
            int[][] imgArrays = (image);
            //生成新图像的二维数组
            int[][] newImgArrays = EdgeDetectionUtils.laplacianV2(imgArrays);
            //生成新图片对象,填充像素
            BufferedImage newImage = new BufferedImage((), (), BufferedImage.TYPE_INT_RGB);
            (newImage, newImgArrays);
            //生成图片文件
            (newImage, "JPEG", new File(targetPath));
            (1);
        } catch (Exception e) {
            ();
        }
    }
}

2、核心代码。

package ;

/**
 * 图像边缘检测
 * G = (Gx^2 + Gy^2)
 * angle = arctan(Gy / Gx)
 */
public class EdgeDetectionUtils {
    /**
     * 边缘检测--Laplacian--v2--噪声敏感
     * G^2=f(x-1,y-1)+f(x,y-1)+f(x+1,y-1)+f(x-1,y)+f(x+1,y)+f(x-1,y+1)+f(x,y+1)+f(x+1,y+1)-8f(x,y)
     * G^2=[1  1  1]
     * ----[1 -8  1]
     * ----[1  1  1]
     */
    public static int[][] laplacianV2(int[][] array) {
        int row = ;
        int column = array[0].length;
        int[][] newArray = new int[row][column];
        //注意:x = j = column , y = i = row
        for (int i = 1; i < row - 1; i++) {//图片第几行
            for (int j = 1; j < column - 1; j++) {//图片第几列
                //G^2=f(x-1,y-1)+f(x,y-1)+f(x+1,y-1)+f(x-1,y)-8f(x,y)+f(x+1,y)+f(x-1,y+1)+f(x,y+1)+f(x+1,y+1)
                int sum = array[i - 1][j - 1] + array[i - 1][j] + array[i - 1][j + 1] +
                        array[i][j - 1] - 8 * array[i][j] + array[i][j + 1] +
                        array[i + 1][j - 1] + array[i + 1][j] + array[i + 1][j + 1];
                newArray[i][j] = (int) ((sum));
            }
        }
        return (newArray);
    }
}

3、结果。

(1)原图和结果。

(2)结果二值化,阈值分别为32、64、96。

(3)截图。

四、详细代码

1、。

package ;

import ;

public class FilterUtils {
    /**
     * 中值滤波
     *
     * @param imgArrays    图像二维数组
     * @param filterLength 过滤的长度(阈值)
     */
    public static int[][] getMiddleFilter(int[][] imgArrays, int filterLength) {
        final int imgHeight = ;
        final int imgWidth = imgArrays[0].length;
        int[][] newImgArrays = new int[imgHeight][imgWidth];
        //模版半径
        final int filterRadius = (filterLength - 1) / 2;
        final int filterSize = filterLength * filterLength;
        //获取数据
        for (int h = 0; h < imgHeight; h++) {//图片第几行
            for (int w = 0; w < imgWidth; w++) {//图片第几列
                int count = 0;
                int[] templateArray = new int[filterSize];
                for (int templateH = -filterRadius; templateH <= filterRadius; templateH++) {//模版第几行
                    int rowIndex = h + templateH;
                    if (rowIndex < 0 || rowIndex > imgHeight - 1) {
                        continue;
                    }
                    for (int templateW = -filterRadius; templateW <= filterRadius; templateW++) {//模版第几列
                        int columnIndex = w + templateW;
                        if (columnIndex < 0 || columnIndex > imgWidth - 1) {
                            continue;
                        }
                        templateArray[count++] = imgArrays[rowIndex][columnIndex];
                    }
                }
                //
                int[] newTemplateArray;
                if (count != ) {
                    newTemplateArray = new int[count];
                    for (int i = 0; i < count; i++) {
                        newTemplateArray[i] = templateArray[i];
                    }
                    (newTemplateArray);//计数排序
                    newImgArrays[h][w] = newTemplateArray[count / 2];
                } else {
                    (templateArray);//计数排序
                    newImgArrays[h][w] = templateArray[filterRadius];
                }
            }
        }
        return newImgArrays;
    }

    /**
     * 均值滤波
     *
     * @param imgArrays    图像二维数组
     * @param filterLength 过滤的长度(阈值)
     */
    public static int[][] getAverageFilter(int[][] imgArrays, int filterLength) {
        final int imgHeight = ;
        final int imgWidth = imgArrays[0].length;
        int[][] newImgArrays = new int[imgHeight][imgWidth];
        //模版半径
        final int filterRadius = (filterLength - 1) / 2;
        //获取数据
        for (int h = 0; h < imgHeight; h++) {//图片第几行
            for (int w = 0; w < imgWidth; w++) {//图片第几列
                int count = 0;
                int sum = 0;
                for (int templateH = -filterRadius; templateH <= filterRadius; templateH++) {//模版第几行
                    int rowIndex = h + templateH;
                    if (rowIndex < 0 || rowIndex > imgHeight - 1) {
                        continue;
                    }
                    for (int templateW = -filterRadius; templateW < filterRadius; templateW++) {//模版第几列
                        int columnIndex = w + templateW;
                        if (columnIndex < 0 || columnIndex > imgWidth - 1) {
                            continue;
                        }
                        sum += imgArrays[rowIndex][columnIndex];
                        count++;
                    }
                }
                newImgArrays[h][w] = sum / count;
            }
        }
        return newImgArrays;
    }

    /**
     * 获取高斯滤波模版的二维数组
     *
     * @param length 模版长度,length=6sigma(%99.74), length=4sigma(95%), length=2sigma(68%)
     * @param sigma  模版标准差,length=6sigma(%99.74), length=4sigma(95%), length=2sigma(68%)
     */
    public static double[][] getGaussTemplate(int length, double sigma) {
        double[][] gaussTemplate = new double[length][length];    // 用于存储结果
        int centerIndex = (length - 1) / 2;//模版的中心点
        double variance = sigma * sigma;//方差
        double sum = 0;
        for (int i = 0; i < length; ++i) {
            for (int j = 0; j < length; ++j) {
                int xLength = j - centerIndex;//x上的距离
                int yLength = i - centerIndex;//y上的距离
                double e = (-(xLength * xLength + yLength * yLength) / (2 * variance));
                gaussTemplate[i][j] = e / (2 *  * variance);
                sum += gaussTemplate[i][j];
            }
        }
        //占得比重
        for (int i = 0; i < length; ++i) {
            for (int j = 0; j < length; ++j) {
                gaussTemplate[i][j] = gaussTemplate[i][j] / sum;
            }
        }
//        //打印模版
//        (("生成大小为%d,标准差为%.3f的二维高斯模版:", length, sigma));
//        for (double[] doubles : gaussTemplate) {
//            for (int j = 0; j < ; j++) {
//                (("%8.3f", doubles[j]));
//            }
//            ();
//        }
        return gaussTemplate;
    }

    /**
     * 高斯滤波
     *
     * @param imgArrays     图像二维数组
     * @param gaussTemplate 高斯滤波模版的二维数组
     */
    public static int[][] getGaussFilter(int[][] imgArrays, double[][] gaussTemplate) {
        final int imgHeight = ;
        final int imgWidth = imgArrays[0].length;
        int[][] newImgArrays = new int[imgHeight][imgWidth];
        //模版长度和半径
        final int filterLength = ;
        final int filterRadius = (filterLength - 1) / 2;
        //高斯过滤
        for (int h = 0; h < imgHeight; h++) {//图片第几行
            for (int w = 0; w < imgWidth; w++) {//图片第几列
                double weightTotal = 0;
                double sum = 0;
                for (int templateH = -filterRadius; templateH <= filterRadius; templateH++) {//模版第几行
                    int rowIndex = h + templateH;
                    if (rowIndex < 0 || rowIndex > imgHeight - 1) {
                        continue;
                    }
                    for (int templateW = -filterRadius; templateW < filterRadius; templateW++) {//模版第几列
                        int columnIndex = w + templateW;
                        if (columnIndex < 0 || columnIndex > imgWidth - 1) {
                            continue;
                        }
                        double weight = gaussTemplate[templateH + filterRadius][templateW + filterRadius];
                        sum += (imgArrays[rowIndex][columnIndex] * weight);
                        weightTotal += weight;
                    }
                }
                newImgArrays[h][w] = (int) (sum / weightTotal);
            }
        }
        return newImgArrays;
    }
}

2、。

package ;

import ;
import ;
import ;

public class ImageService {
    /**
     * 图片灰度化的方法
     *
     * @param sourcePath 源图片路径
     * @param targetPath 目标图片路径
     * @param grayType   灰度化方法
     */
    public static void toGrayImg(String sourcePath, String targetPath, int grayType) {
        try {
            //获取原图像对象,并获取原图像的二维数组
            BufferedImage image = (new File(sourcePath));
            int[][] imgArrays = (image);
            //生成新图像的二维数组
            int[][] newImgArrays = (imgArrays, grayType);//灰度化
            //生成新图片对象,填充像素
            BufferedImage newImage = new BufferedImage((), (), BufferedImage.TYPE_INT_RGB);
            (newImage, newImgArrays);
            //生成图片文件
            (newImage, "JPEG", new File(targetPath));
            (1);
        } catch (Exception e) {
            ();
        }
    }

    /**
     * 生成不同rgb通道图片的方法
     *
     * @param sourcePath   源图片路径
     * @param targetPath   rgb通道图片路径
     * @param channelColor r、g、b颜色通道
     */
    public static void toChannelImg(String sourcePath, String targetPath, int channelColor) {
        try {
            //获取原图像对象,并获取原图像的二维数组
            BufferedImage image = (new File(sourcePath));
            int[][] imgArrays = (image);
            //生成新图像的二维数组
            int[][] newImgArrays = (imgArrays, channelColor);
            //生成新图片对象,填充像素
            BufferedImage newImage = new BufferedImage((), (), BufferedImage.TYPE_INT_RGB);
            (newImage, newImgArrays);
            //生成图片文件
            (newImage, "JPEG", new File(targetPath));
            (1);
        } catch (Exception e) {
            ();
        }
    }

    /**
     * 二值化
     *
     * @param sourcePath 源图片路径
     * @param targetPath 目标二值图片路径
     * @param threshold  阈值
     */
    public static void toBinaryImg(String sourcePath, String targetPath, int threshold) {
        try {
            //获取原图像对象,并获取原图像的二维数组
            BufferedImage image = (new File(sourcePath));
            int[][] imgArrays = (image);
            //生成新图像的二维数组
            int[][] newImgArrays = (imgArrays, threshold);//二值化
            //生成新图片对象,填充像素
            BufferedImage newImage = new BufferedImage((), (), BufferedImage.TYPE_BYTE_GRAY);
            (newImage, newImgArrays);
            //生成图片文件
            (newImage, "JPEG", new File(targetPath));
            (1);
        } catch (Exception e) {
            ();
        }
    }
}

3、。

package ;

import ;

/**
 * 要用int,不能使用byte,因为byte最大值为128
 */
public class ImageUtils {
    /**
     * 灰度处理的方法
     */
    public static final byte Gray_Type_Min = 1;//最大值法
    public static final byte Gray_Type_Max = 2;//最小值法
    public static final byte Gray_Type_Average = 3;//平均值法
    public static final byte Gray_Type_Weight = 4;//加权法
    public static final byte Gray_Type_Red = 5;//红色值法
    public static final byte Gray_Type_Green = 6;//绿色值法
    public static final byte Gray_Type_Blue = 7;//蓝色值法
    public static final byte Gray_Type_Default = Gray_Type_Weight;//默认加权法

    /**
     * 不同颜色通道的图片
     */
    public static final byte Channel_Color_Red = 1;
    public static final byte Channel_Color_Green = 2;
    public static final byte Channel_Color_Blue = 3;

    /**
     * 灰度化处理,彩色int[][] 转 灰度byte[][]
     *
     * @param imgArrays 图像二维数组
     * @param grayType  灰度化方法
     */
    public static int[][] grayProcess(int[][] imgArrays, int grayType) throws Exception {
        int[][] newImgArrays = new int[][imgArrays[0].length];
        for (int h = 0; h < ; h++)
            for (int w = 0; w < imgArrays[0].length; w++)
                newImgArrays[h][w] = getImageGray(getImageRgb(imgArrays[h][w]), grayType);
        return newImgArrays;
    }

    /**
     * 颜色通道处理,彩色int[][] 转 彩色int[][]
     *
     * @param imgArrays    图像二维数组
     * @param channelColor 不同颜色通道
     */
    public static int[][] channelProcess(int[][] imgArrays, int channelColor) {
        int[][] newImgArrays = new int[][imgArrays[0].length];
        for (int h = 0; h < ; h++) {
            for (int w = 0; w < imgArrays[0].length; w++) {
                final int pixel = imgArrays[h][w];
                if (channelColor == Channel_Color_Red) {
                    newImgArrays[h][w] = pixel & 0xff0000;
                } else if (channelColor == Channel_Color_Green) {
                    newImgArrays[h][w] = pixel & 0x00ff00;
                } else if (channelColor == Channel_Color_Blue) {
                    newImgArrays[h][w] = pixel & 0x0000ff;
                }
            }
        }
        return newImgArrays;
    }

    /**
     * 二值化处理,灰度byte[][] 转 二值byte[][]
     *
     * @param imgArrays 灰度 int[][]
     * @param threshold 阈值
     */
    public static int[][] binaryProcess(int[][] imgArrays, int threshold) {
        int[][] newImgArrays = new int[][imgArrays[0].length];
        for (int h = 0; h < ; h++)
            for (int w = 0; w < imgArrays[0].length; w++) {
                newImgArrays[h][w] = (imgArrays[h][w] < threshold ? 0 : 0xff);
            }
        return newImgArrays;
    }

    /**
     * 根据像素,返回r、g、b 的 byte[]
     *
     * @param pixel 像素值
     */
    private static int[] getImageRgb(int pixel) {
        int[] rgb = new int[3];
        rgb[0] = ((pixel >> 16) & 0xff);
        rgb[1] = ((pixel >> 8) & 0xff);
        rgb[2] = (pixel & 0xff);
        return rgb;
    }

    /**
     * 获取像素值
     *
     * @param pixel 像素值
     */
    public static long getPixel(int pixel) {
        return (pixel & 0xff) + (pixel & 0xff00) + (pixel & 0xff0000);
    }

    /**
     * 获取像素值
     *
     * @param rgb r、g、b 的 byte[]
     */
    public static long getPixel(int[] rgb) {
        return (rgb[0] << 16) + (rgb[1] << 8) + rgb[2];
    }

    /**
     * 根据r、g、b 的 byte[],返回灰度值
     *
     * @param rgb      r、g、b颜色通道的值
     * @param grayType 不同灰度处理的方法
     */
    private static int getImageGray(int[] rgb, int grayType) throws Exception {
        if (grayType == Gray_Type_Average) {
            return ((rgb[0] + rgb[1] + rgb[2]) / 3);   //rgb之和除以3
        } else if (grayType == Gray_Type_Weight) {
            return (int) (0.3 * rgb[0] + 0.59 * rgb[1] + 0.11 * rgb[2]);
        } else if (grayType == Gray_Type_Red) {
            return rgb[0];//取红色值
        } else if (grayType == Gray_Type_Green) {
            return rgb[1];//取绿色值
        } else if (grayType == Gray_Type_Blue) {
            return rgb[2];//取蓝色值
        }
        //比较三个数的大小
        int gray = rgb[0];
        for (int i = 1; i < ; i++) {
            if (grayType == Gray_Type_Min) {
                if (gray > rgb[i]) {
                    gray = rgb[i];//取最小值
                }
            } else if (grayType == Gray_Type_Max) {
                if (gray < rgb[i]) {
                    gray = rgb[i];//取最大值
                }
            } else {
                throw new Exception("grayType出错");
            }
        }
        return gray;
    }

    /**
     * 获取图像像素 byte[][] rgb值
     *
     * @param image BufferedImage图像对象
     */
    public static int[][] getImageRgb(BufferedImage image) {
        int[][] imgArrays = new int[()][()];
        for (int i = 0; i < (); i++)
            for (int j = 0; j < (); j++)
                imgArrays[i][j] = (j, i);
        return imgArrays;
    }

    /**
     * 获取图像像素 byte[][] 灰度值
     *
     * @param image BufferedImage图像对象
     */
    public static int[][] getImageGray(BufferedImage image) throws Exception {
        return grayProcess(getImageRgb(image), Gray_Type_Default);
    }

    /**
     * 获取图像像素 byte[][] 二值
     *
     * @param image BufferedImage图像对象
     */
    public static int[][] getImageBinary(BufferedImage image) throws Exception {
        return binaryProcess(grayProcess(getImageRgb(image), Gray_Type_Default), 0xff / 2);
    }

    /**
     * 图像像素填充 byte[][]
     *
     * @param image     BufferedImage图像对象
     * @param imgArrays 二维像素
     */
    public static void setImageBytes(BufferedImage image, int[][] imgArrays) {
        for (int i = 0; i < (); i++)
            for (int j = 0; j < (); j++)
                (j, i, (byte) imgArrays[i][j]);
    }

    /**
     * 图像像素填充 byte[][]
     *
     * @param image     BufferedImage图像对象
     * @param imgArrays 二维像素
     */
    public static void setImageBytes(BufferedImage image, byte[][] imgArrays) {
        for (int i = 0; i < (); i++)
            for (int j = 0; j < (); j++)
                (j, i, imgArrays[i][j]);
    }

    /**
     * 图像像素填充 int[][]
     *
     * @param image     BufferedImage图像对象
     * @param imgArrays 二维像素
     */
    public static void setImageRgb(BufferedImage image, int[][] imgArrays) {
        for (int i = 0; i < (); i++)
            for (int j = 0; j < (); j++)
                (j, i, imgArrays[i][j]);
    }

    /**
     * 图像像素填充 将 byte[][] 变为 int[][] 进行填充
     *
     * @param image     BufferedImage图像对象
     * @param imgArrays 二维像素
     */
    public static void setImageRgbByByte(BufferedImage image, int[][] imgArrays) {
        for (int i = 0; i < (); i++)
            for (int j = 0; j < (); j++)
                (j, i, imgArrays[i][j] + (imgArrays[i][j] << 8) + (imgArrays[i][j] << 16));
    }

    /**
     * @param arrays 将数组的值域分布到0--0xff之间
     */
    public static int[][] rangeToByte(int[][] arrays) {
        int max = arrays[0][0], min = arrays[0][0];
        for (int[] array : arrays) {
            for (int value : array) {
                if (value > max) {
                    max = value;
                } else if (value < min) {
                    min = value;
                }
            }
        }
        //
        int[][] newArrays = new int[][];
        int range = max - min + 1;
        double multiply = (0xff + 1.0) / range;
        for (int i = 0; i < ; i++) {
            int[] array = arrays[i];
            int[] newArray = new int[];
            for (int j = 0; j < ; j++) {
                int value = (int) ((array[j] - min) * multiply);
                newArray[j] = value > 0xff ? 0xff : value;
            }
            newArrays[i] = newArray;
        }
        return newArrays;
    }
}