3、图像预处理:去噪滤波、对比度增强与图像锐化

各位同学,欢迎来到第三章。这一章我们聊聊图像预处理。说白了,就是给超声图像“洗把脸”,让它更干净、更清晰。

我做了这么多年超声图像处理,可以负责任地告诉你:预处理做得好,后面分析省一半的力气。你想想看,一张满是噪声、对比度又差的图像,你拿去做分割、做测量,结果能准吗?

这一章我们重点讲三块:去噪滤波对比度增强图像锐化。这三步是流水线的“基本功”,也是我每次必做的步骤。

3.1 去噪滤波:把脏东西去掉

超声图像天生就有噪声。斑点噪声、散斑噪声,说白了就是图像上那些“雪花点”。我刚开始做的时候,总想着用最复杂的滤波器,后来发现——简单的方法往往最有效

3.1.1 中值滤波

中值滤波的原理很简单:把像素点周围的值排个序,取中间那个。它对椒盐噪声特别有效。什么是椒盐噪声?就是图像上随机出现的白点和黑点,像撒了盐和胡椒一样。

我个人习惯用 3×35×5 的窗口。窗口太小去噪效果差,窗口太大图像会模糊。嗯,这里要注意:中值滤波不会产生新的像素值,所以边缘保留得比较好。

# Python 示例:中值滤波
import cv2
import numpy as np

img = cv2.imread('ultrasound.png', 0)
# 3x3 中值滤波
median = cv2.medianBlur(img, 3)
# 5x5 中值滤波
median_5 = cv2.medianBlur(img, 5)
我的经验:对于超声图像,我一般先用 3×3 试试。如果噪声还明显,再换 5×5。别一上来就用大窗口,图像细节会丢。

3.1.2 高斯滤波

高斯滤波用的是加权平均。离中心越近的像素,权重越大。它对付高斯噪声(就是那种服从正态分布的噪声)效果很好。

我记得有一次处理胎儿超声图像,斑点噪声特别重。中值滤波效果一般,换成高斯滤波后,图像立马干净了。为什么?因为超声的散斑噪声其实更接近高斯分布。

# 高斯滤波
gaussian = cv2.GaussianBlur(img, (5, 5), 1.0)
# (5,5) 是核大小,1.0 是标准差
避坑指南:我曾经在项目中直接用默认参数,结果图像糊成一团。后来发现,标准差 sigma 要根据图像分辨率调整。分辨率高的图像,sigma 可以大一点;分辨率低的,sigma 要小。

3.1.3 双边滤波

双边滤波是“高级版”的高斯滤波。它不光考虑空间距离,还考虑像素值的差异。说白了,就是在去噪的同时尽量保留边缘

我特别喜欢双边滤波处理超声图像。因为超声图像里,组织边界往往就是诊断的关键。如果边界被模糊了,医生就没法判断了。

# 双边滤波
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
# 9 是直径,75 是颜色空间和坐标空间的 sigma
核心要点:双边滤波的参数调起来比较麻烦。我建议先固定直径(9或15),然后调颜色 sigma。颜色 sigma 越大,边缘保留越弱。一般 50-100 之间比较合适。

3.2 对比度增强:让细节“跳”出来

去完噪,下一步就是让图像“亮”起来。超声图像经常对比度不足,暗的地方太暗,亮的地方太亮。我们需要把灰度分布拉开。

3.2.1 直方图均衡化

直方图均衡化,说白了就是把灰度直方图“拉平”。原来集中在某个区间的灰度值,被均匀分布到整个 0-255 范围。

这个方法简单粗暴,效果立竿见影。但有个问题:它会放大噪声。你想想看,噪声也被“均衡”了,变得明显了。

# 全局直方图均衡化
equ = cv2.equalizeHist(img)
我的建议:如果图像整体偏暗,可以用全局均衡化。但如果图像本身有亮有暗,就别用了。我吃过这个亏——把一张正常的图像均衡后,反而出现了伪影。

3.2.2 CLAHE(限制对比度自适应直方图均衡化)

CLAHE 是直方图均衡化的“升级版”。它把图像分成一个个小块,每个块单独做均衡化。而且它限制了对比度的放大倍数,不会让噪声“爆炸”。

我个人非常推荐 CLAHE。在超声图像处理中,它几乎是标配。为什么?因为超声图像往往局部对比度不足,全局均衡化解决不了这个问题。

# CLAHE
clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
clahe_img = clahe.apply(img)
参数调优:
  • clipLimit:对比度限制。一般 2.0-3.0。太大噪声会放大,太小效果不明显。
  • tileGridSize:分块大小。8×8 是常用值。块越小,局部增强越强,但计算量也越大。

3.3 图像锐化:让边界更清晰

去噪和增强之后,图像可能还是有点“软”。锐化的目的就是让边缘更锐利,让组织边界更清晰。

3.3.1 拉普拉斯算子

拉普拉斯算子是二阶微分算子。它检测图像中灰度变化剧烈的地方——说白了就是边缘。把边缘叠加回原图,图像就变锐了。

# 拉普拉斯锐化
laplacian = cv2.Laplacian(img, cv2.CV_64F)
sharpened = cv2.addWeighted(img, 1.5, laplacian, -0.5, 0)
注意:拉普拉斯算子对噪声非常敏感。如果图像去噪不彻底,锐化后噪声会被放大。我曾经在项目中先锐化后去噪,结果图像全是噪点。后来改成先去噪后锐化,效果就好多了。

3.3.2 Sobel 算子

Sobel 算子是一阶微分算子。它计算图像在 x 方向和 y 方向的梯度。相比拉普拉斯,它对噪声不那么敏感。

# Sobel 锐化
sobelx = cv2.Sobel(img, cv2.CV_64F, 1, 0, ksize=3)
sobely = cv2.Sobel(img, cv2.CV_64F, 0, 1, ksize=3)
sobel = cv2.magnitude(sobelx, sobely)
sharpened_sobel = cv2.addWeighted(img, 1.0, sobel, 0.5, 0)

3.3.3 非锐化掩模(Unsharp Masking)

这个方法我特别喜欢。原理很简单:先模糊图像,然后用原图减去模糊图,得到“边缘信息”,再把边缘信息加回去。

# 非锐化掩模
blurred = cv2.GaussianBlur(img, (5, 5), 1.0)
edges = cv2.subtract(img, blurred)
sharpened_um = cv2.addWeighted(img, 1.0, edges, 1.5, 0)
我的经验:非锐化掩模的参数很好调。模糊程度控制边缘的粗细,叠加权重控制锐化强度。我一般先用 1.0 的权重试试,不够再加。

3.4 完整的预处理流水线

好了,我们把这三步串起来。这是我个人常用的流水线:

  1. 去噪:先用双边滤波(保留边缘),或者中值滤波(速度快)。
  2. 增强:用 CLAHE 提升局部对比度。
  3. 锐化:用非锐化掩模让边界清晰。
# 完整流水线示例
def preprocess_ultrasound(img):
    # 1. 去噪
    denoised = cv2.bilateralFilter(img, 9, 75, 75)
    # 2. 增强
    clahe = cv2.createCLAHE(clipLimit=2.0, tileGridSize=(8,8))
    enhanced = clahe.apply(denoised)
    # 3. 锐化
    blurred = cv2.GaussianBlur(enhanced, (5, 5), 1.0)
    edges = cv2.subtract(enhanced, blurred)
    sharpened = cv2.addWeighted(enhanced, 1.0, edges, 1.5, 0)
    return sharpened
总结一下:
  • 去噪是基础,不做后面都是白搭。
  • CLAHE 是超声图像的“神器”,强烈推荐。
  • 锐化要适度,过犹不及。

嗯,这一章就到这里。下一章我们聊聊图像分割——把感兴趣的区域从背景中“抠”出来。到时候见。