在遥感研究、制图等工作中遥感影像容易受大气吸收和散射等因素的影响。由于影像中部分区域被云层覆盖,云噪声会严重影响数据的质量,因此在尽量不丢失原始有效信息的前提下消除云层的遮挡变得尤为重要。
遥感图像去云工作具有十分重大的意义,也有了多种成熟的方法。我们针对薄云覆盖的单幅灰度遥感图像其清晰度低、难于判读地物信息等问题学习了同态滤波和小波变换这两种经典方法并加以实现,对比了两者的效果。
同时我们还对遥感图像厚云去除方法做了一定的了解,由于云检测技术是厚云去除中的关键步骤,我们学习了并实现了两种云检测算法,分别是经典的Fmask方法与基于U-net神经网络的云检测算法。
同态滤波是一种十分经典的去云方法,可以在无参考影像时使用,它结合了频率过滤和灰度变换,依靠图像照度/反射率模型为基础进行处理,可以增强细节,消除图像照度不均匀的问题。
import cv2
import numpy as np
import scipy.fftpack
def imclearborder(imgBW, radius):
# 读取灰度图像
imgBWcopy = imgBW.copy()
contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
imgRows = imgBW.shape[0]
imgCols = imgBW.shape[1]
contourList = []
for idx in np.arange(len(contours)):
cnt = contours[idx]
for pt in cnt:
rowCnt = pt[0][1]
colCnt = pt[0][0]
check1 = (rowCnt >= 0 and rowCnt < radius) or (rowCnt >= imgRows-1-radius and rowCnt < imgRows)
check2 = (colCnt >= 0 and colCnt < radius) or (colCnt >= imgCols-1-radius and colCnt < imgCols)
if check1 or check2:
contourList.append(idx)
break
for idx in contourList:
cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)
return imgBWcopy
def bwareaopen(imgBW, areaPixels):
imgBWcopy = imgBW.copy()
contours,hierarchy = cv2.findContours(imgBWcopy.copy(), cv2.RETR_LIST,
cv2.CHAIN_APPROX_SIMPLE)
for idx in np.arange(len(contours)):
area = cv2.contourArea(contours[idx])
if (area >= 0 and area <= areaPixels):
cv2.drawContours(imgBWcopy, contours, idx, (0,0,0), -1)
return imgBWcopy
if __name__ == "__main__":
img = cv2.imread('ori1.jpg', 0)
rows = img.shape[0]
cols = img.shape[1]
rows = img.shape[0]
cols = img.shape[1]
imgLog = np.log1p(np.array(img, dtype="float") / 255)
# 创建高斯掩膜
M = 2*rows + 1
N = 2*cols + 1
sigma = 10
(X,Y) = np.meshgrid(np.linspace(0,N-1,N), np.linspace(0,M-1,M))
centerX = np.ceil(N/2)
centerY = np.ceil(M/2)
gaussianNumerator = (X - centerX)**2 + (Y - centerY)**2
# 低通和高通滤波器
Hlow = np.exp(-gaussianNumerator / (2*sigma*sigma))
Hhigh = 1 - Hlow
HlowShift = scipy.fftpack.ifftshift(Hlow.copy())
HhighShift = scipy.fftpack.ifftshift(Hhigh.copy())
If = scipy.fftpack.fft2(imgLog.copy(), (M,N))
Ioutlow = scipy.real(scipy.fftpack.ifft2(If.copy() * HlowShift, (M,N)))
Iouthigh = scipy.real(scipy.fftpack.ifft2(If.copy() * HhighShift, (M,N)))
gamma1 = 0.3
gamma2 = 1.5
Iout = gamma1*Ioutlow[0:rows,0:cols] + gamma2*Iouthigh[0:rows,0:cols]
Ihmf = np.expm1(Iout)
Ihmf = (Ihmf - np.min(Ihmf)) / (np.max(Ihmf) - np.min(Ihmf))
Ihmf2 = np.array(255*Ihmf, dtype="uint8")
Ithresh = Ihmf2 < 65
Ithresh = 255*Ithresh.astype("uint8")
Iclear = imclearborder(Ithresh, 5)
Iopen = bwareaopen(Iclear, 120)
# 显示结果
cv2.imwrite('result.jpg', Ihmf2)
cv2.waitKey(0)
cv2.destroyAllWindows()
小波变换是一种经典的变换分析方法, 能将图像信号分解为由原始小波位移和缩放之后的一组小波。它可以通过低通和高通滤波器将图片信息一层一层分解剥离开来。
图像经过小波分解后,会得到四个分别表示水平和竖直的高频或低频分量,其中LL1(水平低频分量和竖直低频分量)包括了图像的低频信息,是能量集中的主要部分,反应了图像的亮度分布和基本面貌,为近似系数。而其他三个部分是高频细节分量,为细节系数。
我们可以根据云噪声的特点减小近似系数,增大细节系数增强图像清晰度,最后重构图像完成去云。
在实验过程中,我们选择了Haar小波作为小波基,近似系数权重设为0.6,高频系数权重设为4。
import numpy as np
import pywt
import cv2
import matplotlib.pyplot as plt
img = cv2.imread("ori1.jpg")
img = cv2.resize(img, (448, 448))
# img = cv2.cvtColor(img, cv2.COLOR_BGR2RGB).astype(np.float32)
img = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY).astype(np.float32)
cv2.imwrite('1.jpg', img)
plt.figure('二维图像多级分解')
coeffs = pywt.wavedec2(img, 'haar', level=3)
cA3,(cH3,cV3,cD3), (cH2, cV2, cD2), (cH1, cV1, cD1) = coeffs
#近似系数权重
cA3*=0.6
#细节系数权重
i=4
# 将每个子图的像素范围都归一化到与CA2一致 CA2 [0,255* 2**level]
AH3 = np.concatenate([cA3, (cH3 + 510)*i], axis=1)
VD3 = np.concatenate([(cV3 + 510)*i, (cD3 + 510)*i], axis=1)
cA2 = np.concatenate([AH3, VD3], axis=0)
AH2 = np.concatenate([cA2, (cH2 + 510)*i], axis=1)
VD2 = np.concatenate([(cV2 + 510)*i, (cD2 + 510)*i], axis=1)
cA1 = np.concatenate([AH2, VD2], axis=0)
AH = np.concatenate([cA1, (cH1 + 255) * 2*i], axis=1)
VD = np.concatenate([(cV1 + 255) * 2*i, (cD1 + 255) * 2*i], axis=1)
img = np.concatenate([AH, VD], axis=0)
# 重构图片
rimg = pywt.waverec2(coeffs, 'haar')
cv2.imwrite('2.jpg', rimg)
plt.imshow(img, 'gray')
plt.title('Result')
plt.imsave('Result.jpg',img,cmap='gray')
plt.show()
主观的评价方法主要是依靠视觉对图像的去云效果进行判定,以及在人眼可识别范围内的灰度过渡情况,原图的纹理变化等状况。
从同态滤波去云的结果我们可以看出,云层的边缘部分及整幅图像的灰度值被减弱,利用同态滤波法去云会使无云区域的地物灰度值降低,它是一种整体的大幅度的处理方法,适用于大范围的薄云的去除,而对于小范围的高频云处理效果一般。
从小波变换去云方法的结果我们可以看出,处理云层边缘的薄云得到了有效的去除,处于高频区云的突变云没有得到好的处理效果,如果继续增大权重因子,图像会变得模糊,所以传统的小波变换在去除高频突变区的云及小面积的云噪声时显得乏力。
根据香农信息论,信息熵反映了图像的信息量,其值越大,信息越丰富,它可以作为评价一个图像质量的判别标准。
import numpy as np
import cv2
from matplotlib import pyplot as plt
img = cv2.imread("res3.jpg")
# 计算信息熵
def entropy(labels, base=None):
value, counts = np.unique(labels, return_counts=True)
norm_counts = counts / counts.sum()
print(norm_counts)
base = e if base is None else base
return -(norm_counts * np.log(norm_counts) / np.log(base)).sum() # log(a) b=log (c) b÷log (c) a
#
f = img
h = entropy(f, 2)
print('h=', h)
#
cv2.waitKey(0)
cv2.destroyAllWindows()
从结果中我们可以看出,信息熵均降低了,其中同态滤波的信息熵下降很多,说明在去除云层的处理过程中损失了一部分图像信息;小波变换去云也损失了一部分信息,但由于增强了高频细节,损失相对较少。
Fmask云检测算法是一种经典的提取遥感图像中云和云阴影区域的方法,主要针对Landsat4~5、7、8的TM/ETM+/OLI以及Sentinel2 的MSI图像。它的基本思路是首先利用光谱特性进行一系列的光谱测试,选取归一化植被指数NDVI和归一化雪指数NDSI 等参数的最优阈值,以识别较为明显的云,从而得到潜在云像素层;然后根据统计概率学中的概率因子计算云概率参数,从而得到潜在云层,并使用形态学变换中的填充变换法,结合散射相关计算,得到潜在云阴影层;最后根据NDSI阈值计算潜在雪层。
由于在ENVI5.3版本中已经集成了Fmask云检测算法的相关模块,我们就在ENVI中进行了实验,以下是实验步骤。
1.首先以头文件MTL打开一幅Landsat8/OSI卫星影像数据,打开ENVI中的辐射定标工具,分别对多波段、热红外波段、卷云波段进行辐射定标工作
2.然后在工具箱中搜索Cloud Mask云掩膜计算工具,使用Fmask算法计算云掩膜。计算需要刚刚辐射定标完成的三个表观反射率、热反射率文件作为输入。
3.接下来打开计算的云掩膜结果,并在符号系统中修改图层颜色以区分有云与无云部分,得到实验结果。
在深度学习网络中,大量的训练样本是网络高效工作的保障。然而在遥感影像处理中,有限的样本限制了深度网络的使用。U-net 是基于 FCN 网络的一种分割网络,能够在少量的数据上训练学习并且获得较好的效果。U-net 本质上是编码器-解码器结构。编码器逐渐减小汇集层的空间维度,而解码器逐渐恢复对象的细节和空间维度,以通过使用反卷积层来补偿信息的丢失。因此基于U-net,以便很好地保留边缘和细节信息。
我们在阅读了多篇论文并学习了该方法的基本原理以后,在Kaggle平台参考成熟的U-net模型代码,使用了作者提供的公开数据集,初步实现了简单的云检测效果。
Kaggle平台U-Net云检测模型
从整体上看,Fmask能够识别出大部分云。但在不同的下垫面下出现了不同程度的云遗漏现象;植被和水体地区,Fmask算法的云检测效果较好,但是对薄云和碎云的效果不佳,并且整体的云检测结果缺乏连接性,云像元识别破碎。此外,Fmask算法存在将许多非云像素划分为云的现象,这将导致过多的云覆盖。
对于U-net模型: (1)相比Fmask算法, 薄云和碎云的检测效果更好(2)U-net网络对不同大小的目标对象的敏感性不同。通过深度网络的重复下采样和重复上采样,容易丢失小对象的边缘信息。由于云的几何形状是不规则的,导致U-net模型忽略小对象边界信息和破碎的小云。