4. 坏点检测与校正
任务目标
开发坏点检测与校正算法,识别图像传感器中的固定坏点(包括死点与热噪点),并通过邻域插值完成校正,消除图像中的固定噪点伪影。
核心原理
坏点表现为与邻域灰度差异极大的固定位置像素。检测策略:对多帧静态场景图像取中值或均值作为基准帧,计算每个像素与邻域中值的偏差,超过自适应阈值则判定为坏点。校正采用3×3邻域中值替换,保留边缘细节。
完整实现
import numpy as np
from scipy import ndimage
def detect_bad_pixels(frames, k=3, threshold_ratio=3.0):
"""
基于多帧静态场景检测坏点
frames: list/np.array, 形状 (N, H, W) 的灰度帧序列
k: 邻域窗口大小(奇数)
threshold_ratio: 偏差阈值倍数(相对于邻域标准差)
返回: 坏点掩码 (H, W),True表示坏点
"""
frames = np.asarray(frames, dtype=np.float32)
N, H, W = frames.shape
# 1. 多帧中值合成基准帧(抑制随机噪声)
base = np.median(frames, axis=0)
# 2. 邻域中值与标准差(排除中心像素)
footprint = np.ones((k, k), dtype=bool)
footprint[k//2, k//2] = False
neigh_median = ndimage.median_filter(base, size=k, mode='nearest')
# 邻域标准差:用每个像素与邻域中值的偏差的局部中值近似(鲁棒)
abs_dev = np.abs(base - neigh_median)
local_std = ndimage.median_filter(abs_dev, size=k, mode='nearest') * 1.4826
# 3. 自适应阈值判定
threshold = threshold_ratio * local_std + 1e-6
mask = abs_dev > threshold
# 4. 边缘保护:排除图像边界2像素(邻域不完整)
border = k // 2
mask[:border, :] = False
mask[-border:, :] = False
mask[:, :border] = False
mask[:, -border:] = False
return mask
def correct_bad_pixels(image, bad_mask, k=3):
"""
对坏点进行邻域中值校正
image: (H, W) 灰度图
bad_mask: (H, W) bool掩码
返回: 校正后图像
"""
image = np.asarray(image, dtype=np.float32)
corrected = image.copy()
# 仅对坏点位置计算邻域中值(排除坏点自身)
footprint = np.ones((k, k), dtype=bool)
footprint[k//2, k//2] = False
# 计算全图邻域中值(不含中心)
neigh_median = ndimage.median_filter(
image, size=k, footprint=footprint, mode='nearest'
)
# 替换坏点
corrected[bad_mask] = neigh_median[bad_mask]
return corrected
# ========== 最小验证闭环 ==========
if __name__ == "__main__":
# 生成模拟数据:100x100 灰度图,含5个坏点
H, W = 100, 100
rng = np.random.default_rng(42)
clean = rng.integers(50, 200, size=(H, W), dtype=np.uint8).astype(np.float32)
# 注入坏点(固定位置)
bad_positions = [(20, 30), (45, 67), (80, 15), (10, 90), (60, 50)]
for y, x in bad_positions:
clean[y, x] = 255.0 # 死点(过亮)
# 生成8帧静态场景(含轻微随机噪声)
frames = []
for _ in range(8):
noisy = clean + rng.normal(0, 2.0, size=(H, W))
frames.append(np.clip(noisy, 0, 255))
# 检测
mask = detect_bad_pixels(frames, k=3, threshold_ratio=3.0)
# 校正
corrected = correct_bad_pixels(clean, mask)
# 验证:检测准确率
true_mask = np.zeros((H, W), dtype=bool)
for y, x in bad_positions:
true_mask[y, x] = True
tp = np.sum(mask & true_mask)
fp = np.sum(mask & ~true_mask)
fn = np.sum(~mask & true_mask)
print(f"检测到坏点: {np.sum(mask)} 个")
print(f"真阳性(正确检出): {tp} / {len(bad_positions)}")
print(f"误检: {fp} 个")
print(f"漏检: {fn} 个")
# 校正效果:坏点位置校正前后对比
print("\n坏点校正值对比(原值 -> 校正值):")
for y, x in bad_positions:
print(f" ({y},{x}): {clean[y,x]:.1f} -> {corrected[y,x]:.1f}")
运行方式
pip install numpy scipy
python bad_pixel_correction.py
验证方法与通过标准
- 检测准确率:程序输出中“真阳性”应等于5(全部检出),误检与漏检为0或极小值(受噪声影响可能有个别误检,但应远小于正确检出数)。
- 校正效果:坏点位置校正值应接近周围邻域灰度(约50~200区间),不再保持255的异常值。
- 视觉确认:将校正前后图像并排显示或保存,坏点位置不再出现固定亮点。
关键参数说明
threshold_ratio=3.0:偏差超过邻域中值偏差的3倍(对应约3σ)判为坏点。若误检多可调至3.5~4.0;若漏检多可调至2.0~2.5。frames数量建议≥5帧,帧间噪声独立时中值合成可有效抑制随机噪声,避免将噪声误判为坏点。- 实际相机场景中,应拍摄同一静态物体(如均匀光照下的平坦表面)的多帧图像作为输入。