3、图像预处理:去噪滤波、对比度增强与图像锐化
各位同学,欢迎来到第三章。这一章我们聊聊图像预处理。说白了,就是给超声图像“洗把脸”,让它更干净、更清晰。
我做了这么多年超声图像处理,可以负责任地告诉你:预处理做得好,后面分析省一半的力气。你想想看,一张满是噪声、对比度又差的图像,你拿去做分割、做测量,结果能准吗?
这一章我们重点讲三块:去噪滤波、对比度增强、图像锐化。这三步是流水线的“基本功”,也是我每次必做的步骤。
3.1 去噪滤波:把脏东西去掉
超声图像天生就有噪声。斑点噪声、散斑噪声,说白了就是图像上那些“雪花点”。我刚开始做的时候,总想着用最复杂的滤波器,后来发现——简单的方法往往最有效。
3.1.1 中值滤波
中值滤波的原理很简单:把像素点周围的值排个序,取中间那个。它对椒盐噪声特别有效。什么是椒盐噪声?就是图像上随机出现的白点和黑点,像撒了盐和胡椒一样。
我个人习惯用 3×3 或 5×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.1.2 高斯滤波
高斯滤波用的是加权平均。离中心越近的像素,权重越大。它对付高斯噪声(就是那种服从正态分布的噪声)效果很好。
我记得有一次处理胎儿超声图像,斑点噪声特别重。中值滤波效果一般,换成高斯滤波后,图像立马干净了。为什么?因为超声的散斑噪声其实更接近高斯分布。
# 高斯滤波
gaussian = cv2.GaussianBlur(img, (5, 5), 1.0)
# (5,5) 是核大小,1.0 是标准差
3.1.3 双边滤波
双边滤波是“高级版”的高斯滤波。它不光考虑空间距离,还考虑像素值的差异。说白了,就是在去噪的同时尽量保留边缘。
我特别喜欢双边滤波处理超声图像。因为超声图像里,组织边界往往就是诊断的关键。如果边界被模糊了,医生就没法判断了。
# 双边滤波
bilateral = cv2.bilateralFilter(img, 9, 75, 75)
# 9 是直径,75 是颜色空间和坐标空间的 sigma
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)
3.4 完整的预处理流水线
好了,我们把这三步串起来。这是我个人常用的流水线:
- 去噪:先用双边滤波(保留边缘),或者中值滤波(速度快)。
- 增强:用 CLAHE 提升局部对比度。
- 锐化:用非锐化掩模让边界清晰。
# 完整流水线示例
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 是超声图像的“神器”,强烈推荐。
- 锐化要适度,过犹不及。
嗯,这一章就到这里。下一章我们聊聊图像分割——把感兴趣的区域从背景中“抠”出来。到时候见。