1、選擇一張椒鹽噪聲比較明顯的圖片。椒鹽噪聲,椒鹽噪聲又稱脈衝噪聲,它隨機改變一些像素值,是由圖像傳感器,傳輸信道,解碼處理等產生的黑白相間的亮暗點噪聲。椒鹽噪聲往往由圖像切割引起。
2、代碼。生成模版半徑分別是3、5和7的圖片。
package com.zxj.reptile.test.mnist;
import com.zxj.reptile.utils.image.ImageService;
import com.zxj.reptile.utils.image.ImageUtils;
public class ImgTest {
private static final String File_Path = "G:\\xiaojie-java-test\\img\\";
public static void main(String[] args) {
//中值濾波
String gaussSourcePath = File_Path + "濾波\\椒鹽濾波\\椒鹽噪聲.jpg";
String averageTargetPath = File_Path + "濾波\\椒鹽濾波\\椒鹽濾波_";
ImageService.middleFilter(gaussSourcePath, averageTargetPath, ImageUtils.Gray_Type_Default, 3);//中值過濾
ImageService.middleFilter(gaussSourcePath, averageTargetPath, ImageUtils.Gray_Type_Default, 5);//中值過濾
ImageService.middleFilter(gaussSourcePath, averageTargetPath, ImageUtils.Gray_Type_Default, 7);//中值過濾
}
}
package com.zxj.reptile.utils.image;
import javax.imageio.ImageIO;
import java.awt.image.BufferedImage;
import java.io.File;
public class ImageService {
/**
* 中值過濾
*
* @param sourcePath 原圖像
* @param targetPath 目標圖像
* @param grayType 灰度化方式
* @param filterLength 過濾的長度(閾值)
*/
public static void middleFilter(String sourcePath, String targetPath, int grayType, int filterLength) {
try {
//獲取原圖像對象,並獲取原圖像的二維數組
BufferedImage image = ImageIO.read(new File(sourcePath));
int[][] imgArrays = ImageUtils.getTwoDimension(image);
//生成新圖像的二維數組
imgArrays = ImageUtils.getGrayImg(imgArrays, grayType);//灰度化
int[][] newImgArrays = ImageUtils.getMiddleFilter(imgArrays, filterLength);//中值過濾
//生成新圖片對象,填充像素
BufferedImage newImage = new BufferedImage(image.getWidth(), image.getHeight(), BufferedImage.TYPE_BYTE_GRAY);
ImageUtils.setTwoDimension(newImage, newImgArrays, ImageUtils.Channel_Type_1);
//生成圖片文件
ImageIO.write(newImage, "JPEG", new File(targetPath + filterLength + ".jpg"));
Thread.sleep(1);
} catch (Exception e) {
e.printStackTrace();
}
}
}
package com.zxj.reptile.utils.image;
import com.zxj.reptile.utils.algorithm.SortUtils;
import java.awt.image.BufferedImage;
public class ImageUtils {
/**
* 中值濾波
*
* @param imgArrays 圖像二維數組
* @param filterLength 過濾的長度(閾值)
*/
public static int[][] getMiddleFilter(int[][] imgArrays, int filterLength) {
final int imgHeight = imgArrays.length;
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 != templateArray.length) {
newTemplateArray = new int[count];
for (int i = 0; i < count; i++) {
newTemplateArray[i] = templateArray[i];
}
SortUtils.countSort(newTemplateArray);//計數排序
newImgArrays[h][w] = newTemplateArray[count / 2];
} else {
SortUtils.countSort(templateArray);//計數排序
newImgArrays[h][w] = templateArray[filterRadius];
}
}
}
return newImgArrays;
}
//灰度處理的方法
public static final byte Gray_Type_Default = 0;//默認加權法
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 Channel_Type_Default = 0;//默認三通道
public static final byte Channel_Type_1 = 1;//單通道
public static final byte Channel_Type_3 = 3;//三通道
/**
* 灰度化處理
*
* @param imgArrays 圖像二維數組
* @param grayType 灰度化方法
*/
public static int[][] getGrayImg(int[][] imgArrays, int grayType) throws Exception {
final int imgHeight = imgArrays.length;
final int imgWidth = imgArrays[0].length;
int[][] newImgArrays = new int[imgHeight][imgWidth];
for (int h = 0; h < imgHeight; h++) {
for (int w = 0; w < imgWidth; w++) {
final int[] grb = getRgb(imgArrays[h][w]);
newImgArrays[h][w] = getGray(grb, grayType);
}
}
return newImgArrays;
}
/**
* 通過像素值,返回r、g、b顏色通道的值
*
* @param pixel 像素值
*/
public static int[] getRgb(int pixel) {
int[] rgb = new int[3];
rgb[0] = (pixel >> 16) & 0xff;
rgb[1] = (pixel >> 8) & 0xff;
rgb[2] = pixel & 0xff;
return rgb;
}
/**
* 根據不同的灰度化方法,返回灰度值
*
* @param rgb r、g、b顏色通道的值
* @param grayType 不同灰度處理的方法
*/
public static int getGray(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 || grayType == Gray_Type_Default) {
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 < rgb.length; 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;
}
/**
* 獲取二維像素
*
* @param image BufferedImage圖像對象
*/
public static int[][] getTwoDimension(BufferedImage image) {
final int imgWidth = image.getWidth();
final int imgHeight = image.getHeight();
int[][] imgArrays = new int[imgHeight][imgWidth];
for (int i = 0; i < imgHeight; i++) {
for (int j = 0; j < imgWidth; j++) {
imgArrays[i][j] = image.getRGB(j, i);
}
}
return imgArrays;
}
/**
* 將二維像素填充到圖像中
*
* @param image BufferedImage圖像對象
* @param imgArrays 二維像素
* @param channelType 單通道還是三通道
*/
public static void setTwoDimension(BufferedImage image, int[][] imgArrays, int channelType) throws Exception {
final int imgWidth = image.getWidth();
final int imgHeight = image.getHeight();
for (int i = 0; i < imgHeight; i++) {
for (int j = 0; j < imgWidth; j++) {
if (channelType == Channel_Type_1) {
image.setRGB(j, i, (byte) imgArrays[i][j]);
} else if (channelType == Channel_Type_3 || channelType == Channel_Type_Default) {
image.setRGB(j, i, imgArrays[i][j]);
} else {
throw new Exception("channelType錯誤");
}
}
}
}
}
3、結果。半徑越長,圖像越模糊,效果越不理想。
4、結論。
中值濾波採用非線性的方法,它在平滑脈衝噪聲方面非常有效,同時它可以保護圖像尖銳的邊緣,選擇適當的點來替代污染點的值,所以處理效果好,對椒鹽噪聲表現較好,對高斯噪聲表現較差。半徑越長,圖像越模糊,效果越不理想。