C语言处理rppg信号,计算心率

主要流程为:

1.对rppg信号进行滤波,用FIR滤波器进行滤波,对应函数为:bpfilter

2.对滤波后信号利用fftw库进行傅里叶变换并求功率谱密度

3.利用功率谱密度估计心率,呼吸率并且利用峰值检测法计算HRV(当前算法有问题,不准)

后续发现问题会继续更新

文件路径示意如下:

---rPPGc

        ----fftw

        ----tool

                ----physiology_postprocess.c

                ----physiology_postprocess.h

        ----main.c

        ----CMakeLists.txt

        

main.c

可以按照文件夹读取或者直接读取文件

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <math.h>
#include <fftw3.h>
#include <physiology_postprocess.h>
#include <dirent.h>

void calculateByFile(const char* filename,float* ans);
char** readLinesFromFile(const char* filename, int* numLines);
float CalculateMean(const float* array, int size);
float CalculateStandardDeviation(const float* array, int size);
void calculateByDir(const char* dirname);
void calculateWhole(float* finput,int numLines,float fs,int segment,float* ans);
void calculateSegment(float* finput,int numLines,float fs,int segment,float* ans);
void calculateAsPython(float* finput,int numLines,float fs,int segment,float* ans);

int main() {
    char* folderPath = "rppg_pure";
    calculateByDir(folderPath);
    
    // char* filepath = "0103ecg.txt";
    // float* ans = (float*)malloc(3*sizeof(float));
    // calculateByFile(filepath,ans);
    // printf("%s\n",filepath);
    return 0;
    
}

void calculateByDir(const char* dirname){
    char* folderPath = "/mnt/sn750_1/guoyunsheng/rppg/iai-PhysNet-rPPG/rppg_pure";
    // 打开目标文件夹
    DIR *dir;
    struct dirent *entry;
    // 打开文件夹
    dir = opendir(folderPath);

    if (dir == NULL) {
        perror("无法打开文件夹");
        return 1;
    }
    // 读取文件夹中的文件名称并组成绝对路径
    while ((entry = readdir(dir))) {
        if (entry->d_type == DT_REG) { // 仅处理普通文件
            char filepath[1000];
            snprintf(filepath, sizeof(filepath), "%s/%s", folderPath, entry->d_name);
            float* ans = (float*)malloc(3*sizeof(float));
            calculateByFile(filepath,ans);
            // printf("%s|%f|%f\n",entry->d_name,ans[0],ans[1]);
            // printf("%s\n",entry->d_name);
        }
    }
    // 关闭文件夹
    closedir(dir);
}

void calculateWhole(float* finput,int numLines,float fs,int segment,float* ans){
    // printf("Calculate By whole\n");
    //全体处理之后输入到算法中计算
    float* longinput = (float*)malloc(numLines*sizeof(float));
    float meanLong = CalculateMean(finput,numLines);
    float stdlong = CalculateStandardDeviation(finput,numLines);
    float minlong = 100,maxlong = -100;
    //归一化处理
    for(int i = 0;i < numLines;i++){
        longinput[i] = (finput[i] - meanLong)/stdlong;
        if(longinput[i] > maxlong) maxlong = longinput[i];
        if(longinput[i] < minlong) minlong = longinput[i];
    }
    //归一化处理
    for(int i = 0;i < numLines;i++){
        longinput[i] = (longinput[i] - minlong)/(maxlong-minlong);
    }
    //将计算的心率,呼吸率,心率变异性通过ans返回
    CalculatePhysiologyRate(longinput,numLines,segment,fs,ans);
    printf("HR:%f,HRV:%f,BR:%f\n",ans[0],ans[2],ans[1]);
    free(longinput);
}

void calculateSegment(float* finput,int numLines,float fs,int segment,float* ans){
    //input划分单个segment进行计算
    // printf("Calculate By segment\n");
    float* seginput = (float*)malloc(segment*sizeof(float));
    int segnum = numLines/segment;
    for(int i = 0;i < segnum;i++){
        for(int j = 0;j < segment;j++){
            seginput[j] = finput[j+i*segment];
        }
        float meanSeg = CalculateMean(seginput,segment);
        float stdSeg = CalculateStandardDeviation(seginput,segment);
        float segmin = 100.0;
        float segmax = -100.0;
        //归一化处理
        for(int j = 0;j < segment;j++){
            seginput[j] = (seginput[j]-meanSeg)/stdSeg;
            if(seginput[j] - segmax > 0){
                segmax = seginput[j];
            } 
            if(seginput[j] < segmin){
                segmin = seginput[j];
            } 
        }
        //printf("segmax:%f,segmin:%f\n",segmax,segmin);
        for(int j = 0;j < segment;j++){
            seginput[j] = (seginput[j]-segmin)/(segmax-segmin);
        }
        int window_size = 100;
        CalculatePhysiologyRate(seginput,segment,window_size,fs,ans);
        // printf("HR:%f\nHRV:%f\nBR:%f\n",ans[0],ans[2],ans[1]);
        printf("HR:%f,BR:%f\n",ans[0],ans[1]);
    }
    free(seginput);
}

void calculateAsPython(float* finput,int numLines,float fs,int segment,float* ans){
    //input划分单个segment进行计算
    // printf("Calculate By segment\n");
    int window_size = 100;
    float* seginput = (float*)malloc(segment*sizeof(float));
    float* window_input = (float*)malloc(window_size*sizeof(float));
    int segnum = numLines/segment;
    int window_len = numLines/window_size;
    int window_per_seg = segment / window_len;
    for(int i = 0;i < segnum;i++){
        int startindex = i * segment;
        float segmin = 100.0;
        float segmax = -100.0;
        for(int j = 0;j < window_per_seg;j++){
            //第j个窗口
            for(int k = 0;k < window_len;k++){
                window_input[k] = finput[startindex+j*window_len+k];
            }
            float mean_window = CalculateMean(window_input,window_len);
            float std_window = CalculateStandardDeviation(window_input,window_len);
            //均值方差归一化
            for(int k = 0;k < window_len;k++){
                window_input[k] = (window_input[k]-mean_window)/std_window;
                //归一化后的结果放到seginput中
                seginput[j*window_len+k] = window_input[k];
                if(window_input[k] - segmax > 0){
                    segmax = window_input[k];
                } 
                if(window_input[k] < segmin){
                    segmin = window_input[k];
                }
            }
        }
        //将所有的window组合起来形成一个segment计算最大最小值归一化
        for(int j = 0;j < segment;j++){
            seginput[j] = (seginput[j]-segmin)/(segmax-segmin);
        }
        CalculatePhysiologyRate(seginput,segment,window_size,fs,ans);
        // printf("HR:%f\nHRV:%f\nBR:%f\n",ans[0],ans[2],ans[1]);
        printf("%f|%f\n",ans[0],ans[1]);
    }
    free(seginput);
    free(window_input);
}

void calculateByFile(const char* filename,float* ans){
    int numLines;
    char** input = readLinesFromFile(filename, &numLines);
    if(numLines == 0){
        printf("file empty");
        return;
    }
    //printf("NumLines:%d\n",numLines);
    if (input == NULL) {
        printf("line null");
        return;
    }
    //转换成浮点型数据
    float* finput = (float*)malloc(numLines*sizeof(float));
    for(int i = 0;i < numLines;i++){
        float value;
        if (sscanf(input[i], "%f", &value) == 1) {
            finput[i] = value;
        } else {
            // 处理解析失败的情况,例如可以赋予默认值
            finput[i] = 0.0;
        }
    }
    float fs = 30;
    //分段计算心率
    int segment = 1000;
    // calculateWhole(finput,numLines,fs,segment,ans);
    // calculateSegment(finput,numLines,fs,segment,ans);
    calculateAsPython(finput,numLines,fs,segment,ans);
    free(finput);
    free(input);
}

//计算均值
float CalculateMean(const float* array, int size) {
    float sum = 0.0;
    for (int i = 0; i < size; i++) {
        sum += array[i];
    }
    return sum / size;
}
//计算方差
float CalculateStandardDeviation(const float* array, int size) {
    float mean = CalculateMean(array, size);
    float sumOfSquares = 0.0;

    for (int i = 0; i < size; i++) {
        float diff = array[i] - mean;
        sumOfSquares += diff * diff;
    }

    float variance = sumOfSquares / size;
    float stdDeviation = sqrt(variance);

    return stdDeviation;
}


//读取文件中的内容
/***
 * 将filename中的数据写入到numLines中
 * 参数说明
 * filename:读取的文件名称
 * numLine:读取的行数
**/
char** readLinesFromFile(const char* filename, int* numLines) {
    FILE* file = fopen(filename, "r");
    if (file == NULL) {
        printf("无法打开文件\n");
        return NULL;
    }
    
    char** lines = NULL;
    int capacity = 0;
    int size = 0;
    
    char buffer[256];
    while (fgets(buffer, sizeof(buffer), file) != NULL) {
        char* line = malloc(strlen(buffer) + 1);
        strcpy(line, buffer);
        
        if (size >= capacity) {
            capacity += 10;
            lines = realloc(lines, capacity * sizeof(char*));
        }
        lines[size] = line;
        size++;
    }
    
    fclose(file);
    
    *numLines = size;
    return lines;
}

calclulate_HR接受一个文件名作为输入,按行读取该文件中的rppg信号值,返回计算好的心率。

physiology_postprocess.h头文件如下:

#ifndef PHYSIOLOGYPOSTPROCESS
#define PHYSIOLOGYPOSTPROCESS

/***
 * 功能:将input划分为多个segment大小的段分别计算心率和呼吸率,根据多段的心率计算心率变异性并返回
 * input:输入原始的rppg信号
 * numLine:rppg信号的长度
 * segment:需要分割的每个段的大小
 * fs:采样频率
 * ans:用来返回计算的结果,ans[0]:心率,ans[1]呼吸率,ans[2]心率变异性
*/
void CalculatePhysiologyRate(const float* input,const int numLines,const int segment,const float fs,float* ans);

#endif

tool.c文件如下:

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <fftw3.h>

//写入文件
void writeFile(const char* filename,float* numbers,int length){

    FILE* file = fopen(filename, "w");
    if (file == NULL) {
        printf("无法打开文件。\n");
    }

    for (int i = 0; i < length; i++) {
        float x = numbers[i];
        char a[50];
        sprintf(a,"%f\n", x);
        fprintf(file, "%s", a);
    }

    fclose(file);
    //printf("write to %s already done.\n",filename);

}

static float bessel0(float x)
{
    int i;
    float d;
    float y;
    float d2;
    float sum;
    y = x / 2.0;
    d = 1.0;
    sum = 1.0;
    for (i=1; i<=25; i++)
    {
        d = d * y / i;
        d2 = d * d;
        sum = sum + d2;
        if (d2 < sum*(1.0e-8))
        {
            break;
        }
    }
    return(sum);
}

static float kaiser(int i, int n, float beta)
{
    float a;
    float w;
    float a2;
    float b1;
    float b2;
    float beta1;

    b1 = bessel0(beta);
    a = 2.0 * i / (float)(n - 1) - 1.0;
    a2 = a * a;
    beta1 = beta * sqrt(1.0 - a2);
    b2 = bessel0(beta1);
    w = b2 / b1;
    return(w);
}

//n:窗口长度 type:选择窗函数的类型 beta:生成凯塞窗的系数
static float window(int type, int n, int i, float beta)
{
    int k;
    float pi;
    float w;
    pi = 4.0 * atan(1.0);  //pi=PI;
    w = 1.0;

    switch (type)
    {
    case 1:
    {
        w = 1.0;  //矩形窗
        break;
    }
    case 2:
    {
        k = (n - 2) / 10;
        if (i <= k)
        {
            w = 0.5 * (1.0 - cos(i * pi / (k + 1)));  //图基窗
        }
        if (i > n-k-2)
        {
            w = 0.5 * (1.0 - cos((n - i - 1) * pi / (k + 1)));
        }
        break;
    }
    case 3:
    {
        w = 1.0 - fabs(1.0 - 2 * i / (n - 1.0));//三角窗
        break;
    }
    case 4:
    {
        w = 0.5 * (1.0 - cos( 2 * i * pi / (n - 1)));//汉宁窗
        break;
    }
    case 5:
    {
        w = 0.54 - 0.46 * cos(2 * i * pi / (n - 1));//海明窗
        break;
    }
    case 6:
    {
        w = 0.42 - 0.5 * cos(2 * i * pi / (n - 1)) + 0.08 * cos(4 * i * pi / (n - 1));//布莱克曼窗
        break;
    }
    case 7:
    {
        w = kaiser(i, n, beta);//凯塞窗
        break;
    }
    }
    return(w);
}

/***
 *  参数说明
    n:滤波器的阶数
    band:滤波器的类型,取值1-4,分别为低通、带通、带阻、高通滤波器
    wn:窗函数的类型,取值1-7,分别对应矩形窗、图基窗、三角窗、汉宁窗、海明窗、布拉克曼窗和凯塞窗
    fs:采样频率
    h:存放滤波器的系数
    kaiser:beta值
    fln:带通下边界频率
    fhn:带通上边界频率 
***/

void firwin(int n, int band, int wn, float fs, float *h, float kaiser, float fln, float fhn)
{
    int i;
    int n2;
    int mid;
    float s;
    float pi;
    float wc1;
    float wc2;
    float beta;
    float delay;
    beta = kaiser;
    pi = 4.0 * atan(1.0);   //pi=PI;
    
    if ((n % 2) == 0)/*如果阶数n是偶数*/
    {
        n2 = (n / 2) - 1;
        mid = 1;
    }
    else
    {
        n2 = n / 2;//n是奇数,则窗口长度为偶数
        mid = 0;
    }
    
    delay = n / 2.0;
    wc1 = 2 * pi * fln;
    wc2 = 2 * pi * fhn;
    
    switch (band)
    {
    case 1:
    {
        for (i=0; i<=n2; ++i)
        {
            s = i - delay;
            h[i] = (sin(wc1 * s / fs) / (pi * s)) * window(wn, n+1, i, beta);//低通,窗口长度=阶数+1,故为n+1
            h[n - i] = h[i];
        }
        if (mid == 1)
        {
            h[n / 2] = wc1 / pi;//n为偶数时,修正中间值系数
        }
        break;
    }
    case 2:
    {
        for (i=0; i<=n2; i++)
        {
            s = i - delay;
            h[i] = (sin(wc2 * s / fs) - sin(wc1 * s / fs)) / (pi * s);//带通
            h[i] = h[i] * window(wn, n+1, i, beta);
            h[n-i] = h[i];
        }
        if (mid == 1)
        {
            h[n / 2] = (wc2 - wc1) / pi;
        }
        break;
    }
    case 3:
    {
        for (i=0; i<=n2; ++i)
        {
            s = i - delay;
            h[i] = (sin(wc1 * s / fs) + sin(pi * s) - sin(wc2 * s / fs)) / (pi * s);//带阻
            h[i] = h[i] * window(wn, n+1, i, beta);
            h[n - i] = h[i];
        }
        if (mid == 1)
        {
            h[n / 2] = (wc1 + pi - wc2) / pi;
        }
        break;
    }
    case 4:
    {
        for (i=0; i<=n2; i++)
        {
            s = i - delay;
            h[i] = (sin(pi * s) - sin(wc1 * s / fs)) / (pi * s);//高通
            h[i] = h[i] * window(wn, n+1, i, beta);
            h[n-i] = h[i];
        }
        if (mid == 1)
        {
            h[n / 2] = 1.0 - wc1 / pi;
        }
        break;
    }
    }
}



//bp滤波器
void BpFilter(const float* input, float* filt,const int num,const float fs){
    //滤波器的开始频率
    float minfq = (0.8 * 2) / fs;
    //滤波器的截止频率
    float maxfq = (3 * 2) / fs;
    //n为阶数
    int n = num / 10;
    //动态分配内存,h为最后生成的滤波器系数
    float* h = (float *)malloc(sizeof(float) * (n+1));
    
    if(h == NULL){
        perror("bpfilter realloc memory error");
    }
    firwin(n, 2, 5, fs, h, 0,  minfq,  maxfq);
    //FIR滤波器处理过程
    for(int i = 0; i < num; i++){
        filt[0] = 0;
        for(int k = 0;k < n+1; k++){
            if(i - k >= 0)
                filt[i] += input[i-k]*h[k];
        }
    }
    // printf("-----------parameter-----------------\n");
    // for(int i = 0;i < n+1;i++){
    //     printf("%f\n",h[i]);
    // }
    free(h);
}

//计算功率谱密度
void CalSpectrum(float *filt,float *powerSpectrum,int numLines, int fs,int N){
    //filt = readLinesFromFile("s2f.txt", &numLines);
    //printf("-----------fft start-----------------\n");
    N = numLines*10;
    //傅里叶变换
    fftw_complex *in,*out;
    out = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);
    in = (fftw_complex*) fftw_malloc(sizeof(fftw_complex)*N);
    fftw_plan my_plan;
    //确认傅里叶变换输入
    for (int i = 0;i < numLines;i++){
        in[i][0] = filt[i];
        in[i][1] = 0.0;
        //printf("%f\n",filt[i]);
    }
    //补充不足的部分为0
    for(int i = numLines;i < N;i++){
        in[i][0] = 0.0;
        in[i][1] = 0.0;
    }
    my_plan = fftw_plan_dft_1d(N, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
    fftw_execute(my_plan);
    //printf("-----------fft end-----------------\n");
    //printf("-----------calculate spectrum start-----------------\n");
    //傅里叶变换后,取幅值平方除以N,以此作为对真实功率谱的估计
    //计算功率谱密度
    
    float fremax = 0.0;
    for (int i = 0; i < N; i++) {
        float real = out[i][0];
        float imag = out[i][1];
        //printf("real:%f,imag:%f",real,imag);
        //printf("%d\n",i);
        powerSpectrum[i] = (real * real + imag * imag) / (N*fs);
        //printf("%f\n",powerSpectrum[i]);
        if(fremax < powerSpectrum[i]){
            fremax = powerSpectrum[i];
        }
        //printf("%f\n",powerSpectrum[i]);
    }
    //printf("-----------calculate spectrum end-----------------\n");
    //释放资源
    fftw_destroy_plan(my_plan);
    fftw_free(in); 
    fftw_free(out);
}
/***
 * welch法计算功率谱密度
 * signal为信号序列
 * signal_length为需要计算的信号总长度
 * segment_length为子段长度
 * len为信号原本长度
 * powerSpectrum保留记录出的计算功率谱密度
 * overlap重叠比例
*/
float* WelchPsd(float* signal, int signal_length, int segment_length,int len,int overlap) {
    int num_segments = signal_length / (segment_length * (1 - overlap));
    int i, j;
    // 分配FFTW输入和输出数组
    fftw_complex* in = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * segment_length);
    fftw_complex* out = (fftw_complex*)fftw_malloc(sizeof(fftw_complex) * segment_length);

    // 创建FFTW计划
    fftw_plan plan;

    // 初始化FFTW计划
    plan = fftw_plan_dft_1d(segment_length, in, out, FFTW_FORWARD, FFTW_ESTIMATE);
    //将signal补充到segment_length的长度
    float* temp = (float*)realloc(signal, signal_length * sizeof(float));
    //将powerSpectrum补充到足够的长度
    float* powerSpectrum = (float*)malloc((segment_length /2) * sizeof(float));

    for(int i = len;i < signal_length;i++){
        temp[i] = 0.0;
    }
    int k = 0;
    printf("num_segments:%d\n",num_segments);
    // 计算每个子段的功率谱密度
    for (i = 0; i < num_segments; i++) {
        int start = i * segment_length * (1 - overlap);

        // 将信号加载到输入数组
        for (j = 0; j < segment_length; j++) {
            in[j][0] = temp[start + j];
            in[j][1] = 0.0;
        }

        // 执行FFT
        fftw_execute(plan);

        // 计算功率谱密度
        float* psd = (float*) malloc(sizeof(float) * (segment_length / 2));
        for (j = 0; j < segment_length / 2; j++) {
            float real = out[j][0];
            float imag = out[j][1];
            psd[j] = (real * real + imag * imag) / segment_length;
            //记录在powerSpectrum中
            powerSpectrum[k] = psd[j];
            k++;
        }
        powerSpectrum = realloc(powerSpectrum, ((i+2) * segment_length / 2) * sizeof(float));
        // 打印功率谱密度
        printf("Segment %d PSD:\n", i + 1);
        for (j = 0; j < segment_length / 2; j++) {
            printf("%f\n", psd[j]);
        }
        printf("\n");
    }
    printf("powerSpectrum length:%d\n",k);

    // 释放内存和FFTW计划
    fftw_free(in);
    fftw_free(out);
    fftw_destroy_plan(plan);
    return powerSpectrum;
}

float CalculateMean(const float* array, int size) {
    float sum = 0.0;
    for (int i = 0; i < size; i++) {
        sum += array[i];
    }
    return sum / size;
}

float CalculateStandardDeviation(const float* array, int size) {
    float mean = CalculateMean(array, size);
    float sumOfSquares = 0.0;

    for (int i = 0; i < size; i++) {
        float diff = array[i] - mean;
        sumOfSquares += diff * diff;
    }

    float variance = sumOfSquares / size;
    float stdDeviation = sqrt(variance);

    return stdDeviation;
}

float CalculateMeanSquaredDeviation(const float* array, int size) {
    float mean = CalculateMean(array, size);
    float sumOfSquares = 0.0;

    for (int i = 0; i < size; i++) {
        float diff = array[i] - mean;
        sumOfSquares += diff * diff;
    }

    float msd = sumOfSquares / size;

    return msd;
}
/***
 * 将功率谱滤波计算心率
 * powerSpectrum:是功率谱密度
 * signal_length:功率谱密度的长度
 * fs:采样频率
 * lowfre-highfre:频率范围
*/
float FiltSpectrum(const float *powerSpectrum,float signal_length,int fs,float lowfre,float highfre){
    //有效功率谱范围
    int realfre_count = (highfre-lowfre)/(fs/signal_length)+3;
    //printf("realfre_count:%d\n",realfre_count);
    float *real_power = (float*)malloc(sizeof(float)*realfre_count);
    //初始化
    for(int i = 0;i < realfre_count;i++){
        real_power[i] = 0.0;
    }
    float *real_frequency = (float*)malloc(sizeof(float)*realfre_count);
    for(int i = 0;i < realfre_count;i++){
        real_frequency[i] = 0.0;
    }
    //计算频率轴的坐标
    float *frequecy = (float*)malloc(sizeof(float)*signal_length/2);
    int j = 0;
    //记录功率谱的最大值,以及最大值对应的index
    float powermax = 0.0;
    int maxindex = 0;
    for(int i = 0;i < signal_length/2;i++){
        frequecy[i] = i * fs / signal_length;
        if(frequecy[i] >= lowfre && frequecy[i] <= highfre){
            //printf("frequecy%d:%f\n",i,frequecy[i]);
            //对功率谱进行分析,首先排除不在指定范围内的数据,正常心率范围为lowfre-highfre,按照频率取功率谱
            //real_power[j] = powerSpectrum[i];
            if(powerSpectrum[i] > powermax){
                powermax = powerSpectrum[i];
                maxindex = j; 
            }
            real_frequency[j] = frequecy[i];
            //printf("real_frequency%d:%f\n",j,real_frequency[j]);
            j++;
        }
    }
    float hr = real_frequency[maxindex];
    //printf("max index:%d\nmax signal power:%f\nfrequency:%f\n",maxindex,real_power[maxindex],real_frequency[maxindex]);
    //printf("Heart rate:%f\n",hr);
    // char *frequencyfile = "frequency.txt";
    // writeFile(frequencyfile,frequecy,signal_length/2);
    // char *real_frequency_file = "real_frequency.txt";
    // writeFile(real_frequency_file,real_frequency,realfre_count);
    free(real_frequency);
    //free(real_power);
    return hr;
}
/***
 * input:输入的rppg信号
 * fs:采集的频率
 * numLines:rppg信号的长度
 * rate:记录心率和呼吸率
*/
void CalcHrBr(const float* input,const float fs,const float numLines,float* rate){
    //filt中存储经过滤波的数据
    float* filt = (float*)malloc(sizeof(float) * numLines);
    //初始化
    for(int i = 0;i < numLines;i++){
        filt[i] = 0.0;
    }
    BpFilter(input,filt,numLines, fs);
    //writeFile("filt.txt",filt,numLines);
    float signal_length = numLines*10;
    int N = numLines;
    float* powerSpectrum = (float*) malloc(sizeof(float) * numLines *10);
    for(int i = 0;i < numLines*10;i++){
        powerSpectrum[i] = 0.0;
    }
    CalSpectrum(filt, powerSpectrum, numLines,fs,&N);
    //计算心率
    float hr_lowfre = 1,hr_highfre = 2.5;
    float HR = FiltSpectrum(powerSpectrum,signal_length,fs,hr_lowfre,hr_highfre) * 60;
    //计算呼吸率
    float br_lowfre = 0.2,br_highfre = 0.3;
    float BR = FiltSpectrum(powerSpectrum,signal_length,fs,br_lowfre,br_highfre) * 60;
    // 释放内存时需要释放每行字符串的内存
    free(filt);
    free(powerSpectrum);
    rate[0] = HR;
    rate[1] = BR;
}

// 寻找数组最小值的下标
int argmin(int* index, int index_len)
{
	int min_index = 0;
	int min = index[0];
	for (int i = 1; i < index_len; i++)
	{
		if (index[i] < min)
		{
			min = index[i];
			min_index = i;
		}
	}
	return min_index;
}

//寻找极值点函数
// data是存放数据的数组
//index是存放峰值点下标的数组
//len_index是峰值个数,即index数组长度
void AMPD(float* data,int* index,int *len_index,int size)
{
	int* p_data = (int*)malloc(sizeof(int) * size); //size可以最大为数组长度
	int* arr_rowsum = (int*)malloc(sizeof(int) * size);
	int min_index, max_window_length;
	for (int i = 0; i < size; i++)
	{
		p_data[i] = 0;
	}
	for (int k = 1; k <= size / 2 + 1; k++)
	{
		int row_sum = 0;
		for (int i = k; i <= size - k; i++)
		{
			if ((data[i] > data[i - k]) && (data[i] > data[i + k]))
				row_sum -= 1;
		}
		*(arr_rowsum + k - 1) = row_sum;
	}
	/*for (int i = 0; i < size/2; i++)
	{
		printf("%d\n", arr_rowsum[i]);
	}*/
	min_index = argmin(arr_rowsum, size/2); //此处为最大的窗口
	//printf("%d\n", min_index);
	max_window_length = min_index;
	for (int k = 1; k < max_window_length + 1;k++)
	{
		for (int i = 1; i < size - k; i++)
		{
			if ((data[i] > data[i - k]) && (data[i] > data[i + k]))
				p_data[i] += 1;
		}
	}
	for (int i_find = 0; i_find < size; i_find++)
	{
		if (p_data[i_find] == max_window_length)
		{
			index[*len_index] = i_find;
			(*len_index) += 1;
		}
	}
	free(p_data);
	free(arr_rowsum);
}


/**
 * 函数返回HRV的值,同时将峰值的时间点记录在output指针中
 * 通过计算rppg信号中的峰值计算HRV
 * input为输入的rppg信号
 * numLines为rppg信号长度
 * fps为采样频率
*/
float FindPeaksCalcHRV(const float* input,int numLines, int fps) {
    int* result = (int*)malloc(numLines * sizeof(int));
    int resultCount = 0;
    float* filt = (float*)malloc(sizeof(float) * numLines);
    //初始化
    for(int i = 0;i < numLines;i++){
        filt[i] = 0.0;
    }
    BpFilter(input,filt,numLines, fps);
    //writeFile("hr62br14hrv54.txt",input,numLines);
    AMPD(filt,result,&resultCount,numLines);
    printf("resultCount:%d\n",resultCount);
    //计算每个波峰之间的差值
    float* de_peak = (float*)malloc(numLines * sizeof(float));
    for(int i = 0;i < resultCount-1;i++){
        de_peak[i] = (result[i+1] - result[i])/fps;
    }
    //计算差值的均值
    float peakmean = CalculateMean(de_peak, resultCount-1);
    float hrv = CalculateStandardDeviation(de_peak,resultCount-1);
    float hr = 60 / (peakmean);
    printf("Peak Calculate HR:%f\n",hr);
    printf("Peak Calculate HRV:%f\n",hrv);
    return hr;
}

/***
 * 功能:将input划分为多个segment大小的段分别计算心率和呼吸率,根据多段的心率计算心率变异性并返回
 * input:输入原始的rppg信号
 * numLine:rppg信号的长度
 * segment:需要分割的每个段的大小
 * fs:采样频率
 * ans:用来返回计算的结果,ans[0]:心率,ans[1]呼吸率,ans[2]心率变异性
*/
void CalculatePhysiologyRate(const float* input,const int numLines,const int segment,const float fs,float* ans){
    int numseg = numLines/segment;
    float* HRList = (float*) malloc(numseg*sizeof(float));
    float* BRList = (float*) malloc(numseg*sizeof(float));
    float* HR_BR = (float*) malloc(2*sizeof(float));
    float* newarray = (float*)malloc(segment * sizeof(float));
    for(int i = 0;i < numseg;i++){
        for(int j = 0;j < segment;j++){
            int index = i*segment + j;
            newarray[j] = input[index];
        }
        //对newarray中的数据求心率和呼吸率
        // if(i == 0){
        //     writeFile("orig_whole_0.txt",newarray,segment);
        //     CalcHrBr(newarray,fs,segment,HR_BR);
        // }
        CalcHrBr(newarray,fs,segment,HR_BR);
        //printf("%f\n",HR_BR[0]);
        HRList[i] = HR_BR[0];
        BRList[i] = HR_BR[1];
    }
    float meanHR = CalculateMean(HRList,numseg);
    float meanBR = CalculateMean(BRList,numseg);
    //float std = CalculateMeanSquaredDeviation(HRList,numseg);
    float std = CalculateStandardDeviation(HRList,numseg);
    ans[0] = meanHR;
    ans[1] = meanBR;
    ans[2] = std;
    // printf("---------------------------\n");
    // float HRV = FindPeaksCalcHRV(input,numLines,fs);
    // printf("---------------------------\n");
    free(newarray);
    free(HR_BR);
    free(HRList);
    free(BRList);
}


CMakeList如下:

cmake_minimum_required(VERSION 3.16)
project(rPPG)

set(COMPILE_CLANG OFF) 

if(COMPILE_FILE)
    message("generate by clang")
    set (CMAKE_C_COMPILER "/mnt/wdssd0/xuao/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi22-clang")
    set (CMAKE_CXX_COMPILER "/mnt/wdssd0/xuao/android-ndk-r25c/toolchains/llvm/prebuilt/linux-x86_64/bin/armv7a-linux-androideabi22-clang++")
endif()

SET(CMAKE_BUILD_TYPE "Debug")  # 定义编译类型
SET(CMAKE_CXX_FLAGS_DEBUG "$ENV{CXXFLAGS} -O0 -Wall -g2 -ggdb -fpermissive -pthread") # 定义Debug编译参数
SET(CMAKE_CXX_FLAGS_RELEASE "$ENV{CXXFLAGS} -O3 -Wall") # 定义Release编译参数

set(FFTW_PATH ${PROJECT_SOURCE_DIR}/fftw-3.3.10/build)
set(FFTWINCLUDE ${FFTW_PATH}/include)

# 这一块添加fftw的库
set(FFTW_LIB ${FFTW_PATH}/lib/libfftw3.a)

#添加静态库
add_library(PhysiologyPostprocess SHARED ${PROJECT_SOURCE_DIR}/tool/physiology_postprocess.c)

# 添加包含头文件的目录
target_include_directories(PhysiologyPostprocess PUBLIC ${PROJECT_SOURCE_DIR}/tool)

add_executable(rPPG rPPG.c)

target_include_directories(rPPG PRIVATE ${FFTWINCLUDE})
target_link_libraries(rPPG PhysiologyPostprocess m ${FFTW_LIB} )

  • 1
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值