4. 坏点校正与动态缺陷检测
任务说明
本案例实现CMOS图像传感器坏点校正算法,核心思路为:静态坏点表校正(针对出厂标定的固定坏点)与动态坏点检测(针对使用中新增的坏点)相结合。最终输出校正后的图像,并给出坏点统计与图像质量评估指标。
算法原理
静态坏点表由传感器出厂标定或离线检测获得,记录坏点坐标与类型(亮点/暗点)。动态检测采用邻域中值比较法:对每个像素,计算其与周围8邻域中值的偏差,若超过自适应阈值且邻域本身方差较小(排除纹理区域),则判定为动态坏点。校正时,静态坏点直接查表替换,动态坏点用邻域中值替换。
完整实现代码
import numpy as np
from scipy import ndimage
import cv2
class BadPixelCorrector:
def __init__(self, static_map=None, dyn_thresh_ratio=3.0, min_var=25.0):
"""
static_map: dict {(row,col): 'hot'|'cold'} 静态坏点表
dyn_thresh_ratio: 动态阈值 = ratio * 邻域标准差
min_var: 邻域方差下限,低于此值视为平坦区才做动态检测
"""
self.static_map = static_map or {}
self.dyn_thresh_ratio = dyn_thresh_ratio
self.min_var = min_var
self.dynamic_bad_pixels = [] # 记录本次检测到的动态坏点
def _neighbor_median(self, img, r, c, half_win=1):
"""提取邻域像素(排除中心点)并计算中值"""
h, w = img.shape
r0, r1 = max(0, r-half_win), min(h, r+half_win+1)
c0, c1 = max(0, c-half_win), min(w, c+half_win+1)
block = img[r0:r1, c0:c1].flatten()
center_idx = (r - r0) * (c1 - c0) + (c - c0)
neighbors = np.delete(block, center_idx)
return np.median(neighbors)
def detect_dynamic(self, img):
"""动态坏点检测:返回坏点坐标列表"""
h, w = img.shape
# 使用3x3中值滤波结果作为参考
median_filtered = ndimage.median_filter(img, size=3)
# 局部方差(3x3窗口)
local_var = ndimage.generic_filter(img, lambda x: np.var(x), size=3)
bad_pixels = []
for r in range(1, h-1):
for c in range(1, w-1):
if (r, c) in self.static_map:
continue # 静态坏点不重复检测
if local_var[r, c] < self.min_var: # 平坦区域才检测
diff = abs(img[r, c] - median_filtered[r, c])
# 邻域标准差近似
std_est = np.sqrt(local_var[r, c])
if diff > self.dyn_thresh_ratio * std_est:
bad_pixels.append((r, c))
self.dynamic_bad_pixels = bad_pixels
return bad_pixels
def correct(self, img):
"""执行校正:先静态表,再动态检测与校正"""
img_out = img.copy()
h, w = img.shape
# 1. 静态坏点校正
for (r, c), btype in self.static_map.items():
if 0 <= r < h and 0 <= c < w:
img_out[r, c] = self._neighbor_median(img, r, c)
# 2. 动态坏点检测(基于原始图,避免静态校正影响检测)
self.detect_dynamic(img)
# 3. 动态坏点校正
for (r, c) in self.dynamic_bad_pixels:
img_out[r, c] = self._neighbor_median(img, r, c)
return img_out
def evaluate(self, img_orig, img_corrected):
"""图像质量评估:PSNR与坏点统计"""
# 模拟无坏点参考图(用中值滤波近似)
ref = ndimage.median_filter(img_orig, size=3)
mse = np.mean((img_corrected.astype(float) - ref.astype(float)) ** 2)
psnr = 10 * np.log10(255**2 / max(mse, 1e-10))
stats = {
'static_count': len(self.static_map),
'dynamic_count': len(self.dynamic_bad_pixels),
'total_corrected': len(self.static_map) + len(self.dynamic_bad_pixels),
'psnr_vs_median_ref': round(psnr, 2)
}
return stats
# ========== 最小可复现示例 ==========
if __name__ == "__main__":
# 生成模拟图像(含噪声与坏点)
np.random.seed(42)
h, w = 200, 200
clean = np.random.randint(50, 200, (h, w), dtype=np.uint8)
# 添加高斯噪声
noisy = clean + np.random.normal(0, 5, (h, w))
noisy = np.clip(noisy, 0, 255).astype(np.uint8)
# 注入坏点:5个静态坏点 + 3个动态坏点
static_map = {}
for i, (r, c) in enumerate([(50, 50), (100, 30), (150, 160), (80, 120), (20, 180)]):
noisy[r, c] = 255 if i % 2 == 0 else 0 # 亮点/暗点交替
static_map[(r, c)] = 'hot' if i % 2 == 0 else 'cold'
dynamic_pos = [(60, 140), (120, 90), (170, 50)]
for r, c in dynamic_pos:
noisy[r, c] = 255 # 亮点
# 执行校正
corrector = BadPixelCorrector(static_map=static_map, dyn_thresh_ratio=3.0, min_var=25.0)
corrected = corrector.correct(noisy)
# 验证:检测到的动态坏点应包含注入的3个
detected = set(corrector.dynamic_bad_pixels)
injected = set(dynamic_pos)
detection_rate = len(detected & injected) / len(injected)
# 输出评估
stats = corrector.evaluate(noisy, corrected)
print(f"检测到动态坏点: {sorted(corrector.dynamic_bad_pixels)}")
print(f"注入动态坏点检出率: {detection_rate:.0%}")
print(f"静态坏点校正数: {stats['static_count']}")
print(f"动态坏点校正数: {stats['dynamic_count']}")
print(f"PSNR(与中值参考比): {stats['psnr_vs_median_ref']} dB")
# 保存结果(可选)
# cv2.imwrite('corrected.png', corrected)
运行与验证
依赖安装:
pip install numpy scipy opencv-python
运行方式:直接执行脚本,观察终端输出。
通过条件:
- 注入的3个动态坏点全部被检出(检出率=100%),且无额外误检(平坦区阈值合理时);
- 静态坏点表5个坐标全部被校正;
- PSNR值高于未校正的含坏点图(可自行对比),表明校正后图像更接近真实场景。
关键参数说明
dyn_thresh_ratio:动态检测灵敏度,值越小越容易检出坏点,但误检率升高;min_var:平坦区判定阈值,纹理区域(方差大)不检测,避免误杀边缘细节;- 静态坏点表在真实工程中由传感器厂商提供或离线标定生成,本示例用注入方式模拟。
常见问题
- 动态坏点误检:若图像纹理丰富,可适当提高
min_var或增大dyn_thresh_ratio; - 静态表坐标越界:校正前需检查坐标是否在图像范围内,代码已做边界保护;
- 彩色图像:本算法针对单通道,彩色图需逐通道处理或转换到YUV后在亮度通道检测。