2. 去除模糊图像

任务说明

在AI训练数据集中,模糊图像会引入噪声标签,降低模型收敛速度与最终精度。本案例实现一个基于拉普拉斯方差(Variance of Laplacian)的模糊图像过滤器:输入一批图像路径,输出清晰图像列表与模糊图像列表,并将模糊图像移入指定目录,完成数据清洗。

实现原理

拉普拉斯算子对图像二阶求导,可突出边缘与纹理区域。清晰图像包含丰富的高频细节,其拉普拉斯响应方差较大;模糊图像高频分量少,方差较小。通过设定阈值即可区分清晰与模糊图像。本实现使用OpenCV的cv2.Laplacian计算灰度图的拉普拉斯响应,再求方差作为清晰度评分。

代码实现


import cv2
import os
import shutil
import numpy as np
from pathlib import Path

def compute_laplacian_variance(image_path):
    """计算单张图像的拉普拉斯方差(清晰度评分)"""
    img = cv2.imread(str(image_path))
    if img is None:
        raise ValueError(f"无法读取图像: {image_path}")
    gray = cv2.cvtColor(img, cv2.COLOR_BGR2GRAY)
    # ksize=3 使用3x3拉普拉斯核
    laplacian = cv2.Laplacian(gray, cv2.CV_64F, ksize=3)
    return laplacian.var()

def filter_blurry_images(input_dir, output_clear_dir, output_blurry_dir, threshold=100.0):
    """
    过滤模糊图像
    
    参数:
        input_dir: 原始图像目录
        output_clear_dir: 清晰图像输出目录
        output_blurry_dir: 模糊图像输出目录
        threshold: 拉普拉斯方差阈值,低于该值判定为模糊
    
    返回:
        (清晰图像列表, 模糊图像列表, 各图像评分字典)
    """
    # 创建输出目录
    Path(output_clear_dir).mkdir(parents=True, exist_ok=True)
    Path(output_blurry_dir).mkdir(parents=True, exist_ok=True)
    
    # 支持的图像扩展名
    valid_ext = {'.jpg', '.jpeg', '.png', '.bmp', '.tiff', '.webp'}
    
    clear_images = []
    blurry_images = []
    scores = {}
    
    # 遍历输入目录中的所有图像
    for img_path in Path(input_dir).iterdir():
        if img_path.suffix.lower() not in valid_ext:
            continue
        
        # 计算清晰度评分
        score = compute_laplacian_variance(img_path)
        scores[img_path.name] = score
        
        # 根据阈值分类并移动文件
        if score >= threshold:
            dest = Path(output_clear_dir) / img_path.name
            shutil.move(str(img_path), str(dest))
            clear_images.append(dest.name)
        else:
            dest = Path(output_blurry_dir) / img_path.name
            shutil.move(str(img_path), str(dest))
            blurry_images.append(dest.name)
    
    return clear_images, blurry_images, scores

if __name__ == "__main__":
    # 使用示例:请根据实际路径修改
    input_dir = "./raw_images"          # 待清洗的原始图像目录
    clear_dir = "./cleared_images"      # 清洗后清晰图像目录
    blurry_dir = "./blurry_images"      # 被过滤的模糊图像目录
    
    # 执行过滤,阈值设为100.0(可根据数据分布调整)
    clear_list, blurry_list, score_dict = filter_blurry_images(
        input_dir, clear_dir, blurry_dir, threshold=100.0
    )
    
    # 打印统计信息
    print(f"清晰图像数量: {len(clear_list)}")
    print(f"模糊图像数量: {len(blurry_list)}")
    print("\n各图像清晰度评分:")
    for name, score in sorted(score_dict.items(), key=lambda x: x[1]):
        status = "清晰" if score >= 100.0 else "模糊"
        print(f"  {name}: {score:.2f} -> {status}")

落地与运行

依赖安装:


pip install opencv-python numpy

运行步骤:

  1. 创建目录结构:raw_images(存放待清洗图像)、cleared_images、blurry_images(后两个可自动创建);
  2. 将待清洗图像放入raw_images目录;
  3. 按需调整代码末尾的input_dir路径与threshold阈值;
  4. 执行脚本:python blur_filter.py。

验证方法与判定标准

功能验证:

  1. 运行脚本后,检查cleared_images与blurry_images目录是否已生成,且原raw_images目录中的图像已被移走;
  2. 控制台输出的清晰/模糊图像数量之和应等于输入图像总数;
  3. 随机打开两个目录中的图像,目视确认清晰目录中无明显模糊图像,模糊目录中图像确实模糊。

阈值调优验证:

  1. 若清晰图像被误判为模糊,可降低阈值(如50.0);若模糊图像未被过滤,可提高阈值(如150.0);
  2. 观察输出的评分列表,找到清晰与模糊图像评分的分界值,据此设置合理阈值。

判定标准:脚本无报错执行完毕;输出统计数量与输入一致;两个输出目录中的图像内容与分类结果相符,即达到本案例目标。

数据流与判定逻辑

raw_images/ 原始图像目录 拉普拉斯方差计算 cv2.Laplacian(CV_64F) score = var(响应) ksize=3 score ≥ threshold? cleared_images/ 清晰图像保留 是 blurry_images/ 模糊图像过滤 否 输出:清晰/模糊图像列表 + 每张图像评分 用于人工复核与阈值调优

上图展示了本案例的数据处理流水线:图像从输入目录读取后,经拉普拉斯方差计算得到清晰度评分,再与阈值比较,最终分别落入清晰或模糊输出目录。评分列表同时输出,便于人工复核与阈值调整。