第1章:图像增强技术——对比度拉伸、直方图均衡化、滤波处理、锐化与平滑

各位同学,咱们直接进入正题。图像增强,说白了就是让卫星图“更好看、更好用”。你拿到的原始数据,往往对比度低、噪声多、边缘模糊。不处理一下,后续的分类、目标检测根本没法做。我这些年处理过上千景卫星影像,可以负责任地告诉你:增强做得好,模型事半功倍;增强做得糙,后面全是坑。

这一章,咱们就围绕四个核心操作展开:对比度拉伸、直方图均衡化、滤波处理、锐化与平滑。它们各自解决什么问题?怎么用代码实现?有哪些坑要避开?咱们一个一个来。

核心观点:图像增强不是“美化”,而是“信息提取的预处理”。目标是把人眼或算法关注的特征凸显出来,把干扰信息压下去。

图像增强技术体系 图像增强 预处理核心 对比度拉伸 线性/非线性映射 直方图均衡化 全局/自适应 滤波处理 均值/高斯/中值 锐化与平滑 Laplacian/Sobel 四者协同:先拉伸/均衡化 → 滤波去噪 → 锐化增强边缘

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 实战流程:一个完整的增强管线

好了,四个技术都讲完了。在实际项目中,我不会单独用某一个,而是组合成一个管线。下面是我常用的流程:

  1. 第一步:对比度拉伸(百分比截断,2%-98%)—— 把动态范围拉开
  2. 第二步:CLAHE自适应直方图均衡化(clip_limit=0.03)—— 增强局部对比度
  3. 第三步:中值滤波(3x3或5x5)—— 去除传感器噪声和椒盐噪声
  4. 第四步:非锐化掩模锐化(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%的场景都能给出不错的结果。你拿自己的数据试试,根据实际情况调整参数就好。

最后提醒一句:增强后的图像一定要做目视检查。算法再漂亮,也得人眼说了算。我见过太多“算法上完美、视觉上一塌糊涂”的案例。尤其是锐化过度导致的伪边缘、均衡化导致的噪声放大,这些在屏幕上一眼就能看出来。


专注资料整理