贝叶斯非局部均值降噪算法C语言实现

一、算法原理和头文件

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 性能优化

  1. 使用OpenMP并行加速
  2. 使用积分图像加速块距离计算
  3. PCA降维减少计算量
  4. 多尺度处理提高质量
  5. 使用快速算法(近似最近邻搜索)
相关推荐
量子炒饭大师4 小时前
【优化算法】滑动窗口的「义体化」重构 ——【滑动窗口】何为滑动窗口?滑动窗口算法的核心目的是什么?
c++·算法·重构·优化算法·双指针·滑动窗口
玖釉-4 小时前
C++ 中的 buckets 详解:从哈希桶到 unordered_map 底层原理
算法·哈希算法·散列表
计算机安禾4 小时前
【c++面向对象编程】第35篇:构造函数与异常:如何避免资源泄露?
开发语言·javascript·c++·算法·性能优化
z200509304 小时前
今日算法(二叉树剪枝)
数据结构·c++·算法·剪枝
IronMurphy4 小时前
【算法四十八】416. 分割等和子集
算法
WYH2874 小时前
C语言结构体变量和结构体指针详解:定义、访问、传参与易错点总结
c语言·开发语言·算法
我不是懒洋洋4 小时前
从零实现Transformer:从注意力机制到ChatGPT
c语言·数据结构·c++·经验分享
m0_748839494 小时前
利用C 图形界面展示MATLAB算法的高效混合编程实践
开发语言·算法·matlab
进击的荆棘4 小时前
优选算法——哈希表
c++·算法·leetcode·哈希算法·散列表