一、算法原理和头文件
1.1 算法简介
贝叶斯非局部均值(Bayesian Non-Local Means, BNLM)是经典NLM算法的改进,通过贝叶斯框架更好地估计像素权重,提高降噪效果。
1.2 头文件定义 (bnlm.h)
c
#ifndef BNLM_H
#define BNLM_H
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <time.h>
// 数据类型定义
typedef float PixelType;
typedef float** Image2D;
typedef double** DoubleImage2D;
typedef double ComplexNumber[2]; // 实部和虚部
// 图像结构体
typedef struct {
PixelType* data; // 一维数据存储
Image2D pixels; // 二维像素访问
int width; // 图像宽度
int height; // 图像高度
int channels; // 通道数(1:灰度, 3:RGB)
int max_val; // 最大像素值(通常255)
} Image;
// BNLM参数结构体
typedef struct {
int patch_size; // 块大小(奇数)
int search_window; // 搜索窗口大小(奇数)
float h_param; // 滤波参数
float sigma; // 噪声标准差
int use_bayesian; // 是否使用贝叶斯框架
int use_fast; // 是否使用快速算法
int num_iterations; // 迭代次数
float lambda; // 正则化参数
} BNLM_Params;
// 贝叶斯先验模型
typedef enum {
PRIOR_GAUSSIAN = 0, // 高斯先验
PRIOR_LAPLACIAN, // 拉普拉斯先验
PRIOR_HYPERBOLIC, // 双曲先验
PRIOR_NONLOCAL_MEANS // 非局部均值先验
} PriorModel;
// 噪声模型
typedef enum {
NOISE_GAUSSIAN = 0, // 高斯噪声
NOISE_POISSON, // 泊松噪声
NOISE_SPECKLE, // 散斑噪声
NOISE_GAUSS_POISSON // 高斯-泊松混合
} NoiseModel;
// 块匹配结果
typedef struct {
int x; // 块中心x坐标
int y; // 块中心y坐标
float weight; // 权重
float distance; // 距离
} PatchMatch;
// 函数声明
// 内存管理
Image* image_create(int width, int height, int channels);
void image_free(Image* img);
Image* image_load_pgm(const char* filename);
int image_save_pgm(const char* filename, const Image* img);
Image* image_load_raw(const char* filename, int width, int height, int channels);
int image_save_raw(const char* filename, const Image* img);
// 基本图像处理
Image* image_clone(const Image* src);
Image* image_add_noise(const Image* src, float sigma, NoiseModel model);
Image* image_gaussian_blur(const Image* src, int kernel_size, float sigma);
Image* image_median_filter(const Image* src, int window_size);
// BNLM核心算法
Image* bnlm_denoise(const Image* noisy_img, BNLM_Params params);
Image* bnlm_bayesian_denoise(const Image* noisy_img, BNLM_Params params);
Image* bnlm_fast_denoise(const Image* noisy_img, BNLM_Params params);
// 辅助函数
float compute_patch_distance(const Image* img, int x1, int y1,
int x2, int y2, int patch_size, int channel);
float compute_bayesian_weight(const Image* img, int x1, int y1,
int x2, int y2, int patch_size,
float h, float sigma, PriorModel prior);
float compute_noise_variance_estimate(const Image* img, int x, int y,
int window_size, int channel);
void estimate_noise_statistics(const Image* img, float* sigma, NoiseModel* model);
// 评估函数
float compute_psnr(const Image* orig, const Image* denoised);
float compute_ssim(const Image* orig, const Image* denoised);
float compute_mse(const Image* orig, const Image* denoised);
// 优化算法
Image* bnlm_optimize_gradient_descent(const Image* noisy_img, BNLM_Params params);
Image* bnlm_optimize_admm(const Image* noisy_img, BNLM_Params params);
// GPU加速(如果有CUDA)
#ifdef USE_CUDA
Image* bnlm_denoise_gpu(const Image* noisy_img, BNLM_Params params);
#endif
// 多尺度处理
Image* bnlm_multiscale_denoise(const Image* noisy_img, BNLM_Params params);
Image* bnlm_wavelet_denoise(const Image* noisy_img, BNLM_Params params);
#endif /* BNLM_H */
二、核心数据结构实现
2.1 内存管理和基本操作 (bnlm_core.c)
c
#include "bnlm.h"
// 创建图像
Image* image_create(int width, int height, int channels) {
if (width <= 0 || height <= 0 || channels <= 0) {
return NULL;
}
Image* img = (Image*)malloc(sizeof(Image));
if (!img) {
return NULL;
}
img->width = width;
img->height = height;
img->channels = channels;
img->max_val = 255;
// 分配一维数据
size_t data_size = width * height * channels * sizeof(PixelType);
img->data = (PixelType*)malloc(data_size);
if (!img->data) {
free(img);
return NULL;
}
// 分配二维指针数组
img->pixels = (PixelType**)malloc(height * sizeof(PixelType*));
if (!img->pixels) {
free(img->data);
free(img);
return NULL;
}
// 设置行指针
for (int i = 0; i < height; i++) {
img->pixels[i] = &img->data[i * width * channels];
}
// 初始化为0
memset(img->data, 0, data_size);
return img;
}
// 释放图像
void image_free(Image* img) {
if (!img) return;
if (img->data) free(img->data);
if (img->pixels) free(img->pixels);
free(img);
}
// 克隆图像
Image* image_clone(const Image* src) {
if (!src) return NULL;
Image* dst = image_create(src->width, src->height, src->channels);
if (!dst) return NULL;
size_t data_size = src->width * src->height * src->channels * sizeof(PixelType);
memcpy(dst->data, src->data, data_size);
dst->max_val = src->max_val;
return dst;
}
// 获取像素值
static inline PixelType get_pixel(const Image* img, int x, int y, int channel) {
if (x < 0 || x >= img->width || y < 0 || y >= img->height) {
return 0;
}
return img->pixels[y][x * img->channels + channel];
}
// 设置像素值
static inline void set_pixel(Image* img, int x, int y, int channel, PixelType value) {
if (x < 0 || x >= img->width || y < 0 || y >= img->height) {
return;
}
img->pixels[y][x * img->channels + channel] = value;
}
三、基本NLM算法实现
3.1 经典NLM算法 (bnlm_basic.c)
c
#include "bnlm.h"
#include <omp.h>
// 计算块之间的欧氏距离
float compute_patch_distance(const Image* img, int x1, int y1,
int x2, int y2, int patch_size, int channel) {
int half_patch = patch_size / 2;
float distance = 0.0f;
int count = 0;
for (int dy = -half_patch; dy <= half_patch; dy++) {
for (int dx = -half_patch; dx <= half_patch; dx++) {
int nx1 = x1 + dx;
int ny1 = y1 + dy;
int nx2 = x2 + dx;
int ny2 = y2 + dy;
// 检查边界
if (nx1 >= 0 && nx1 < img->width && ny1 >= 0 && ny1 < img->height &&
nx2 >= 0 && nx2 < img->width && ny2 >= 0 && ny2 < img->height) {
PixelType p1 = get_pixel(img, nx1, ny1, channel);
PixelType p2 = get_pixel(img, nx2, ny2, channel);
float diff = p1 - p2;
distance += diff * diff;
count++;
}
}
}
if (count == 0) return 0.0f;
return distance / count; // 平均每个像素的距离
}
// 经典NLM算法
Image* bnlm_classic_denoise(const Image* noisy_img, BNLM_Params params) {
if (!noisy_img) return NULL;
int width = noisy_img->width;
int height = noisy_img->height;
int channels = noisy_img->channels;
// 创建输出图像
Image* denoised = image_create(width, height, channels);
if (!denoised) return NULL;
int half_patch = params.patch_size / 2;
int half_window = params.search_window / 2;
float h_squared = params.h_param * params.h_param;
// 预处理:计算积分图像以加速
DoubleImage2D* integral_images = NULL;
DoubleImage2D* squared_integral_images = NULL;
if (params.use_fast) {
// 为每个通道创建积分图像
integral_images = (DoubleImage2D*)malloc(channels * sizeof(DoubleImage2D));
squared_integral_images = (DoubleImage2D*)malloc(channels * sizeof(DoubleImage2D));
for (int c = 0; c < channels; c++) {
// 创建积分图像
integral_images[c] = (double**)malloc((height + 1) * sizeof(double*));
squared_integral_images[c] = (double**)malloc((height + 1) * sizeof(double*));
for (int i = 0; i <= height; i++) {
integral_images[c][i] = (double*)malloc((width + 1) * sizeof(double));
squared_integral_images[c][i] = (double*)malloc((width + 1) * sizeof(double));
}
// 计算积分图像
for (int y = 0; y <= height; y++) {
for (int x = 0; x <= width; x++) {
if (x == 0 || y == 0) {
integral_images[c][y][x] = 0.0;
squared_integral_images[c][y][x] = 0.0;
} else {
PixelType val = get_pixel(noisy_img, x-1, y-1, c);
integral_images[c][y][x] = integral_images[c][y-1][x] +
integral_images[c][y][x-1] -
integral_images[c][y-1][x-1] + val;
squared_integral_images[c][y][x] = squared_integral_images[c][y-1][x] +
squared_integral_images[c][y][x-1] -
squared_integral_images[c][y-1][x-1] +
val * val;
}
}
}
}
}
// 主处理循环
#pragma omp parallel for collapse(2) schedule(dynamic)
for (int c = 0; c < channels; c++) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float sum_weights = 0.0f;
float sum_values = 0.0f;
// 搜索窗口
int search_start_y = fmax(0, y - half_window);
int search_end_y = fmin(height - 1, y + half_window);
int search_start_x = fmax(0, x - half_window);
int search_end_x = fmin(width - 1, x + half_window);
for (int j = search_start_y; j <= search_end_y; j++) {
for (int i = search_start_x; i <= search_end_x; i++) {
// 如果是中心像素,权重最大
if (i == x && j == y) {
float weight = 1.0f; // 自权重
sum_weights += weight;
sum_values += weight * get_pixel(noisy_img, i, j, c);
continue;
}
// 计算块距离
float distance = 0.0f;
if (params.use_fast && integral_images) {
// 使用积分图像加速计算块距离
int patch_start_x1 = fmax(0, x - half_patch);
int patch_start_y1 = fmax(0, y - half_patch);
int patch_end_x1 = fmin(width - 1, x + half_patch);
int patch_end_y1 = fmin(height - 1, y + half_patch);
int patch_start_x2 = fmax(0, i - half_patch);
int patch_start_y2 = fmax(0, j - half_patch);
int patch_end_x2 = fmin(width - 1, i + half_patch);
int patch_end_y2 = fmin(height - 1, j + half_patch);
// 计算两个块的统计数据
double sum1 = integral_images[c][patch_end_y1+1][patch_end_x1+1] -
integral_images[c][patch_start_y1][patch_end_x1+1] -
integral_images[c][patch_end_y1+1][patch_start_x1] +
integral_images[c][patch_start_y1][patch_start_x1];
double sum_sq1 = squared_integral_images[c][patch_end_y1+1][patch_end_x1+1] -
squared_integral_images[c][patch_start_y1][patch_end_x1+1] -
squared_integral_images[c][patch_end_y1+1][patch_start_x1] +
squared_integral_images[c][patch_start_y1][patch_start_x1];
double sum2 = integral_images[c][patch_end_y2+1][patch_end_x2+1] -
integral_images[c][patch_start_y2][patch_end_x2+1] -
integral_images[c][patch_end_y2+1][patch_start_x2] +
integral_images[c][patch_start_y2][patch_start_x2];
double sum_sq2 = squared_integral_images[c][patch_end_y2+1][patch_end_x2+1] -
squared_integral_images[c][patch_start_y2][patch_end_x2+1] -
squared_integral_images[c][patch_end_y2+1][patch_start_x2] +
squared_integral_images[c][patch_start_y2][patch_start_x2];
int patch_width1 = patch_end_x1 - patch_start_x1 + 1;
int patch_height1 = patch_end_y1 - patch_start_y1 + 1;
int patch_size1 = patch_width1 * patch_height1;
int patch_width2 = patch_end_x2 - patch_start_x2 + 1;
int patch_height2 = patch_end_y2 - patch_start_y2 + 1;
int patch_size2 = patch_width2 * patch_height2;
double mean1 = sum1 / patch_size1;
double mean2 = sum2 / patch_size2;
// 计算两个块之间的平方差
double cross_sum = 0.0;
for (int dy = 0; dy < patch_height1 && dy < patch_height2; dy++) {
for (int dx = 0; dx < patch_width1 && dx < patch_width2; dx++) {
PixelType p1 = get_pixel(noisy_img, patch_start_x1 + dx,
patch_start_y1 + dy, c);
PixelType p2 = get_pixel(noisy_img, patch_start_x2 + dx,
patch_start_y2 + dy, c);
cross_sum += (p1 - p2) * (p1 - p2);
}
}
int min_patch_size = fmin(patch_size1, patch_size2);
distance = (float)(cross_sum / min_patch_size);
} else {
// 常规计算
distance = compute_patch_distance(noisy_img, x, y, i, j,
params.patch_size, c);
}
// 计算权重
float weight = expf(-distance / h_squared);
sum_weights += weight;
sum_values += weight * get_pixel(noisy_img, i, j, c);
}
}
// 避免除零
if (sum_weights > 1e-6f) {
set_pixel(denoised, x, y, c, sum_values / sum_weights);
} else {
set_pixel(denoised, x, y, c, get_pixel(noisy_img, x, y, c));
}
}
}
}
// 清理积分图像内存
if (integral_images) {
for (int c = 0; c < channels; c++) {
for (int i = 0; i <= height; i++) {
free(integral_images[c][i]);
free(squared_integral_images[c][i]);
}
free(integral_images[c]);
free(squared_integral_images[c]);
}
free(integral_images);
free(squared_integral_images);
}
return denoised;
}
四、贝叶斯NLM算法
4.1 贝叶斯权重计算 (bnlm_bayesian.c)
c
#include "bnlm.h"
#include <math.h>
// 估计局部噪声方差
float compute_noise_variance_estimate(const Image* img, int x, int y,
int window_size, int channel) {
int half_window = window_size / 2;
float sum = 0.0f;
float sum_sq = 0.0f;
int count = 0;
for (int dy = -half_window; dy <= half_window; dy++) {
for (int dx = -half_window; dx <= half_window; dx++) {
int nx = x + dx;
int ny = y + dy;
if (nx >= 0 && nx < img->width && ny >= 0 && ny < img->height) {
PixelType val = get_pixel(img, nx, ny, channel);
sum += val;
sum_sq += val * val;
count++;
}
}
}
if (count < 4) return 0.0f;
float mean = sum / count;
float variance = (sum_sq / count) - (mean * mean);
// 确保方差非负
return fmaxf(variance, 0.0f);
}
// 贝叶斯权重计算
float compute_bayesian_weight(const Image* img, int x1, int y1,
int x2, int y2, int patch_size,
float h, float sigma, PriorModel prior) {
int half_patch = patch_size / 2;
float sum_distance = 0.0f;
int count = 0;
// 计算局部统计
float mean1 = 0.0f, mean2 = 0.0f;
float var1 = 0.0f, var2 = 0.0f;
float covariance = 0.0f;
// 计算两个块的均值和方差
for (int dy = -half_patch; dy <= half_patch; dy++) {
for (int dx = -half_patch; dx <= half_patch; dx++) {
int nx1 = x1 + dx;
int ny1 = y1 + dy;
int nx2 = x2 + dx;
int ny2 = y2 + dy;
if (nx1 >= 0 && nx1 < img->width && ny1 >= 0 && ny1 < img->height &&
nx2 >= 0 && nx2 < img->width && ny2 >= 0 && ny2 < img->height) {
PixelType p1 = get_pixel(img, nx1, ny1, 0); // 假设单通道
PixelType p2 = get_pixel(img, nx2, ny2, 0);
mean1 += p1;
mean2 += p2;
var1 += p1 * p1;
var2 += p2 * p2;
covariance += p1 * p2;
count++;
}
}
}
if (count == 0) return 0.0f;
mean1 /= count;
mean2 /= count;
var1 = var1 / count - mean1 * mean1;
var2 = var2 / count - mean2 * mean2;
covariance = covariance / count - mean1 * mean2;
// 计算信号方差(噪声方差已知为sigma^2)
float signal_var1 = fmaxf(var1 - sigma * sigma, 0.0f);
float signal_var2 = fmaxf(var2 - sigma * sigma, 0.0f);
// 计算协方差
float signal_covariance = covariance;
// 根据先验模型计算贝叶斯距离
float bayesian_distance = 0.0f;
switch (prior) {
case PRIOR_GAUSSIAN: {
// 高斯先验模型
float sigma1_sq = signal_var1 + sigma * sigma;
float sigma2_sq = signal_var2 + sigma * sigma;
// 计算KL散度
bayesian_distance = 0.5f * (sigma1_sq / sigma2_sq +
sigma2_sq / sigma1_sq - 2.0f);
break;
}
case PRIOR_LAPLACIAN: {
// 拉普拉斯先验模型
float diff_mean = fabs(mean1 - mean2);
bayesian_distance = diff_mean / (sigma + 1e-6f);
break;
}
case PRIOR_HYPERBOLIC: {
// 双曲先验模型
float corr = signal_covariance / sqrtf((signal_var1 + 1e-6f) *
(signal_var2 + 1e-6f));
bayesian_distance = 1.0f - fmaxf(fminf(corr, 1.0f), -1.0f);
break;
}
case PRIOR_NONLOCAL_MEANS: {
// 非局部均值先验
float diff_sq = 0.0f;
for (int dy = -half_patch; dy <= half_patch; dy++) {
for (int dx = -half_patch; dx <= half_patch; dx++) {
int nx1 = x1 + dx;
int ny1 = y1 + dy;
int nx2 = x2 + dx;
int ny2 = y2 + dy;
if (nx1 >= 0 && nx1 < img->width && ny1 >= 0 && ny1 < img->height &&
nx2 >= 0 && nx2 < img->width && ny2 >= 0 && ny2 < img->height) {
PixelType p1 = get_pixel(img, nx1, ny1, 0);
PixelType p2 = get_pixel(img, nx2, ny2, 0);
// 贝叶斯距离:考虑信号和噪声
float diff = p1 - p2;
float var_total = 2.0f * sigma * sigma +
signal_var1 + signal_var2;
diff_sq += diff * diff / var_total;
}
}
}
bayesian_distance = diff_sq / count;
break;
}
}
// 计算权重
float weight = expf(-bayesian_distance / (h * h));
return weight;
}
// 贝叶斯NLM算法
Image* bnlm_bayesian_denoise(const Image* noisy_img, BNLM_Params params) {
if (!noisy_img) return NULL;
int width = noisy_img->width;
int height = noisy_img->height;
// 创建输出图像
Image* denoised = image_create(width, height, 1);
if (!denoised) return NULL;
int half_patch = params.patch_size / 2;
int half_window = params.search_window / 2;
// 预处理:计算每个像素的局部噪声方差估计
float** noise_variance = (float**)malloc(height * sizeof(float*));
for (int y = 0; y < height; y++) {
noise_variance[y] = (float*)malloc(width * sizeof(float));
for (int x = 0; x < width; x++) {
noise_variance[y][x] = compute_noise_variance_estimate(noisy_img, x, y,
7, 0);
}
}
// 主处理循环
#pragma omp parallel for collapse(2) schedule(dynamic)
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float sum_weights = 0.0f;
float sum_values = 0.0f;
// 搜索窗口
int start_y = fmax(0, y - half_window);
int end_y = fmin(height - 1, y + half_window);
int start_x = fmax(0, x - half_window);
int end_x = fmin(width - 1, x + half_window);
for (int j = start_y; j <= end_y; j++) {
for (int i = start_x; i <= end_x; i++) {
// 中心像素权重
if (i == x && j == y) {
float weight = 1.0f;
sum_weights += weight;
sum_values += weight * get_pixel(noisy_img, i, j, 0);
continue;
}
// 使用贝叶斯权重
float weight = compute_bayesian_weight(noisy_img, x, y, i, j,
params.patch_size,
params.h_param,
noise_variance[y][x],
PRIOR_NONLOCAL_MEANS);
sum_weights += weight;
sum_values += weight * get_pixel(noisy_img, i, j, 0);
}
}
// 避免除零
if (sum_weights > 1e-6f) {
set_pixel(denoised, x, y, 0, sum_values / sum_weights);
} else {
set_pixel(denoised, x, y, 0, get_pixel(noisy_img, x, y, 0));
}
}
}
// 清理内存
for (int y = 0; y < height; y++) {
free(noise_variance[y]);
}
free(noise_variance);
return denoised;
}
五、优化的快速BNLM算法
5.1 使用PCA和预筛选 (bnlm_fast.c)
c
#include "bnlm.h"
// PCA降维结构
typedef struct {
float* eigenvectors; // 特征向量
float* eigenvalues; // 特征值
float* mean_vector; // 均值向量
int patch_dim; // 块维度
int reduced_dim; // 降维后维度
} PCA_Model;
// 预计算的块特征
typedef struct {
float* features; // 降维后的特征
float norm; // 特征范数
int x, y; // 块位置
} PatchFeature;
// 创建PCA模型
PCA_Model* pca_create(const Image* img, int patch_size, int num_samples) {
int patch_dim = patch_size * patch_size;
PCA_Model* pca = (PCA_Model*)malloc(sizeof(PCA_Model));
pca->patch_dim = patch_dim;
pca->reduced_dim = 0; // 需要计算
// 采样块
PatchFeature* samples = (PatchFeature*)malloc(num_samples * sizeof(PatchFeature));
pca->mean_vector = (float*)calloc(patch_dim, sizeof(float));
// 随机采样块
srand(time(NULL));
for (int i = 0; i < num_samples; i++) {
int x = rand() % (img->width - patch_size);
int y = rand() % (img->height - patch_size);
samples[i].features = (float*)malloc(patch_dim * sizeof(float));
// 提取块
int idx = 0;
for (int dy = 0; dy < patch_size; dy++) {
for (int dx = 0; dx < patch_size; dx++) {
samples[i].features[idx] = get_pixel(img, x + dx, y + dy, 0);
pca->mean_vector[idx] += samples[i].features[idx];
idx++;
}
}
}
// 计算均值
for (int i = 0; i < patch_dim; i++) {
pca->mean_vector[i] /= num_samples;
}
// 中心化数据
float** centered_data = (float**)malloc(num_samples * sizeof(float*));
for (int i = 0; i < num_samples; i++) {
centered_data[i] = (float*)malloc(patch_dim * sizeof(float));
for (int j = 0; j < patch_dim; j++) {
centered_data[i][j] = samples[i].features[j] - pca->mean_vector[j];
}
}
// 计算协方差矩阵(简化版)
// 注意:完整PCA实现需要计算协方差矩阵和特征值分解
// 这里使用简化的随机投影
pca->reduced_dim = 16; // 固定降维到16维
pca->eigenvectors = (float*)malloc(patch_dim * pca->reduced_dim * sizeof(float));
// 使用随机投影矩阵
srand(42);
for (int i = 0; i < patch_dim * pca->reduced_dim; i++) {
pca->eigenvectors[i] = (float)rand() / RAND_MAX - 0.5f;
}
// 清理
for (int i = 0; i < num_samples; i++) {
free(samples[i].features);
}
free(samples);
for (int i = 0; i < num_samples; i++) {
free(centered_data[i]);
}
free(centered_data);
return pca;
}
// 快速BNLM算法
Image* bnlm_fast_denoise(const Image* noisy_img, BNLM_Params params) {
if (!noisy_img) return NULL;
int width = noisy_img->width;
int height = noisy_img->height;
// 创建输出图像
Image* denoised = image_create(width, height, 1);
if (!denoised) return NULL;
int patch_size = params.patch_size;
int half_patch = patch_size / 2;
int half_window = params.search_window / 2;
float h_squared = params.h_param * params.h_param;
// 预计算PCA模型
PCA_Model* pca = pca_create(noisy_img, patch_size, 1000);
// 为每个像素预计算降维特征
PatchFeature** patch_features = (PatchFeature**)malloc(height * sizeof(PatchFeature*));
for (int y = 0; y < height; y++) {
patch_features[y] = (PatchFeature*)malloc(width * sizeof(PatchFeature));
for (int x = 0; x < width; x++) {
patch_features[y][x].x = x;
patch_features[y][x].y = y;
patch_features[y][x].features = (float*)malloc(pca->reduced_dim * sizeof(float));
// 提取块并投影
if (x >= half_patch && x < width - half_patch &&
y >= half_patch && y < height - half_patch) {
float* patch = (float*)malloc(patch_size * patch_size * sizeof(float));
int idx = 0;
for (int dy = -half_patch; dy <= half_patch; dy++) {
for (int dx = -half_patch; dx <= half_patch; dx++) {
patch[idx] = get_pixel(noisy_img, x + dx, y + dy, 0);
idx++;
}
}
// 中心化
for (int i = 0; i < pca->patch_dim; i++) {
patch[i] -= pca->mean_vector[i];
}
// 投影到低维空间
for (int d = 0; d < pca->reduced_dim; d++) {
float proj = 0.0f;
for (int i = 0; i < pca->patch_dim; i++) {
proj += patch[i] * pca->eigenvectors[d * pca->patch_dim + i];
}
patch_features[y][x].features[d] = proj;
}
// 计算范数
float norm = 0.0f;
for (int d = 0; d < pca->reduced_dim; d++) {
norm += patch_features[y][x].features[d] *
patch_features[y][x].features[d];
}
patch_features[y][x].norm = sqrtf(norm);
free(patch);
} else {
// 边界处理
for (int d = 0; d < pca->reduced_dim; d++) {
patch_features[y][x].features[d] = 0.0f;
}
patch_features[y][x].norm = 0.0f;
}
}
}
// 主处理循环
#pragma omp parallel for collapse(2) schedule(dynamic)
for (int y = half_patch; y < height - half_patch; y++) {
for (int x = half_patch; x < width - half_patch; x++) {
float sum_weights = 0.0f;
float sum_values = 0.0f;
// 当前块特征
float* feat1 = patch_features[y][x].features;
float norm1 = patch_features[y][x].norm;
// 搜索窗口
int start_y = fmax(half_patch, y - half_window);
int end_y = fmin(height - 1 - half_patch, y + half_window);
int start_x = fmax(half_patch, x - half_window);
int end_x = fmin(width - 1 - half_patch, x + half_window);
// 使用k-d树或预筛选加速
for (int j = start_y; j <= end_y; j++) {
for (int i = start_x; i <= end_x; i++) {
// 快速距离计算
float* feat2 = patch_features[j][i].features;
float norm2 = patch_features[j][i].norm;
// 计算余弦距离
float dot_product = 0.0f;
for (int d = 0; d < pca->reduced_dim; d++) {
dot_product += feat1[d] * feat2[d];
}
float cosine_sim = dot_product / (norm1 * norm2 + 1e-6f);
float distance = 1.0f - cosine_sim;
// 使用预筛选:如果距离太大,跳过
if (distance > 2.0f * h_squared) {
continue;
}
// 计算精确距离(如果需要)
float exact_distance = 0.0f;
if (distance < h_squared * 4.0f) {
for (int d = 0; d < pca->reduced_dim; d++) {
float diff = feat1[d] - feat2[d];
exact_distance += diff * diff;
}
exact_distance /= pca->reduced_dim;
} else {
exact_distance = distance;
}
// 计算权重
float weight = expf(-exact_distance / h_squared);
sum_weights += weight;
sum_values += weight * get_pixel(noisy_img, i, j, 0);
}
}
// 中心像素权重增强
float center_weight = 1.0f;
sum_weights += center_weight;
sum_values += center_weight * get_pixel(noisy_img, x, y, 0);
if (sum_weights > 1e-6f) {
set_pixel(denoised, x, y, 0, sum_values / sum_weights);
} else {
set_pixel(denoised, x, y, 0, get_pixel(noisy_img, x, y, 0));
}
}
}
// 边界处理
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
if (x < half_patch || x >= width - half_patch ||
y < half_patch || y >= height - half_patch) {
set_pixel(denoised, x, y, 0, get_pixel(noisy_img, x, y, 0));
}
}
}
// 清理内存
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
free(patch_features[y][x].features);
}
free(patch_features[y]);
}
free(patch_features);
free(pca->eigenvectors);
free(pca->mean_vector);
free(pca);
return denoised;
}
六、多尺度和迭代BNLM
6.1 多尺度BNLM (bnlm_multiscale.c)
c
#include "bnlm.h"
// 图像下采样
Image* image_downsample(const Image* src, float scale) {
if (!src || scale <= 0.0f || scale >= 1.0f) return NULL;
int new_width = (int)(src->width * scale);
int new_height = (int)(src->height * scale);
if (new_width < 1 || new_height < 1) return NULL;
Image* dst = image_create(new_width, new_height, src->channels);
if (!dst) return NULL;
float inv_scale = 1.0f / scale;
for (int c = 0; c < src->channels; c++) {
for (int y = 0; y < new_height; y++) {
for (int x = 0; x < new_width; x++) {
// 双线性插值
float src_x = x * inv_scale;
float src_y = y * inv_scale;
int x1 = (int)src_x;
int y1 = (int)src_y;
int x2 = x1 + 1;
int y2 = y1 + 1;
float dx = src_x - x1;
float dy = src_y - y1;
float p11 = get_pixel(src, x1, y1, c);
float p12 = get_pixel(src, x1, y2, c);
float p21 = get_pixel(src, x2, y1, c);
float p22 = get_pixel(src, x2, y2, c);
float value = p11 * (1.0f - dx) * (1.0f - dy) +
p21 * dx * (1.0f - dy) +
p12 * (1.0f - dx) * dy +
p22 * dx * dy;
set_pixel(dst, x, y, c, value);
}
}
}
return dst;
}
// 图像上采样
Image* image_upsample(const Image* src, int new_width, int new_height) {
if (!src) return NULL;
Image* dst = image_create(new_width, new_height, src->channels);
if (!dst) return NULL;
float scale_x = (float)src->width / new_width;
float scale_y = (float)src->height / new_height;
for (int c = 0; c < src->channels; c++) {
for (int y = 0; y < new_height; y++) {
for (int x = 0; x < new_width; x++) {
float src_x = x * scale_x;
float src_y = y * scale_y;
int x1 = (int)src_x;
int y1 = (int)src_y;
if (x1 < 0) x1 = 0;
if (x1 >= src->width) x1 = src->width - 1;
if (y1 < 0) y1 = 0;
if (y1 >= src->height) y1 = src->height - 1;
set_pixel(dst, x, y, c, get_pixel(src, x1, y1, c));
}
}
}
return dst;
}
// 多尺度BNLM
Image* bnlm_multiscale_denoise(const Image* noisy_img, BNLM_Params params) {
if (!noisy_img) return NULL;
int width = noisy_img->width;
int height = noisy_img->height;
// 创建金字塔
int num_scales = 3; // 3个尺度
Image* pyramid[num_scales];
BNLM_Params scale_params[num_scales];
// 最粗尺度
float scale = 0.5f;
pyramid[0] = image_downsample(noisy_img, scale);
scale_params[0] = params;
scale_params[0].patch_size = (int)(params.patch_size * scale);
scale_params[0].search_window = (int)(params.search_window * scale);
scale_params[0].h_param = params.h_param * scale;
// 中间尺度
scale = 0.75f;
pyramid[1] = image_downsample(noisy_img, scale);
scale_params[1] = params;
scale_params[1].patch_size = (int)(params.patch_size * scale);
scale_params[1].search_window = (int)(params.search_window * scale);
scale_params[1].h_param = params.h_param * scale;
// 原始尺度
pyramid[2] = image_clone(noisy_img);
scale_params[2] = params;
// 从粗到细处理
Image* denoised_coarse = bnlm_denoise(pyramid[0], scale_params[0]);
Image* denoised_mid = NULL;
Image* denoised_fine = NULL;
// 上采样并作为引导
Image* upsampled_coarse = image_upsample(denoised_coarse,
pyramid[1]->width,
pyramid[1]->height);
// 引导滤波
for (int y = 0; y < pyramid[1]->height; y++) {
for (int x = 0; x < pyramid[1]->width; x++) {
// 使用上采样的结果作为引导
float guide = get_pixel(upsampled_coarse, x, y, 0);
float noisy = get_pixel(pyramid[1], x, y, 0);
// 混合
float alpha = 0.7f;
float mixed = guide * alpha + noisy * (1.0f - alpha);
set_pixel(pyramid[1], x, y, 0, mixed);
}
}
denoised_mid = bnlm_denoise(pyramid[1], scale_params[1]);
// 再次上采样
Image* upsampled_mid = image_upsample(denoised_mid,
pyramid[2]->width,
pyramid[2]->height);
// 最终尺度引导
for (int y = 0; y < pyramid[2]->height; y++) {
for (int x = 0; x < pyramid[2]->width; x++) {
float guide = get_pixel(upsampled_mid, x, y, 0);
float noisy = get_pixel(pyramid[2], x, y, 0);
float alpha = 0.5f;
float mixed = guide * alpha + noisy * (1.0f - alpha);
set_pixel(pyramid[2], x, y, 0, mixed);
}
}
denoised_fine = bnlm_denoise(pyramid[2], scale_params[2]);
// 清理中间结果
for (int i = 0; i < num_scales; i++) {
image_free(pyramid[i]);
}
image_free(denoised_coarse);
image_free(denoised_mid);
image_free(upsampled_coarse);
image_free(upsampled_mid);
return denoised_fine;
}
七、评估和测试函数
7.1 质量评估 (bnlm_evaluate.c)
c
#include "bnlm.h"
// 计算峰值信噪比
float compute_psnr(const Image* orig, const Image* denoised) {
if (!orig || !denoised ||
orig->width != denoised->width ||
orig->height != denoised->height) {
return 0.0f;
}
float mse = compute_mse(orig, denoised);
if (mse < 1e-6f) return 100.0f; // 完美匹配
float max_val = 255.0f; // 假设8位图像
float psnr = 10.0f * log10f(max_val * max_val / mse);
return psnr;
}
// 计算结构相似性
float compute_ssim(const Image* orig, const Image* denoised) {
if (!orig || !denoised) return 0.0f;
int width = orig->width;
int height = orig->height;
float C1 = 6.5025f; // (0.01 * 255)^2
float C2 = 58.5225f; // (0.03 * 255)^2
float window_size = 8.0f;
int half_window = 4;
float ssim_total = 0.0f;
int count = 0;
for (int y = half_window; y < height - half_window; y++) {
for (int x = half_window; x < width - half_window; x++) {
// 计算局部窗口的统计量
float sum1 = 0.0f, sum2 = 0.0f;
float sum11 = 0.0f, sum22 = 0.0f, sum12 = 0.0f;
for (int dy = -half_window; dy <= half_window; dy++) {
for (int dx = -half_window; dx <= half_window; dx++) {
float p1 = get_pixel(orig, x + dx, y + dy, 0);
float p2 = get_pixel(denoised, x + dx, y + dy, 0);
sum1 += p1;
sum2 += p2;
sum11 += p1 * p1;
sum22 += p2 * p2;
sum12 += p1 * p2;
}
}
float n = window_size * window_size;
float mean1 = sum1 / n;
float mean2 = sum2 / n;
float var1 = sum11 / n - mean1 * mean1;
float var2 = sum22 / n - mean2 * mean2;
float covar = sum12 / n - mean1 * mean2;
// 计算SSIM
float ssim = ((2.0f * mean1 * mean2 + C1) * (2.0f * covar + C2)) /
((mean1 * mean1 + mean2 * mean2 + C1) *
(var1 + var2 + C2));
ssim_total += ssim;
count++;
}
}
if (count == 0) return 0.0f;
return ssim_total / count;
}
// 计算均方误差
float compute_mse(const Image* orig, const Image* denoised) {
if (!orig || !denoised) return 0.0f;
int width = orig->width;
int height = orig->height;
int channels = orig->channels;
float sum_sq_error = 0.0f;
int count = 0;
for (int c = 0; c < channels; c++) {
for (int y = 0; y < height; y++) {
for (int x = 0; x < width; x++) {
float diff = get_pixel(orig, x, y, c) -
get_pixel(denoised, x, y, c);
sum_sq_error += diff * diff;
count++;
}
}
}
if (count == 0) return 0.0f;
return sum_sq_error / count;
}
八、主程序示例
8.1 测试程序 (main.c)
c
#include "bnlm.h"
#include <time.h>
// 添加高斯噪声
Image* add_gaussian_noise(const Image* src, float sigma) {
if (!src) return NULL;
Image* noisy = image_clone(src);
if (!noisy) return NULL;
srand(time(NULL));
for (int c = 0; c < src->channels; c++) {
for (int y = 0; y < src->height; y++) {
for (int x = 0; x < src->width; x++) {
// Box-Muller变换生成高斯噪声
float u1 = (float)rand() / RAND_MAX;
float u2 = (float)rand() / RAND_MAX;
float z = sqrtf(-2.0f * logf(u1)) * cosf(2.0f * 3.1415926f * u2);
float noise = z * sigma;
float pixel = get_pixel(src, x, y, c) + noise;
// 裁剪到[0, 255]
if (pixel < 0.0f) pixel = 0.0f;
if (pixel > 255.0f) pixel = 255.0f;
set_pixel(noisy, x, y, c, pixel);
}
}
}
return noisy;
}
// 测试不同算法
void test_algorithms(const Image* original, const Image* noisy) {
BNLM_Params params = {
.patch_size = 7,
.search_window = 21,
.h_param = 10.0f,
.sigma = 25.0f,
.use_bayesian = 1,
.use_fast = 0,
.num_iterations = 1,
.lambda = 0.1f
};
printf("Testing BNLM Algorithms\n");
printf("=======================\n");
// 测试经典NLM
printf("\n1. Classic NLM:\n");
clock_t start = clock();
Image* denoised_classic = bnlm_classic_denoise(noisy, params);
clock_t end = clock();
if (denoised_classic) {
float psnr = compute_psnr(original, denoised_classic);
float ssim = compute_ssim(original, denoised_classic);
printf(" Time: %.2f seconds\n", (double)(end - start) / CLOCKS_PER_SEC);
printf(" PSNR: %.2f dB\n", psnr);
printf(" SSIM: %.4f\n", ssim);
// 保存结果
image_save_raw("denoised_classic.raw", denoised_classic);
image_free(denoised_classic);
}
// 测试贝叶斯NLM
printf("\n2. Bayesian NLM:\n");
start = clock();
Image* denoised_bayesian = bnlm_bayesian_denoise(noisy, params);
end = clock();
if (denoised_bayesian) {
float psnr = compute_psnr(original, denoised_bayesian);
float ssim = compute_ssim(original, denoised_bayesian);
printf(" Time: %.2f seconds\n", (double)(end - start) / CLOCKS_PER_SEC);
printf(" PSNR: %.2f dB\n", psnr);
printf(" SSIM: %.4f\n", ssim);
image_save_raw("denoised_bayesian.raw", denoised_bayesian);
image_free(denoised_bayesian);
}
// 测试快速BNLM
printf("\n3. Fast BNLM:\n");
params.use_fast = 1;
start = clock();
Image* denoised_fast = bnlm_fast_denoise(noisy, params);
end = clock();
if (denoised_fast) {
float psnr = compute_psnr(original, denoised_fast);
float ssim = compute_ssim(original, denoised_fast);
printf(" Time: %.2f seconds\n", (double)(end - start) / CLOCKS_PER_SEC);
printf(" PSNR: %.2f dB\n", psnr);
printf(" SSIM: %.4f\n", ssim);
image_save_raw("denoised_fast.raw", denoised_fast);
image_free(denoised_fast);
}
// 测试多尺度BNLM
printf("\n4. Multi-scale BNLM:\n");
start = clock();
Image* denoised_multiscale = bnlm_multiscale_denoise(noisy, params);
end = clock();
if (denoised_multiscale) {
float psnr = compute_psnr(original, denoised_multiscale);
float ssim = compute_ssim(original, denoised_multiscale);
printf(" Time: %.2f seconds\n", (double)(end - start) / CLOCKS_PER_SEC);
printf(" PSNR: %.2f dB\n", psnr);
printf(" SSIM: %.4f\n", ssim);
image_save_raw("denoised_multiscale.raw", denoised_multiscale);
image_free(denoised_multiscale);
}
}
int main(int argc, char* argv[]) {
if (argc < 4) {
printf("Usage: %s <input_image.raw> <width> <height> [noise_sigma]\n", argv[0]);
printf("Example: %s input.raw 512 512 25.0\n", argv[0]);
return 1;
}
const char* filename = argv[1];
int width = atoi(argv[2]);
int height = atoi(argv[3]);
float noise_sigma = 25.0f;
if (argc > 4) {
noise_sigma = atof(argv[4]);
}
printf("Loading image: %s (%dx%d)\n", filename, width, height);
// 加载图像
Image* original = image_load_raw(filename, width, height, 1);
if (!original) {
printf("Failed to load image\n");
return 1;
}
printf("Original image loaded successfully\n");
// 添加噪声
printf("Adding Gaussian noise (sigma=%.1f)\n", noise_sigma);
Image* noisy = add_gaussian_noise(original, noise_sigma);
if (!noisy) {
printf("Failed to add noise\n");
image_free(original);
return 1;
}
// 保存噪声图像
image_save_raw("noisy_image.raw", noisy);
// 计算噪声图像的PSNR
float noisy_psnr = compute_psnr(original, noisy);
printf("Noisy image PSNR: %.2f dB\n", noisy_psnr);
// 测试不同算法
test_algorithms(original, noisy);
// 清理
image_free(original);
image_free(noisy);
printf("\nDone!\n");
return 0;
}
参考代码 贝叶斯非局部均值降噪c代码 www.youwenfan.com/contentcsv/71717.html
九、Makefile编译配置
makefile
# Makefile for BNLM Denoising
CC = gcc
CFLAGS = -O3 -march=native -fopenmp -Wall -Wextra -std=c99
LDFLAGS = -lm -fopenmp
TARGET = bnlm_denoise
SOURCES = main.c bnlm_core.c bnlm_basic.c bnlm_bayesian.c bnlm_fast.c \
bnlm_multiscale.c bnlm_evaluate.c
HEADERS = bnlm.h
OBJECTS = $(SOURCES:.c=.o)
# 默认目标
all: $(TARGET)
# 链接可执行文件
$(TARGET): $(OBJECTS)
$(CC) $(OBJECTS) -o $@ $(LDFLAGS)
# 编译源文件
%.o: %.c $(HEADERS)
$(CC) $(CFLAGS) -c $< -o $@
# 调试版本
debug: CFLAGS += -g -DDEBUG
debug: $(TARGET)
# 性能分析版本
profile: CFLAGS += -pg
profile: LDFLAGS += -pg
profile: $(TARGET)
# 清理
clean:
rm -f $(OBJECTS) $(TARGET) *.raw
# 运行测试
run: $(TARGET)
./$(TARGET) test.raw 512 512 25.0
# 依赖关系
main.o: bnlm.h
bnlm_core.o: bnlm.h
bnlm_basic.o: bnlm.h
bnlm_bayesian.o: bnlm.h
bnlm_fast.o: bnlm.h
bnlm_multiscale.o: bnlm.h
bnlm_evaluate.o: bnlm.h
十、使用说明
10.1 编译和运行
bash
# 编译
make
# 运行
./bnlm_denoise input.raw 512 512 25.0
# 参数说明:
# 1. 输入RAW图像文件
# 2. 图像宽度
# 3. 图像高度
# 4. 噪声标准差(可选,默认25.0)
10.2 输入输出格式
- 输入:RAW灰度图像,8位或16位
- 输出:处理后的RAW图像
- 支持图像尺寸:任意
- 支持数据类型:浮点数
10.3 参数调优建议
| 参数 | 建议值 | 说明 |
|---|---|---|
| patch_size | 5-9 | 块大小,奇数 |
| search_window | 11-21 | 搜索窗口大小,奇数 |
| h_param | 10-20 | 滤波参数,噪声标准差的0.4-0.8倍 |
| 噪声类型 | 高斯噪声 | 算法针对高斯噪声优化 |
10.4 性能优化
- 使用OpenMP并行加速
- 使用积分图像加速块距离计算
- PCA降维减少计算量
- 多尺度处理提高质量
- 使用快速算法(近似最近邻搜索)