1. 指纹图像采集与预处理

指纹图像采集与预处理 学习卡片

任务说明

本案例实现从指纹传感器读取原始图像数据,并完成灰度化、去噪与增强三项预处理操作。输入为传感器输出的RAW格式图像(8位灰度或Bayer模式),输出为可直接用于后续特征提取的增强灰度图。代码采用模块化设计,核心处理逻辑与传感器硬件解耦,便于在不同平台上验证。

传感器数据读取与格式转换

指纹传感器通常输出8位灰度RAW数据或Bayer模式数据。本案例以8位灰度RAW为输入格式,通过标准文件读取模拟传感器数据流。若传感器输出Bayer格式,则需先进行去马赛克处理,本案例代码中已包含Bayer到RGB的转换函数,但默认路径为直接灰度读取。


// sensor_capture.h
#ifndef SENSOR_CAPTURE_H
#define SENSOR_CAPTURE_H

#include <stdint.h>
#include <stddef.h>

#define IMG_WIDTH   256
#define IMG_HEIGHT  256
#define RAW_SIZE    (IMG_WIDTH * IMG_HEIGHT)

// 读取传感器RAW数据(模拟:从文件读取)
int sensor_read_raw(const char* filepath, uint8_t* buffer, size_t size);

// Bayer模式转灰度(若传感器输出Bayer)
void bayer_to_gray(const uint8_t* bayer, uint8_t* gray, int w, int h);

#endif

// sensor_capture.c
#include "sensor_capture.h"
#include <stdio.h>
#include <string.h>

int sensor_read_raw(const char* filepath, uint8_t* buffer, size_t size) {
    FILE* fp = fopen(filepath, "rb");
    if (!fp) return -1;
    size_t rd = fread(buffer, 1, size, fp);
    fclose(fp);
    return (rd == size) ? 0 : -1;
}

void bayer_to_gray(const uint8_t* bayer, uint8_t* gray, int w, int h) {
    // 简化双线性插值去马赛克后转灰度
    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            int r = bayer[(y * w + x) * 3];
            int g = bayer[(y * w + x) * 3 + 1];
            int b = bayer[(y * w + x) * 3 + 2];
            gray[y * w + x] = (uint8_t)((r * 77 + g * 150 + b * 29) >> 8);
        }
    }
}

图像预处理核心实现

预处理流程包含三个步骤:灰度化(若输入为Bayer则转换,否则直接使用)、去噪(采用3×3中值滤波,有效去除椒盐噪声同时保留脊线边缘)、增强(使用CLAHE对比度受限自适应直方图均衡化,提升脊谷对比度)。中值滤波窗口大小和CLAHE的clip限制均设为经验值,可根据传感器噪声特性调整。


// preprocess.h
#ifndef PREPROCESS_H
#define PREPROCESS_H

#include <stdint.h>

void median_filter_3x3(const uint8_t* src, uint8_t* dst, int w, int h);
void clahe_enhance(const uint8_t* src, uint8_t* dst, int w, int h, 
                   int tile_size, float clip_limit);

#endif

// preprocess.c
#include "preprocess.h"
#include <stdlib.h>
#include <string.h>

// 3x3中值滤波
void median_filter_3x3(const uint8_t* src, uint8_t* dst, int w, int h) {
    int window[9];
    for (int y = 1; y < h - 1; y++) {
        for (int x = 1; x < w - 1; x++) {
            int idx = 0;
            for (int dy = -1; dy <= 1; dy++) {
                for (int dx = -1; dx <= 1; dx++) {
                    window[idx++] = src[(y + dy) * w + (x + dx)];
                }
            }
            // 简单排序取中值(插入排序)
            for (int i = 1; i < 9; i++) {
                int key = window[i];
                int j = i - 1;
                while (j >= 0 && window[j] > key) {
                    window[j + 1] = window[j];
                    j--;
                }
                window[j + 1] = key;
            }
            dst[y * w + x] = window[4];
        }
    }
    // 边界复制
    memcpy(dst, src, w);
    memcpy(dst + (h - 1) * w, src + (h - 1) * w, w);
    for (int y = 0; y < h; y++) {
        dst[y * w] = src[y * w];
        dst[y * w + w - 1] = src[y * w + w - 1];
    }
}

// CLAHE增强(简化实现:8x8 tile,双线性插值)
void clahe_enhance(const uint8_t* src, uint8_t* dst, int w, int h,
                   int tile_size, float clip_limit) {
    int tiles_x = w / tile_size;
    int tiles_y = h / tile_size;
    if (tiles_x < 2 || tiles_y < 2) return;

    // 计算每个tile的直方图并裁剪
    int hist[256];
    int* cdf = (int*)malloc(tiles_x * tiles_y * 256 * sizeof(int));
    if (!cdf) return;

    for (int ty = 0; ty < tiles_y; ty++) {
        for (int tx = 0; tx < tiles_x; tx++) {
            memset(hist, 0, sizeof(hist));
            for (int y = ty * tile_size; y < (ty + 1) * tile_size; y++) {
                for (int x = tx * tile_size; x < (tx + 1) * tile_size; x++) {
                    hist[src[y * w + x]]++;
                }
            }
            // 裁剪
            int clip = (int)(tile_size * tile_size * clip_limit / 256.0f);
            int excess = 0;
            for (int i = 0; i < 256; i++) {
                if (hist[i] > clip) {
                    excess += hist[i] - clip;
                    hist[i] = clip;
                }
            }
            int redist = excess / 256;
            for (int i = 0; i < 256; i++) hist[i] += redist;

            // CDF
            int sum = 0;
            int* tile_cdf = cdf + (ty * tiles_x + tx) * 256;
            for (int i = 0; i < 256; i++) {
                sum += hist[i];
                tile_cdf[i] = sum;
            }
            // 归一化到0-255
            int min_cdf = tile_cdf[0];
            int max_cdf = tile_cdf[255];
            if (max_cdf > min_cdf) {
                for (int i = 0; i < 256; i++) {
                    tile_cdf[i] = (int)((tile_cdf[i] - min_cdf) * 255.0f / (max_cdf - min_cdf));
                }
            }
        }
    }

    // 双线性插值映射
    for (int y = 0; y < h; y++) {
        for (int x = 0; x < w; x++) {
            float fx = (float)x / tile_size - 0.5f;
            float fy = (float)y / tile_size - 0.5f;
            int tx0 = (int)fx;
            int ty0 = (int)fy;
            if (tx0 < 0) tx0 = 0;
            if (ty0 < 0) ty0 = 0;
            if (tx0 >= tiles_x - 1) tx0 = tiles_x - 2;
            if (ty0 >= tiles_y - 1) ty0 = tiles_y - 2;
            float dx = fx - tx0;
            float dy = fy - ty0;

            int val = src[y * w + x];
            int* cdf00 = cdf + (ty0 * tiles_x + tx0) * 256;
            int* cdf10 = cdf + (ty0 * tiles_x + tx0 + 1) * 256;
            int* cdf01 = cdf + ((ty0 + 1) * tiles_x + tx0) * 256;
            int* cdf11 = cdf + ((ty0 + 1) * tiles_x + tx0 + 1) * 256;

            float v = (1 - dy) * ((1 - dx) * cdf00[val] + dx * cdf10[val]) +
                      dy * ((1 - dx) * cdf01[val] + dx * cdf11[val]);
            dst[y * w + x] = (uint8_t)v;
        }
    }
    free(cdf);
}

主流程与验证

主程序读取RAW文件,依次执行去噪和增强,输出处理后的图像文件。验证方法为:将输出图像与原始图像进行视觉对比,观察脊线是否更清晰、噪声是否减少。判定标准为:增强后图像脊谷对比度明显提升,且无块状伪影。


// main.c
#include <stdio.h>
#include <stdlib.h>
#include "sensor_capture.h"
#include "preprocess.h"

int main(int argc, char* argv[]) {
    if (argc < 3) {
        printf("Usage: %s <input.raw> <output.raw>\n", argv[0]);
        return 1;
    }

    uint8_t* raw = (uint8_t*)malloc(RAW_SIZE);
    uint8_t* denoised = (uint8_t*)malloc(RAW_SIZE);
    uint8_t* enhanced = (uint8_t*)malloc(RAW_SIZE);
    if (!raw || !denoised || !enhanced) {
        printf("Memory allocation failed\n");
        return 1;
    }

    if (sensor_read_raw(argv[1], raw, RAW_SIZE) != 0) {
        printf("Failed to read input file\n");
        return 1;
    }

    // 预处理流程
    median_filter_3x3(raw, denoised, IMG_WIDTH, IMG_HEIGHT);
    clahe_enhance(denoised, enhanced, IMG_WIDTH, IMG_HEIGHT, 32, 2.0f);

    // 保存结果
    FILE* fp = fopen(argv[2], "wb");
    if (!fp) {
        printf("Failed to open output file\n");
        return 1;
    }
    fwrite(enhanced, 1, RAW_SIZE, fp);
    fclose(fp);

    printf("Preprocessing complete. Output saved to %s\n", argv[2]);

    free(raw);
    free(denoised);
    free(enhanced);
    return 0;
}

编译与运行


gcc -O2 -o fingerprint_preprocess main.c sensor_capture.c preprocess.c -lm
./fingerprint_preprocess input.raw output.raw

输入文件为256×256的8位灰度RAW图像。运行后输出文件为预处理结果。可使用图像查看工具(如ImageJ)将RAW文件按256×256、8位灰度格式打开,对比输入输出图像的视觉效果。

验证判定标准:
  • 输出图像中指纹脊线连续清晰,无明显断点
  • 背景噪声(颗粒感)显著减少
  • 脊谷边界锐利,对比度高于原始图像
  • 图像边缘无异常伪影
指纹图像预处理流水线 RAW数据读取 sensor_read_raw() 灰度化 bayer_to_gray() 中值滤波去噪 median_filter_3x3() CLAHE增强 clahe_enhance() 256×256×8bit 灰度图 去噪后 增强图 关键参数 • 中值滤波窗口:3×3(保留脊线边缘) • CLAHE tile大小:32×32像素 • CLAHE clip限制:2.0(控制对比度增强幅度)

预期输出示例

对于一张包含清晰脊线的指纹RAW图像,处理后预期得到:脊线灰度值更接近255(白色),谷线更接近0(黑色),且图像中无孤立噪点。具体像素值取决于输入图像内容,但整体对比度应显著提升。