环境:Windows10专业版 + IDEA2021.2.3 + jdk11.0.1 + OpenCV-460.jar
系列文章:
(一)Python+GDAL实现BSQ,BIP,BIL格式的相互转换
目录
一、梯度倒数加权简介
图像的梯度倒数加权平滑是一种基于像素邻域梯度信息的数字图像处理技术,主要用于降低图像噪声同时保留边缘特征。
以下是关于图像梯度倒数加权平滑的具体介绍:
1、基本原理:在一幅图像中,相邻区域之间的变化通常大于区域内部的变化。在同一区域内,中心像素的变化小于边缘像素的变化。图像梯度反映了这种变化率,即在图像变化缓慢的区域梯度值较小,而在变化快的区域梯度值较大。梯度倒数加权平滑正是利用这个原理,对图像中的像素进行加权平均,使得变化平缓区域的像素获得较大的平滑效果,而变化剧烈的边缘区域则保持原有特征。
2、应用范围:由于其在去噪的同时能够有效保留图像边缘,梯度倒数加权平滑广泛应用于图像预处理、医学成像、遥感图像分析等领域。
3、注意事项:在进行梯度倒数加权平滑时,应注意选择适当的窗口大小和权重函数,以达到最佳的去噪效果和边缘保持能力。
4、与其他方法对比:与直方图修正法等其他图像增强技术相比,梯度倒数加权平滑更注重于局部特征的保持,而不是全局的对比度调整。
5、数学基础:从数学角度来看,图像可以被视为一个二维离散函数。图像梯度是一个矢量,表示函数在某点沿着该方向变化最快,变化率为梯度的模。在数字图像处理中,通常通过计算像素点的偏微分来估计图像梯度。
图像的梯度倒数加权平滑是一种有效的图像去噪技术,它通过对图像局部区域进行加权平均来降低噪声,同时保留重要的边缘信息。
二、算法流程
(以单波段图像为例)
1、利用OpenCV读入图像,将像素存储在数组里
2、边缘像素不做处理,以3×3模板为例,模板为
规定w( i ,j ) = 1/2,其余系数之和为1/2,定义除中心像素外的其他系数为
对于中心像素与其他像素的差值为0的情况,用如下公式:
通过下面公式将w总和归一化为1/2:
3、进行卷积运算:
新像素=0.022×21+0.218×30+0.073×28+0.031×24+0.5×31+0.027×23+0.044×36+0.031×38+0.054×27≈30
4、将经过梯度倒数加权平滑后的像素值存入数组合成图像并存储
三、代码实现
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
/**
* @Author: HNUST_jue_chen
* @Date: 2022/11/02/ 19:40
* @Attention: 转载, 引用请注明出处
*/
public class GradientReciprocalWeighting {
//加载本地动态链接库
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
//梯度倒数加权
public Mat gradientReciprocalWeightingFilter(String path) {
//使用Mat类存储图像信息
Mat mat = Imgcodecs.imread(path);
//图像的大小
int rows = mat.rows();
int cols = mat.cols();
//获得原图像像素数组
int[][] mat_arr = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
mat_arr[i][j] = (int) mat.get(i, j)[0];
}
}
//用3×3窗口进行梯度倒数加权平滑
int[][] mat_arr_gradientReciWeight = new int[rows][cols];
for (int i = 0; i < rows; i++) {
for (int j = 0; j < cols; j++) {
//处理非边缘像素
if (i != 0 && i != rows - 1 && j != 0 && j != cols - 1) {
//3×3窗口的原始图像梯度倒数
double[][] temp = new double[3][3];
temp[0][0] = judgment(mat_arr[i - 1][j - 1] , mat_arr[i][j]);
temp[0][1] = judgment(mat_arr[i - 1][j] , mat_arr[i][j]);
temp[0][2] = judgment(mat_arr[i - 1][j + 1] , mat_arr[i][j]);
temp[1][0] = judgment(mat_arr[i][j - 1] , mat_arr[i][j]);
temp[1][1] = 0;
temp[1][2] = judgment(mat_arr[i][j + 1] , mat_arr[i][j]);
temp[2][0] = judgment(mat_arr[i + 1][j - 1] , mat_arr[i][j]);
temp[2][1] = judgment(mat_arr[i + 1][j] , mat_arr[i][j]);
temp[2][2] = judgment(mat_arr[i + 1][j + 1] , mat_arr[i][j]);
//得到梯度倒数之和
double temp_sum = 0.0;
for (double[] m : temp) {
for (double n : m) {
temp_sum += n;
}
}
//归一化后的梯度权重矩阵
double[][] temp_normalize = new double[3][3];
for (int p = 0; p < temp_normalize.length; p++) {
for (int q = 0; q < temp_normalize[0].length; q++) {
if (p == 1 && q == 1) {
temp_normalize[p][q] = 0.5;
} else {
temp_normalize[p][q] = temp[p][q] / (2 * temp_sum);
}
}
}
//得到新中心像素
mat_arr_gradientReciWeight[i][j] = (int) (temp_normalize[0][0] * mat_arr[i - 1][j - 1]
+ temp_normalize[0][1] * mat_arr[i - 1][j]
+ temp_normalize[0][2] * mat_arr[i - 1][j + 1]
+ temp_normalize[1][0] * mat_arr[i][j - 1]
+ temp_normalize[1][1] * mat_arr[i][j]
+ temp_normalize[1][2] * mat_arr[i][j + 1]
+ temp_normalize[2][0] * mat_arr[i + 1][j - 1]
+ temp_normalize[2][1] * mat_arr[i + 1][j]
+ temp_normalize[2][2] * mat_arr[i + 1][j + 1]);
} else { //处理边缘像素
mat_arr_gradientReciWeight[i][j] = mat_arr[i][j];
}
}
}
//合成图像
Mat mat_gradientReciWeight = new Mat(rows, cols, CvType.CV_32SC1);
//将像素放入图像
for (int i = 0; i < rows; i++) {
//一次放入一行像素值
mat_gradientReciWeight.put(i, 0, mat_arr_gradientReciWeight[i]);
}
return mat_gradientReciWeight;
}
//进行判断中心像素与边缘像素的差值是否为0
public static double judgment(int a, int b) {
double c;
if (a - b == 0) {
c = 1.0;
} else {
c = 1.0 / (Math.abs(a - b));
}
return c;
}
public static void main(String[] args) {
GradientReciprocalWeighting grw = new GradientReciprocalWeighting();
Mat mat = grw.gradientReciprocalWeightingFilter("D:\\Project\\IDEA_Project\\RS01\\src\\rs01\\img\\2_gray1.png");
//将经过梯度倒数加权平滑后的图像写入文件
Imgcodecs.imwrite("D:\\Project\\IDEA_Project\\RS01\\src\\rs01\\img\\2_gray1_grw.png", mat);
}
}