导语:
在遥感图像处理过程中,我们常使用的波段有可见光、红外、微波等波段。但是,在获取Landsat、Sentinel等卫星影像数据时,往往有其他波段影像存在,QA波段就是其中之一。QA波段是什么,它又有什么作用呢?
图1是Landsat卫星OLI影像文件的主要数据信息。从文件名可以分析出这是一景Landsat8的矫正等级Level2的影像,行列号为132027,时间是2019年8月16日,处理时间是2020年8月27日(从最后的修改日期也可以看出),产品级别为Collecton 2 Tier 1。
图1 Landsat 8数据内容
from osgeo import gdal
import os
import matplotlib.pyplot as plt
import numpy as np
os.chdir(r'D:\Users\Administrator\Downloads\LC08_L2SP_132027_20190816_20200827_02_T1')
def readtiff(fileName):
"""
输入:影像文件名
输出:numpy格式数据
"""
dataset = gdal.Open(fileName)
im_width = dataset.RasterXSize #列数
im_height = dataset.RasterYSize #行数
im_bands = dataset.RasterCount #波段数
im_data = dataset.ReadAsArray(0,0,im_width,im_height)
return im_data
def stretch_2_precentage(gray):
"""
输入:灰度图像
输出:2%线性拉伸之后的图像
"""
high_value = np.percentile(gray, 98)#98%
low_value = np.percentile(gray, 2)#2%
min_v = gray.min()
max_v = gray.max()
streched_gray = np.clip(gray, a_min=low_value, a_max=high_value)
results = ((streched_gray - low_value)/(high_value - low_value))
return results
data = readtiff(r'LC08_L2SP_132027_20190816_20200827_02_T1_QA_PIXEL.TIF')
plt.figure(figsize=(10,10))
plt.imshow(stretch_2_precentage(data),'gray')
plt.axis("off")
plt.show()
QA波段即Quality Assessment Band,是利用FMASK算法得到的影像质量评估数据,对比图2、图3不难发现其中包含了云、云影、卷云、水体等信息。该波段与其他常用波段的差异主要在,(1)QA波段是一种产品,是FMASK算法得到的地物信息产品,其他常用波段是观测数据。(2)QA波段数据是间断的,其数值的大小没有可比较的意义。而可见光、红外等波段是连续,其数值的大小表示了地物反射率、发射率的大小。(3)QA波段是二进制数据转十进制实现的。赋予二进制不同位置数值的含义,最后转成十进制。其思想与机器学习中的one-hot编码有异曲同工之妙。
表1 QA波段各位数信息
位数 | 信息 |
---|---|
Bit 0 | Fill |
Bit 1 | Dilated Cloud |
Bit 2 | Cirrus (high confidence) |
Bit 3 | Cloud |
Bit 4 | Cloud Shadow |
Bit 5 | Snow |
Bit 6 | Clear |
0 | Cloud or Dilated Cloud bits are set |
1 | Cloud and Dilated Cloud bits are not set |
Bit 7 | Water |
Bits 8-9 | Cloud Confidence |
0 | None |
1 | Low |
2 | Medium |
3 | High |
Bits 10-11 | Cloud Shadow Confidence |
0 | None |
1 | Low |
2 | Medium |
3 | High |
Bits 12-13 | Snow/Ice Confidence |
0 | None |
1 | Low |
2 | Medium |
3 | High |
Bits 14-15 | Cirrus Confidence |
0 | None |
1 | Low |
2 | Medium |
3 | High |
如上表所示,不同bit记录了不同地物信息。其中,0-7位记录了地物类型,包括云、云影、水体、冰雪等。8-15位则记录了云、云影、冰雪、卷云地类的可能性,由两位组成,00,01,10,11分别表示无、低、中、高四种可能性。下面举个例子以便更详细了解QA波段(以前8bit为例)。
表3 二进制数据含义
因此,在去云过程中,我们常利用QA波段进行云提取。因为这些信息是按位存储的,所以也需要进行按位运算。表4记录了像元是云的可能性,其余位数不考虑。
**表4 **
这里主要有rightShift和bitWiseAnd两种按位运算的参与。其中rightShift表示按位右移。例如“01100110”,按位右移一位为“0110011”,右移三位为““01100”。bitWiseAnd表示同一位数都为1则返回该位数为1,否则返回0。例如,bitWiseAnd运算“01100110”与“1101”,返回值为“0100”,bitWiseAnd运算“01100110”与“01001011”,返回值为“01000010”。因此判断像元是否为云,可以通过righShift(1)右移一位,再与15(“1111”)做bitWiseAnd运算,若返回值为0,则表示该区域无云,反之可能存在云/云影/卷云/扩散云。去云代码如下所示:
def cloud(data):
"""
提取云
输入:原始pixel_qa数据
输出:云掩膜
"""
return np.bitwise_and(np.right_shift(data,1),15) == 0
def cloud_free(cloud, img):
"""
对影像掩膜
输入:云掩膜,图像数据
输出:掩膜后的影像
"""
re = []
for i in range(7):
re.append(img[:,:,i]*cloud)
return np.array(re).transpose([1,2,0])
c = cloud(data)
f_img = cloud_free(c, y)
plt.figure(figsize=(10,10))
plt.imshow(c,'gray')
plt.axis('off')
plt.figure(figsize=(10,10))
plt.imshow(f_img[:,:,4:1:-1])
plt.axis("off")
经过上述步骤,就得到了一景去云影像,若要获得该区域的无云影像,则需要对不同时间该位置影像做去云处理,然后镶嵌。这种数据量比较大的操作建议在Google Earth Engine中操作。
同样的,若要获得单景影像的水体信息,也可以利用上述方式得到。代码如下:
water = np.bitwise_and(np.right_shift(data,7),1)
plt.figure(figsize=(10,10))
plt.imshow(water,'gray')
plt.axis('off')
plt.show()
所有代码如下:
from osgeo import gdal
import os
import matplotlib.pyplot as plt
import numpy as np
os.chdir(r'D:\Users\Administrator\Downloads\LC08_L2SP_132027_20190816_20200827_02_T1')
def readtiff(fileName):
"""
输入:影像文件名
输出:numpy格式数据
"""
dataset = gdal.Open(fileName)
im_width = dataset.RasterXSize #列数
im_height = dataset.RasterYSize #行数
im_bands = dataset.RasterCount #波段数
im_data = dataset.ReadAsArray(0,0,im_width,im_height)
return im_data
def stretch_2_precentage(gray):
"""
输入:灰度图像
输出:2%线性拉伸之后的图像
"""
high_value = np.percentile(gray, 98)#98%
low_value = np.percentile(gray, 2)#2%
min_v = gray.min()
max_v = gray.max()
streched_gray = np.clip(gray, a_min=low_value, a_max=high_value)
results = ((streched_gray - low_value)/(high_value - low_value))
return results
data = readtiff(r'LC08_L2SP_132027_20190816_20200827_02_T1_QA_PIXEL.TIF')
plt.figure(figsize=(10,10))
plt.imshow(stretch_2_precentage(data),'gray')
plt.axis("off")
plt.show()
def read_multibands_tiff(fileName):
"""
输入:影像文件名
输出:numpy格式数据
"""
re = []
for i in range(7):
dataset = gdal.Open(fileName+str(i+1)+".TIF")
im_width = dataset.RasterXSize #列数
im_height = dataset.RasterYSize #行数
im = dataset.ReadAsArray(0,0,im_width,im_height)
re.append(im)
return np.array(re)
y = read_multibands_tiff(r'LC08_L2SP_132027_20190816_20200827_02_T1_SR_B')
re = []
for i in y:
re.append(stretch_2_precentage(i))
y = np.array(re).transpose([1,2,0])
plt.figure(figsize=(10,10))
plt.imshow(y[:,:,4:1:-1])
plt.axis('off')
def cloud(data):
"""
提取云
输入:原始pixel_qa数据
输出:云掩膜
"""
return np.bitwise_and(np.right_shift(data,1),15) == 0
def cloud_free(cloud, img):
"""
对影像掩膜
输入:云掩膜,图像数据
输出:掩膜后的影像
"""
re = []
for i in range(7):
re.append(img[:,:,i]*cloud)
return np.array(re).transpose([1,2,0])
c = cloud(data)
f_img = cloud_free(c, y)
plt.figure(figsize=(10,10))
plt.imshow(c,'gray')
plt.axis('off')
plt.figure(figsize=(10,10))
plt.imshow(f_img[:,:,4:1:-1])
plt.axis("off")
water = np.bitwise_and(np.right_shift(data,7),1)
plt.figure(figsize=(10,10))
plt.imshow(water,'gray')
plt.axis('off')
plt.show()
参考文献:
[1] Landsat数据下载与介绍 - icydengyw - 博客园 (cnblogs.com)
[2] Landsat Collection 1 Level-1 Quality Assessment Band | U.S. Geological Survey (usgs.gov)