环境:Windows10专业版 + IDEA2021.2.3 + jdk11.0.1 + GDAL(release-1928-x64-gdal-3-5-2-mapserver-8-0-0) + OpenCV-460.jar
系列文章:
(一)Python+GDAL实现BSQ,BIP,BIL格式的相互转换
(二)BSQ,BIL,BIP存储格式的相互转换算法
(三)单波段图像的伪彩色合成:密度分割(含介绍OpenCV中的Mat类)
(四)图像的%2线性拉伸
(五)图像的标准假彩色合成
(六)图像的直方图均衡化
(七)图像的均值滤波
(八)图像的中值滤波
(九)图像的高斯低通滤波
(十)图像的梯度倒数加权平滑
(十一)图像的罗伯特梯度锐化
(十二)图像的Sobel梯度锐化
(十三)图像的拉普拉斯梯度锐化
目录
一、标准假彩色合成简介
二、算法流程
三、代码实现
四、实验结果
1、原图像
2、假彩色合成结果
3、ENVI中标准假彩色的合成结果
一、标准假彩色合成简介
标准假彩色合成是一种遥感影像处理技术,用于增强图像的视觉效果和地物信息的提取。
标准假彩色合成通常涉及以下几个关键步骤:
1、波段选择:从多光谱遥感图像中选择三个不同的波段。这些波段通常是根据能够最大化地物特征差异的原则来选择的。
2、颜色赋予:将选定的波段分别赋予红、绿、蓝三种颜色。由于这些波段的波长与对应的颜色波长不同,因此合成后的图像颜色并不是地物的真实颜色。
3、图像合成:利用加色法原理,将赋予颜色的波段合成一张彩色图像。这种方法可以使得某些地物特征在视觉上更加突出,便于分析和识别。
4、应用:标准假彩色合成广泛应用于植被信息提取、土地利用分类、环境监测等领域。例如,在ENVI软件中,可以通过加载CIR(Color Infrared)功能快速实现标准假彩色合成。
二、算法流程
1、利用GDAL读取landset图像波段4,波段3,波段2的像素并存放在数组中
2、一张16位的图像,图像的每个像素点的像素值都由16位的二进制数表示,每个像素点的颜色有 2^16 = 65536 种可能,也就是说,图像的颜色区间被划分成了 65536份;同理,8位图像,图像的颜色区间被划分成了2^8 = 256份。
因为TIFF图像的颜色存储位数为16位,需要进行线性拉伸转换为8位,将16位转换成8位,即为将区间 [0,65535] 映射到 [0,255] ,算出每个波段像素的最大值和最小值,进行线性拉伸,
将最小值 pmin 映射到1,这样不会造成像素丢失,最后得到像素值范围在[1,255]的图像
3、将拉伸后的4,3,2波段的像素数组分别赋予R,G,B通道利用OpenCV合成图像并存储
三、代码实现
import org.gdal.gdal.Band;
import org.gdal.gdal.Dataset;
import org.gdal.gdal.gdal;
import org.gdal.gdalconst.gdalconstConstants;
import org.opencv.core.Core;
import org.opencv.core.CvType;
import org.opencv.core.Mat;
import org.opencv.imgcodecs.Imgcodecs;
import java.util.ArrayList;
/**
* @Author: HNUST_jue_chen
* @Date: 2022/10/28/ 11:01
* @Attention: 转载, 引用请注明出处
*/
//假彩色合成
public class FalseColorSynthesis {
//加载本地动态链接库
static {
System.loadLibrary(Core.NATIVE_LIBRARY_NAME);
}
//获得波段4,3,2
public ArrayList<int[][]> getBands(String path) {
//支持所有驱动
gdal.AllRegister();
//以只读方式读取数据存入Dataset类型里
Dataset dataset = gdal.Open(path, gdalconstConstants.GA_ReadOnly);
//获得图像的大小
int rows = dataset.getRasterYSize();
int cols = dataset.getRasterXSize();
int count = dataset.getRasterCount();
System.out.println(rows);
System.out.println(cols);
System.out.println(count);
//获得需要的三个波段
Band band4 = dataset.GetRasterBand(4);
Band band3 = dataset.GetRasterBand(3);
Band band2 = dataset.GetRasterBand(2);
//定义波段三个二维数组存放三个波段的像素
int[][] band4_arr = new int[rows][cols];
int[][] band3_arr = new int[rows][cols];
int[][] band2_arr = new int[rows][cols];
//将三个波段的像素的像素存放入二维数组
for (int i = 0; i < rows; i++) {
//一次读取一行像素值
band4.ReadRaster(0, i, cols, 1, band4_arr[i]);
band3.ReadRaster(0, i, cols, 1, band3_arr[i]);
band2.ReadRaster(0, i, cols, 1, band2_arr[i]);
}
//将三个二维数组存放在集合中
ArrayList<int[][]> band_arr = new ArrayList<>();
band_arr.add(band4_arr);
band_arr.add(band3_arr);
band_arr.add(band2_arr);
return band_arr;
}
//获得本图像像素的最大值和最小值
public int[] getMinMaxPixel(int[][] arr) {
//先将数组的第一个数赋予最大值和最小值
int min = arr[0][0];
int max = arr[0][0];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
//更新最小值
if (arr[i][j] < min) {
min = arr[i][j];
}
//更新最大值
if (arr[i][j] > max) {
max = arr[i][j];
}
}
}
//将最大值和最小值存入数组
int[] minMaxArr = new int[2];
minMaxArr[0] = min;
minMaxArr[1] = max;
return minMaxArr;
}
//将16位像素转换为8位像素,线性拉伸[a,b]到[c,d]
//a=min,b=max,c=1,d=255
//新像素值=(d-c)/(max-min)*(原像素值-min)+1
public int[][] linearTo8Bit(int[][] arr, int min, int max) {
//定义线性拉伸后的数组
int[][] arr_linear = new int[arr.length][arr[0].length];
for (int i = 0; i < arr.length; i++) {
for (int j = 0; j < arr[0].length; j++) {
arr_linear[i][j] = (int) ((double) (255 - 1) / (max - min) * (arr[i][j] - min) + 1);
}
}
return arr_linear;
}
//将三个波段合成为假彩色图像
public Mat falseColorSynthesis(ArrayList<int[][]> arrayList) {
//获得三个波段的数组
int[][] band4_arr = arrayList.get(0);
int[][] band3_arr = arrayList.get(1);
int[][] band2_arr = arrayList.get(2);
//定义存储三个波段的三通道数组
int[][] image_arr = new int[band4_arr.length][band4_arr[0].length * 3];
//Mat类三通道的的顺序为B,G,R,在标准假彩色合成中应该分别赋予band2,band3,band4
for (int i = 0; i < image_arr.length; i++) {
//将波段2赋予蓝色通道
for (int j = 0; j < band2_arr[0].length; j++) {
image_arr[i][3 * j] = band2_arr[i][j];
}
//将波段3赋予绿色通道
for (int j = 0; j < band3_arr[0].length; j++) {
image_arr[i][3 * j + 1] = band3_arr[i][j];
}
//将波段4赋予红色通道
for (int j = 0; j < band4_arr[0].length; j++) {
image_arr[i][3 * j + 2] = band4_arr[i][j];
}
}
//定义假彩色图像
Mat mat = new Mat(band2_arr.length, band2_arr[0].length, CvType.CV_32SC3);
//将像素放入图像
for (int i = 0; i < image_arr.length; i++) {
//一次放入一行三通道像素值
mat.put(i, 0, image_arr[i]);
}
return mat;
}
public static void main(String[] args) {
FalseColorSynthesis fsc = new FalseColorSynthesis();
ArrayList<int[][]> band_arr = fsc.getBands("D:\\Project\\IDEA_Project\\RS01\\src\\rs01\\img\\13.tif");
//得到波段4的最小最大像素值
int[][] band4_arr = band_arr.get(0);
int[] band4_minMax = fsc.getMinMaxPixel(band4_arr);
int band4_min = band4_minMax[0];
int band4_max = band4_minMax[1];
//得到波段3的最小最大像素值
int[][] band3_arr = band_arr.get(1);
int[] band3_minMax = fsc.getMinMaxPixel(band3_arr);
int band3_min = band3_minMax[0];
int band3_max = band3_minMax[1];
//得到波段2的最小最大像素值
int[][] band2_arr = band_arr.get(2);
int[] band2_minMax = fsc.getMinMaxPixel(band2_arr);
int band2_min = band2_minMax[0];
int band2_max = band2_minMax[1];
//波段4线性拉伸
int[][] band4_arrLinear = fsc.linearTo8Bit(band4_arr, band4_min, band4_max);
//波段3线性拉伸
int[][] band3_arrLinear = fsc.linearTo8Bit(band3_arr, band3_min, band3_max);
//波段2线性拉伸
int[][] band2_arrLinear = fsc.linearTo8Bit(band2_arr, band2_min, band2_max);
//将线性拉伸后的数组存放在集合中
ArrayList<int[][]> band_arrLinear = new ArrayList<>();
band_arrLinear.add(band4_arrLinear);
band_arrLinear.add(band3_arrLinear);
band_arrLinear.add(band2_arrLinear);
//合成假彩色图像
Mat mat = fsc.falseColorSynthesis(band_arrLinear);
//将假彩色图像写入文件
Imgcodecs.imwrite("D:\\Project\\IDEA_Project\\RS01\\src\\rs01\\img\\13_fsc.png", mat);
}
}
四、实验结果
1、原图像
TIFF格式的图像为16位深度的颜色,直接在Window里打开一张16位tif格式的图片是无法获得有效信息甚至无法打开,所以在ENVI中进行查看。
2、假彩色合成结果
3、ENVI中标准假彩色的合成结果
和ENVI中合成的假彩色图像非常接近