底下有详细代码
一、介绍
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;
}
}