第1章:图像增强技术——对比度拉伸、直方图均衡化、滤波处理、锐化与平滑
各位同学,咱们直接进入正题。图像增强,说白了就是让卫星图“更好看、更好用”。你拿到的原始数据,往往对比度低、噪声多、边缘模糊。不处理一下,后续的分类、目标检测根本没法做。我这些年处理过上千景卫星影像,可以负责任地告诉你:增强做得好,模型事半功倍;增强做得糙,后面全是坑。
这一章,咱们就围绕四个核心操作展开:对比度拉伸、直方图均衡化、滤波处理、锐化与平滑。它们各自解决什么问题?怎么用代码实现?有哪些坑要避开?咱们一个一个来。
核心观点:图像增强不是“美化”,而是“信息提取的预处理”。目标是把人眼或算法关注的特征凸显出来,把干扰信息压下去。
4.1 对比度拉伸:把灰蒙蒙的图“拉开”
卫星影像有个通病——动态范围窄。你想想看,传感器拍到的地物,水体、植被、裸土、建筑,它们的反射率差异其实很大,但原始数据往往集中在某个狭窄区间。结果就是:整张图灰蒙蒙的,像蒙了一层雾。
对比度拉伸,就是把这个窄区间映射到0-255的全范围。最简单的做法是线性拉伸:
import numpy as np
from skimage import exposure
def linear_stretch(img, low_percent=2, high_percent=98):
"""线性对比度拉伸,去掉两端百分位后的极值"""
p_low, p_high = np.percentile(img, [low_percent, high_percent])
stretched = exposure.rescale_intensity(img, in_range=(p_low, p_high))
return stretched
# 使用示例
img_stretched = linear_stretch(original_img)
我个人习惯用百分比截断的方式。为什么?因为卫星影像里经常有云、雪、或者传感器坏点,这些极端值会拉偏整个映射。去掉两端2%的极值,效果往往更稳定。我在处理Landsat 8的OLI影像时,就遇到过因为云顶反射率太高,导致拉伸后地面细节全丢的情况。后来改用百分比截断,问题就解决了。
小技巧:对于多光谱影像,每个波段单独拉伸后再合成RGB,效果比整体拉伸好得多。因为不同波段的动态范围差异很大。
4.2 直方图均衡化:让分布更均匀
对比度拉伸是线性映射,直方图均衡化则是非线性的。它的核心思想是:让像素值的分布尽可能均匀。说白了,就是把原来集中在某个区间的像素,重新分配到整个0-255范围。
效果呢?原本暗的地方变亮,亮的地方稍微压暗,细节就都出来了。我处理过一张高分二号的城市影像,建筑物阴影里完全看不清地物。用直方图均衡化之后,阴影里的车辆、行道树轮廓都显现出来了。
from skimage import exposure
# 全局直方图均衡化
img_eq = exposure.equalize_hist(img)
# 自适应直方图均衡化(CLAHE)—— 推荐
from skimage import exposure
img_clahe = exposure.equalize_adapthist(img, clip_limit=0.03, kernel_size=None)
这里我要特别强调一下:全局均衡化容易过度增强噪声。尤其是卫星影像中均匀的区域(比如大面积水体、农田),均衡化后会出现明显的“颗粒感”。
避坑指南:我曾经在处理Sentinel-2的沿海水域影像时,用了全局均衡化,结果把传感器噪声放大成了“伪纹理”,后续的水体分类精度直接掉了15%。后来改用CLAHE(限制对比度自适应直方图均衡化),把clip_limit设到0.02,才压住噪声。记住:有噪声的图,慎用全局均衡化。
4.3 滤波处理:去噪与特征提取的基础
滤波,本质上是用一个“窗口”在图像上滑动,对窗口内的像素做某种运算。卫星影像里最常见的滤波需求有两个:去噪和提取特定空间特征。
咱们先看三种基础滤波器:
| 滤波器类型 | 原理 | 适用场景 | 缺点 |
|---|---|---|---|
| 均值滤波 | 窗口内像素取平均 | 去除高斯噪声 | 边缘模糊严重 |
| 高斯滤波 | 按高斯权重加权平均 | 平滑噪声,保留一定边缘 | 计算量稍大 |
| 中值滤波 | 取窗口内像素的中位数 | 去除椒盐噪声、斑点噪声 | 对高斯噪声效果一般 |
from scipy.ndimage import median_filter
from skimage.filters import gaussian
# 中值滤波 —— 对付SAR影像的斑点噪声特别好用
img_median = median_filter(img, size=3)
# 高斯滤波 —— 平滑噪声
img_gaussian = gaussian(img, sigma=1.0)
嗯,这里要注意:滤波窗口大小很关键。3x3的窗口去噪能力弱但细节保留好,7x7的窗口去噪强但边缘会糊。我一般从3x3开始试,如果噪声还明显,再逐步增大。对于高分辨率影像(比如0.5米的WorldView),我甚至会用5x5的中值滤波,因为地物细节足够丰富,稍微模糊一点不影响判读。
4.4 锐化与平滑:一对“相爱相杀”的操作
平滑是让图像变模糊(去除噪声和细节),锐化是让边缘变清晰(增强细节)。你想想看,这两个操作其实是矛盾的。但实际处理中,我们经常需要先平滑去噪,再锐化增强边缘。顺序不能乱。
锐化的核心原理:提取图像的高频信息(边缘、纹理),叠加回原图。常用的算子有Laplacian和Sobel。
from skimage.filters import laplace, sobel
from skimage import img_as_float
# 拉普拉斯锐化
img_float = img_as_float(img)
lap = laplace(img_float)
sharpened = img_float - lap # 原图减去拉普拉斯结果
# Sobel边缘增强
edges = sobel(img_float)
enhanced = img_float + edges * 0.5 # 叠加边缘信息
我个人习惯用非锐化掩模(Unsharp Masking)。这个方法先对图像做高斯模糊,然后用原图减去模糊图得到“细节层”,再把细节层按一定比例加回去。控制起来非常灵活。
from skimage.filters import gaussian
def unsharp_mask(img, sigma=1.0, amount=1.0):
"""非锐化掩模"""
blurred = gaussian(img, sigma=sigma, preserve_range=True)
detail = img - blurred
return img + amount * detail
# 使用示例
img_sharp = unsharp_mask(img, sigma=1.5, amount=1.2)
经验之谈:锐化的amount参数不要超过2.0,否则会出现“光晕效应”——边缘周围出现一圈亮边或暗边,这在遥感影像中非常显眼,一看就是处理过度了。我一般设到0.8~1.5之间,根据影像分辨率调整。高分辨率影像可以多锐化一点,低分辨率影像要保守。
4.5 实战流程:一个完整的增强管线
好了,四个技术都讲完了。在实际项目中,我不会单独用某一个,而是组合成一个管线。下面是我常用的流程:
- 第一步:对比度拉伸(百分比截断,2%-98%)—— 把动态范围拉开
- 第二步:CLAHE自适应直方图均衡化(clip_limit=0.03)—— 增强局部对比度
- 第三步:中值滤波(3x3或5x5)—— 去除传感器噪声和椒盐噪声
- 第四步:非锐化掩模锐化(sigma=1.0, amount=1.2)—— 恢复边缘清晰度
def enhance_pipeline(img):
# 1. 对比度拉伸
p_low, p_high = np.percentile(img, [2, 98])
img = exposure.rescale_intensity(img, in_range=(p_low, p_high))
# 2. CLAHE
img = exposure.equalize_adapthist(img, clip_limit=0.03)
# 3. 中值滤波
img = median_filter(img, size=3)
# 4. 非锐化掩模
img = unsharp_mask(img, sigma=1.0, amount=1.2)
return img
这个管线我用了好几年,处理过Landsat、Sentinel-2、高分系列、WorldView等多种数据源。不能说万能,但80%的场景都能给出不错的结果。你拿自己的数据试试,根据实际情况调整参数就好。
最后提醒一句:增强后的图像一定要做目视检查。算法再漂亮,也得人眼说了算。我见过太多“算法上完美、视觉上一塌糊涂”的案例。尤其是锐化过度导致的伪边缘、均衡化导致的噪声放大,这些在屏幕上一眼就能看出来。