2. 指纹图像分割

任务说明

本案例处理一张包含背景噪声的指纹灰度图像,目标是将有效指纹区域(脊线与谷线所在的皮肤区域)与背景分离,生成二值掩膜,并去除图像四角及边缘的无效区域。输入为单张8位灰度指纹图像,输出为与输入同尺寸的掩膜图像(有效区域为白色,背景为黑色),以及应用掩膜后的分割结果图像。

实现方案

采用局部灰度方差与形态学后处理相结合的方法:指纹脊谷交替结构在局部窗口内产生较高灰度方差,而平滑背景区域方差较低。通过自适应阈值分割方差图得到初始掩膜,再使用开运算去除孤立噪点、闭运算填充孔洞,最终保留最大连通域作为有效指纹区域。

灰度指纹图 512×512 局部方差图 15×15窗口 σ²(x,y) 自适应阈值 Otsu二值化 初始掩膜 形态学后处理 开运算(5×5)去噪 闭运算(5×5)填洞 最大连通域提取 二值掩膜 有效区域=255 分割结果图 原图×掩膜 处理流水线

代码实现


import cv2
import numpy as np

def segment_fingerprint(image_path, output_mask_path, output_result_path):
    """
    指纹图像分割:将有效指纹区域与背景分离。
    参数:
        image_path: 输入灰度指纹图像路径
        output_mask_path: 输出二值掩膜保存路径
        output_result_path: 输出分割结果保存路径
    """
    # 1. 读取图像(强制灰度)
    img = cv2.imread(image_path, cv2.IMREAD_GRAYSCALE)
    if img is None:
        raise ValueError(f"无法读取图像: {image_path}")

    # 2. 计算局部灰度方差(15×15窗口)
    mean = cv2.blur(img, (15, 15))
    sqr_mean = cv2.blur(img.astype(np.float32) ** 2, (15, 15))
    variance = sqr_mean - mean.astype(np.float32) ** 2
    variance = np.clip(variance, 0, None).astype(np.uint8)

    # 3. Otsu自适应阈值分割方差图
    _, initial_mask = cv2.threshold(variance, 0, 255,
                                    cv2.THRESH_BINARY + cv2.THRESH_OTSU)

    # 4. 形态学后处理
    kernel = cv2.getStructuringElement(cv2.MORPH_ELLIPSE, (5, 5))
    opened = cv2.morphologyEx(initial_mask, cv2.MORPH_OPEN, kernel)   # 去除孤立噪点
    closed = cv2.morphologyEx(opened, cv2.MORPH_CLOSE, kernel)        # 填充内部孔洞

    # 5. 保留最大连通域(去除边缘残留背景块)
    num_labels, labels, stats, _ = cv2.connectedComponentsWithStats(closed, 8)
    if num_labels <= 1:
        final_mask = np.zeros_like(closed)
    else:
        # 排除背景标签0,取面积最大的连通域
        largest_label = 1 + np.argmax(stats[1:, cv2.CC_STAT_AREA])
        final_mask = np.where(labels == largest_label, 255, 0).astype(np.uint8)

    # 6. 应用掩膜得到分割结果
    result = cv2.bitwise_and(img, final_mask)

    # 7. 保存输出
    cv2.imwrite(output_mask_path, final_mask)
    cv2.imwrite(output_result_path, result)

    return final_mask, result

if __name__ == "__main__":
    # 示例调用(请替换为实际文件路径)
    mask, seg = segment_fingerprint(
        "fingerprint.png",
        "fingerprint_mask.png",
        "fingerprint_segmented.png"
    )
    print("分割完成,掩膜尺寸:", mask.shape)

依赖与运行

需要安装 opencv-python 与 numpy:


pip install opencv-python numpy

将待处理的指纹灰度图像命名为 fingerprint.png 放在脚本同目录,运行后生成 fingerprint_mask.png(二值掩膜)与 fingerprint_segmented.png(分割结果)。

验证方法

运行脚本后,通过以下标准判定分割是否成功:

  • 掩膜合理性:fingerprint_mask.png 中白色区域应覆盖指纹脊线密集的中央区域,四角与边缘背景为黑色,白色区域内部无明显孔洞,外部无大面积孤立白块。
  • 分割准确性:fingerprint_segmented.png 中背景区域像素值全部为0(纯黑),有效指纹区域的脊线纹理保持清晰可辨,未被误删。
  • 定量检查:在Python中执行以下代码统计掩膜前景占比,通常指纹有效区域占整幅图像的30%~60%为合理范围:

import cv2
mask = cv2.imread("fingerprint_mask.png", cv2.IMREAD_GRAYSCALE)
foreground_ratio = (mask > 0).sum() / mask.size
print(f"有效区域占比: {foreground_ratio:.2%}")

若前景占比明显偏离该范围(如低于10%或高于90%),说明阈值或形态学参数不适配当前图像,可调整窗口大小(15改为11或21)或核对输入图像是否为包含大面积背景的指纹图。